Skip to content

Commit 0cebe80

Browse files
authored
Only service interfaces listed in .serviceconfig file (#368)
* Only service interfaces listed in .serviceconfig file * Fix Ubuntu errors because of incorrect capitalization of a file name * Fork v1 and v2 only measurements from loopback_measurement * Use 'in' rather than __contains__ * Use if elseif else for checking interfaces. Add a test for the unknown interface case * Use correct derived Exception classes
1 parent f0800ec commit 0cebe80

8 files changed

Lines changed: 203 additions & 20 deletions

File tree

ni_measurementlink_service/_internal/service_manager.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from ni_measurementlink_service.measurement.info import MeasurementInfo, ServiceInfo
2121

2222
_logger = logging.getLogger(__name__)
23+
_V1_INTERFACE = "ni.measurementlink.measurement.v1.MeasurementService"
24+
_V2_INTERFACE = "ni.measurementlink.measurement.v2.MeasurementService"
2325

2426

2527
class GrpcService:
@@ -85,24 +87,31 @@ def start(
8587
("grpc.max_send_message_length", -1),
8688
],
8789
)
88-
servicer_v1 = MeasurementServiceServicerV1(
89-
measurement_info,
90-
configuration_parameter_list,
91-
output_parameter_list,
92-
measure_function,
93-
)
94-
v1_measurement_service_pb2_grpc.add_MeasurementServiceServicer_to_server(
95-
servicer_v1, self.server
96-
)
97-
servicer_v2 = MeasurementServiceServicerV2(
98-
measurement_info,
99-
configuration_parameter_list,
100-
output_parameter_list,
101-
measure_function,
102-
)
103-
v2_measurement_service_pb2_grpc.add_MeasurementServiceServicer_to_server(
104-
servicer_v2, self.server
105-
)
90+
for interface in service_info.provided_interfaces:
91+
if interface == _V1_INTERFACE:
92+
servicer_v1 = MeasurementServiceServicerV1(
93+
measurement_info,
94+
configuration_parameter_list,
95+
output_parameter_list,
96+
measure_function,
97+
)
98+
v1_measurement_service_pb2_grpc.add_MeasurementServiceServicer_to_server(
99+
servicer_v1, self.server
100+
)
101+
elif interface == _V2_INTERFACE:
102+
servicer_v2 = MeasurementServiceServicerV2(
103+
measurement_info,
104+
configuration_parameter_list,
105+
output_parameter_list,
106+
measure_function,
107+
)
108+
v2_measurement_service_pb2_grpc.add_MeasurementServiceServicer_to_server(
109+
servicer_v2, self.server
110+
)
111+
else:
112+
raise ValueError(
113+
f"Unknown interface was provided in the .serviceconfig file: {interface}"
114+
)
106115
port = str(self.server.add_insecure_port("[::]:0"))
107116
self.server.start()
108117
_logger.info("Measurement service hosted on port: %s", port)

tests/integration/test_service_manager.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import grpc
55
import pytest
6+
from grpc import RpcError
67

