Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various bugfixes around tag processing #347

Merged
merged 2 commits into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion synse_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

__title__ = 'synse_server'
__version__ = '3.0.0-alpha.12'
__version__ = '3.0.0-alpha.13'
__api_version__ = 'v3'
__description__ = 'An API to monitor and control physical and virtual devices.'
__author__ = 'Vapor IO'
Expand Down
2 changes: 1 addition & 1 deletion synse_server/api/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ async def tags(request: Request) -> HTTPResponse:

return utils.http_json_response(
await cmd.tags(
*namespaces,
namespaces,
with_id_tags=include_ids,
),
)
Expand Down
4 changes: 2 additions & 2 deletions synse_server/api/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ async def dispatch(self, payload: Payload) -> None:
logger.info(_('websocket request cancelled'), handler=handler)
return
except Exception as e:
logger.error(_('error processing websocket request'), err=e)
logger.exception(_('error processing websocket request'), err=e)
await self.send(**error(
msg_id=payload.id,
ex=e,
Expand Down Expand Up @@ -329,7 +329,7 @@ async def handle_request_tags(self, payload: Payload) -> None:
await self.send(
id=payload.id,
event='response/tags',
data=await cmd.tags(*ns, with_id_tags=ids),
data=await cmd.tags(ns, with_id_tags=ids),
)

async def handle_request_info(self, payload: Payload) -> None:
Expand Down
13 changes: 9 additions & 4 deletions synse_server/cmd/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import asyncio
import queue
import threading
from typing import Any, AsyncIterable, Dict, List
from typing import Any, AsyncIterable, Dict, List, Union

import synse_grpc.utils
import websockets
Expand Down Expand Up @@ -47,7 +47,7 @@ def reading_to_dict(reading: api.V3Reading) -> Dict[str, Any]:
}


async def read(ns: str, tag_groups: List[List[str]]) -> List[Dict[str, Any]]:
async def read(ns: str, tag_groups: Union[List[str], List[List[str]]]) -> List[Dict[str, Any]]:
"""Generate the readings response data.

Args:
Expand Down Expand Up @@ -80,10 +80,15 @@ async def read(ns: str, tag_groups: List[List[str]]) -> List[Dict[str, Any]]:
return readings

# Otherwise, there is at least one tag group. We need to issue a read request
# for each group and collect the results of each group.
# for each group and collect the results of each group. The provided tag groups
# may take the form of a List[str] in the case of a single tag group, or a
# List[List[str]] in the case of multiple tag groups.
if all(isinstance(x, str) for x in tag_groups):
tag_groups = [tag_groups]

results = {}
for group in tag_groups:
logger.debug(_('parsing tag groups'), command='READ')
logger.debug(_('parsing tag groups'), command='READ', group=group)
# Apply the default namespace to the tags in the group which do not
# have any namespace defined.
for i, tag in enumerate(group):
Expand Down
2 changes: 1 addition & 1 deletion synse_server/cmd/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from synse_server.log import logger


async def tags(*namespaces: str, with_id_tags: bool = False) -> List[str]:
async def tags(namespaces: List[str], with_id_tags: bool = False) -> List[str]:
"""Generate the tags response data.

Args:
Expand Down
5 changes: 4 additions & 1 deletion tests/unit/api/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ def test_ok(self, synse_app):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
[],
with_id_tags=False,
)

Expand All @@ -698,6 +699,7 @@ def test_error(self, synse_app):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
[],
with_id_tags=False
)

Expand Down Expand Up @@ -731,7 +733,7 @@ def test_param_ns(self, synse_app, qparam, expected):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
*expected,
expected,
with_id_tags=False,
)

Expand Down Expand Up @@ -773,6 +775,7 @@ def test_param_ids(self, synse_app, qparam, expected):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
[],
with_id_tags=expected,
)