78
from ni_measurementlink_service._internal.discovery_client import DiscoveryClient
89
from ni_measurementlink_service._internal.service_manager import GrpcService
@@ -13,7 +14,12 @@
1314
measurement_service_pb2,
1415
measurement_service_pb2_grpc,
1516
)
16-
from tests.utilities import loopback_measurement
17+
from tests.utilities import (
18+
loopback_measurement,
19+
unknown_interface_measurement,
20+
v1_only_measurement,
21+
v2_only_measurement,
22+
)
1723
from tests.utilities.fake_discovery_service import (
1824
FakeDiscoveryServiceError,
1925
FakeDiscoveryServiceStub,
@@ -72,10 +78,52 @@ def test___grpc_service_started___stop_service___service_stopped(grpc_service: G
7278

7379
grpc_service.stop()
7480

75-
with pytest.raises(Exception):
81+
with pytest.raises(RpcError):
82+
_validate_if_service_running_by_making_rpc(port_number)
83+
84+
85+
def test___grpc_service_v2_only___start_service_and_check_v1___raises_error(
86+
grpc_service: GrpcService,
87+
):
88+
port_number = grpc_service.start(
89+
v2_only_measurement.measurement_service.measurement_info,
90+
v2_only_measurement.measurement_service.service_info,
91+
v2_only_measurement.measurement_service.configuration_parameter_list,
92+
v2_only_measurement.measurement_service.output_parameter_list,
93+
v2_only_measurement.measurement_service.measure_function,
94+
)
95+
96+
with pytest.raises(RpcError):
7697
_validate_if_service_running_by_making_rpc(port_number)
7798

7899

100+
def test___grpc_service_v1_only___start_service_and_check_v1___service_hosted(
101+
grpc_service: GrpcService,
102+
):
103+
port_number = grpc_service.start(
104+
v1_only_measurement.measurement_service.measurement_info,
105+
v1_only_measurement.measurement_service.service_info,
106+
v1_only_measurement.measurement_service.configuration_parameter_list,
107+
v1_only_measurement.measurement_service.output_parameter_list,
108+
v1_only_measurement.measurement_service.measure_function,
109+
)
110+
111+
_validate_if_service_running_by_making_rpc(port_number)
112+
113+
114+
def test___grpc_service_unknown_interface___start_service_and_check_v1___raises_error(
115+
grpc_service: GrpcService,
116+
):
117+
with pytest.raises(ValueError):
118+
grpc_service.start(
119+
unknown_interface_measurement.measurement_service.measurement_info,
120+
unknown_interface_measurement.measurement_service.service_info,
121+
unknown_interface_measurement.measurement_service.configuration_parameter_list,
122+
unknown_interface_measurement.measurement_service.output_parameter_list,
123+
unknown_interface_measurement.measurement_service.measure_function,
124+
)
125+
126+
79127
@pytest.fixture
80128
def grpc_service(discovery_client: DiscoveryClient) -> GrpcService:
81129
"""Create a GrpcService."""
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Contains utility functions to test loopback measurement service. """
2+
import pathlib
3+
4+
import ni_measurementlink_service as nims
5+
6+
service_directory = pathlib.Path(__file__).resolve().parent
7+
measurement_service = nims.MeasurementService(
8+
service_config_path=service_directory / "unknown_interface.serviceconfig",
9+
version="0.1.0.0",
10+
ui_file_paths=[
11+
service_directory,
12+
],
13+
)
14+
15+
16+
@measurement_service.register_measurement
17+
@measurement_service.configuration("Float In", nims.DataType.Float, 0.06)
18+
@measurement_service.output("Float out", nims.DataType.Float)
19+
def measure(float_input):
20+
"""Loopback measurement on the float input."""
21+
float_output = float_input
22+
23+
return float_output
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"services": [
3+
{
4+
"displayName": "Unknown Interface Measurement (Py)",
5+
"serviceClass": "ni.tests.UnknownInterface_Python",
6+
"descriptionUrl": "",
7+
"providedInterfaces": [
8+
"acme.unknown.measurement.interface.V1"
9+
],
10+
"path": "start.bat",
11+
"annotations": {
12+
"ni/service.description": "MeasurementLink test service that provides an unknown interface.",
13+
"ni/service.collection": "NI.Tests",
14+
"ni/service.tags": []
15+
}
16+
17+
}
18+
]
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Contains utility functions to test loopback measurement service. """
2+
import pathlib
3+
4+
import ni_measurementlink_service as nims
5+
6+
service_directory = pathlib.Path(__file__).resolve().parent
7+
measurement_service = nims.MeasurementService(
8+
service_config_path=service_directory / "v1_only.serviceconfig",
9+
version="0.1.0.0",
10+
ui_file_paths=[
11+
service_directory,
12+
],
13+
)
14+
15+
16+
@measurement_service.register_measurement
17+
@measurement_service.configuration("Float In", nims.DataType.Float, 0.06)
18+
@measurement_service.output("Float out", nims.DataType.Float)
19+
def measure(float_input):
20+
"""Loopback measurement on the float input."""
21+
float_output = float_input
22+
23+
return float_output
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"services": [
3+
{
4+
"displayName": "V1 Measurement (Py)",
5+
"serviceClass": "ni.tests.V1Measurement_Python",
6+
"descriptionUrl": "",
7+
"providedInterfaces": [
8+
"ni.measurementlink.measurement.v1.MeasurementService"
9+
],
10+
"path": "start.bat",
11+
"annotations": {
12+
"ni/service.description": "MeasurementLink test service that only supports a v1 measurement.",
13+
"ni/service.collection": "NI.Tests",
14+
"ni/service.tags": []
15+
}
16+
17+
}
18+
]
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Contains utility functions to test loopback measurement service. """
2+
import pathlib
3+
4+
import ni_measurementlink_service as nims
5+
6+
service_directory = pathlib.Path(__file__).resolve().parent
7+
measurement_service = nims.MeasurementService(
8+
service_config_path=service_directory / "v2_only.serviceconfig",
9+
version="0.1.0.0",
10+
ui_file_paths=[
11+
service_directory,
12+
],
13+
)
14+
15+
16+
@measurement_service.register_measurement
17+
@measurement_service.configuration("Float In", nims.DataType.Float, 0.06)
18+
@measurement_service.output("Float out", nims.DataType.Float)
19+
def measure(float_input):
20+
"""Loopback measurement on the float input."""
21+
float_output = float_input
22+
23+
return float_output
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"services": [
3+
{
4+
"displayName": "V2 Measurement (Py)",
5+
"serviceClass": "ni.tests.V2Measurement_Python",
6+
"descriptionUrl": "",
7+
"providedInterfaces": [
8+
"ni.measurementlink.measurement.v2.MeasurementService"
9+
],
10+
"path": "start.bat",
11+
"annotations": {
12+
"ni/service.description": "MeasurementLink test service that only supports a v2 measurement.",
13+
"ni/service.collection": "NI.Tests",
14+
"ni/service.tags": []
15+
}
16+
17+
}
18+
]
19+
}

0 commit comments

Comments
 (0)