Expand Down
4 changes: 3 additions & 1 deletion tests/unit/api/test_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ async def test_request_tags_no_data(self):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
[],
with_id_tags=False,
)
mock_send.assert_called_once()
Expand All @@ -511,7 +512,7 @@ async def test_request_tags_data_ns(self):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
'a', 'b', 'c',
['a', 'b', 'c'],
with_id_tags=False,
)
mock_send.assert_called_once()
Expand All @@ -536,6 +537,7 @@ async def test_request_tags_data_ids(self):

mock_cmd.assert_called_once()
mock_cmd.assert_called_with(
[],
with_id_tags=True,
)
mock_send.assert_called_once()
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/cmd/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,52 @@ async def test_read_ok_tags_without_ns(mocker, simple_plugin, state_reading):
])


@pytest.mark.asyncio
async def test_read_ok_single_tag_group_without_ns(mocker, simple_plugin, state_reading):
# Mock test data
mocker.patch.dict('synse_server.plugin.PluginManager.plugins', {
'123': simple_plugin,
'456': simple_plugin,
})

mock_read = mocker.patch(
'synse_grpc.client.PluginClientV3.read',
return_value=[
state_reading,
],
)

# --- Test case -----------------------------
# Set the simple_plugin to active to start.
simple_plugin.active = True

resp = await cmd.read('default', ['foo', 'bar', 'vapor/ware'])

# Note: There are two plugins defined, so we should expect each
# plugin to return a reading. Since the fixture returns the same reading
# at the same timestamp, we take this to be the same reading and deduplicate
# it, hence we only get one reading here.
assert resp == [
{ # from state_reading fixture
'device': 'ccc',
'timestamp': '2019-04-22T13:30:00Z',
'type': 'state',
'device_type': 'led',
'value': 'on',
'unit': None,
'context': {},
},
]

assert simple_plugin.active is True

mock_read.assert_called()
mock_read.assert_has_calls([
mocker.call(tags=['default/foo', 'default/bar', 'vapor/ware']),
mocker.call(tags=['default/foo', 'default/bar', 'vapor/ware']),
])


@pytest.mark.asyncio
async def test_read_device_not_found():
with asynctest.patch('synse_server.cache.get_plugin') as mock_get:
Expand Down
14 changes: 7 additions & 7 deletions tests/unit/cmd/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async def test_tags_failed_get_cached_tags(mocker):

# --- Test case -----------------------------
with pytest.raises(ValueError):
await cmd.tags('default')
await cmd.tags(['default'])

mock_get.assert_called_once()

Expand All @@ -40,7 +40,7 @@ async def test_tags_matches_tag_with_single_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags('default')
resp = await cmd.tags(['default'])
assert resp == [
'annotation:bar',
'default/annotation:bar',
Expand Down Expand Up @@ -72,7 +72,7 @@ async def test_tags_matches_tag_with_multiple_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags('default', 'test')
resp = await cmd.tags(['default', 'test'])
assert resp == [
'annotation:bar',
'default/annotation:bar',
Expand Down Expand Up @@ -106,7 +106,7 @@ async def test_tags_matches_tag_no_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags()
resp = await cmd.tags([])
assert resp == [
'annotation:bar',
'default/annotation:bar',
Expand Down Expand Up @@ -142,7 +142,7 @@ async def test_tags_with_id_tags_all_tags_matching_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags(with_id_tags=True)
resp = await cmd.tags([], with_id_tags=True)
assert resp == [
'annotation:bar',
'default/annotation:bar',
Expand Down Expand Up @@ -181,7 +181,7 @@ async def test_tags_with_id_tags_some_tags_matching_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags('default', 'system', with_id_tags=True)
resp = await cmd.tags(['default', 'system'], with_id_tags=True)
assert resp == [
'annotation:bar',
'default/annotation:bar',
Expand Down Expand Up @@ -217,7 +217,7 @@ async def test_tags_with_id_tags_no_tags_matching_ns(mocker):
)

# --- Test case -----------------------------
resp = await cmd.tags('no-such-namespace', with_id_tags=True)
resp = await cmd.tags(['no-such-namespace'], with_id_tags=True)
assert resp == []

mock_get.assert_called_once()