diff --git a/.coveragerc b/.coveragerc index f894cec..d55c0ad 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,5 @@ [run] -omit = uprotocol/proto/*, uprotocol/cloudevent/*_pb2.py, tests/*, */__init__.py +omit = uprotocol/core/*,uprotocol/v1/*, uprotocol/uoptions_pb2.py, uprotocol/cloudevent/*_pb2.py, tests/*, */__init__.py [report] exclude_lines = pragma: no cover diff --git a/.github/ruff.toml b/.github/ruff.toml index 9f91e69..74f0fbb 100644 --- a/.github/ruff.toml +++ b/.github/ruff.toml @@ -9,7 +9,10 @@ exclude = [ ".venv/", "__pycache__/", "uprotocol/cloudevent/cloudevents_pb2.py", - "uprotocol/proto/*" + "uprotocol/core/*", + "uprotocol/v1/*", + "uprotocol/uoptions_pb2.py" + ] [lint] @@ -25,6 +28,7 @@ select = [ ignore = [ "F811", # Ignore the error for multimethod function + "N999" # Invalid module name: 'up-python' ] [lint.flake8-annotations] @@ -34,4 +38,4 @@ allow-star-arg-any = true quote-style = "preserve" indent-style = "space" docstring-code-format = true -docstring-code-line-length = 100 +docstring-code-line-length = 100 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 63a904c..573f10f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,9 @@ .vscode .coverage target -#**/proto +#uprotocol/core +#uprotocol/v1 +#uprotocol/uoptions_pb2.py poetry.lock htmlcov coverage_report.txt diff --git a/pyproject.toml b/pyproject.toml index 24359b2..4c88d70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ packages = [{ include = "uprotocol" }, [tool.poetry.dependencies] python = "^3.8" cloudevents = "*" -multimethod = "*" gitpython = ">=3.1.41" googleapis-common-protos = ">=1.56.4" protobuf = "4.24.2" diff --git a/scripts/pull_and_compile_protos.py b/scripts/pull_and_compile_protos.py index bf459de..4c40684 100644 --- a/scripts/pull_and_compile_protos.py +++ b/scripts/pull_and_compile_protos.py @@ -1,22 +1,14 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ @@ -28,10 +20,10 @@ import git from git import Repo -REPO_URL = "https://github.com/eclipse-uprotocol/up-core-api.git" +REPO_URL = "https://github.com/eclipse-uprotocol/up-spec.git" PROTO_REPO_DIR = os.path.abspath("../target") -TAG_NAME = "uprotocol-core-api-1.5.7" -PROTO_OUTPUT_DIR = os.path.abspath("../uprotocol/proto") +TAG_NAME = "main" +PROTO_OUTPUT_DIR = os.path.abspath("../uprotocol/") def clone_or_pull(repo_url, proto_repo_dir): @@ -67,7 +59,7 @@ def execute_maven_command(project_dir, command): else: print("Maven command executed successfully.") src_directory = os.path.join( - os.getcwd(), project_dir, "target", "generated-sources", "protobuf", "python" + os.getcwd(), project_dir, "target", "generated-sources", "protobuf", "python", "uprotocol" ) shutil.copytree(src_directory, PROTO_OUTPUT_DIR, dirs_exist_ok=True) @@ -89,19 +81,6 @@ def replace_in_file(file_path, search_pattern, replace_pattern): def process_python_protofiles(directory): for root, dirs, files in os.walk(directory): create_init_py(root) - for file in files: - if file.endswith('.py'): - file_path = os.path.join(root, file) - replace_in_file(file_path, r'import uri_pb2', 'import uprotocol.proto.uri_pb2') - replace_in_file(file_path, r'import uuid_pb2', 'import uprotocol.proto.uuid_pb2') - replace_in_file( - file_path, r'import uprotocol_options_pb2', 'import uprotocol.proto.uprotocol_options_pb2' - ) - replace_in_file(file_path, r'import uattributes_pb2', 'import uprotocol.proto.uattributes_pb2') - replace_in_file(file_path, r'import upayload_pb2', 'import uprotocol.proto.upayload_pb2') - replace_in_file(file_path, r'import ustatus_pb2', 'import uprotocol.proto.ustatus_pb2') - replace_in_file(file_path, r'import upayload_pb2', 'import uprotocol.proto.upayload_pb2') - replace_in_file(file_path, r'import umessage_pb2', 'import uprotocol.proto.umessage_pb2') def create_init_py(directory): diff --git a/tests/test_cloudevent/test_datamodel/test_ucloudevent.py b/tests/test_cloudevent/test_datamodel/test_ucloudevent.py deleted file mode 100644 index a73ef4f..0000000 --- a/tests/test_cloudevent/test_datamodel/test_ucloudevent.py +++ /dev/null @@ -1,551 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import time -import unittest -from datetime import datetime, timezone - -from google.protobuf import any_pb2 - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority -from uprotocol.proto.upayload_pb2 import UPayloadFormat -from uprotocol.proto.uri_pb2 import UEntity, UResource, UUri -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uuid.factory.uuidfactory import Factories -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer - - -def build_uri_for_test(): - uri = UUri( - entity=UEntity(name="body.access"), - resource=UResource(name="door", instance="front_left", message="Door"), - ) - return LongUriSerializer().serialize(uri) - - -def build_proto_payload_for_test(): - ce_proto = CloudEvent( - spec_version="1.0", - source="//VCU.MY_CAR_VIN/body.access//door.front_left#Door", - id="hello", - type="example.demo", - proto_data=any_pb2.Any(), - ) - - any_obj = any_pb2.Any() - any_obj.Pack(ce_proto) - return any_obj - - -def build_cloud_event_for_test(): - source = build_uri_for_test() - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - return cloud_event - - -def build_cloud_event_for_test_with_id(id): - source = build_uri_for_test() - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - id, - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - return cloud_event - - -class TestUCloudEvent(unittest.TestCase): - DATA_CONTENT_TYPE = "application/x-protobuf" - - def test_extract_source_from_cloudevent(self): - cloud_event = build_cloud_event_for_test() - source = UCloudEvent.get_source(cloud_event) - self.assertEqual("/body.access//door.front_left#Door", source) - - def test_extract_sink_from_cloudevent_when_sink_exists(self): - sink = "//bo.cloud/petapp/1/rpc.response" - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("sink", sink) - cloud_event.__setitem__("plevel", 4) - self.assertEqual(sink, UCloudEvent.get_sink(cloud_event)) - - def test_extract_sink_from_cloudevent_when_sink_does_not_exist(self): - cloud_event = build_cloud_event_for_test() - self.assertEqual(None, UCloudEvent.get_sink(cloud_event)) - - def test_extract_request_id_from_cloudevent_when_request_id_exists(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("reqid", "somereqid") - self.assertEqual("somereqid", UCloudEvent.get_request_id(cloud_event)) - - def test_extract_request_id_from_cloudevent_when_request_id_does_not_exist( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(None, UCloudEvent.get_request_id(cloud_event)) - - def test_extract_request_id_from_cloudevent_when_request_id_value_is_null( - self, - ): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("reqid", None) - self.assertEqual(None, UCloudEvent.get_request_id(cloud_event)) - - def test_extract_hash_from_cloudevent_when_hash_exists(self): - cloud_event = build_cloud_event_for_test() - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - - def test_extract_hash_from_cloudevent_when_hash_does_not_exist(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__delitem__("hash") - self.assertEqual(None, UCloudEvent.get_hash(cloud_event)) - - def test_extract_priority_from_cloudevent_when_priority_exists(self): - cloud_event = build_cloud_event_for_test() - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS1), - UCloudEvent.get_priority(cloud_event), - ) - - def test_extract_priority_from_cloudevent_when_priority_does_not_exist( - self, - ): - cloud_event = build_cloud_event_for_test() - cloud_event.__delitem__("priority") - self.assertEqual(None, UCloudEvent.get_priority(cloud_event)) - - def test_extract_ttl_from_cloudevent_when_ttl_exists(self): - cloud_event = build_cloud_event_for_test() - self.assertEqual(UCode.INVALID_ARGUMENT, UCloudEvent.get_ttl(cloud_event)) - - def test_extract_ttl_from_cloudevent_when_ttl_does_not_exists(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__delitem__("ttl") - self.assertEqual(None, UCloudEvent.get_ttl(cloud_event)) - - def test_extract_token_from_cloudevent_when_token_exists(self): - cloud_event = build_cloud_event_for_test() - self.assertEqual("someOAuthToken", UCloudEvent.get_token(cloud_event)) - - def test_extract_token_from_cloudevent_when_token_does_not_exists(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__delitem__("token") - self.assertEqual(None, UCloudEvent.get_token(cloud_event)) - - def test_cloudevent_has_platform_error_when_platform_error_exists(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("commstatus", UCode.ABORTED) - self.assertEqual(UCode.ABORTED, UCloudEvent.get_communication_status(cloud_event)) - - def test_cloudevent_has_platform_error_when_platform_error_does_not_exist( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - def test_extract_platform_error_from_cloudevent_when_platform_error_exists_in_wrong_format( - self, - ): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("commstatus", "boom") - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - def test_extract_platform_error_from_cloudevent_when_platform_error_exists( - self, - ): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("commstatus", UCode.INVALID_ARGUMENT) - self.assertEqual( - UCode.INVALID_ARGUMENT, - UCloudEvent.get_communication_status(cloud_event), - ) - - def test_extract_platform_error_from_cloudevent_when_platform_error_does_not_exist( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - def test_adding_platform_error_to_existing_cloudevent( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - cloud_event_1 = UCloudEvent.add_communication_status(cloud_event, UCode.DEADLINE_EXCEEDED) - - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - self.assertEqual( - UCode.DEADLINE_EXCEEDED, - UCloudEvent.get_communication_status(cloud_event_1), - ) - - def test_adding_empty_platform_error_to_existing_cloudevent( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - cloud_event_1 = UCloudEvent.add_communication_status(cloud_event, None) - - self.assertEqual(UCode.OK, UCloudEvent.get_communication_status(cloud_event)) - - self.assertEqual(cloud_event, cloud_event_1) - - def test_extract_creation_timestamp_from_cloudevent_uuid_id_when_not_a_uuid_v8_id( - self, - ): - cloud_event = build_cloud_event_for_test() - self.assertEqual(None, UCloudEvent.get_creation_timestamp(cloud_event)) - - def test_extract_creation_timestamp_from_cloudevent_uuid_v8_id_when_uuid_v8_id_is_valid( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_cloud_event_for_test_with_id(str_uuid) - maybe_creation_timestamp = UCloudEvent.get_creation_timestamp(cloud_event) - self.assertIsNotNone(maybe_creation_timestamp) - creation_timestamp = maybe_creation_timestamp / 1000 - - now_timestamp = datetime.now(timezone.utc).timestamp() - self.assertAlmostEqual(creation_timestamp, now_timestamp, delta=1) - - def test_cloudevent_is_not_expired_cd_when_no_ttl_configured(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__delitem__("ttl") - self.assertFalse(UCloudEvent.is_expired_by_cloud_event_creation_date(cloud_event)) - - def test_cloudevent_is_not_expired_cd_when_ttl_is_zero(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("ttl", 0) - self.assertFalse(UCloudEvent.is_expired_by_cloud_event_creation_date(cloud_event)) - - def test_cloudevent_is_not_expired_cd_when_ttl_is_minus_one(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("ttl", -1) - self.assertFalse(UCloudEvent.is_expired_by_cloud_event_creation_date(cloud_event)) - - def test_cloudevent_is_expired_cd_when_ttl_is_one(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("ttl", 1) - time.sleep(0.002) - self.assertTrue(UCloudEvent.is_expired_by_cloud_event_creation_date(cloud_event)) - - def test_cloudevent_is_expired_when_ttl_1_mili(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("ttl", 1) - cloud_event.__setitem__("id", str_uuid) - time.sleep(8) - self.assertTrue(UCloudEvent.is_expired(cloud_event)) - - def test_cloudevent_is_expired_for_invalid_uuid(self): - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("ttl", 50000) - cloud_event.__setitem__("id", "") - self.assertFalse(UCloudEvent.is_expired(cloud_event)) - - def test_cloudevent_has_a_uuid_v8_id(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - self.assertTrue(UCloudEvent.is_cloud_event_id(cloud_event)) - - def test_to_message_with_valid_event(self): - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS2).with_ttl(3).build() - ) - cloud_event = CloudEventFactory.publish( - build_uri_for_test(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - u_message = UCloudEvent.to_message(cloud_event) - self.assertIsNotNone(u_message) - - def test_from_message_with_valid_message(self): - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .with_traceparent("someParent") - .build() - ) - cloud_event = CloudEventFactory.publish( - build_uri_for_test(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - u_message = UCloudEvent.to_message(cloud_event) - self.assertIsNotNone(u_message) - cloud_event1 = UCloudEvent.from_message(u_message) - self.assertIsNotNone(cloud_event1) - cloud_event.__delitem__("time") - cloud_event1.__delitem__("time") - self.assertEqual(cloud_event, cloud_event1) - self.assertEqual(cloud_event.get_data(), cloud_event1.get_data()) - self.assertEqual( - UCloudEvent.get_source(cloud_event), - UCloudEvent.get_source(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_specversion(cloud_event), - UCloudEvent.get_specversion(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_priority(cloud_event), - UCloudEvent.get_priority(cloud_event1), - ) - self.assertEqual(UCloudEvent.get_id(cloud_event), UCloudEvent.get_id(cloud_event1)) - self.assertEqual( - UCloudEvent.get_type(cloud_event), - UCloudEvent.get_type(cloud_event1), - ) - - def test_to_from_message_from_request_cloudevent(self): - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - cloud_event = CloudEventFactory.request( - build_uri_for_test(), - "//bo.cloud/petapp/1/rpc.response", - CloudEventFactory.generate_cloud_event_id(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - result = UCloudEvent.to_message(cloud_event) - self.assertIsNotNone(result) - self.assertEqual(UCloudEvent.get_ttl(cloud_event), result.attributes.ttl) - self.assertEqual(UCloudEvent.get_token(cloud_event), result.attributes.token) - self.assertEqual( - UCloudEvent.get_sink(cloud_event), - LongUriSerializer().serialize(result.attributes.sink), - ) - self.assertEqual( - UCloudEvent.get_payload(cloud_event).SerializeToString(), - result.payload.value, - ) - self.assertEqual( - UCloudEvent.get_source(cloud_event), - LongUriSerializer().serialize(result.attributes.source), - ) - self.assertEqual( - UCloudEvent.get_priority(cloud_event), - UPriority.Name(result.attributes.priority), - ) - - cloud_event1 = UCloudEvent.from_message(result) - cloud_event.__delitem__("time") - cloud_event1.__delitem__("time") - self.assertEqual(cloud_event, cloud_event1) - self.assertEqual(cloud_event.get_data(), cloud_event1.get_data()) - self.assertEqual( - UCloudEvent.get_source(cloud_event), - UCloudEvent.get_source(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_sink(cloud_event), - UCloudEvent.get_sink(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_specversion(cloud_event), - UCloudEvent.get_specversion(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_priority(cloud_event), - UCloudEvent.get_priority(cloud_event1), - ) - self.assertEqual(UCloudEvent.get_id(cloud_event), UCloudEvent.get_id(cloud_event1)) - self.assertEqual( - UCloudEvent.get_type(cloud_event), - UCloudEvent.get_type(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_request_id(cloud_event), - UCloudEvent.get_request_id(cloud_event1), - ) - - def test_to_from_message_from_request_cloudevent_without_attributes(self): - # additional attributes - u_cloud_event_attributes = UCloudEventAttributesBuilder().build() - - cloud_event = CloudEventFactory.request( - build_uri_for_test(), - "//bo.cloud/petapp/1/rpc.response", - CloudEventFactory.generate_cloud_event_id(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - result = UCloudEvent.to_message(cloud_event) - self.assertIsNotNone(result) - self.assertFalse(result.attributes.HasField("ttl")) - self.assertEqual( - UCloudEvent.get_sink(cloud_event), - LongUriSerializer().serialize(result.attributes.sink), - ) - self.assertEqual( - UCloudEvent.get_payload(cloud_event).SerializeToString(), - result.payload.value, - ) - self.assertEqual( - UCloudEvent.get_source(cloud_event), - LongUriSerializer().serialize(result.attributes.source), - ) - self.assertEqual(result.attributes.priority, 0) - - cloud_event1 = UCloudEvent.from_message(result) - self.assertEqual(cloud_event.get_data(), cloud_event1.get_data()) - self.assertEqual( - UCloudEvent.get_source(cloud_event), - UCloudEvent.get_source(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_sink(cloud_event), - UCloudEvent.get_sink(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_specversion(cloud_event), - UCloudEvent.get_specversion(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_priority(cloud_event), - UCloudEvent.get_priority(cloud_event1), - ) - self.assertEqual(UCloudEvent.get_id(cloud_event), UCloudEvent.get_id(cloud_event1)) - self.assertEqual( - UCloudEvent.get_type(cloud_event), - UCloudEvent.get_type(cloud_event1), - ) - self.assertEqual( - UCloudEvent.get_request_id(cloud_event), - UCloudEvent.get_request_id(cloud_event1), - ) - - def test_to_from_message_from_ucp_cloudevent(self): - # additional attributes - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_ttl(3).with_token("someOAuthToken").build() - - cloud_event = CloudEventFactory.request( - build_uri_for_test(), - "//bo.cloud/petapp/1/rpc.response", - CloudEventFactory.generate_cloud_event_id(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - cloud_event.__setitem__("priority", "CS4") - cloud_event.__setitem__("commstatus", 16) - cloud_event.__setitem__("permission_level", 4) - - result = UCloudEvent.to_message(cloud_event) - self.assertIsNotNone(result) - self.assertEqual(UPriority.UPRIORITY_CS4, result.attributes.priority) - cloud_event1 = UCloudEvent.from_message(result) - self.assertEqual( - UCloudEvent.get_priority(cloud_event1), - UPriority.Name(result.attributes.priority), - ) - - def test_from_message_with_null_message(self): - with self.assertRaises(ValueError) as context: - UCloudEvent.from_message(None) - self.assertTrue("message cannot be null." in context.exception) - - def test_cloud_event_to_string(self): - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_ttl(3).with_token("someOAuthToken").build() - - cloud_event = CloudEventFactory.request( - build_uri_for_test(), - "//bo.cloud/petapp/1/rpc.response", - CloudEventFactory.generate_cloud_event_id(), - build_proto_payload_for_test(), - u_cloud_event_attributes, - ) - cloud_event_string = UCloudEvent.to_string(cloud_event) - self.assertTrue( - "source='/body.access//door.front_left#Door', sink='//bo.cloud/petapp/1/rpc.response', type='req.v1'}" - in cloud_event_string - ) - - def test_cloud_event_to_string_none(self): - cloud_event_string = UCloudEvent.to_string(None) - self.assertEqual(cloud_event_string, "null") - - def test_get_upayload_format_from_content_type(self): - new_format = UCloudEvent().get_upayload_format_from_content_type("application/json") - self.assertEqual(new_format, UPayloadFormat.UPAYLOAD_FORMAT_JSON) - - def test_to_message_none_entry(self): - with self.assertRaises(ValueError) as context: - UCloudEvent().to_message(None) - self.assertTrue("Cloud Event can't be None" in context.exception) diff --git a/tests/test_cloudevent/test_datamodel/test_ucloudeventattributes.py b/tests/test_cloudevent/test_datamodel/test_ucloudeventattributes.py deleted file mode 100644 index 14925a4..0000000 --- a/tests/test_cloudevent/test_datamodel/test_ucloudeventattributes.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributes, - UCloudEventAttributesBuilder, -) -from uprotocol.proto.uattributes_pb2 import UPriority - - -class TestUCloudEventAttributes(unittest.TestCase): - def test_to_string(self): - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - expected = "UCloudEventAttributes{hash='somehash', priority=UPRIORITY_CS1, ttl=3, token='someOAuthToken'}" - self.assertEqual(expected, str(u_cloud_event_attributes)) - - def test_create_valid_with_blank_traceparent(self): - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .with_traceparent(" ") - .build() - ) - self.assertTrue(u_cloud_event_attributes.get_hash() is not None) - self.assertEqual("somehash", u_cloud_event_attributes.get_hash()) - self.assertFalse(u_cloud_event_attributes.get_traceparent() is not None) - - def test_create_empty_with_only_traceparent(self): - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_traceparent("someTraceParent").build() - self.assertFalse(u_cloud_event_attributes.get_hash() is not None) - self.assertFalse(u_cloud_event_attributes.get_priority() is not None) - self.assertFalse(u_cloud_event_attributes.get_token() is not None) - self.assertFalse(u_cloud_event_attributes.get_ttl() is not None) - self.assertTrue(u_cloud_event_attributes.get_traceparent() is not None) - self.assertFalse(u_cloud_event_attributes.is_empty()) - self.assertEqual("someTraceParent", u_cloud_event_attributes.get_traceparent()) - - def test_create_valid(self): - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS6) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - self.assertEqual("somehash", u_cloud_event_attributes.get_hash()) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS6), - u_cloud_event_attributes.get_priority(), - ) - self.assertEqual(3, u_cloud_event_attributes.get_ttl()) - self.assertEqual("someOAuthToken", u_cloud_event_attributes.get_token()) - - def test_is_empty_function(self): - u_cloud_event_attributes = UCloudEventAttributes.empty() - self.assertTrue(u_cloud_event_attributes.is_empty()) - self.assertTrue(u_cloud_event_attributes.priority is None) - self.assertTrue(u_cloud_event_attributes.token is None) - self.assertTrue(u_cloud_event_attributes.ttl is None) - - def test_is_empty_function_when_built_with_blank_strings(self): - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - self.assertTrue(u_cloud_event_attributes.is_empty()) - self.assertTrue(u_cloud_event_attributes.hash.isspace()) - self.assertTrue(u_cloud_event_attributes.priority is None) - self.assertTrue(u_cloud_event_attributes.token.isspace()) - self.assertTrue(u_cloud_event_attributes.ttl is None) - - def test_is_empty_function_permutations(self): - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - self.assertTrue(u_cloud_event_attributes.is_empty()) - - u_cloud_event_attributes2 = UCloudEventAttributesBuilder().with_hash("someHash").with_token(" ").build() - self.assertFalse(u_cloud_event_attributes2.is_empty()) - - u_cloud_event_attributes3 = UCloudEventAttributesBuilder().with_hash(" ").with_token("SomeToken").build() - self.assertFalse(u_cloud_event_attributes3.is_empty()) - - u_cloud_event_attributes4 = UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS0).build() - self.assertFalse(u_cloud_event_attributes4.is_empty()) - - u_cloud_event_attributes5 = UCloudEventAttributesBuilder().with_ttl(8).build() - self.assertFalse(u_cloud_event_attributes5.is_empty()) - - def test__eq__is_same(self): - u_cloud_event_attributes = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - self.assertTrue(u_cloud_event_attributes.__eq__(u_cloud_event_attributes)) - - def test__eq__is_equal(self): - u_cloud_event_attributes_1 = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - u_cloud_event_attributes_2 = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - self.assertTrue(u_cloud_event_attributes_1.__eq__(u_cloud_event_attributes_2)) - - def test__eq__is_not_equal(self): - u_cloud_event_attributes_1 = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - u_cloud_event_attributes_2 = UCloudEventAttributesBuilder().with_hash(" ").with_token("12345").build() - self.assertFalse(u_cloud_event_attributes_1.__eq__(u_cloud_event_attributes_2)) - - def test__hash__same(self): - u_cloud_event_attributes_1 = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - self.assertEqual(hash(u_cloud_event_attributes_1), hash(u_cloud_event_attributes_1)) - - def test__hash__different(self): - u_cloud_event_attributes_1 = UCloudEventAttributesBuilder().with_hash(" ").with_token(" ").build() - u_cloud_event_attributes_2 = UCloudEventAttributesBuilder().with_hash(" ").with_token("12345").build() - self.assertNotEqual(hash(u_cloud_event_attributes_1), hash(u_cloud_event_attributes_2)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_cloudevent/test_factory/cloudevent.json b/tests/test_cloudevent/test_factory/cloudevent.json deleted file mode 100644 index 2e70355..0000000 --- a/tests/test_cloudevent/test_factory/cloudevent.json +++ /dev/null @@ -1,99 +0,0 @@ -[ - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoidGVzdG1lIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJwdWIudjEiLCJwcmlvcml0eSI6IlVQUklPUklUWV9DUzEiLCJ0dGwiOjMsImhhc2giOiJzb21laGFzaCIsInRva2VuIjoic29tZU9BdXRoVG9rZW4iLCJkYXRhX2Jhc2U2NCI6IkNqQjBlWEJsTG1kdmIyZHNaV0Z3YVhNdVkyOXRMMmx2TG1Oc2IzVmtaWFpsYm5SekxuWXhMa05zYjNWa1JYWmxiblFTTVFvRmFHVnNiRzhTRTJoMGRIQnpPaTh2WlhoaGJYQnNaUzVqYjIwYUF6RXVNQ0lNWlhoaGJYQnNaUzVrWlcxdlFnQT0ifQ==", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "token": "someOAuthToken", - "test_detail": "test_create_base_cloud_event" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoidGVzdG1lIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJwdWIudjEiLCJkYXRhY29udGVudHR5cGUiOiJhcHBsaWNhdGlvbi94LXByb3RvYnVmIiwiZGF0YXNjaGVtYSI6InR5cGUuZ29vZ2xlYXBpcy5jb20vaW8uY2xvdWRldmVudHMudjEuQ2xvdWRFdmVudCIsInByaW9yaXR5IjoiVVBSSU9SSVRZX0NTMSIsInR0bCI6MywiaGFzaCI6InNvbWVoYXNoIiwidG9rZW4iOiJzb21lT0F1dGhUb2tlbiIsImRhdGFfYmFzZTY0IjoiQ2pCMGVYQmxMbWR2YjJkc1pXRndhWE11WTI5dEwybHZMbU5zYjNWa1pYWmxiblJ6TG5ZeExrTnNiM1ZrUlhabGJuUVNNUW9GYUdWc2JHOFNFMmgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjBhQXpFdU1DSU1aWGhoYlhCc1pTNWtaVzF2UWdBPSJ9", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "token": "someOAuthToken", - "dataschema": "type.googleapis.com/io.cloudevents.v1.CloudEvent", - "datacontenttype": "application/x-protobuf", - "test_detail": "test_create_base_cloud_event_with_datacontenttype_and_schema" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoidGVzdG1lIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJwdWIudjEiLCJkYXRhX2Jhc2U2NCI6IkNqQjBlWEJsTG1kdmIyZHNaV0Z3YVhNdVkyOXRMMmx2TG1Oc2IzVmtaWFpsYm5SekxuWXhMa05zYjNWa1JYWmxiblFTTVFvRmFHVnNiRzhTRTJoMGRIQnpPaTh2WlhoaGJYQnNaUzVqYjIwYUF6RXVNQ0lNWlhoaGJYQnNaUzVrWlcxdlFnQT0ifQ==", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "test_detail": "test_create_base_cloud_event_without_attributes" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoiMDE4YmY5YjYtNWMxYy04MDAwLTk4MmYtOGM3MTI1NjQ2OWNjIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJwdWIudjEiLCJwcmlvcml0eSI6IlVQUklPUklUWV9DUzEiLCJ0dGwiOjMsImhhc2giOiJzb21laGFzaCIsImRhdGFfYmFzZTY0IjoiQ2pCMGVYQmxMbWR2YjJkc1pXRndhWE11WTI5dEwybHZMbU5zYjNWa1pYWmxiblJ6TG5ZeExrTnNiM1ZrUlhabGJuUVNNUW9GYUdWc2JHOFNFMmgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjBhQXpFdU1DSU1aWGhoYlhCc1pTNWtaVzF2UWdBPSJ9", - "id": "018bf9b6-5c1c-8000-982f-8c71256469cc", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "test_detail": "test_create_publish_cloud_event" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoiMDE4YmY5YjktNWFkOS04MDAwLTgzZTUtNmE0ODQxNzNkNjlmIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJwdWIudjEiLCJzaW5rIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInByaW9yaXR5IjoiVVBSSU9SSVRZX0NTMiIsInR0bCI6MywiaGFzaCI6InNvbWVoYXNoIiwiZGF0YV9iYXNlNjQiOiJDakIwZVhCbExtZHZiMmRzWldGd2FYTXVZMjl0TDJsdkxtTnNiM1ZrWlhabGJuUnpMbll4TGtOc2IzVmtSWFpsYm5RU01Rb0ZhR1ZzYkc4U0UyaDBkSEJ6T2k4dlpYaGhiWEJzWlM1amIyMGFBekV1TUNJTVpYaGhiWEJzWlM1a1pXMXZRZ0E9In0=", - "id": "018bf9b9-5ad9-8000-83e5-6a484173d69f", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "sink": "/body.access//door.front_left#Door", - "test_detail": "test_create_notification_cloud_event" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoiMDE4YmY5YmItMzFiZC04MDAwLWJkMGEtNjViNjNlZWNlMmJkIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJyZXEudjEiLCJzaW5rIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInByaW9yaXR5IjoiVVBSSU9SSVRZX0NTMiIsInR0bCI6MywiaGFzaCI6InNvbWVoYXNoIiwidG9rZW4iOiJzb21lT0F1dGhUb2tlbiIsImRhdGFfYmFzZTY0IjoiQ2pCMGVYQmxMbWR2YjJkc1pXRndhWE11WTI5dEwybHZMbU5zYjNWa1pYWmxiblJ6TG5ZeExrTnNiM1ZrUlhabGJuUVNNUW9GYUdWc2JHOFNFMmgwZEhCek9pOHZaWGhoYlhCc1pTNWpiMjBhQXpFdU1DSU1aWGhoYlhCc1pTNWtaVzF2UWdBPSJ9", - "id": "018bf9bb-31bd-8000-bd0a-65b63eece2bd", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "req.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "token": "someOAuthToken", - "test_detail": "test_create_request_cloud_event_from_local_use" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoiMDE4YmY5YmQtM2EzYi04MDAwLWE4Y2ItZTk1NTY4MWU4NTMwIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJyZXMudjEiLCJzaW5rIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInByaW9yaXR5IjoiVVBSSU9SSVRZX0NTMiIsInR0bCI6MywiaGFzaCI6InNvbWVoYXNoIiwicmVxaWQiOiJyZXF1ZXN0SWRGcm9tUmVxdWVzdENsb3VkRXZlbnQiLCJkYXRhX2Jhc2U2NCI6IkNqQjBlWEJsTG1kdmIyZHNaV0Z3YVhNdVkyOXRMMmx2TG1Oc2IzVmtaWFpsYm5SekxuWXhMa05zYjNWa1JYWmxiblFTTVFvRmFHVnNiRzhTRTJoMGRIQnpPaTh2WlhoaGJYQnNaUzVqYjIwYUF6RXVNQ0lNWlhoaGJYQnNaUzVrWlcxdlFnQT0ifQ==", - "id": "018bf9bd-3a3b-8000-a8cb-e955681e8530", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "res.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "reqid": "request_idFromRequestCloudEvent", - "sink": "/body.access//door.front_left#Door", - "test_detail": "test_create_response_cloud_event_originating_from_local_use" - }, - { - "serialized_ce": "eyJzcGVjdmVyc2lvbiI6IjEuMCIsImlkIjoiMDE4YmY5YmYtMjdlNC04MDAwLThkZDAtMjYyNzdjNzg3OTBlIiwic291cmNlIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsInR5cGUiOiJyZXMudjEiLCJzaW5rIjoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vciIsImNvbW1zdGF0dXMiOjMsInByaW9yaXR5IjoiVVBSSU9SSVRZX0NTMiIsInR0bCI6MywiaGFzaCI6InNvbWVoYXNoIiwicmVxaWQiOiJyZXF1ZXN0SWRGcm9tUmVxdWVzdENsb3VkRXZlbnQiLCJkYXRhX2Jhc2U2NCI6IkNpbDBlWEJsTG1kdmIyZHNaV0Z3YVhNdVkyOXRMMmR2YjJkc1pTNXdjbTkwYjJKMVppNUZiWEIwZVE9PSJ9", - "id": "018bf9bf-27e4-8000-8dd0-26277c78790e", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "res.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "reqid": "request_idFromRequestCloudEvent", - "sink": "/body.access//door.front_left#Door", - "commstatus": 3, - "test_detail": "test_create_a_failed_response_cloud_event" - } - -] \ No newline at end of file diff --git a/tests/test_cloudevent/test_factory/test_cloudeventfactory.py b/tests/test_cloudevent/test_factory/test_cloudeventfactory.py deleted file mode 100644 index d328030..0000000 --- a/tests/test_cloudevent/test_factory/test_cloudeventfactory.py +++ /dev/null @@ -1,540 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import json -import os -import unittest - -from google.protobuf import any_pb2 - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributes, - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.cloudevent.serialize.base64protobufserializer import ( - Base64ProtobufSerializer, -) -from uprotocol.cloudevent.serialize.cloudeventtojsonserializer import ( - CloudEventToJsonSerializer, -) -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority -from uprotocol.proto.uri_pb2 import UEntity, UResource, UUri -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer - - -def get_json_object(): - current_directory = os.getcwd() - json_file_path = os.path.join( - current_directory, - "tests", - "test_cloudevent", - "test_factory", - "cloudevent.json", - ) - - with open(json_file_path, "r") as json_file: - json_data = json.load(json_file) - - return json_data - - -def build_uri_for_test(): - uri = UUri( - entity=UEntity(name="body.access"), - resource=UResource(name="door", instance="front_left", message="Door"), - ) - return LongUriSerializer().serialize(uri) - - -def build_proto_payload_for_test(): - ce_proto = CloudEvent( - spec_version="1.0", - source="https://example.com", - id="hello", - type="example.demo", - proto_data=any_pb2.Any(), - ) - - any_obj = any_pb2.Any() - any_obj.Pack(ce_proto) - return any_obj - - -class TestCloudEventFactory(unittest.TestCase): - DATA_CONTENT_TYPE = CloudEventFactory.PROTOBUF_CONTENT_TYPE - - def test_all_cloud_events_from_json(self): - cloudevents = get_json_object() - for ce_json in cloudevents: - bytes_ce = Base64ProtobufSerializer().serialize(ce_json["serialized_ce"]) - cloudevent = CloudEventToJsonSerializer().deserialize(bytes_ce) - self.assertEqual(UCloudEvent.get_id(cloudevent), ce_json["id"]) - self.assertEqual(UCloudEvent.get_specversion(cloudevent), ce_json["specversion"]) - if "source" in ce_json: - self.assertEqual(UCloudEvent.get_source(cloudevent), ce_json["source"]) - if "sink" in ce_json: - self.assertEqual(UCloudEvent.get_sink(cloudevent), ce_json["sink"]) - if "type" in ce_json: - self.assertEqual(UCloudEvent.get_type(cloudevent), ce_json["type"]) - if "priority" in ce_json: - self.assertEqual(UCloudEvent.get_priority(cloudevent), ce_json["priority"]) - if "ttl" in ce_json: - self.assertEqual(UCloudEvent.get_ttl(cloudevent), ce_json["ttl"]) - if "hash" in ce_json: - self.assertEqual(UCloudEvent.get_hash(cloudevent), ce_json["hash"]) - if "token" in ce_json: - self.assertEqual(UCloudEvent.get_token(cloudevent), ce_json["token"]) - if "dataschema" in ce_json: - self.assertEqual( - UCloudEvent.get_data_schema(cloudevent), - ce_json["dataschema"], - ) - if "datacontenttype" in ce_json: - self.assertEqual( - UCloudEvent.get_data_content_type(cloudevent), - ce_json["datacontenttype"], - ) - if "commstatus" in ce_json: - self.assertEqual( - UCloudEvent.get_communication_status(cloudevent), - ce_json["commstatus"], - ) - - def test_create_base_cloud_event(self): - source = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .with_traceparent("sometraceparent") - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertEqual("testme", UCloudEvent.get_id(cloud_event)) - self.assertEqual(source, UCloudEvent.get_source(cloud_event)) - self.assertEqual("pub.v1", UCloudEvent.get_type(cloud_event)) - self.assertNotIn("sink", cloud_event.get_attributes()) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS1), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - self.assertEqual("someOAuthToken", UCloudEvent.get_token(cloud_event)) - self.assertEqual("sometraceparent", UCloudEvent.get_traceparent(cloud_event)) - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_base_cloud_event_with_datacontenttype_and_schema(self): - source = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - - cloud_event.__setitem__("datacontenttype", CloudEventFactory.PROTOBUF_CONTENT_TYPE) - cloud_event.__setitem__("dataschema", proto_payload.type_url) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertEqual("testme", UCloudEvent.get_id(cloud_event)) - self.assertEqual(source, UCloudEvent.get_source(cloud_event)) - self.assertEqual( - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - UCloudEvent.get_type(cloud_event), - ) - self.assertEqual( - self.DATA_CONTENT_TYPE, - UCloudEvent.get_data_content_type(cloud_event), - ) - self.assertEqual(proto_payload.type_url, UCloudEvent.get_data_schema(cloud_event)) - self.assertNotIn("sink", cloud_event.get_attributes()) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS1), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - self.assertEqual("someOAuthToken", UCloudEvent.get_token(cloud_event)) - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_base_cloud_event_without_attributes(self): - source = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # no additional attributes - u_cloud_event_attributes = UCloudEventAttributes.empty() - - # build the cloud event - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertEqual("testme", UCloudEvent.get_id(cloud_event)) - self.assertEqual(source, UCloudEvent.get_source(cloud_event)) - self.assertEqual( - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - UCloudEvent.get_type(cloud_event), - ) - self.assertNotIn("sink", cloud_event.get_attributes()) - self.assertNotIn("hash", cloud_event.get_attributes()) - self.assertNotIn("priority", cloud_event.get_attributes()) - self.assertNotIn("ttl", cloud_event.get_attributes()) - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_publish_cloud_event(self): - # source - source = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .build() - ) - - cloud_event = CloudEventFactory.publish(source, proto_payload, u_cloud_event_attributes) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(source, UCloudEvent.get_source(cloud_event)) - self.assertEqual( - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - UCloudEvent.get_type(cloud_event), - ) - self.assertNotIn("sink", cloud_event.get_attributes()) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS1), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_notification_cloud_event(self): - # source - source = build_uri_for_test() - - # sink - sink = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .build() - ) - - # build the cloud event of type publish with destination - a notification - cloud_event = CloudEventFactory.notification(source, sink, proto_payload, u_cloud_event_attributes) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(source, UCloudEvent.get_source(cloud_event)) - - self.assertIn("sink", cloud_event.get_attributes()) - self.assertEqual(sink, UCloudEvent.get_sink(cloud_event)) - - self.assertEqual( - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - UCloudEvent.get_type(cloud_event), - ) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS2), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_request_cloud_event_from_local_use(self): - # UriPart for the application requesting the RPC - application_uri_for_rpc = build_uri_for_test() - - # service Method UriPart - service_method_uri = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - cloud_event = CloudEventFactory.request( - application_uri_for_rpc, - service_method_uri, - CloudEventFactory.generate_cloud_event_id(), - proto_payload, - u_cloud_event_attributes, - ) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(application_uri_for_rpc, UCloudEvent.get_source(cloud_event)) - - self.assertIn("sink", cloud_event.get_attributes()) - self.assertEqual(service_method_uri, UCloudEvent.get_sink(cloud_event)) - - self.assertEqual("req.v1", UCloudEvent.get_type(cloud_event)) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS2), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - self.assertEqual("someOAuthToken", UCloudEvent.get_token(cloud_event)) - - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_response_cloud_event_originating_from_local_use(self): - # UriPart for the application requesting the RPC - application_uri_for_rpc = build_uri_for_test() - - # service Method UriPart - service_method_uri = build_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - - # additional attributes - - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .build() - ) - - cloud_event = CloudEventFactory.response( - application_uri_for_rpc, - service_method_uri, - "request_idFromRequestCloudEvent", - proto_payload, - u_cloud_event_attributes, - ) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(service_method_uri, UCloudEvent.get_source(cloud_event)) - - self.assertIn("sink", cloud_event.get_attributes()) - self.assertEqual(application_uri_for_rpc, UCloudEvent.get_sink(cloud_event)) - - self.assertEqual("res.v1", UCloudEvent.get_type(cloud_event)) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS2), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - - self.assertEqual( - "request_idFromRequestCloudEvent", - UCloudEvent.get_request_id(cloud_event), - ) - - # Use assertEqual to compare byte arrays - self.assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - - def test_create_failed_response_cloud_event_originating_from_local_use( - self, - ): - # UriPart for the application requesting the RPC - application_uri_for_rpc = build_uri_for_test() - - # service Method UriPart - service_method_uri = build_uri_for_test() - - # additional attributes - - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .build() - ) - - cloud_event = CloudEventFactory.failed_response( - application_uri_for_rpc, - service_method_uri, - "request_idFromRequestCloudEvent", - UCode.INVALID_ARGUMENT, - u_cloud_event_attributes, - ) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(service_method_uri, UCloudEvent.get_source(cloud_event)) - - self.assertIn("sink", cloud_event.get_attributes()) - self.assertEqual(application_uri_for_rpc, UCloudEvent.get_sink(cloud_event)) - - self.assertEqual("res.v1", UCloudEvent.get_type(cloud_event)) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS2), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - self.assertEqual( - UCode.INVALID_ARGUMENT, - UCloudEvent.get_communication_status(cloud_event), - ) - - self.assertEqual( - "request_idFromRequestCloudEvent", - UCloudEvent.get_request_id(cloud_event), - ) - - def test_create_failed_response_cloud_event_originating_from_remote_use( - self, - ): - # UriPart for the application requesting the RPC - application_uri_for_rpc = build_uri_for_test() - - # service Method UriPart - service_method_uri = build_uri_for_test() - - # additional attributes - - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS2) - .with_ttl(3) - .build() - ) - - cloud_event = CloudEventFactory.failed_response( - application_uri_for_rpc, - service_method_uri, - "request_idFromRequestCloudEvent", - UCode.INVALID_ARGUMENT, - u_cloud_event_attributes, - ) - - # test all attributes - self.assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) - self.assertIsNotNone(UCloudEvent.get_id(cloud_event)) - self.assertEqual(service_method_uri, UCloudEvent.get_source(cloud_event)) - - self.assertIn("sink", cloud_event.get_attributes()) - self.assertEqual(application_uri_for_rpc, UCloudEvent.get_sink(cloud_event)) - - self.assertEqual("res.v1", UCloudEvent.get_type(cloud_event)) - self.assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) - self.assertEqual( - UPriority.Name(UPriority.UPRIORITY_CS2), - UCloudEvent.get_priority(cloud_event), - ) - self.assertEqual(3, UCloudEvent.get_ttl(cloud_event)) - self.assertEqual( - UCode.INVALID_ARGUMENT, - UCloudEvent.get_communication_status(cloud_event), - ) - - self.assertEqual( - "request_idFromRequestCloudEvent", - UCloudEvent.get_request_id(cloud_event), - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_cloudevent/test_serialize/__init__.py b/tests/test_cloudevent/test_serialize/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_cloudevent/test_serialize/cloudevent_to_protobuf.json b/tests/test_cloudevent/test_serialize/cloudevent_to_protobuf.json deleted file mode 100644 index 35f9c50..0000000 --- a/tests/test_cloudevent/test_serialize/cloudevent_to_protobuf.json +++ /dev/null @@ -1,86 +0,0 @@ -[ - { - "serialized_ce": "CgZ0ZXN0bWUSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcHViLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzEqGQoFdG9rZW4SEBoOc29tZU9BdXRoVG9rZW4qKgoEdGltZRIiGiAyMDIzLTExLTI5VDE4OjE0OjQyLjQ2NDQwMSswMDowMCoSCgRoYXNoEgoaCHNvbWVoYXNoKgkKA3R0bBICEAMyZQowdHlwZS5nb29nbGVhcGlzLmNvbS9pby5jbG91ZGV2ZW50cy52MS5DbG91ZEV2ZW50EjEKBWhlbGxvEhNodHRwczovL2V4YW1wbGUuY29tGgMxLjAiDGV4YW1wbGUuZGVtb0IA", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "token": "someOAuthToken", - "test_detail": "test_create_base_cloud_event" - }, - { - "serialized_ce": "CgZ0ZXN0bWUSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcHViLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzEqEgoEaGFzaBIKGghzb21laGFzaCoJCgN0dGwSAhADKisKD2RhdGFjb250ZW50dHlwZRIYGhZhcHBsaWNhdGlvbi94LXByb3RvYnVmKhkKBXRva2VuEhAaDnNvbWVPQXV0aFRva2VuKioKBHRpbWUSIhogMjAyMy0xMS0yOVQxODoxNDo0Mi40NjUzOTYrMDA6MDAqQAoKZGF0YXNjaGVtYRIyGjB0eXBlLmdvb2dsZWFwaXMuY29tL2lvLmNsb3VkZXZlbnRzLnYxLkNsb3VkRXZlbnQyZQowdHlwZS5nb29nbGVhcGlzLmNvbS9pby5jbG91ZGV2ZW50cy52MS5DbG91ZEV2ZW50EjEKBWhlbGxvEhNodHRwczovL2V4YW1wbGUuY29tGgMxLjAiDGV4YW1wbGUuZGVtb0IA", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "token": "someOAuthToken", - "dataschema": "type.googleapis.com/io.cloudevents.v1.CloudEvent", - "datacontenttype": "application/x-protobuf", - "test_detail": "test_create_base_cloud_event_with_datacontenttype_and_schema" - }, - { - "serialized_ce": "CgZ0ZXN0bWUSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcHViLnYxKioKBHRpbWUSIhogMjAyMy0xMS0yOVQxODoxNDo0Mi40NjcxMjQrMDA6MDAyZQowdHlwZS5nb29nbGVhcGlzLmNvbS9pby5jbG91ZGV2ZW50cy52MS5DbG91ZEV2ZW50EjEKBWhlbGxvEhNodHRwczovL2V4YW1wbGUuY29tGgMxLjAiDGV4YW1wbGUuZGVtb0IA", - "id": "testme", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "test_detail": "test_create_base_cloud_event_without_attributes" - }, - { - "serialized_ce": "CiQwMThiZjliNi01YzFjLTgwMDAtOTgyZi04YzcxMjU2NDY5Y2MSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcHViLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzEqKgoEdGltZRIiGiAyMDIzLTExLTI5VDE4OjE0OjQyLjQ2NzEyNCswMDowMCoSCgRoYXNoEgoaCHNvbWVoYXNoKgkKA3R0bBICEAMyZQowdHlwZS5nb29nbGVhcGlzLmNvbS9pby5jbG91ZGV2ZW50cy52MS5DbG91ZEV2ZW50EjEKBWhlbGxvEhNodHRwczovL2V4YW1wbGUuY29tGgMxLjAiDGV4YW1wbGUuZGVtb0IA", - "id": "018bf9b6-5c1c-8000-982f-8c71256469cc", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS1", - "ttl": 3, - "hash": "somehash", - "test_detail": "test_create_publish_cloud_event" - }, - { - "serialized_ce": "CiQwMThiZjliOS01YWQ5LTgwMDAtODNlNS02YTQ4NDE3M2Q2OWYSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcHViLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzIqLAoEc2luaxIkGiIvYm9keS5hY2Nlc3MvL2Rvb3IuZnJvbnRfbGVmdCNEb29yKioKBHRpbWUSIhogMjAyMy0xMS0yOVQxODoxNDo0Mi40NjcxMjQrMDA6MDAqEgoEaGFzaBIKGghzb21laGFzaCoJCgN0dGwSAhADMmUKMHR5cGUuZ29vZ2xlYXBpcy5jb20vaW8uY2xvdWRldmVudHMudjEuQ2xvdWRFdmVudBIxCgVoZWxsbxITaHR0cHM6Ly9leGFtcGxlLmNvbRoDMS4wIgxleGFtcGxlLmRlbW9CAA==", - "id": "018bf9b9-5ad9-8000-83e5-6a484173d69f", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "pub.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "sink": "/body.access//door.front_left#Door", - "test_detail": "test_create_notification_cloud_event" - }, - { - "serialized_ce": "CiQwMThiZjliZC0zYTNiLTgwMDAtYThjYi1lOTU1NjgxZTg1MzASIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcmVzLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzIqLAoEc2luaxIkGiIvYm9keS5hY2Nlc3MvL2Rvb3IuZnJvbnRfbGVmdCNEb29yKhIKBGhhc2gSChoIc29tZWhhc2gqKgoEdGltZRIiGiAyMDIzLTExLTI5VDE4OjE0OjQyLjQ2ODE0MSswMDowMCopCgVyZXFpZBIgGh5yZXF1ZXN0SWRGcm9tUmVxdWVzdENsb3VkRXZlbnQqCQoDdHRsEgIQAzJlCjB0eXBlLmdvb2dsZWFwaXMuY29tL2lvLmNsb3VkZXZlbnRzLnYxLkNsb3VkRXZlbnQSMQoFaGVsbG8SE2h0dHBzOi8vZXhhbXBsZS5jb20aAzEuMCIMZXhhbXBsZS5kZW1vQgA=", - "id": "018bf9bd-3a3b-8000-a8cb-e955681e8530", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "res.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "reqid": "request_idFromRequestCloudEvent", - "sink": "/body.access//door.front_left#Door", - "test_detail": "test_create_response_cloud_event_originating_from_local_use" - }, - { - "serialized_ce": "CiQwMThiZjliZi0yN2U0LTgwMDAtOGRkMC0yNjI3N2M3ODc5MGUSIi9ib2R5LmFjY2Vzcy8vZG9vci5mcm9udF9sZWZ0I0Rvb3IaAzEuMCIGcmVzLnYxKhsKCHByaW9yaXR5Eg8aDVVQUklPUklUWV9DUzIqEgoEaGFzaBIKGghzb21laGFzaCoJCgN0dGwSAhADKhAKCmNvbW1zdGF0dXMSAhADKiwKBHNpbmsSJBoiL2JvZHkuYWNjZXNzLy9kb29yLmZyb250X2xlZnQjRG9vcioqCgR0aW1lEiIaIDIwMjMtMTEtMjlUMTg6MTQ6NDIuNDY4MTQxKzAwOjAwKikKBXJlcWlkEiAaHnJlcXVlc3RJZEZyb21SZXF1ZXN0Q2xvdWRFdmVudDIrCil0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5wcm90b2J1Zi5FbXB0eQ==", - "id": "018bf9bf-27e4-8000-8dd0-26277c78790e", - "specversion": "1.0", - "source": "/body.access//door.front_left#Door", - "type": "res.v1", - "priority": "UPRIORITY_CS2", - "ttl": 3, - "hash": "somehash", - "reqid": "request_idFromRequestCloudEvent", - "sink": "/body.access//door.front_left#Door", - "commstatus": 3, - "test_detail": "test_create_a_failed_response_cloud_event" - } -] \ No newline at end of file diff --git a/tests/test_cloudevent/test_serialize/test_base64protobufserializer.py b/tests/test_cloudevent/test_serialize/test_base64protobufserializer.py deleted file mode 100644 index e036085..0000000 --- a/tests/test_cloudevent/test_serialize/test_base64protobufserializer.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.serialize.base64protobufserializer import ( - Base64ProtobufSerializer, -) -from uprotocol.cloudevent.serialize.cloudeventserializers import ( - CloudEventSerializers, -) - - -class TestBase64ProtobufSerializer(unittest.TestCase): - def test_deserialize_bytes_to_string(self): - ce = CloudEventFactory.build_base_cloud_event( - "hello", - "http://localhost", - bytearray(), - "", - UCloudEventAttributesBuilder().build(), - "example.vertx", - ) - ce.__delitem__("time") - bytes_data = CloudEventSerializers.PROTOBUF.serializer().serialize(ce) - payload = Base64ProtobufSerializer().deserialize(bytes_data) - self.assertEqual( - "CgVoZWxsbxIQaHR0cDovL2xvY2FsaG9zdBoDMS4wIg1leGFtcGxlLnZlcnR4", - payload, - ) - - def test_deserialize_bytes_to_string_when_bytes_is_null(self): - payload = Base64ProtobufSerializer().deserialize(None) - self.assertEqual("", payload) - - def test_deserialize_bytes_to_string_when_bytes_is_empty(self): - payload = Base64ProtobufSerializer().deserialize(bytearray()) - self.assertEqual("", payload) - - def test_serialize_string_into_bytes(self): - json_str = ( - "eyJzcGVjdmVyc2lvbiI6ICIxLjAiLCAiaWQiOiAiaGVsbG8iLCAic2" - + "91cmNlIjogImh0dHA6Ly9sb2NhbGhvc3QiLCAidHlwZSI6ICJleGFtcGxlLnZlcnR4IiwgImRhdGFfYmFzZTY0IjogIiJ9" - ) - bytes_json = Base64ProtobufSerializer().serialize(json_str) - - ce = CloudEventFactory.build_base_cloud_event( - "hello", - "http://localhost", - bytearray(), - "", - UCloudEventAttributesBuilder().build(), - "example.vertx", - ) - ce.__delitem__("time") - - bytes_data = CloudEventSerializers.JSON.serializer().serialize(ce) - self.assertEqual(bytes_json, bytes_data) - - def test_serialize_string_into_bytes_when_string_is_null(self): - bytes_data = Base64ProtobufSerializer().serialize(None) - self.assertEqual(bytearray(), bytes_data) - - def test_serialize_string_into_bytes_when_string_is_empty(self): - bytes_data = Base64ProtobufSerializer().serialize("") - self.assertEqual(bytearray(), bytes_data) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_cloudevent/test_serialize/test_cloudeventtojsonserializer.py b/tests/test_cloudevent/test_serialize/test_cloudeventtojsonserializer.py deleted file mode 100644 index 7f5540d..0000000 --- a/tests/test_cloudevent/test_serialize/test_cloudeventtojsonserializer.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from google.protobuf import any_pb2 - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.cloudevent.serialize.cloudeventserializers import ( - CloudEventSerializers, -) -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority - -PROTO_CONTENT_TYPE = CloudEventFactory.PROTOBUF_CONTENT_TYPE -serializer = CloudEventSerializers.JSON.serializer() - - -def build_proto_payload_for_test(): - ce_proto = CloudEvent( - spec_version="1.0", - source="https://example.com", - id="hello", - type="example.demo", - proto_data=any_pb2.Any(), - attributes={"ttl": CloudEvent.CloudEventAttributeValue(ce_string="3")}, - ) - - any_obj = any_pb2.Any() - any_obj.Pack(ce_proto) - return any_obj - - -class TestCloudEventToJsonSerializer(unittest.TestCase): - def test_serialize_cloud_event_to_json(self): - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS1).with_ttl(3).build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "hello", - "/body.access/1/door.front_left", - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - cloud_event.__setitem__("datacontenttype", PROTO_CONTENT_TYPE) - cloud_event.__setitem__("dataschema", proto_payload.type_url) - cloud_event.__delitem__("time") - bytes_data = serializer.serialize(cloud_event) - json_str = bytes_data.decode("utf-8") - expected = ( - '{"specversion": "1.0", "id": "hello", "source": "/body.access/1/door.front_left", ' - '"type": "pub.v1", "datacontenttype": "application/x-protobuf", "dataschema": ' - '"type.googleapis.com/io.cloudevents.v1.CloudEvent", "data_base64": ' - '"CjB0eXBlLmdvb2dsZWFwaXMuY29tL2lvLmNsb3VkZXZlbnRzLnYxLkNsb3VkRXZlbnQSPQoFaGVs' - 'bG8SE2h0dHBzOi8vZXhhbXBsZS5jb20aAzEuMCIMZXhhbXBsZS5kZW1vKgoKA3R0bBIDGgEzQgA=", ' - '"ttl": 3, "priority": "UPRIORITY_CS1"}' - ) - self.assertEqual(expected, json_str) - - def test_serialize_and_deserialize_cloud_event_to_json(self): - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS1).with_ttl(3).build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "hello", - "/body.access/1/door.front_left", - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - cloud_event.__setitem__("datacontenttype", PROTO_CONTENT_TYPE) - cloud_event.__setitem__("dataschema", proto_payload.type_url) - cloud_event.__delitem__("time") - serialized_data = serializer.serialize(cloud_event) - deserialized_data = serializer.deserialize(serialized_data) - deserialized_data.__delitem__("time") - - self.assertEqual(cloud_event, deserialized_data) - - def test_double_serialization_protobuf_when_creating_cloud_event_with_factory_methods( - self, - ): - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS1).with_ttl(3).build() - ) - - # build the cloud event - cloud_event1 = CloudEventFactory.build_base_cloud_event( - "hello", - "/body.access/1/door.front_left", - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - cloud_event1.__setitem__("datacontenttype", PROTO_CONTENT_TYPE) - cloud_event1.__setitem__("dataschema", proto_payload.type_url) - cloud_event1.__delitem__("time") - serialized_data1 = serializer.serialize(cloud_event1) - cloud_event2 = serializer.deserialize(serialized_data1) - cloud_event2.__delitem__("time") - self.assertEqual(cloud_event2, cloud_event1) - serialized_data2 = serializer.serialize(cloud_event2) - self.assertEqual(serialized_data1, serialized_data2) - cloud_event3 = serializer.deserialize(serialized_data2) - cloud_event3.__delitem__("time") - - payload1 = UCloudEvent.unpack(cloud_event3, CloudEvent) - self.assertEqual(cloud_event2, cloud_event3) - payload2 = CloudEvent() - payload2.ParseFromString(proto_payload.value) - self.assertEqual(payload1, payload2) - self.assertEqual(cloud_event1, cloud_event3) diff --git a/tests/test_cloudevent/test_serialize/test_cloudeventtoprotobufserializer.py b/tests/test_cloudevent/test_serialize/test_cloudeventtoprotobufserializer.py deleted file mode 100644 index 08212d6..0000000 --- a/tests/test_cloudevent/test_serialize/test_cloudeventtoprotobufserializer.py +++ /dev/null @@ -1,207 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import json -import os -import unittest - -from cloudevents.http import CloudEvent as ApacheCloudEvent -from google.protobuf import any_pb2 - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.cloudevent.serialize.base64protobufserializer import ( - Base64ProtobufSerializer, -) -from uprotocol.cloudevent.serialize.cloudeventserializers import ( - CloudEventSerializers, -) -from uprotocol.cloudevent.serialize.cloudeventtoprotobufserializer import ( - CloudEventToProtobufSerializer, -) -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority -from uprotocol.proto.uri_pb2 import UEntity, UResource, UUri -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer - -serializer = CloudEventSerializers.PROTOBUF.serializer() - - -def build_uuri_for_test(): - uri = UUri( - entity=UEntity(name="body.access"), - resource=UResource(name="door", instance="front_left", message="Door"), - ) - return LongUriSerializer().serialize(uri) - - -def build_proto_payload_for_test(): - ce_proto = CloudEvent( - spec_version="1.0", - source="https://example.com", - id="hello", - type="example.demo", - proto_data=any_pb2.Any(), - attributes={"ttl": CloudEvent.CloudEventAttributeValue(ce_string="3")}, - ) - - any_obj = any_pb2.Any() - any_obj.Pack(ce_proto) - return any_obj - - -def get_json_object(): - current_directory = os.getcwd() - json_file_path = os.path.join( - current_directory, - "tests", - "test_cloudevent", - "test_serialize", - "cloudevent_to_protobuf.json", - ) - with open(json_file_path, "r") as json_file: - json_data = json.load(json_file) - - return json_data - - -class TestCloudEventToProtobufSerializer(unittest.TestCase): - def test_all_cloud_events_from_json(self): - cloudevents = get_json_object() - for ce_json in cloudevents: - bytes_ce = Base64ProtobufSerializer().serialize(ce_json["serialized_ce"]) - cloudevent = CloudEventToProtobufSerializer().deserialize(bytes_ce) - self.assertEqual(UCloudEvent.get_id(cloudevent), ce_json["id"]) - self.assertEqual(UCloudEvent.get_specversion(cloudevent), ce_json["specversion"]) - if "source" in ce_json: - self.assertEqual(UCloudEvent.get_source(cloudevent), ce_json["source"]) - if "sink" in ce_json: - self.assertEqual(UCloudEvent.get_sink(cloudevent), ce_json["sink"]) - if "type" in ce_json: - self.assertEqual(UCloudEvent.get_type(cloudevent), ce_json["type"]) - if "priority" in ce_json: - self.assertEqual(UCloudEvent.get_priority(cloudevent), ce_json["priority"]) - if "ttl" in ce_json: - self.assertEqual(UCloudEvent.get_ttl(cloudevent), ce_json["ttl"]) - if "hash" in ce_json: - self.assertEqual(UCloudEvent.get_hash(cloudevent), ce_json["hash"]) - if "token" in ce_json: - self.assertEqual(UCloudEvent.get_token(cloudevent), ce_json["token"]) - if "dataschema" in ce_json: - self.assertEqual( - UCloudEvent.get_data_schema(cloudevent), - ce_json["dataschema"], - ) - if "datacontenttype" in ce_json: - self.assertEqual( - UCloudEvent.get_data_content_type(cloudevent), - ce_json["datacontenttype"], - ) - if "commstatus" in ce_json: - self.assertEqual( - UCloudEvent.get_communication_status(cloudevent), - ce_json["commstatus"], - ) - - def test_serialize_and_deserialize_cloud_event_to_protobuf(self): - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS0) - .with_ttl(3) - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "hello", - "/body.access/1/door.front_left", - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - - cloud_event.__delitem__("time") - serialized_data = serializer.serialize(cloud_event) - - deserialized_data = serializer.deserialize(serialized_data) - deserialized_data.__delitem__("time") - self.assertEqual(cloud_event, deserialized_data) - - def test_serialize_two_different_cloud_event_are_not_the_same(self): - proto_payload = build_proto_payload_for_test() - json_attributes1 = { - "id": "hello", - "source": "/body.access/1/door.front_left", - "type": "pub.v1", - } - cloud_event1 = ApacheCloudEvent(json_attributes1, proto_payload.SerializeToString()) - cloud_event1.__setitem__("datacontenttype", "application/protobuf") - cloud_event1.__setitem__("dataschema", proto_payload.type_url) - cloud_event1.__delitem__("time") - - json_attributes2 = { - "source": "/body.access/1/door.front_left", - "type": "file.v1", - } - cloud_event2 = ApacheCloudEvent(json_attributes2, proto_payload.SerializeToString()) - cloud_event2.__delitem__("time") - serialized1 = serializer.serialize(cloud_event1) - serialized2 = serializer.serialize(cloud_event2) - self.assertNotEqual(serialized1, serialized2) - - def test_double_serialization_protobuf_when_creating_cloud_event_with_factory_methods( - self, - ): - proto_payload = build_proto_payload_for_test() - source = build_uuri_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - cloud_event.__delitem__("time") - - serialized_data = serializer.serialize(cloud_event) - deserialized_data = serializer.deserialize(serialized_data) - deserialized_data.__delitem__("time") - self.assertEqual(cloud_event, deserialized_data) diff --git a/tests/test_cloudevent/test_validator/__init__.py b/tests/test_cloudevent/test_validator/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_cloudevent/test_validator/test_cloudeventvalidator.py b/tests/test_cloudevent/test_validator/test_cloudeventvalidator.py deleted file mode 100644 index 683fd40..0000000 --- a/tests/test_cloudevent/test_validator/test_cloudeventvalidator.py +++ /dev/null @@ -1,608 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from google.protobuf import any_pb2 - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributesBuilder, -) -from uprotocol.cloudevent.factory.cloudeventfactory import CloudEventFactory -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.cloudevent.validate.cloudeventvalidator import ( - CloudEventValidator, - Validators, -) -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority -from uprotocol.proto.uri_pb2 import UEntity, UResource, UUri -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uuid.factory.uuidfactory import Factories -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer -from uprotocol.validation.validationresult import ValidationResult - - -def build_base_publish_cloud_event_for_test(): - # uri - source = build_long_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - return cloud_event - - pass - - -def build_base_notification_cloud_event_for_test(): - # uri - source = build_long_uri_for_test() - - # fake payload - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder() - .with_hash("somehash") - .with_priority(UPriority.UPRIORITY_CS1) - .with_ttl(3) - .with_token("someOAuthToken") - .build() - ) - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - "testme", - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_NOTIFICATION), - ) - return cloud_event - - pass - - -def build_proto_payload_for_test(): - ce_proto = CloudEvent( - spec_version="1.0", - source="https://example.com", - id="hello", - type="example.demo", - proto_data=any_pb2.Any(), - ) - - any_obj = any_pb2.Any() - any_obj.Pack(ce_proto) - return any_obj - - -def build_uuri_for_test(): - return UUri( - entity=UEntity(name="body.access"), - resource=UResource(name="door", instance="front_left", message="Door"), - ) - - -def build_long_uri_for_test(): - return LongUriSerializer().serialize(build_uuri_for_test()) - - -class TestCloudEventValidator(unittest.TestCase): - def test_get_a_publish_cloud_event_validator(self): - cloud_event = build_base_publish_cloud_event_for_test() - validator = CloudEventValidator.get_validator(cloud_event) - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - self.assertEqual("CloudEventValidator.Publish", str(validator)) - - def test_get_a_notification_cloud_event_validator(self): - cloud_event = build_base_notification_cloud_event_for_test() - cloud_event.__setitem__("sink", "//bo.cloud/petapp") - validator = Validators.NOTIFICATION.validator() - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - self.assertEqual("CloudEventValidator.Notification", str(validator)) - - def test_publish_cloud_event_type(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "res.v1") - validator = Validators.PUBLISH.validator() - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent type [res.v1]. CloudEvent of type Publish must have a type of 'pub.v1'", - status.message, - ) - - def test_notification_cloud_event_type(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "res.v1") - validator = Validators.NOTIFICATION.validator() - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent type [res.v1]. CloudEvent of type Notification must have a type of 'not.v1'", - status.message, - ) - - def test_get_a_request_cloud_event_validator(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "req.v1") - validator = CloudEventValidator.get_validator(cloud_event) - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - self.assertEqual("CloudEventValidator.Request", str(validator)) - - def test_request_cloud_event_type(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "pub.v1") - validator = Validators.REQUEST.validator() - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent type [pub.v1]. CloudEvent of type Request must have a type of 'req.v1'", - status.message, - ) - - def test_get_a_response_cloud_event_validator(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "res.v1") - validator = CloudEventValidator.get_validator(cloud_event) - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - self.assertEqual("CloudEventValidator.Response", str(validator)) - - def test_response_cloud_event_type(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "pub.v1") - validator = Validators.RESPONSE.validator() - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent type [pub.v1]. CloudEvent of type Response must have a type of 'res.v1'", - status.message, - ) - - def test_get_a_publish_cloud_event_validator_when_cloud_event_type_is_unknown( - self, - ): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "lala.v1") - validator = CloudEventValidator.get_validator(cloud_event) - self.assertEqual("CloudEventValidator.Publish", str(validator)) - - def test_validate_cloud_event_version_when_valid(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("id", str_uuid) - status = CloudEventValidator.validate_version(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - - def test_validate_cloud_event_version_when_not_valid(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("specversion", "0.3") - cloud_event.__setitem__("id", str_uuid) - status = CloudEventValidator.validate_version(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent version [0.3]. CloudEvent version must be 1.0.", - status.message, - ) - - def test_validate_cloud_event_id_when_valid(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - status = CloudEventValidator.validate_version(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - - def test_validate_cloud_event_id_when_not_uuidv8_type_id(self): - str_uuid = "1dd9200c-d41b-4658-8102-3101f0b91378" - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - status = CloudEventValidator.validate_id(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent Id [" + str_uuid + "]. CloudEvent Id must be of type UUIDv8.", - status.message, - ) - - def test_validate_cloud_event_id_when_not_valid(self): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", "testme") - cloud_event.__setitem__("type", "pub.v1") - status = CloudEventValidator.validate_id(cloud_event).to_status() - self.assertEqual(UCode.INVALID_ARGUMENT, status.code) - self.assertEqual( - "Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.", - status.message, - ) - - def test_publish_type_cloudevent_is_valid_when_everything_is_valid_local( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "/body.access/1/door.front_left#Door") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_publish_type_cloudevent_is_valid_when_everything_is_valid_remote( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/door.front_left#Door") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_publish_type_cloudevent_is_valid_when_everything_is_valid_remote_with_a_sink( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/door.front_left#Door") - cloud_event.__setitem__("sink", "//bo.cloud/petapp") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_publish_type_cloudevent_is_not_valid_when_remote_with_invalid_sink( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/door.front_left#Door") - cloud_event.__setitem__("sink", "//bo.cloud") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid CloudEvent sink [//bo.cloud]. Uri is missing uSoftware Entity name.", - result.get_message(), - ) - - def test_publish_type_cloudevent_is_not_valid_when_source_is_empty(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("source", "/") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid Publish type CloudEvent source [/]. Uri is empty.", - result.get_message(), - ) - - def test_notification_type_cloudevent_is_not_valid_when_source_is_empty( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_notification_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("source", "/") - validator = Validators.NOTIFICATION.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid Notification type CloudEvent source [/], Uri is empty.," - + "Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.", - result.get_message(), - ) - - def test_publish_type_cloudevent_is_not_valid_when_source_is_missing_authority( - self, - ): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", "testme") - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "/body.access") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.," - + "Invalid Publish type " - + "CloudEvent source [/body.access]. UriPart is missing uResource name.", - result.get_message(), - ) - - def test_publish_type_cloudevent_is_not_valid_when_source_is_missing_message_info( - self, - ): - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", "testme") - cloud_event.__setitem__("type", "pub.v1") - cloud_event.__setitem__("source", "/body.access/1/door.front_left") - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid CloudEvent Id [testme]. CloudEvent Id must be of type UUIDv8.," - + "Invalid Publish type " - + "CloudEvent source [/body.access/1/door.front_left]. UriPart is missing Message information.", - result.get_message(), - ) - - def test_notification_type_cloudevent_is_valid_when_everything_is_valid( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_notification_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "not.v1") - cloud_event.__setitem__("source", "/body.access/1/door.front_left#Door") - cloud_event.__setitem__("sink", "//bo.cloud/petapp") - validator = Validators.NOTIFICATION.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_notification_type_cloudevent_is_not_valid_missing_sink(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_notification_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "not.v1") - cloud_event.__setitem__("source", "/body.access/1/door.front_left#Door") - validator = Validators.NOTIFICATION.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.", - result.get_message(), - ) - - def test_notification_type_cloudevent_is_not_valid_invalid_sink(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_notification_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "not.v1") - cloud_event.__setitem__("sink", "//bo.cloud") - cloud_event.__setitem__("source", "/body.access/1/door.front_left#Door") - validator = Validators.NOTIFICATION.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid Notification type CloudEvent sink [//bo.cloud]. Uri is missing uSoftware Entity name.", - result.get_message(), - ) - - def test_request_type_cloudevent_is_valid_when_everything_is_valid(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "req.v1") - cloud_event.__setitem__("sink", "//VCU.myvin/body.access/1/rpc.UpdateDoor") - cloud_event.__setitem__("source", "//bo.cloud/petapp//rpc.response") - validator = Validators.REQUEST.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_request_type_cloudevent_is_not_valid_invalid_source(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "req.v1") - cloud_event.__setitem__("sink", "//VCU.myvin/body.access/1/rpc.UpdateDoor") - cloud_event.__setitem__("source", "//bo.cloud/petapp//dog") - validator = Validators.REQUEST.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Request CloudEvent source [//bo.cloud/petapp//dog]. " - + "Invalid RPC uri application " - + "response topic. UriPart is missing rpc.response.", - result.get_message(), - ) - - def test_request_type_cloudevent_is_not_valid_missing_sink(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "req.v1") - cloud_event.__setitem__("source", "//bo.cloud/petapp//rpc.response") - validator = Validators.REQUEST.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Request CloudEvent sink. Request CloudEvent sink must be uri for the method to be called.", - result.get_message(), - ) - - def test_request_type_cloudevent_is_not_valid_invalid_sink_not_rpc_command( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "req.v1") - cloud_event.__setitem__("source", "//bo.cloud/petapp//rpc.response") - cloud_event.__setitem__("sink", "//VCU.myvin/body.access/1/UpdateDoor") - validator = Validators.REQUEST.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Request CloudEvent sink [//VCU.myvin/body.access/1/UpdateDoor]. " - + "Invalid RPC method " - + "uri. UriPart should be the method to be called, or method from response.", - result.get_message(), - ) - - def test_response_type_cloudevent_is_valid_when_everything_is_valid(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "res.v1") - cloud_event.__setitem__("sink", "//bo.cloud/petapp//rpc.response") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/rpc.UpdateDoor") - validator = Validators.RESPONSE.validator() - result = validator.validate(cloud_event) - self.assertEqual(ValidationResult.success(), result) - - def test_response_type_cloudevent_is_not_valid_invalid_source(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "res.v1") - cloud_event.__setitem__("sink", "//bo.cloud/petapp//rpc.response") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/UpdateDoor") - validator = Validators.RESPONSE.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Response CloudEvent source [//VCU.myvin/body.access/1/UpdateDoor]. " - + "Invalid RPC " - + "method uri. UriPart should be the method to be called, or method from response.", - result.get_message(), - ) - - def test_response_type_cloudevent_is_not_valid_missing_sink_and_invalid_source( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "res.v1") - cloud_event.__setitem__("source", "//VCU.myvin/body.access/1/UpdateDoor") - validator = Validators.RESPONSE.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Response CloudEvent source [//VCU.myvin/body.access/1/UpdateDoor]. " - + "Invalid RPC " - + "method uri. UriPart should be the method to be called, or method from response.," - + "Invalid" - + " CloudEvent sink. Response CloudEvent sink must be uri the destination of the response.", - result.get_message(), - ) - - def test_response_type_cloudevent_is_not_valid_invalid_sink(self): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "res.v1") - cloud_event.__setitem__("sink", "//bo.cloud") - cloud_event.__setitem__("source", "//VCU.myvin") - validator = Validators.RESPONSE.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Response CloudEvent source [//VCU.myvin]. Invalid RPC method uri. Uri is missing " - + "uSoftware Entity name.,Invalid RPC Response CloudEvent sink [//bo.cloud]. Invalid RPC uri " - + "application response topic. Uri is missing uSoftware Entity name.", - result.get_message(), - ) - - def test_response_type_cloudevent_is_not_valid_invalid_source_not_rpc_command( - self, - ): - uuid = Factories.UPROTOCOL.create() - str_uuid = LongUuidSerializer.instance().serialize(uuid) - cloud_event = build_base_publish_cloud_event_for_test() - cloud_event.__setitem__("id", str_uuid) - cloud_event.__setitem__("type", "res.v1") - cloud_event.__setitem__("source", "//bo.cloud/petapp/1/dog") - cloud_event.__setitem__("sink", "//VCU.myvin/body.access/1/UpdateDoor") - validator = Validators.RESPONSE.validator() - result = validator.validate(cloud_event) - self.assertEqual( - "Invalid RPC Response CloudEvent source [//bo.cloud/petapp/1/dog]. Invalid RPC method uri. UriPart " - + "should be the method to be called, or method from response.," - + "Invalid RPC Response " - + "CloudEvent " - "sink [" - "//VCU.myvin/body.access/1/UpdateDoor]. " - + "Invalid RPC uri application " - + "response topic. UriPart is missing rpc.response.", - result.get_message(), - ) - - def test_create_a_v6_cloudevent_and_validate_it_against_sdk(self): - source = build_long_uri_for_test() - uuid = Factories.UUIDV6.create() - id = LongUuidSerializer.instance().serialize(uuid) - proto_payload = build_proto_payload_for_test() - # additional attributes - u_cloud_event_attributes = ( - UCloudEventAttributesBuilder().with_priority(UPriority.UPRIORITY_CS0).with_ttl(1000).build() - ) - # build the cloud event - cloud_event = CloudEventFactory.build_base_cloud_event( - id, - source, - proto_payload.SerializeToString(), - proto_payload.type_url, - u_cloud_event_attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - validator = Validators.PUBLISH.validator() - result = validator.validate(cloud_event) - self.assertTrue(result.is_success()) - self.assertFalse(UCloudEvent.is_expired(cloud_event)) - - def fetching_the_notification_validator(self): - cloud_event = build_base_notification_cloud_event_for_test() - validator = CloudEventValidator.get_validator(cloud_event) - status = validator.validate_type(cloud_event).to_status() - self.assertEqual(status, ValidationResult.STATUS_SUCCESS) - self.assertEqual("CloudEventValidator.Notification", str(validator)) diff --git a/tests/test_cloudevent/test_validator/test_validationresult.py b/tests/test_cloudevent/test_validator/test_validationresult.py deleted file mode 100644 index 225efa3..0000000 --- a/tests/test_cloudevent/test_validator/test_validationresult.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.ustatus_pb2 import UCode, UStatus -from uprotocol.validation.validationresult import ValidationResult - - -class TestValidationResultTest(unittest.TestCase): - def test_success_validation_result_to_string(self): - success = ValidationResult.success() - self.assertEqual("ValidationResult.Success()", str(success)) - - def test_failure_validation_result_to_string(self): - failure = ValidationResult.failure("boom") - self.assertEqual("ValidationResult.Failure(message='boom')", str(failure)) - - def test_success_validation_result_is_success(self): - success = ValidationResult.success() - self.assertTrue(success.is_success()) - - def test_failure_validation_result_is_success(self): - failure = ValidationResult.failure("boom") - self.assertFalse(failure.is_success()) - - def test_success_validation_result_get_message(self): - success = ValidationResult.success() - self.assertTrue(success.get_message() == '') - - def test_failure_validation_result_get_message(self): - failure = ValidationResult.failure("boom") - self.assertEqual("boom", failure.get_message()) - - def test_success_validation_result_to_status(self): - success = ValidationResult.success() - self.assertEqual(ValidationResult.STATUS_SUCCESS, success.to_status()) - - def test_failure_validation_result_to_status(self): - failure = ValidationResult.failure("boom") - status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(status, failure.to_status()) diff --git a/tests/test_rpc/__init__.py b/tests/test_rpc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_rpc/test_calloptions.py b/tests/test_rpc/test_calloptions.py deleted file mode 100644 index 15b39ee..0000000 --- a/tests/test_rpc/test_calloptions.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.uattributes_pb2 import CallOptions - - -class TestCallOptions(unittest.TestCase): - def test_to_string(self): - call_options = CallOptions(ttl=30, token="someToken") - self.assertEqual(30, call_options.ttl) - self.assertEqual("someToken", call_options.token) - - def test_creating_call_options_with_a_token(self): - call_options = CallOptions(token="someToken") - self.assertTrue(call_options.HasField("token")) - self.assertTrue(call_options.token == "someToken") - - def test_creating_call_options_without_token(self): - call_options = CallOptions() - self.assertFalse(call_options.HasField("token")) - self.assertEqual(0, call_options.ttl) - - def test_creating_call_options_with_an_empty_string_token(self): - call_options = CallOptions(token="") - self.assertTrue(call_options.HasField("token")) - self.assertTrue(call_options.token == "") - - def test_creating_call_options_with_a_token_with_only_spaces(self): - call_options = CallOptions(token=" ") - self.assertTrue(call_options.HasField("token")) - self.assertTrue(call_options.token.strip() == "") - - def test_creating_call_options_with_a_timeout(self): - call_options = CallOptions(ttl=30) - self.assertEqual(30, call_options.ttl) - self.assertTrue(call_options.token is None or call_options.token == "") - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_rpc/test_rpc.py b/tests/test_rpc/test_rpc.py deleted file mode 100644 index 08a2053..0000000 --- a/tests/test_rpc/test_rpc.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest -from concurrent.futures import Future - -from google.protobuf.any_pb2 import Any -from google.protobuf.wrappers_pb2 import Int32Value - -from uprotocol.cloudevent.cloudevents_pb2 import CloudEvent -from uprotocol.proto.uattributes_pb2 import CallOptions -from uprotocol.proto.umessage_pb2 import UMessage -from uprotocol.proto.upayload_pb2 import UPayload, UPayloadFormat -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UUri -from uprotocol.proto.ustatus_pb2 import UCode, UStatus -from uprotocol.rpc.rpcclient import RpcClient -from uprotocol.rpc.rpcmapper import RpcMapper -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer - - -def build_source(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_request(None), - ) - - -def build_cloud_event(): - return CloudEvent( - spec_version="1.0", - source="https://example.com", - id="HARTLEY IS THE BEST", - ) - - -def build_upayload(): - any_obj = Any() - any_obj.Pack(build_cloud_event()) - return UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=any_obj.SerializeToString(), - ) - - -def build_topic(): - return LongUriSerializer().deserialize("//vcu.vin/hartley/1/rpc.Raise") - - -def build_calloptions(): - return CallOptions(ttl=1000) - - -class ReturnsNumber3(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - any_obj = Any() - any_obj.Pack(Int32Value(value=3)) - data = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=any_obj.SerializeToString(), - ) - future.set_result(UMessage(payload=data)) - return future - - -class HappyPath(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - data = build_upayload() - future.set_result(UMessage(payload=data)) - return future - - -class WithUStatusCodeInsteadOfHappyPath(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - any_value = Any() - any_value.Pack(status) - data = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=any_value.SerializeToString(), - ) - future.set_result(UMessage(payload=data)) - return future - - -class WithUStatusCodeHappyPath(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - status = UStatus(code=UCode.OK, message="all good") - any_value = Any() - any_value.Pack(status) - data = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=any_value.SerializeToString(), - ) - future.set_result(UMessage(payload=data)) - return future - - -class ThatBarfsCrapyPayload(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - response = UPayload(format=UPayloadFormat.UPAYLOAD_FORMAT_RAW, value=bytes([0])) - future.set_result(UMessage(payload=response)) - return future - - -class ThatCompletesWithAnException(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - future.set_exception(RuntimeError("Boom")) - return future - - -class ThatReturnsTheWrongProto(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - any_value = Any() - any_value.Pack(Int32Value(value=42)) - data = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=any_value.SerializeToString(), - ) - future.set_result(UMessage(payload=data)) - return future - - -class WithNullMessage(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - future.set_result(UMessage()) - return future - - -class WithNullInPayload(RpcClient): - def invoke_method(self, topic: UUri, payload: UPayload, options: CallOptions): - future = Future() - future.set_result(None) - return future - - -def rpc_response(invoke_method_response: Future): - payload, exception = invoke_method_response.result() - - any_value = Any() - try: - any_value.ParseFromString(payload.value) - except Exception as e: - raise RuntimeError(str(e)) from e - - # invoke method had some unexpected problem. - if exception is not None: - raise RuntimeError(str(exception)) from exception - - # test to see if we have the expected type - if any_value.Is(CloudEvent.DESCRIPTOR): - try: - cloud_event = CloudEvent() - any_value.Unpack(cloud_event) - return cloud_event - except Exception as e: - raise RuntimeError(str(e)) from e - - # this will be called only if the expected return type is not status, but status was returned to - # indicate a problem. - if any_value.Is(UStatus.DESCRIPTOR): - try: - status = UStatus() - any_value.Unpack(status) - raise RuntimeError(f"Error returned, status code: [{status.code}], message: [{status.message}]") - except Exception as e: - raise RuntimeError(f"{str(e)} [com.google.grpc.UStatus]") from e - - raise RuntimeError(f"Unknown payload type [{any_value.type_url}]") - - -class TestRpc(unittest.TestCase): - def test_compose_happy_path(self): - rpc_response = RpcMapper.map_response_to_result( - ReturnsNumber3().invoke_method(build_topic(), build_upayload(), build_calloptions()), - Int32Value, - ) - mapped = rpc_response.map(lambda x: x.value + 5) - self.assertTrue(rpc_response.is_success()) - self.assertEqual(8, mapped.success_value()) - - def test_compose_that_returns_status(self): - rpc_response = RpcMapper.map_response_to_result( - WithUStatusCodeInsteadOfHappyPath().invoke_method(build_topic(), build_upayload(), build_calloptions()), - Int32Value, - ) - mapped = rpc_response.map(lambda x: x.value + 5) - self.assertTrue(rpc_response.is_failure()) - self.assertEqual(UCode.INVALID_ARGUMENT, mapped.failure_value().code) - self.assertEqual("boom", mapped.failure_value().message) - - def test_compose_with_failure(self): - rpc_response = RpcMapper.map_response_to_result( - ThatCompletesWithAnException().invoke_method(build_topic(), build_upayload(), build_calloptions()), - Int32Value, - ) - mapped = rpc_response.map(lambda x: x.value + 5) - self.assertTrue(rpc_response.is_failure()) - status = UStatus(code=UCode.UNKNOWN, message="Boom") - self.assertEqual(status, mapped.failure_value()) - - def test_map_response_with_payload_is_null(self): - rpc_response = RpcMapper.map_response_to_result( - WithNullInPayload().invoke_method(build_topic(), None, build_calloptions()), - UStatus, - ) - mapped = rpc_response.map(lambda x: x.value + 5) - self.assertTrue(rpc_response.is_failure()) - self.assertEqual(UCode.UNKNOWN, mapped.failure_value().code) - self.assertEqual( - "Server returned a null payload. Expected UStatus", - mapped.failure_value().message, - ) - - def test_success_invoke_method_happy_flow_using_map_response_to_rpc_response( - self, - ): - rpc_response = RpcMapper.map_response_to_result( - HappyPath().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - self.assertTrue(rpc_response.is_success()) - self.assertEqual(build_cloud_event(), rpc_response.success_value()) - - def test_fail_invoke_method_when_invoke_method_returns_a_status_using_map_response_to_rpc_response( - self, - ): - rpc_response = RpcMapper.map_response_to_result( - WithUStatusCodeInsteadOfHappyPath().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - self.assertTrue(rpc_response.is_failure()) - self.assertEqual(UCode.INVALID_ARGUMENT, rpc_response.failure_value().code) - self.assertEqual("boom", rpc_response.failure_value().message) - - def test_fail_invoke_method_when_invoke_method_threw_an_exception_using_map_response_to_rpc_response( - self, - ): - rpc_response = RpcMapper.map_response_to_result( - ThatCompletesWithAnException().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - self.assertTrue(rpc_response.is_failure()) - self.assertEqual(UCode.UNKNOWN, rpc_response.failure_value().code) - self.assertEqual("Boom", rpc_response.failure_value().message) - - def test_fail_invoke_method_when_invoke_method_returns_a_bad_proto_using_map_response_to_rpc_response( - self, - ): - rpc_response = RpcMapper.map_response_to_result( - ThatReturnsTheWrongProto().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - self.assertTrue(rpc_response.is_failure()) - self.assertEqual(UCode.UNKNOWN, rpc_response.failure_value().code) - self.assertEqual( - "Unknown payload type [type.googleapis.com/google.protobuf.Int32Value]. Expected [" - "io.cloudevents.v1.CloudEvent]", - rpc_response.failure_value().message, - ) - - def test_success_invoke_method_happy_flow_using_map_response(self): - rpc_response = RpcMapper.map_response( - HappyPath().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - self.assertEqual(build_cloud_event(), rpc_response.result()) - - def test_fail_invoke_method_when_invoke_method_returns_a_status_using_map_response( - self, - ): - rpc_response = RpcMapper.map_response( - WithUStatusCodeInsteadOfHappyPath().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - exception = RuntimeError( - "Unknown payload type [type.googleapis.com/uprotocol.v1.UStatus]. Expected [CloudEvent]" - ) - self.assertEqual(str(exception), str(rpc_response.exception())) - - def test_map_response_when_response_message_is_null(self): - rpc_response = RpcMapper.map_response( - WithNullMessage().invoke_method(build_topic(), build_upayload(), build_calloptions()), - CloudEvent, - ) - exception = RuntimeError("Server returned a null payload. Expected CloudEvent") - self.assertEqual(str(exception), str(rpc_response.exception())) diff --git a/tests/test_rpc/test_rpcresult.py b/tests/test_rpc/test_rpcresult.py deleted file mode 100644 index c33d137..0000000 --- a/tests/test_rpc/test_rpcresult.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.ustatus_pb2 import UCode, UStatus -from uprotocol.rpc.rpcresult import RpcResult - - -def get_default(): - return 5 - - -def multiply_by_2(x): - return RpcResult.success(x * 2) - - -class TestRpcResult(unittest.TestCase): - def test_is_success_on_success(self): - result = RpcResult.success(2) - self.assertTrue(result.is_success()) - - def test_is_success_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertFalse(result.is_success()) - - def test_is_failure_on_success(self): - result = RpcResult.success(2) - self.assertFalse(result.is_failure()) - - def test_is_failure_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertTrue(result.is_failure()) - - def test_get_or_else_on_success(self): - result = RpcResult.success(2) - self.assertEqual(2, result.get_or_else(get_default())) - - def test_get_or_else_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(get_default(), result.get_or_else(get_default)) - - def test_get_or_else_on_success_(self): - result = RpcResult.success(2) - self.assertEqual(2, result.get_or_else(5)) - - def test_get_or_else_on_failure_(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(5, result.get_or_else(5)) - - def test_success_value_on_success(self): - result = RpcResult.success(2) - self.assertEqual(2, result.success_value()) - - def test_success_value_on_failure_(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - with self.assertRaises(Exception) as context: - result.success_value() - self.assertEqual(str(context.exception), "Method success_value() called on a Failure instance") - - def test_failure_value_on_success(self): - result = RpcResult.success(2) - with self.assertRaises(Exception) as context: - result.failure_value() - self.assertEqual(str(context.exception), "Method failure_value() called on a Success instance") - - def test_failure_value_on_failure_(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - result_value = result.failure_value() - expected_result = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(expected_result, result_value) - - def test_map_on_success(self): - result = RpcResult.success(2) - mapped = result.map(lambda x: x * 2) - self.assertTrue(mapped.is_success()) - self.assertEqual(4, mapped.success_value()) - - def test_map_success_when_function_throws_exception(self): - result = RpcResult.success(2) - mapped = result.map(self.fun_that_throws_exception_for_map) - self.assertTrue(mapped.is_failure()) - self.assertEqual(UCode.UNKNOWN, mapped.failure_value().code) - self.assertEqual("2 went boom", mapped.failure_value().message) - - def test_map_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - mapped = result.map(lambda x: x * 2) - self.assertTrue(mapped.is_failure()) - expected_status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(expected_status, mapped.failure_value()) - - def test_flat_map_success_when_function_throws_exception(self): - result = RpcResult.success(2) - flat_mapped = result.flat_map(self.fun_that_throws_exception_for_flat_map) - self.assertTrue(flat_mapped.is_failure()) - self.assertEqual(UCode.UNKNOWN, flat_mapped.failure_value().code) - self.assertEqual("2 went boom", flat_mapped.failure_value().message) - - def fun_that_throws_exception_for_flat_map(self, x): - raise ValueError(f"{x} went boom") - - def fun_that_throws_exception_for_map(self, x): - raise ValueError(f"{x} went boom") - - def test_flat_map_on_success(self): - result = RpcResult.success(2) - flat_mapped = result.flat_map(lambda x: RpcResult.success(x * 2)) - self.assertTrue(flat_mapped.is_success()) - self.assertEqual(4, flat_mapped.success_value()) - - def test_flat_map_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - flat_mapped = result.flat_map(lambda x: RpcResult.success(x * 2)) - self.assertTrue(flat_mapped.is_failure()) - expected_status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(expected_status, flat_mapped.failure_value()) - - def test_filter_on_success_that_fails(self): - result = RpcResult.success(2) - filter_result = result.filter(lambda i: i > 5) - self.assertTrue(filter_result.is_failure()) - expected_status = UStatus(code=UCode.FAILED_PRECONDITION, message="filtered out") - self.assertEqual(expected_status, filter_result.failure_value()) - - def test_filter_on_success_that_succeeds(self): - result = RpcResult.success(2) - filter_result = result.filter(lambda i: i < 5) - self.assertTrue(filter_result.is_success()) - self.assertEqual(2, filter_result.success_value()) - - def test_filter_on_success_when_function_throws_exception(self): - result = RpcResult.success(2) - filter_result = result.filter(self.predicate_that_throws_exception) - self.assertTrue(filter_result.is_failure()) - expected_status = UStatus(code=UCode.UNKNOWN, message="2 went boom") - self.assertEqual(expected_status, filter_result.failure_value()) - - def predicate_that_throws_exception(self, x): - raise ValueError(f"{x} went boom") - - def test_filter_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - filter_result = result.filter(lambda i: i > 5) - self.assertTrue(filter_result.is_failure()) - expected_status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(expected_status, filter_result.failure_value()) - - def test_flatten_on_success(self): - result = RpcResult.success(2) - mapped = result.map(multiply_by_2) - mapped_flattened = RpcResult.flatten(mapped) - self.assertTrue(mapped_flattened.is_success()) - self.assertEqual(4, mapped_flattened.success_value()) - - def test_flatten_on_success_with_function_that_fails(self): - result = RpcResult.success(2) - mapped = result.map(self.fun_that_throws_exception_for_flat_map) - mapped_flattened = RpcResult.flatten(mapped) - self.assertTrue(mapped_flattened.is_failure()) - expected_status = UStatus(code=UCode.UNKNOWN, message="2 went boom") - self.assertEqual(expected_status, mapped_flattened.failure_value()) - - def test_flatten_on_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - mapped = result.map(multiply_by_2) - mapped_flattened = RpcResult.flatten(mapped) - self.assertTrue(mapped_flattened.is_failure()) - expected_status = UStatus(code=UCode.INVALID_ARGUMENT, message="boom") - self.assertEqual(expected_status, mapped_flattened.failure_value()) - - def test_to_string_success(self): - result = RpcResult.success(2) - self.assertEqual("Success(2)", str(result)) - - def test_to_string_failure(self): - result = RpcResult.failure(code=UCode.INVALID_ARGUMENT, message="boom") - expected_string = "Failure(code: INVALID_ARGUMENT\n" "message: \"boom\"\n)" - self.assertEqual(expected_string, str(result)) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_transport/test_builder/test_uattributesbuilder.py b/tests/test_transport/test_builder/test_uattributesbuilder.py deleted file mode 100644 index 25872c7..0000000 --- a/tests/test_transport/test_builder/test_uattributesbuilder.py +++ /dev/null @@ -1,204 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.uattributes_pb2 import UMessageType, UPriority -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UUri -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.transport.builder.uattributesbuilder import UAttributesBuilder -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uuid.factory.uuidfactory import Factories - - -def build_source(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_request(None), - ) - - -def build_sink(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_response(), - ) - - -def get_uuid(): - return Factories.UPROTOCOL.create() - - -class TestUAttributesBuilder(unittest.TestCase): - def test_publish(self): - source = build_source() - builder = UAttributesBuilder.publish(source, UPriority.UPRIORITY_CS1) - self.assertIsNotNone(builder) - attributes = builder.build() - self.assertIsNotNone(attributes) - self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, attributes.type) - self.assertEqual(UPriority.UPRIORITY_CS1, attributes.priority) - - def test_notification(self): - source = build_source() - sink = build_sink() - builder = UAttributesBuilder.notification(source, sink, UPriority.UPRIORITY_CS1) - self.assertIsNotNone(builder) - attributes = builder.build() - self.assertIsNotNone(attributes) - self.assertEqual(UMessageType.UMESSAGE_TYPE_NOTIFICATION, attributes.type) - self.assertEqual(UPriority.UPRIORITY_CS1, attributes.priority) - self.assertEqual(sink, attributes.sink) - - def test_request(self): - source = build_source() - sink = build_sink() - ttl = 1000 - builder = UAttributesBuilder.request(source, sink, UPriority.UPRIORITY_CS4, ttl) - self.assertIsNotNone(builder) - attributes = builder.build() - self.assertIsNotNone(attributes) - self.assertEqual(UMessageType.UMESSAGE_TYPE_REQUEST, attributes.type) - self.assertEqual(UPriority.UPRIORITY_CS4, attributes.priority) - self.assertEqual(sink, attributes.sink) - self.assertEqual(ttl, attributes.ttl) - - def test_response(self): - source = build_source() - sink = build_sink() - req_id = get_uuid() - builder = UAttributesBuilder.response(source, sink, UPriority.UPRIORITY_CS6, req_id) - self.assertIsNotNone(builder) - attributes = builder.build() - self.assertIsNotNone(attributes) - self.assertEqual(UMessageType.UMESSAGE_TYPE_RESPONSE, attributes.type) - self.assertEqual(UPriority.UPRIORITY_CS6, attributes.priority) - self.assertEqual(sink, attributes.sink) - self.assertEqual(req_id, attributes.reqid) - - def test_response_with_existing_request(self): - request = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS6, 1000).build() - builder = UAttributesBuilder.response(request) - self.assertIsNotNone(builder) - response = builder.build() - self.assertIsNotNone(response) - self.assertEqual(UMessageType.UMESSAGE_TYPE_RESPONSE, response.type) - self.assertEqual(UPriority.UPRIORITY_CS6, response.priority) - self.assertEqual(request.sink, response.source) - self.assertEqual(request.source, response.sink) - self.assertEqual(request.id, response.reqid) - - def test_build(self): - req_id = get_uuid() - builder = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS1) - .with_ttl(1000) - .with_token("test_token") - .with_sink(build_sink()) - .with_permission_level(2) - .with_comm_status(UCode.CANCELLED) - .with_req_id(req_id) - .with_traceparent("test_traceparent") - ) - attributes = builder.build() - self.assertIsNotNone(attributes) - self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, attributes.type) - self.assertEqual(UPriority.UPRIORITY_CS1, attributes.priority) - self.assertEqual(1000, attributes.ttl) - self.assertEqual("test_token", attributes.token) - self.assertEqual(build_source(), attributes.source) - self.assertEqual(build_sink(), attributes.sink) - self.assertEqual(2, attributes.permission_level) - self.assertEqual(UCode.CANCELLED, attributes.commstatus) - self.assertEqual(req_id, attributes.reqid) - self.assertEqual("test_traceparent", attributes.traceparent) - - def test_publish_source_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(None, UPriority.UPRIORITY_CS1) - self.assertTrue("Source cannot be None." in context.exception) - - def test_publish_priority_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(build_source(), None) - self.assertTrue("UPriority cannot be null." in context.exception) - - def test_notification_source_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.notification(None, build_sink(), UPriority.UPRIORITY_CS1) - self.assertTrue("Source cannot be None." in context.exception) - - def test_notification_priority_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.notification(build_source(), build_sink(), None) - self.assertTrue("UPriority cannot be null." in context.exception) - - def test_notification_sink_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.notification(build_source(), None, UPriority.UPRIORITY_CS1) - self.assertTrue("sink cannot be null." in context.exception) - - def test_request_source_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request(None, build_sink(), UPriority.UPRIORITY_CS1, 1000) - self.assertTrue("Source cannot be None." in context.exception) - - def test_request_priority_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request(build_source(), build_sink(), None, 1000) - self.assertTrue("UPriority cannot be null." in context.exception) - - def test_request_sink_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request(build_source(), None, UPriority.UPRIORITY_CS1, 1000) - self.assertTrue("sink cannot be null." in context.exception) - - def test_request_ttl_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS1, None) - self.assertTrue("ttl cannot be null." in context.exception) - - def test_response_priority_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response(build_source(), build_sink(), None, get_uuid()) - self.assertTrue("UPriority cannot be null." in context.exception) - - def test_response_sink_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response(build_source(), None, UPriority.UPRIORITY_CS1, get_uuid()) - self.assertTrue("sink cannot be null." in context.exception) - - def test_response_reqid_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response(build_source(), build_sink(), UPriority.UPRIORITY_CS1, None) - self.assertTrue("reqid cannot be null." in context.exception) - - def test_response_request_is_none(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response(None) - self.assertTrue("request cannot be null." in context.exception) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_transport/test_builder/test_umessagebuilder.py b/tests/test_transport/test_builder/test_umessagebuilder.py new file mode 100644 index 0000000..1df670f --- /dev/null +++ b/tests/test_transport/test_builder/test_umessagebuilder.py @@ -0,0 +1,306 @@ +""" +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + +See the NOTICE file(s) distributed with this work for additional +information regarding copyright ownership. + +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at + + http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 +""" + +import unittest + +from google.protobuf.any_pb2 import Any + +from uprotocol.transport.builder.umessagebuilder import UMessageBuilder +from uprotocol.uuid.factory.uuidfactory import Factories +from uprotocol.v1.uattributes_pb2 import ( + UAttributes, + UMessageType, + UPayloadFormat, + UPriority, +) +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.umessage_pb2 import UMessage +from uprotocol.v1.uri_pb2 import UUri + + +def build_source(): + return UUri(ue_id=2, ue_version_major=1, resource_id=0) + + +def build_sink(): + return UUri(ue_id=2, ue_version_major=1, resource_id=0) + + +def get_uuid(): + return Factories.UPROTOCOL.create() + + +class TestUMessageBuilder(unittest.TestCase): + def test_publish(self): + """ + Test Publish + """ + publish: UMessage = UMessageBuilder.publish(build_source()).build() + self.assertIsNotNone(publish) + self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS1, publish.attributes.priority) + + def test_notification(self): + """ + Test Notification + """ + sink = build_sink() + notification: UMessage = UMessageBuilder.notification(build_source(), sink).build() + self.assertIsNotNone(notification) + self.assertEqual( + UMessageType.UMESSAGE_TYPE_NOTIFICATION, + notification.attributes.type, + ) + self.assertEqual(UPriority.UPRIORITY_CS1, notification.attributes.priority) + self.assertEqual(sink, notification.attributes.sink) + + def test_request(self): + """ + Test Request + """ + sink = build_sink() + ttl = 1000 + request: UMessage = UMessageBuilder.request(build_source(), sink, ttl).build() + self.assertIsNotNone(request) + self.assertEqual(UMessageType.UMESSAGE_TYPE_REQUEST, request.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, request.attributes.priority) + self.assertEqual(sink, request.attributes.sink) + self.assertEqual(ttl, request.attributes.ttl) + + def test_request_with_priority(self): + """ + Test Request + """ + sink = build_sink() + ttl = 1000 + request: UMessage = ( + UMessageBuilder.request(build_source(), sink, ttl).with_priority(UPriority.UPRIORITY_CS5).build() + ) + self.assertIsNotNone(request) + self.assertEqual(UMessageType.UMESSAGE_TYPE_REQUEST, request.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS5, request.attributes.priority) + self.assertEqual(sink, request.attributes.sink) + self.assertEqual(ttl, request.attributes.ttl) + + def test_response(self): + """ + Test Response + """ + sink = build_sink() + req_id = get_uuid() + response: UMessage = UMessageBuilder.response(build_source(), sink, req_id).build() + self.assertIsNotNone(response) + self.assertEqual(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, response.attributes.priority) + self.assertEqual(sink, response.attributes.sink) + self.assertEqual(req_id, response.attributes.reqid) + + def test_response_with_existing_request(self): + """ + Test Response with existing request + """ + request: UMessage = UMessageBuilder.request(build_source(), build_sink(), 1000).build() + response: UMessage = UMessageBuilder.response_for_request(request.attributes).build() + self.assertIsNotNone(response) + self.assertEqual(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, request.attributes.priority) + self.assertEqual(UPriority.UPRIORITY_CS4, response.attributes.priority) + self.assertEqual(request.attributes.source, response.attributes.sink) + self.assertEqual(request.attributes.sink, response.attributes.source) + self.assertEqual(request.attributes.id, response.attributes.reqid) + + def test_build(self): + """ + Test Build + """ + builder: UMessageBuilder = ( + UMessageBuilder.publish(build_source()) + .with_token("test_token") + .with_permission_level(2) + .with_commstatus(UCode.CANCELLED) + .with_traceparent("myParents") + ) + message: UMessage = builder.build() + attributes: UAttributes = message.attributes + self.assertIsNotNone(message) + self.assertIsNotNone(attributes) + self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS1, attributes.priority) + self.assertEqual("test_token", attributes.token) + self.assertEqual(2, attributes.permission_level) + self.assertEqual(UCode.CANCELLED, attributes.commstatus) + self.assertEqual("myParents", attributes.traceparent) + + def test_build_with_payload(self): + """ + Test Build with google.protobuf.Message payload + """ + message: UMessage = UMessageBuilder.publish(build_source()).build(build_sink()) + self.assertIsNotNone(message) + self.assertIsNotNone(message.payload) + self.assertEqual( + UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, + message.attributes.payload_format, + ) + self.assertEqual(message.payload, build_sink().SerializeToString()) + + def test_build_with_upayload(self): + """ + Test building UMessage with UPayload payload + """ + message: UMessage = UMessageBuilder.publish(build_source()).build( + UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, + build_sink().SerializeToString(), + ) + self.assertIsNotNone(message) + self.assertIsNotNone(message.payload) + self.assertEqual( + UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, + message.attributes.payload_format, + ) + self.assertEqual(message.payload, build_sink().SerializeToString()) + + def test_build_with_any_payload(self): + """ + Test building UMessage with Any payload + """ + message: UMessage = UMessageBuilder.publish(build_source()).build(Any()) + self.assertIsNotNone(message) + self.assertIsNotNone(message.payload) + self.assertEqual( + UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, + message.attributes.payload_format, + ) + self.assertEqual(message.payload, Any().SerializeToString()) + + def test_build_response_with_wrong_priority(self): + """ + Test building response with wrong priority + """ + sink = build_sink() + req_id = get_uuid() + response = UMessageBuilder.response(build_source(), sink, req_id).with_priority(UPriority.UPRIORITY_CS3).build() + self.assertIsNotNone(response) + self.assertEqual(UMessageType.UMESSAGE_TYPE_RESPONSE, response.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, response.attributes.priority) + self.assertEqual(sink, response.attributes.sink) + self.assertEqual(req_id, response.attributes.reqid) + + def test_build_request_with_wrong_priority(self): + """ + Test building request with wrong priority + """ + sink = build_sink() + ttl = 1000 + request = UMessageBuilder.request(build_source(), sink, ttl).with_priority(UPriority.UPRIORITY_CS0).build() + self.assertIsNotNone(request) + self.assertEqual(UMessageType.UMESSAGE_TYPE_REQUEST, request.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, request.attributes.priority) + self.assertEqual(sink, request.attributes.sink) + self.assertEqual(ttl, request.attributes.ttl) + + def test_build_notification_with_wrong_priority(self): + """ + Test building notification with wrong priority + """ + sink = build_sink() + notification = UMessageBuilder.notification(build_source(), sink).with_priority(UPriority.UPRIORITY_CS0).build() + self.assertIsNotNone(notification) + self.assertEqual( + UMessageType.UMESSAGE_TYPE_NOTIFICATION, + notification.attributes.type, + ) + self.assertEqual(UPriority.UPRIORITY_CS1, notification.attributes.priority) + self.assertEqual(sink, notification.attributes.sink) + + def test_build_publish_with_wrong_priority(self): + """ + Test building publish with wrong priority + """ + publish = UMessageBuilder.publish(build_source()).with_priority(UPriority.UPRIORITY_CS0).build() + self.assertIsNotNone(publish) + self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS1, publish.attributes.priority) + + def test_build_publish_with_priority(self): + """ + Test building publish with priority + """ + publish = UMessageBuilder.publish(build_source()).with_priority(UPriority.UPRIORITY_CS4).build() + self.assertIsNotNone(publish) + self.assertEqual(UMessageType.UMESSAGE_TYPE_PUBLISH, publish.attributes.type) + self.assertEqual(UPriority.UPRIORITY_CS4, publish.attributes.priority) + + def test_publish_source_is_none(self): + """ + Test publish with source is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.publish(None) + + def test_notification_source_is_none(self): + """ + Test notification with source is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.notification(None, build_sink()) + + def test_notification_sink_is_none(self): + """ + Test notification with sink is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.notification(build_source(), None) + + def test_request_source_is_none(self): + """ + Test request with source is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.request(None, build_sink(), 1000) + + def test_request_sink_is_none(self): + """ + Test request with sink is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.request(build_source(), None, 1000) + + def test_request_ttl_is_none(self): + """ + Test request with ttl is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.request(build_source(), build_sink(), None) + + def test_response_source_is_none(self): + """ + Test response with source is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.response(None, build_sink(), get_uuid()) + + def test_response_sink_is_none(self): + """ + Test response with sink is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.response(build_source(), None, get_uuid()) + + def test_response_req_id_is_none(self): + """ + Test response with req_id is None + """ + with self.assertRaises(ValueError): + UMessageBuilder.response(build_source(), build_sink(), None) diff --git a/tests/test_transport/test_builder/test_upayloadbuilder.py b/tests/test_transport/test_builder/test_upayloadbuilder.py deleted file mode 100644 index ffb38d5..0000000 --- a/tests/test_transport/test_builder/test_upayloadbuilder.py +++ /dev/null @@ -1,265 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from google.protobuf.any_pb2 import Any -from google.protobuf.api_pb2 import Method -from google.protobuf.message import Message -from google.protobuf.wrappers_pb2 import BoolValue - -from uprotocol.proto.upayload_pb2 import UPayload, UPayloadFormat -from uprotocol.transport.builder.upayloadbuilder import UPayloadBuilder - - -class TestUPayloadBuilder(unittest.TestCase): - def _create_upayload_builder(self): - return UPayloadBuilder() - - def _assert_pack_to_any(self, expected: Message, actual: UPayload): - self.assertEqual( - UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, - actual.format, - ) - self.assertEqual(0, actual.length) - self.assertEqual(0, actual.reference) - self.assertEqual(expected.SerializeToString(), actual.value) - - def test_pack_to_any_given_boolvalue_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = BoolValue(value=True) - any_message = Any() - any_message.Pack(msg) - - upayload: UPayload = builder.pack_to_any(msg) - self.assertIsNotNone(upayload) - - self._assert_pack_to_any(any_message, upayload) - - def test_pack_to_any_given_any_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = Any(type_url=None, value=b"bytes") - any_message = Any() - any_message.Pack(msg) - - upayload: UPayload = builder.pack_to_any(msg) - self.assertIsNotNone(upayload) - - self._assert_pack_to_any(any_message, upayload) - - def test_pack_to_any_given_method_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = Method( - name="name", - request_type_url="request_type_url", - response_type_url="response_type_url", - request_streaming=None, - ) - any_message = Any() - any_message.Pack(msg) - - upayload: UPayload = builder.pack_to_any(msg) - self.assertIsNotNone(upayload) - - self._assert_pack_to_any(any_message, upayload) - - def _assert_pack(self, expected: Message, actual: UPayload): - self.assertEqual(UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, actual.format) - self.assertEqual(0, actual.length) - self.assertEqual(0, actual.reference) - self.assertEqual(expected.SerializeToString(), actual.value) - - def test_pack_given_boolvalue_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = BoolValue(value=True) - - upayload: UPayload = builder.pack(msg) - - self._assert_pack(msg, upayload) - - def test_pack_given_any_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = Any(type_url=None, value=b"bytes") - - upayload: UPayload = builder.pack(msg) - - self._assert_pack(msg, upayload) - - def test_pack_given_method_returns_upayload(self): - builder = self._create_upayload_builder() - - msg: Message = Method( - name="name", - request_type_url="request_type_url", - response_type_url="response_type_url", - request_streaming=None, - ) - - upayload: UPayload = builder.pack(msg) - - self._assert_pack(msg, upayload) - - def test_unpack_given_pack_returns_boolvalue(self): - builder = self._create_upayload_builder() - - original_msg: Message = BoolValue(value=False) - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=original_msg.SerializeToString(), - ) - - unpacked_msg: BoolValue = builder.unpack(upayload, BoolValue) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_given_upayload_proto_returns_method(self): - builder = self._create_upayload_builder() - - original_msg: Message = Method( - name="name", - request_type_url="request_type_url", - response_type_url="response_type_url", - request_streaming=None, - ) - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=original_msg.SerializeToString(), - ) - - unpacked_msg: Method = builder.unpack(upayload, Method) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_exception(self): - builder = self._create_upayload_builder() - - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - ) - - unpacked_msg: Method = builder.unpack(upayload, Method) - - self.assertEqual(unpacked_msg, None) - - def test_unpack_given_upayload_proto_returns_any(self): - builder = self._create_upayload_builder() - - original_msg: Message = Any(type_url=None, value=b"bytes") - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=original_msg.SerializeToString(), - ) - - unpacked_msg: Any = builder.unpack(upayload, Any) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_given_any_wrapped_upayload_returns_boolvalue(self): - builder = self._create_upayload_builder() - - original_msg: Message = BoolValue(value=False) - any_message = Any() - any_message.Pack(original_msg) - - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, - value=any_message.SerializeToString(), - ) - - unpacked_msg: BoolValue = builder.unpack(upayload, BoolValue) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_given_any_wrapped_upayload_returns_any(self): - builder = self._create_upayload_builder() - - original_msg: Message = Any(value=b"bytes") - any_message = Any() - any_message.Pack(original_msg) - - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, - value=any_message.SerializeToString(), - ) - - unpacked_msg: Any = builder.unpack(upayload, Any) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_given_any_wrapped_upayload_returns_method(self): - builder = self._create_upayload_builder() - - original_msg: Message = Method( - name="name", - request_type_url="request_type_url", - response_type_url="response_type_url", - request_streaming=None, - ) - any_message = Any() - any_message.Pack(original_msg) - - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, - value=any_message.SerializeToString(), - ) - - unpacked_msg: Method = builder.unpack(upayload, Method) - - self.assertEqual(original_msg, unpacked_msg) - - def test_unpack_given_wrong_format_returns_none(self): - builder = self._create_upayload_builder() - - original_msg: Message = Any() - any_message = Any() - any_message.Pack(original_msg) - - upayload: UPayload = UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_JSON, - value=any_message.SerializeToString(), - ) - - unpacked_msg: Any = builder.unpack(upayload, Any) - - self.assertIsNone(unpacked_msg) - - def test_unpack_given_none_returns_none(self): - builder = self._create_upayload_builder() - - unpacked_msg: Any = builder.unpack(None, Any) - - self.assertIsNone(unpacked_msg) - - def test_unpack_given_no_upayload_value_returns_none(self): - builder = self._create_upayload_builder() - - upayload: UPayload = UPayload(format=UPayloadFormat.UPAYLOAD_FORMAT_JSON) - - unpacked_msg: Any = builder.unpack(upayload, Any) - - self.assertIsNone(unpacked_msg) diff --git a/tests/test_transport/test_utransport.py b/tests/test_transport/test_utransport.py index 9e20390..d095dec 100644 --- a/tests/test_transport/test_utransport.py +++ b/tests/test_transport/test_utransport.py @@ -1,32 +1,26 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ import unittest +from typing import Optional -from uprotocol.proto.umessage_pb2 import UMessage -from uprotocol.proto.uri_pb2 import UUri -from uprotocol.proto.ustatus_pb2 import UCode, UStatus from uprotocol.transport.ulistener import UListener from uprotocol.transport.utransport import UTransport +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.umessage_pb2 import UMessage +from uprotocol.v1.uri_pb2 import UUri +from uprotocol.v1.ustatus_pb2 import UStatus class MyListener(UListener): @@ -37,34 +31,33 @@ def on_receive(self, message): class HappyUTransport(UTransport): def send(self, message): - super().send(message) - return UStatus(code=UCode.INVALID_ARGUMENT if message is None else UCode.OK) - def register_listener(self, topic, listener): - super().register_listener(topic, listener) + def register_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener: UListener): listener.on_receive(UMessage()) return UStatus(code=UCode.OK) - def unregister_listener(self, topic, listener): - super().unregister_listener(topic, listener) + def unregister_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener): return UStatus(code=UCode.OK) + def get_source(self): + return UUri() + class SadUTransport(UTransport): def send(self, message): - super().send(message) return UStatus(code=UCode.INTERNAL) - def register_listener(self, topic, listener): - super().register_listener(topic, listener) + def register_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener): listener.on_receive(None) return UStatus(code=UCode.INTERNAL) - def unregister_listener(self, topic, listener): - super().unregister_listener(topic, listener) + def unregister_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener): return UStatus(code=UCode.INTERNAL) + def get_source(self): + return UUri() + class UTransportTest(unittest.TestCase): def test_happy_send_message_parts(self): @@ -72,19 +65,14 @@ def test_happy_send_message_parts(self): status = transport.send(UMessage()) self.assertEqual(status.code, UCode.OK) - def test_happy_send_message(self): - transport = HappyUTransport() - status = transport.send(UMessage()) - self.assertEqual(status.code, UCode.OK) - def test_happy_register_listener(self): transport = HappyUTransport() - status = transport.register_listener(UUri(), MyListener()) + status = transport.register_listener(UUri(), None, MyListener()) self.assertEqual(status.code, UCode.OK) def test_happy_register_unlistener(self): transport = HappyUTransport() - status = transport.unregister_listener(UUri(), MyListener()) + status = transport.unregister_listener(UUri(), None, MyListener()) self.assertEqual(status.code, UCode.OK) def test_sending_null_message(self): @@ -97,19 +85,14 @@ def test_unhappy_send_message_parts(self): status = transport.send(UMessage()) self.assertEqual(status.code, UCode.INTERNAL) - def test_unhappy_send_message(self): - transport = SadUTransport() - status = transport.send(UMessage()) - self.assertEqual(status.code, UCode.INTERNAL) - def test_unhappy_register_listener(self): transport = SadUTransport() - status = transport.register_listener(UUri(), MyListener()) + status = transport.register_listener(UUri(), None, MyListener()) self.assertEqual(status.code, UCode.INTERNAL) def test_unhappy_register_unlistener(self): transport = SadUTransport() - status = transport.unregister_listener(UUri(), MyListener()) + status = transport.unregister_listener(UUri(), None, MyListener()) self.assertEqual(status.code, UCode.INTERNAL) diff --git a/tests/test_transport/test_validate/__init__.py b/tests/test_transport/test_validate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_transport/test_validate/test_uattributesvalidator.py b/tests/test_transport/test_validate/test_uattributesvalidator.py deleted file mode 100644 index dc57fa1..0000000 --- a/tests/test_transport/test_validate/test_uattributesvalidator.py +++ /dev/null @@ -1,686 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import time -import unittest - -from uprotocol.proto.uattributes_pb2 import UAttributes, UPriority -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UUri -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.transport.builder.uattributesbuilder import UAttributesBuilder -from uprotocol.transport.validate.uattributesvalidator import ( - UAttributesValidator, - Validators, -) -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uuid.factory.uuidfactory import Factories -from uprotocol.validation.validationresult import ValidationResult - - -def build_sink(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_response(), - ) - - -def build_source(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_request(None), - ) - - -class TestUAttributesValidator(unittest.TestCase): - def test_fetching_validator_for_publish_type(self): - publish = UAttributesValidator.get_validator( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - ) - self.assertEqual("UAttributesValidator.Publish", str(publish)) - - def test_fetching_validator_for_request_type(self): - request = UAttributesValidator.get_validator( - UAttributesBuilder.request(build_source(), UUri(), UPriority.UPRIORITY_CS4, 1000).build() - ) - self.assertEqual("UAttributesValidator.Request", str(request)) - - def test_fetching_validator_for_response_type(self): - response = UAttributesValidator.get_validator( - UAttributesBuilder.response( - build_source(), - UUri(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ).build() - ) - self.assertEqual("UAttributesValidator.Response", str(response)) - - def test_fetching_validator_for_notification_type(self): - response = UAttributesValidator.get_validator( - UAttributesBuilder.notification(build_source(), UUri(), UPriority.UPRIORITY_CS4).build() - ) - self.assertEqual("UAttributesValidator.Notification", str(response)) - - def test_fetching_validator_for_no_type(self): - response = UAttributesValidator.get_validator(UAttributes()) - self.assertEqual("UAttributesValidator.Publish", str(response)) - - def test_validate_u_attributes_for_publish_message_payload(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_publish_message_payload_alls(self): - attributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0) - .with_ttl(1000) - .with_sink(build_sink()) - .with_permission_level(2) - .with_comm_status(3) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_publish_message_payload_invalid_type( - self, - ): - attributes = UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS0, - Factories.UPROTOCOL.create(), - ).build() - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE]", - status.get_message(), - ) - - def test_validate_u_attributes_for_publish_message_payload_invalid_ttl( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(-1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validate_u_attributes_for_publish_message_payload_invalid_sink( - self, - ): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_sink(UUri()).build() - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_u_attributes_for_publish_message_payload_invalid_permission_level( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_permission_level(-42).build() - self.assertTrue("Value out of range: -42" in context.exception) - - def test_validate_u_attributes_for_rpc_request_message_payload(self): - attributes = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS4, 1000).build() - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_rpc_request_message_payload_alls(self): - attributes = ( - UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS4, 1000) - .with_permission_level(2) - .with_comm_status(3) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_rpc_request_message_payload_invalid_type( - self, - ): - attributes = ( - UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ) - .with_ttl(1000) - .build() - ) - - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE]", - status.get_message(), - ) - - def test_validate_u_attributes_for_rpc_request_message_payload_invalid_ttl( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS4, -1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validate_u_attributes_for_rpc_request_message_payload_invalid_sink( - self, - ): - attributes = UAttributesBuilder.request(build_source(), UUri(), UPriority.UPRIORITY_CS4, 1000).build() - - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_u_attributes_for_rpc_request_message_payload_invalid_permission_level( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request( - build_source(), build_sink(), UPriority.UPRIORITY_CS4, 1000 - ).with_permission_level(-42).build() - self.assertTrue("Value out of range: -42" in context.exception) - - def test_validate_u_attributes_for_rpc_response_message_payload(self): - attributes = UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ).build() - - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_rpc_response_message_payload_alls(self): - attributes = ( - UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ) - .with_permission_level(2) - .with_comm_status(3) - .build() - ) - - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validate_u_attributes_for_rpc_response_message_payload_invalid_type( - self, - ): - attributes = UAttributesBuilder.notification(build_source(), build_sink(), UPriority.UPRIORITY_CS4).build() - - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_NOTIFICATION],Missing correlationId", - status.get_message(), - ) - - def test_validate_u_attributes_for_rpc_response_message_payload_invalid_ttl( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ).with_ttl(-1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validate_u_attributes_for_rpc_response_message_payload_missing_sink_and_missing_request_id( - self, - ): - attributes = UAttributesBuilder.response(build_source(), UUri(), UPriority.UPRIORITY_CS4, UUID()).build() - - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing Sink,Missing correlationId", status.get_message()) - - def test_validate_u_attributes_for_rpc_response_message_payload_invalid_permission_level( - self, - ): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS4, - Factories.UPROTOCOL.create(), - ).with_permission_level(-42).build() - self.assertTrue("Value out of range: -42" in context.exception) - - def test_validate_u_attributes_for_rpc_response_message_payload_missing_request_id( - self, - ): - attributes = UAttributesBuilder.response(build_source(), build_sink(), UPriority.UPRIORITY_CS4, UUID()).build() - - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing correlationId", status.get_message()) - - def test_validate_u_attributes_for_publish_message_payload_not_expired( - self, - ): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - - validator = Validators.PUBLISH.validator() - self.assertFalse(validator.is_expired(attributes)) - - def test_validate_u_attributes_for_publish_message_payload_not_expired_with_ttl_zero( - self, - ): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(0).build() - - validator = Validators.PUBLISH.validator() - self.assertFalse(validator.is_expired(attributes)) - - def test_validate_u_attributes_for_publish_message_payload_not_expired_with_ttl( - self, - ): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(10000).build() - - validator = Validators.PUBLISH.validator() - self.assertFalse(validator.is_expired(attributes)) - - def test_validate_u_attributes_for_publish_message_payload_expired_with_ttl( - self, - ): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(1).build() - - time.sleep(0.8) - - validator = Validators.PUBLISH.validator() - self.assertTrue(validator.is_expired(attributes)) - - # ---- - - def test_validating_publish_invalid_ttl_attribute(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(-1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validating_valid_ttl_attribute(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(100).build() - - validator = Validators.PUBLISH.validator() - status = validator.validate_ttl(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_invalid_sink_attribute(self): - uri = LongUriSerializer().deserialize("//") - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_sink(uri).build() - validator = Validators.PUBLISH.validator() - status = validator.validate_sink(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validating_valid_sink_attribute(self): - uri = UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResourceBuilder.for_rpc_response(), - ) - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_sink(uri).build() - - validator = Validators.PUBLISH.validator() - status = validator.validate_sink(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_valid_req_id_attribute(self): - attributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.PUBLISH.validator() - status = validator.validate_req_id(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_valid_type_attribute(self): - attributes = ( - UAttributesBuilder.notification(build_source(), build_sink(), UPriority.UPRIORITY_CS0) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.NOTIFICATION.validator() - status = validator.validate_type(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_valid_sink_attribute(self): - attributes = ( - UAttributesBuilder.notification(build_source(), build_sink(), UPriority.UPRIORITY_CS0) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.NOTIFICATION.validator() - status = validator.validate_sink(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_none_attribute(self): - attributes = None - - validator = Validators.NOTIFICATION.validator() - status = validator.validate_sink(attributes) - self.assertEqual("UAttributes cannot be null.", status.get_message()) - - def test_validating_invalid_sink_attribute(self): - attributes = ( - UAttributesBuilder.notification(build_source(), UUri(), UPriority.UPRIORITY_CS0) - .with_req_id(Factories.UPROTOCOL.create()) - .build() - ) - - validator = Validators.NOTIFICATION.validator() - status = validator.validate_sink(attributes) - self.assertEqual("Missing Sink", status.get_message()) - - def test_validating_invalid_permission_level_attribute(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_permission_level(-1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validating_valid_permission_level_attribute(self): - attributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_permission_level(3).build() - ) - - validator = Validators.PUBLISH.validator() - status = validator.validate_permission_level(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_validating_valid_permission_level_attribute_invalid(self): - attributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_permission_level(0).build() - ) - - validator = Validators.PUBLISH.validator() - status = validator.validate_permission_level(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid Permission Level", status.get_message()) - - def test_validating_request_message_types(self): - attributes = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS6, 100).build() - - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Request", str(validator)) - status = validator.validate(attributes) - self.assertTrue(status.is_success()) - self.assertEqual("", status.get_message()) - - def test_validating_request_validator_with_wrong_messagetype(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS6).build() - - validator = Validators.REQUEST.validator() - self.assertEqual("UAttributesValidator.Request", str(validator)) - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing TTL,Missing Sink", - status.get_message(), - ) - - def test_validating_request_validator_with_wrong_bad_ttl(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.request( - build_source(), - LongUriSerializer().deserialize("/hartley/1/rpc.response"), - UPriority.UPRIORITY_CS6, - -1, - ).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validating_response_validator_with_wrong_bad_ttl(self): - with self.assertRaises(ValueError) as context: - UAttributesBuilder.response( - build_source(), - LongUriSerializer().deserialize("/hartley/1/rpc.response"), - UPriority.UPRIORITY_CS6, - Factories.UPROTOCOL.create(), - ).with_ttl(-1).build() - self.assertTrue("Value out of range: -1" in context.exception) - - def test_validating_publish_validator_with_wrong_messagetype(self): - attributes = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS6, 1000).build() - validator = Validators.PUBLISH.validator() - self.assertEqual("UAttributesValidator.Publish", str(validator)) - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_REQUEST]", - status.get_message(), - ) - - def test_validating_response_validator_with_wrong_messagetype(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS6).build() - - validator = Validators.RESPONSE.validator() - self.assertEqual("UAttributesValidator.Response", str(validator)) - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink,Missing correlationId", - status.get_message(), - ) - - def test_validating_request_containing_token(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_token("null").build() - - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Publish", str(validator)) - status = validator.validate(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_valid_request_methoduri_in_sink(self): - sink = LongUriSerializer().deserialize("/test.service/1/rpc.method") - attributes = UAttributesBuilder.request(build_source(), sink, UPriority.UPRIORITY_CS0, 3000).build() - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Request", str(validator)) - status = validator.validate(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_invalid_request_methoduri_in_sink(self): - sink = LongUriSerializer().deserialize("/test.client/1/test.response") - attributes = UAttributesBuilder.request(build_source(), sink, UPriority.UPRIORITY_CS0, 3000).build() - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Request", str(validator)) - status = validator.validate(attributes) - self.assertEqual( - "Invalid RPC method uri. Uri should be the method to be called, or method from response.", - status.get_message(), - ) - - def test_valid_response_uri_in_sink(self): - sink = LongUriSerializer().deserialize("/test.client/1/rpc.response") - attributes = UAttributesBuilder.response( - build_source(), - sink, - UPriority.UPRIORITY_CS0, - Factories.UPROTOCOL.create(), - ).build() - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Response", str(validator)) - status = validator.validate(attributes) - self.assertEqual(ValidationResult.success(), status) - - def test_invalid_response_uri_in_sink(self): - sink = LongUriSerializer().deserialize("/test.client/1/rpc.method") - attributes = UAttributesBuilder.response( - build_source(), - sink, - UPriority.UPRIORITY_CS0, - Factories.UPROTOCOL.create(), - ).build() - validator = UAttributesValidator.get_validator(attributes) - self.assertEqual("UAttributesValidator.Response", str(validator)) - status = validator.validate(attributes) - self.assertEqual("Invalid RPC response type.", status.get_message()) - - def test_publish_validation_without_id(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - attributes.ClearField("id") - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing id", status.get_message()) - - def test_notification_validation_without_id(self): - attributes = UAttributesBuilder.notification(build_source(), build_sink(), UPriority.UPRIORITY_CS0).build() - attributes.ClearField("id") - validator = Validators.NOTIFICATION.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing id", status.get_message()) - - def test_request_validation_without_id(self): - attributes = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS0, 1000).build() - attributes.ClearField("id") - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing id", status.get_message()) - - def test_response_validation_without_id(self): - attributes = UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS0, - Factories.UPROTOCOL.create(), - ).build() - attributes.ClearField("id") - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Missing id", status.get_message()) - - def test_publish_validation_invalid_id(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - attributes.id.CopyFrom(UUID()) - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Attributes must contain valid uProtocol UUID in id property", - status.get_message(), - ) - - def test_notification_validation_invalid_id(self): - attributes = UAttributesBuilder.notification(build_source(), build_sink(), UPriority.UPRIORITY_CS0).build() - attributes.id.CopyFrom(UUID()) - validator = Validators.NOTIFICATION.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Attributes must contain valid uProtocol UUID in id property", - status.get_message(), - ) - - def test_request_validation_invalid_id(self): - attributes = UAttributesBuilder.request(build_source(), build_sink(), UPriority.UPRIORITY_CS0, 1000).build() - attributes.id.CopyFrom(UUID()) - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Attributes must contain valid uProtocol UUID in id property", - status.get_message(), - ) - - def test_response_validation_invalid_id(self): - attributes = UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_CS0, - Factories.UPROTOCOL.create(), - ).build() - attributes.id.CopyFrom(UUID()) - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual( - "Attributes must contain valid uProtocol UUID in id property", - status.get_message(), - ) - - def test_publish_validation_when_an_invalid_priority(self): - attributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_UNSPECIFIED).build() - validator = Validators.PUBLISH.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid UPriority [UPRIORITY_UNSPECIFIED]", status.get_message()) - - def test_request_validation_with_invalid_priority(self): - attributes = UAttributesBuilder.request( - build_source(), build_sink(), UPriority.UPRIORITY_UNSPECIFIED, 1000 - ).build() - validator = Validators.REQUEST.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid UPriority [UPRIORITY_UNSPECIFIED]", status.get_message()) - - def test_response_validation_with_invalid_priority(self): - attributes = UAttributesBuilder.response( - build_source(), - build_sink(), - UPriority.UPRIORITY_UNSPECIFIED, - Factories.UPROTOCOL.create(), - ).build() - validator = Validators.RESPONSE.validator() - status = validator.validate(attributes) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid UPriority [UPRIORITY_UNSPECIFIED]", status.get_message()) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_cloudevent/__init__.py b/tests/test_transport/test_validator/__init__.py similarity index 100% rename from tests/test_cloudevent/__init__.py rename to tests/test_transport/test_validator/__init__.py diff --git a/tests/test_transport/test_validator/test_uattributesvalidator.py b/tests/test_transport/test_validator/test_uattributesvalidator.py new file mode 100644 index 0000000..29d03f4 --- /dev/null +++ b/tests/test_transport/test_validator/test_uattributesvalidator.py @@ -0,0 +1,325 @@ +""" +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + +See the NOTICE file(s) distributed with this work for additional +information regarding copyright ownership. + +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at + + http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 +""" + +import time +import unittest + +from uprotocol.transport.builder.umessagebuilder import UMessageBuilder +from uprotocol.transport.validator.uattributesvalidator import UAttributesValidator, Validators +from uprotocol.v1.uattributes_pb2 import UAttributes, UMessageType, UPriority +from uprotocol.v1.uri_pb2 import UUri +from uprotocol.v1.uuid_pb2 import UUID + + +def build_default_uuri(): + return UUri(ue_id=1, ue_version_major=1, resource_id=0) + + +def build_method_uuri(): + return UUri(ue_id=1, ue_version_major=1, resource_id=1) + + +def build_topic_uuri(): + return UUri(ue_id=1, ue_version_major=1, resource_id=0x8000) + + +class TestUAttributesValidator(unittest.TestCase): + def test_uattributes_validator_happy_path(self): + message = UMessageBuilder.publish(build_topic_uuri()).build() + + validator = UAttributesValidator.get_validator(message.attributes) + + result = validator.validate(message.attributes) + + self.assertTrue(result.is_success()) + + self.assertEqual(str(validator), "UAttributesValidator.Publish") + + def test_uattributes_validator_notification(self): + message = UMessageBuilder.notification(build_topic_uuri(), build_default_uuri()).build() + + validator = UAttributesValidator.get_validator(message.attributes) + + result = validator.validate(message.attributes) + + self.assertTrue(result.is_success()) + + self.assertEqual(str(validator), "UAttributesValidator.Notification") + + def test_uattributes_validator_request(self): + message = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + + validator = UAttributesValidator.get_validator(message.attributes) + + result = validator.validate(message.attributes) + + self.assertTrue(result.is_success()) + + self.assertEqual(str(validator), "UAttributesValidator.Request") + + def test_uattributes_validator_response(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + message = UMessageBuilder.response(build_method_uuri(), build_default_uuri(), request.attributes.id).build() + + validator = UAttributesValidator.get_validator(message.attributes) + + result = validator.validate(message.attributes) + + self.assertTrue(result.is_success()) + + self.assertEqual(str(validator), "UAttributesValidator.Response") + + def test_uattributes_validator_response_with_request_attributes(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + message = UMessageBuilder.response_for_request(request.attributes).build() + + validator = UAttributesValidator.get_validator(message.attributes) + + result = validator.validate(message.attributes) + + self.assertTrue(result.is_success()) + + self.assertEqual(str(validator), "UAttributesValidator.Response") + + def test_uattributes_validator_request_with_publish_validator(self): + message = UMessageBuilder.request(build_default_uuri(), build_topic_uuri(), 1000).build() + + validator = Validators.PUBLISH.validator() + + result = validator.validate(message.attributes) + + self.assertFalse(result.is_success()) + self.assertEqual(str(validator), "UAttributesValidator.Publish") + self.assertEqual(result.message, "Wrong Attribute Type [UMESSAGE_TYPE_REQUEST],Sink should not be present") + + def test_uattributes_validator_publish_with_notification_validator(self): + message = UMessageBuilder.publish(build_topic_uuri()).build() + + validator = Validators.NOTIFICATION.validator() + + result = validator.validate(message.attributes) + + self.assertFalse(result.is_success()) + self.assertEqual(str(validator), "UAttributesValidator.Notification") + self.assertEqual(result.message, "Wrong Attribute Type [UMESSAGE_TYPE_PUBLISH],Missing Sink") + + def test_uattributes_validator_response_with_request_validator(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + + validator = Validators.REQUEST.validator() + result = validator.validate(response.attributes) + + self.assertFalse(result.is_success()) + self.assertEqual(str(validator), "UAttributesValidator.Request") + self.assertEqual( + result.message, + "Wrong Attribute Type [UMESSAGE_TYPE_RESPONSE],Missing TTL," + "Invalid Sink Uri,Message should not have a reqid", + ) + + def test_uattributes_validator_notification_with_response_validator(self): + message = UMessageBuilder.notification(build_topic_uuri(), build_default_uuri()).build() + + validator = Validators.RESPONSE.validator() + + result = validator.validate(message.attributes) + + self.assertFalse(result.is_success()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual( + result.message, + "Wrong Attribute Type [UMESSAGE_TYPE_NOTIFICATION],Invalid UPriority [UPRIORITY_CS1],Missing correlationId", + ) + + def test_uattribute_validator_request_missing_sink(self): + message = UMessageBuilder.request(build_default_uuri(), build_default_uuri(), 1000).build() + + validator = UAttributesValidator.get_validator(message.attributes) + result = validator.validate(message.attributes) + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Request") + self.assertEqual(result.message, "Invalid Sink Uri") + + def test_uattribute_validator_request_valid_permission_level(self): + message = ( + UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).with_permission_level(1).build() + ) + + validator = UAttributesValidator.get_validator(message.attributes) + result = validator.validate(message.attributes) + self.assertTrue(result.is_success()) + self.assertFalse(validator.is_expired(message.attributes)) + self.assertEqual(str(validator), "UAttributesValidator.Request") + + def test_uattribute_validator_request_expired(self): + message = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1).build() + time.sleep(0.1) + validator = UAttributesValidator.get_validator(message.attributes) + self.assertTrue(validator.is_expired(message.attributes)) + + def test_uattribute_validator_request_expired_malformed_id(self): + attributes = UAttributes( + id=UUID(), + type=UMessageType.UMESSAGE_TYPE_REQUEST, + ) + validator = UAttributesValidator.get_validator(attributes) + self.assertFalse(validator.is_expired(attributes)) + + def test_uattribute_validator_publish_with_req_id(self): + publish = UMessageBuilder.publish(build_topic_uuri()).build() + attributes = publish.attributes + + attributes.reqid.CopyFrom(UUID()) + + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Publish") + self.assertEqual(result.message, "Message should not have a reqid") + + def test_uattribute_validator_notification_missing_sink(self): + message = UMessageBuilder.notification(build_topic_uuri(), build_default_uuri()).build() + attributes = message.attributes + attributes.ClearField("sink") + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Notification") + self.assertEqual(result.message, "Missing Sink") + + def test_uattribute_validator_notification_default_sink(self): + message = UMessageBuilder.notification(build_topic_uuri(), build_default_uuri()).build() + attributes = message.attributes + attributes.sink.CopyFrom(UUri()) + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Notification") + self.assertEqual(result.message, "Missing Sink") + + def test_uattribute_validator_notification_default_resource_id(self): + message = UMessageBuilder.notification(build_topic_uuri(), build_topic_uuri()).build() + validator = UAttributesValidator.get_validator(message.attributes) + result = validator.validate(message.attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Notification") + self.assertEqual(result.message, "Invalid Sink Uri") + + def test_uattribute_validator_validate_priority_less_than_cs0(self): + message = UMessageBuilder.publish(build_topic_uuri()).build() + attributes = message.attributes + attributes.priority = UPriority.UPRIORITY_UNSPECIFIED + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Publish") + self.assertEqual(result.message, "Invalid UPriority [UPRIORITY_UNSPECIFIED]") + + def test_uattribute_validator_validate_id_missing(self): + message = UMessageBuilder.publish(build_topic_uuri()).build() + attributes = message.attributes + attributes.ClearField("id") + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Publish") + self.assertEqual(result.message, "Missing id") + + def test_uattribute_validator_validate_priority_less_than_cs4(self): + message = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + attributes = message.attributes + attributes.priority = UPriority.UPRIORITY_CS3 + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Request") + self.assertEqual(result.message, "Invalid UPriority [UPRIORITY_CS3]") + + def test_uattribute_validator_validate_sink_response_missing(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + attributes = response.attributes + attributes.ClearField("sink") + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Missing Sink") + + def test_uattribute_validator_validate_sink_response_default(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + attributes = response.attributes + attributes.sink.CopyFrom(UUri()) + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Missing Sink") + + def test_uattribute_validator_validate_sink_response_default_resource_id(self): + request = UMessageBuilder.request(build_method_uuri(), build_default_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + validator = UAttributesValidator.get_validator(response.attributes) + result = validator.validate(response.attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Invalid Sink Uri") + + def test_uattribute_validator_validate_reqid_missing(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + attributes = response.attributes + attributes.ClearField("reqid") + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Missing correlationId") + + def test_uattribute_validator_validate_reqid_default(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + attributes = response.attributes + attributes.ClearField("reqid") + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Missing correlationId") + + def test_uattribute_validator_validate_reqid_invalid(self): + request = UMessageBuilder.request(build_default_uuri(), build_method_uuri(), 1000).build() + response = UMessageBuilder.response_for_request(request.attributes).build() + attributes = response.attributes + attributes.reqid.lsb = 0xBEADBEEF + attributes.reqid.msb = 0xDEADBEEF + validator = UAttributesValidator.get_validator(attributes) + result = validator.validate(attributes) + + self.assertTrue(result.is_failure()) + self.assertEqual(str(validator), "UAttributesValidator.Response") + self.assertEqual(result.message, "Invalid correlation UUID") diff --git a/tests/test_uri/test_factory/test_uentityfactory.py b/tests/test_uri/test_factory/test_uentityfactory.py deleted file mode 100644 index e48d573..0000000 --- a/tests/test_uri/test_factory/test_uentityfactory.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from google.protobuf.descriptor import FileDescriptor, ServiceDescriptor - -from uprotocol.proto.core.udiscovery.v3.udiscovery_pb2 import ( - DESCRIPTOR as U_DISCOVERY_FILE_DESCRIPTOR, -) -from uprotocol.proto.core.usubscription.v3.usubscription_pb2 import ( - DESCRIPTOR as U_SUBSCRIPTION_FILE_DESCRIPTOR, -) -from uprotocol.proto.core.utwin.v2.utwin_pb2 import ( - DESCRIPTOR as U_TWIN_FILE_DESCRIPTOR, -) -from uprotocol.proto.uri_pb2 import UEntity -from uprotocol.uri.factory.uentityfactory import UEntityFactory - - -class TestUEntityFactory(unittest.TestCase): - def test_from_proto_given_usubscription_descriptor_return_uentity(self): - """ - Get default service values: - Example: in uSubscription.proto... - - // Subscription Service Interface definition - service uSubscription { - option (name) = "core.usubscription"; // Service name - option (version_major) = 3; - option (version_minor) = 0; - option (id) = 0; - ... - } - """ - file_descriptor: FileDescriptor = U_SUBSCRIPTION_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uSubscription"] - default_service_name: str = "core.usubscription" - default_version_major: str = 3 - default_version_minor: str = 0 - default_id = 0 - - actual_uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - self.assertIsNotNone(actual_uentity) - - self.assertEqual(default_service_name, actual_uentity.name) - self.assertEqual(default_version_major, actual_uentity.version_major) - self.assertEqual(default_version_minor, actual_uentity.version_minor) - self.assertEqual(default_id, actual_uentity.id) - - def test_from_proto_given_udiscovery_descriptor_return_uentity(self): - file_descriptor: FileDescriptor = U_DISCOVERY_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uDiscovery"] - default_service_name: str = "core.udiscovery" - default_version_major: int = 3 - default_version_minor: int = 0 - default_id = 1 - - actual_uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - self.assertIsNotNone(actual_uentity) - - self.assertEqual(default_service_name, actual_uentity.name) - self.assertEqual(default_version_major, actual_uentity.version_major) - self.assertEqual(default_version_minor, actual_uentity.version_minor) - self.assertEqual(default_id, actual_uentity.id) - - def test_from_proto_given_utwin_descriptor_return_uentity(self): - file_descriptor: FileDescriptor = U_TWIN_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uTwin"] - default_service_name: str = "core.utwin" - default_version_major: str = 2 - default_version_minor: str = 0 - default_id = 26 - - actual_uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - self.assertIsNotNone(actual_uentity) - - self.assertEqual(default_service_name, actual_uentity.name) - self.assertEqual(default_version_major, actual_uentity.version_major) - self.assertEqual(default_version_minor, actual_uentity.version_minor) - self.assertEqual(default_id, actual_uentity.id) - - def test_from_proto_given_none_return_empty_uentity(self): - empty_service_name: str = "" - empty_version_major: str = 0 - empty_version_minor: str = 0 - empty_id = 0 - - actual_uentity: UEntity = UEntityFactory.from_proto(None) - self.assertIsNotNone(actual_uentity) - - self.assertEqual(empty_service_name, actual_uentity.name) - self.assertEqual(empty_version_major, actual_uentity.version_major) - self.assertEqual(empty_version_minor, actual_uentity.version_minor) - self.assertEqual(empty_id, actual_uentity.id) diff --git a/tests/test_uri/test_factory/test_uresourcebuilder.py b/tests/test_uri/test_factory/test_uresourcebuilder.py deleted file mode 100644 index 009609d..0000000 --- a/tests/test_uri/test_factory/test_uresourcebuilder.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.uprotocol_options_pb2 import UServiceTopic -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder - - -class TestUResourceBuilder(unittest.TestCase): - def test_from_id_valid_id(self): - id = 0 - resource = UResourceBuilder.from_id(id) - self.assertEqual(resource.name, "rpc") - self.assertEqual(resource.instance, "response") - self.assertEqual(resource.id, 0) - - def test_from_id_invalid_id(self): - id = -1 - with self.assertRaises(ValueError) as context: - UResourceBuilder.from_id(id) - self.assertEqual(str(context.exception), "Value out of range: -1") - - def test_from_id_valid_id_below_min_topic_id(self): - id = 0x7FFF - resource = UResourceBuilder.from_id(id) - self.assertEqual(resource.name, "rpc") - self.assertEqual(resource.instance, "") - self.assertEqual(resource.id, 0x7FFF) - - def test_from_id_valid_id_above_min_topic_id(self): - id = 0x8000 - resource = UResourceBuilder.from_id(id) - self.assertEqual(resource.name, "") - self.assertEqual(resource.instance, "") - self.assertEqual(resource.id, 0x8000) - - def test_from_uservice_topic_valid_service_topic(self): - topic = UServiceTopic(name="SubscriptionChange", id=0, message="Update") - resource = UResourceBuilder.from_uservice_topic(topic) - self.assertEqual(resource.name, "SubscriptionChange") - self.assertEqual(resource.instance, "") - self.assertEqual(resource.id, 0) - self.assertEqual(resource.message, "Update") - - def test_from_uservice_topic_valid_service_topic_with_instance(self): - topic = UServiceTopic(name="door.front_left", id=0x8000, message="Door") - resource = UResourceBuilder.from_uservice_topic(topic) - self.assertEqual(resource.name, "door") - self.assertEqual(resource.instance, "front_left") - self.assertEqual(resource.id, 0x8000) - self.assertEqual(resource.message, "Door") - - def test_from_uservice_topic_invalid_service_topic(self): - topic = None - with self.assertRaises(ValueError) as context: - UResourceBuilder.from_uservice_topic(topic) - self.assertEqual(str(context.exception), "topic cannot be None.") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_uri/test_factory/test_uri_factory.py b/tests/test_uri/test_factory/test_uri_factory.py new file mode 100644 index 0000000..9f20bb2 --- /dev/null +++ b/tests/test_uri/test_factory/test_uri_factory.py @@ -0,0 +1,47 @@ +""" +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + +See the NOTICE file(s) distributed with this work for additional +information regarding copyright ownership. + +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at + + http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 +""" + +import unittest + +from uprotocol.core.usubscription.v3 import usubscription_pb2 +from uprotocol.uri.factory.uri_factory import UriFactory + + +class TestUriFactory(unittest.TestCase): + def test_from_proto(self): + service_descriptor = usubscription_pb2.DESCRIPTOR.services_by_name["uSubscription"] + uri = UriFactory.from_proto(service_descriptor, 0, "") + + self.assertIsNotNone(uri) + self.assertEqual(uri.resource_id, 0) + self.assertEqual(uri.ue_id, 0) + self.assertEqual(uri.ue_version_major, 3) + self.assertEqual(uri.authority_name, "") + + def test_any(self): + uri = UriFactory.any_func() + self.assertIsNotNone(uri) + self.assertEqual(uri.resource_id, 65535) + self.assertEqual(uri.ue_id, 65535) + self.assertEqual(uri.ue_version_major, 255) + self.assertEqual(uri.authority_name, "*") + + def test_from_proto_with_null_descriptor(self): + uri = UriFactory.from_proto(None, 0, "") + + self.assertIsNotNone(uri) + self.assertEqual(uri.resource_id, 0) + self.assertEqual(uri.ue_id, 0) + self.assertEqual(uri.ue_version_major, 0) + self.assertEqual(uri.authority_name, "") diff --git a/tests/test_uri/test_serializer/test_ipaddress.py b/tests/test_uri/test_serializer/test_ipaddress.py deleted file mode 100644 index 2c9e886..0000000 --- a/tests/test_uri/test_serializer/test_ipaddress.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import socket -import unittest - -from uprotocol.uri.serializer.ipaddress import IpAddress - - -class TestIpAddress(unittest.TestCase): - def test_to_bytes_with_null_ip_address(self): - bytes_ = IpAddress.to_bytes(None) - self.assertEqual(len(bytes_), 0) - - def test_to_bytes_with_empty_ip_address(self): - bytes_ = IpAddress.to_bytes("") - self.assertEqual(len(bytes_), 0) - - def test_to_bytes_with_invalid_ip_address(self): - bytes_ = IpAddress.to_bytes("invalid") - self.assertEqual(len(bytes_), 0) - - def test_to_bytes_with_valid_ipv4_address(self): - bytes_ = IpAddress.to_bytes("192.168.1.100") - self.assertEqual(len(bytes_), 4) - self.assertEqual(socket.inet_ntoa(bytes_), "192.168.1.100") - - def test_to_bytes_with_valid_ipv6_address(self): - bytes_ = IpAddress.to_bytes("2001:db8:85a3:0:0:8a2e:370:7334") - self.assertEqual(len(bytes_), 16) - self.assertEqual( - socket.inet_ntop(socket.AF_INET6, bytes_), - "2001:db8:85a3::8a2e:370:7334", - ) - - def test_is_valid_with_null_ip_address(self): - self.assertFalse(IpAddress.is_valid(None)) - - def test_is_valid_with_empty_ip_address(self): - self.assertFalse(IpAddress.is_valid("")) - - def test_is_valid_with_invalid_ip_address(self): - self.assertFalse(IpAddress.is_valid("invalid")) - - def test_is_valid_with_valid_ipv4_address(self): - self.assertTrue(IpAddress.is_valid("192.168.1.100")) - - def test_is_valid_with_valid_ipv6_address(self): - self.assertTrue(IpAddress.is_valid("2001:db8:85a3:0:0:8a2e:370:7334")) - - def test_is_valid_with_invalid_ipv4_address(self): - self.assertFalse(IpAddress.is_valid("192.168.1.2586")) - - def test_is_valid_with_invalid_ipv4_passing_large_number(self): - self.assertFalse(IpAddress.is_valid("2875687346587326457836485623874658723645782364875623847562378465.1.1.abc")) - - def test_is_valid_with_invalid_ipv4_passing_negative(self): - self.assertFalse(IpAddress.is_valid("-1.1.1.abc")) - - def test_is_valid_with_invalid_ipv4_passing_characters(self): - self.assertFalse(IpAddress.is_valid("1.1.1.abc")) - - def test_is_valid_with_invalid_ipv6_address(self): - self.assertFalse(IpAddress.is_valid("ZX1:db8::")) - - def test_is_valid_with_invalid_ipv6_address_passing_weird_values(self): - self.assertFalse(IpAddress.is_valid("-1:ZX1:db8::")) - - def test_is_valid_with_invalid_ipv6_address_that_has_way_too_many_groups( - self, - ): - self.assertFalse(IpAddress.is_valid("2001:db8:85a3:0:0:8a2e:370:7334:1234")) - - def test_is_valid_with_valid_ipv6_address_that_has_8_groups(self): - self.assertTrue(IpAddress.is_valid("2001:db8:85a3:0:0:8a2e:370:7334")) - - def test_is_valid_with_invalid_ipv6_address_with_too_many_empty_groups( - self, - ): - self.assertFalse(IpAddress.is_valid("2001::85a3::8a2e::7334")) - - def test_is_valid_with_valid_ipv6_address_with_one_empty_group(self): - self.assertTrue(IpAddress.is_valid("2001:db8:85a3::8a2e:370:7334")) - - def test_is_valid_with_invalid_ipv6_address_that_ends_with_a_colon(self): - self.assertFalse(IpAddress.is_valid("2001:db8:85a3::8a2e:370:7334:")) - - def test_is_valid_with_invalid_ipv6_address_that_doesnt_have_double_colon_and_not_enough_groups( - self, - ): - self.assertFalse(IpAddress.is_valid("2001:db8:85a3:0:0:8a2e:370")) - - def test_is_valid_with_valid_ipv6_address_that_ends_with_double_colons( - self, - ): - self.assertTrue(IpAddress.is_valid("2001:db8:85a3:8a2e::")) - - def test_is_valid_with_all_number_values(self): - self.assertTrue(IpAddress.is_valid("123:456:7890::")) - - def test_is_valid_with_valid_lowercase_hexidecimal_letters(self): - self.assertTrue(IpAddress.is_valid("abcd:ef12:3456::")) - - def test_is_valid_with_valid_uppercase_hexidecimal_letters(self): - self.assertTrue(IpAddress.is_valid("ABCD:EF12:3456::")) - - def test_is_valid_with_invalid_uppercase_hexidecimal_letters(self): - self.assertFalse(IpAddress.is_valid("ABCD:EFG2:3456::")) - - def test_is_valid_with_invalid_lowercase_hexidecimal_letters(self): - self.assertFalse(IpAddress.is_valid("-C=[]:E{12g:3456")) - - def test_is_valid_with_invalid_digit1(self): - self.assertFalse(IpAddress.is_valid("aC=[]:E{12g:3456")) - - def test_is_valid_with_invalid_digit2(self): - self.assertFalse(IpAddress.is_valid("aCd[:E{12g:3456")) - - def test_is_valid_with_invalid_digit3(self): - self.assertFalse(IpAddress.is_valid("aCd:E{2g:3456")) - - def test_is_valid_with_invalid_ipv6_address_that_has_double_colon_and_8_groups( - self, - ): - self.assertTrue(IpAddress.is_valid("dead:beef:85a3::0:0:8a2e:370")) - - def test_is_valid_with_invalid_ipv6_address_that_has_only_7_groups(self): - self.assertFalse(IpAddress.is_valid("dead:beef:85a3:0:0:8a2e:370")) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_uri/test_serializer/test_longuriserializer.py b/tests/test_uri/test_serializer/test_longuriserializer.py deleted file mode 100644 index 091b675..0000000 --- a/tests/test_uri/test_serializer/test_longuriserializer.py +++ /dev/null @@ -1,792 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator - - -class TestLongUriSerializer(unittest.TestCase): - def test_using_the_serializers(self): - uri = UUri( - entity=UEntity(name="hartley"), - resource=UResourceBuilder.for_rpc_request("raise"), - ) - - str_uri = LongUriSerializer().serialize(uri) - self.assertEqual("/hartley//rpc.raise", str_uri) - uri2 = LongUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_parse_protocol_uri_when_is_null(self): - uri = LongUriSerializer().deserialize(None) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertFalse(UriValidator.is_resolved(uri)) - self.assertFalse(UriValidator.is_long_form(uri)) - - def test_parse_protocol_uri_when_is_empty_string(self): - uri = "" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_empty(uuri)) - uri2 = LongUriSerializer().serialize(None) - self.assertTrue(len(uri2) == 0) - - def test_parse_protocol_uri_with_schema_and_slash(self): - uri = "/" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(uuri.HasField("authority")) - self.assertTrue(UriValidator.is_empty(uuri)) - self.assertFalse(uuri.HasField("resource")) - self.assertFalse(uuri.HasField("entity")) - uri2 = LongUriSerializer().serialize(UUri()) - self.assertTrue(len(uri2) == 0) - - def test_parse_protocol_uri_with_schema_and_double_slash(self): - uri = "//" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(uuri.HasField("authority")) - self.assertFalse(uuri.HasField("resource")) - self.assertFalse(uuri.HasField("entity")) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_parse_protocol_uri_with_schema_and_3_slash_and_something(self): - uri = "///body.access" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(uuri.HasField("authority")) - self.assertFalse(uuri.HasField("resource")) - self.assertFalse(uuri.HasField("entity")) - self.assertTrue(UriValidator.is_empty(uuri)) - self.assertNotEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - - def test_parse_protocol_uri_with_schema_and_4_slash_and_something(self): - uri = "////body.access" - uuri = LongUriSerializer().deserialize(uri) - - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertFalse(uuri.HasField("resource")) - self.assertFalse(uuri.HasField("entity")) - self.assertTrue(len(uuri.entity.name) == 0) - self.assertEqual(0, uuri.entity.version_major) - - def test_parse_protocol_uri_with_schema_and_5_slash_and_something(self): - uri = "/////body.access" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertFalse(uuri.HasField("resource")) - self.assertFalse(uuri.HasField("entity")) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_parse_protocol_uri_with_schema_and_6_slash_and_something(self): - uri = "//////body.access" - uuri = LongUriSerializer().deserialize(uri) - - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_parse_protocol_uri_with_local_service_no_version(self): - uri = "/body.access" - uuri = LongUriSerializer().deserialize(uri) - - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual(0, uuri.entity.version_minor) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_local_service_with_version(self): - uri = "/body.access/1" - uuri = LongUriSerializer().deserialize(uri) - - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(1, uuri.entity.version_major) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_local_service_no_version_with_resource_name_only( - self, - ): - uri = "/body.access//door" - uuri = LongUriSerializer().deserialize(uri) - - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual(0, uuri.entity.version_minor) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_local_service_with_version_with_resource_name_only( - self, - ): - uri = "/body.access/1/door" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_local_service_no_version_with_resource_with_instance( - self, - ): - uri = "/body.access//door.front_left" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_local_service_with_version_with_resource_with_get_message( - self, - ): - uri = "/body.access/1/door.front_left" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_local_service_no_version_with_resource_with_instance_and_get_message( - self, - ): - uri = "/body.access//door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_uri_with_local_service_with_version_with_resource_with_instance_and_get_message( - self, - ): - uri = "/body.access/1/door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_rpc_uri_with_local_service_no_version(self): - uri = "/petapp//rpc.response" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("petapp", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("rpc", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("response", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_rpc_uri_with_local_service_with_version(self): - uri = "/petapp/1/rpc.response" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("petapp", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("rpc", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("response", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_only_device_and_domain( - self, - ): - uri = "//VCU.MY_CAR_VIN" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - - def test_parse_protocol_uri_with_remote_service_only_device_and_cloud_domain( - self, - ): - uri = "//cloud.uprotocol.example.com" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(uuri.HasField("entity")) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_remote_service_no_version(self): - uri = "//VCU.MY_CAR_VIN/body.access" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_remote_cloud_service_no_version(self): - uri = "//cloud.uprotocol.example.com/body.access" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_remote_service_with_version(self): - uri = "//VCU.MY_CAR_VIN/body.access/1" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_remote_cloud_service_with_version(self): - uri = "//cloud.uprotocol.example.com/body.access/1" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertFalse(uuri.HasField("resource")) - - def test_parse_protocol_uri_with_remote_service_no_version_with_resource_name_only( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access//door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_cloud_service_no_version_with_resource_name_only( - self, - ): - uri = "//cloud.uprotocol.example.com/body.access//door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_with_version_with_resource_name_only( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access/1/door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_cloud_with_version_with_resource_name_only( - self, - ): - uri = "//cloud.uprotocol.example.com/body.access/1/door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertTrue(len(uuri.resource.instance) == 0) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_no_version_with_resource_and_instance_no_get_message( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access//door.front_left" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_with_version_with_resource_and_instance_no_get_message( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_uri_with_remote_service_no_version_with_resource_and_instance_and_get_message( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access//door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_uri_with_remote_cloud_service_no_version_with_resource_and_instance_and_get_message( - self, - ): - uri = "//cloud.uprotocol.example.com/body.access//door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_uri_with_remote_service_with_version_with_resource_and_instance_and_get_message( - self, - ): - uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU.MY_CAR_VIN", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_uri_with_remote_cloud_service_with_version_with_resource_and_instance_and_get_message( - self, - ): - uri = "//cloud.uprotocol.example.com/body.access/1/door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("cloud.uprotocol.example.com", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_protocol_uri_with_remote_service_with_version_with_resource_with_message_device_no_domain( - self, - ): - uri = "//VCU/body.access/1/door.front_left" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("VCU", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("body.access", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_rpc_uri_with_remote_service_no_version(self): - uri = "//bo.cloud/petapp//rpc.response" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("bo.cloud", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("petapp", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("rpc", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("response", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_parse_protocol_rpc_uri_with_remote_service_with_version(self): - uri = "//bo.cloud/petapp/1/rpc.response" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertFalse(len(uuri.authority.name) == 0) - self.assertEqual("bo.cloud", uuri.authority.name) - self.assertFalse(len(uuri.entity.name) == 0) - self.assertEqual("petapp", uuri.entity.name) - self.assertNotEqual(0, uuri.entity.version_major) - self.assertEqual(1, uuri.entity.version_major) - self.assertEqual("rpc", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance) == 0) - self.assertEqual("response", uuri.resource.instance) - self.assertTrue(len(uuri.resource.message) == 0) - - def test_build_protocol_uri_from__uri_when__uri_isnull(self): - uprotocol_uri = LongUriSerializer().serialize(None) - self.assertEqual("", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_is_empty(self): - uuri = UUri() - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_has_empty_use(self): - uuri = UUri(resource=UResource(name="door")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("///door", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version( - self, - ): - uuri = UUri(entity=UEntity(name="body.access")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri(entity=use, resource=UResource()) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access/1", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_no_version_with_resource( - self, - ): - use = UEntity(name="body.access") - uuri = UUri(entity=use, resource=UResource(name="door")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access//door", uprotocol_uri) - - def test_build_protocol_uri_from__uri_when__uri_has_local_authority_service_and_version_with_resource( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri(entity=use, resource=UResource(name="door")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access/1/door", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_l_authority_service_n_vsn_with_rsrc_with_instance_n_get_message( - self, - ): - use = UEntity(name="body.access") - uuri = UUri(entity=use, resource=UResource(name="door", instance="front_left")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access//door.front_left", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_l_authority_service_and_vsn_with_rsrc_with_instance_n_get_message( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri(entity=use, resource=UResource(name="door", instance="front_left")) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access/1/door.front_left", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_l_authority_svc_no_vsn_with_rsrc_with_instance_with_get_message( - self, - ): - use = UEntity(name="body.access") - uuri = UUri( - entity=use, - resource=UResource(name="door", instance="front_left", message="Door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access//door.front_left#Door", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_l_authority_svc_and_vsn_with_rsrc_with_instance_with_get_message( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - entity=use, - resource=UResource(name="door", instance="front_left", message="Door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("/body.access/1/door.front_left#Door", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_remote_authority_service_no_version( - self, - ): - use = UEntity(name="body.access") - uuri = UUri(authority=UAuthority(name="vcu.my_car_vin"), entity=use) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_remote_authority_service_and_version( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri(authority=UAuthority(name="vcu.my_car_vin"), entity=use) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access/1", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_remote_cloud_authority_service_and_version( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - authority=UAuthority(name="cloud.uprotocol.example.com"), - entity=use, - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//cloud.uprotocol.example.com/body.access/1", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_remote_authority_service_and_version_with_resource( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access/1/door", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_remote_authority_service_no_version_with_resource( - self, - ): - use = UEntity(name="body.access") - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access//door", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_r_authority_svc_and_vsn_with_rsrc_with_instance_no_get_message( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door", instance="front_left"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access/1/door.front_left", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_r_cld_authority_svc_and_vsn_with_rsrc_with_instance_n_get_message( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - authority=UAuthority(name="cloud.uprotocol.example.com"), - entity=use, - resource=UResource(name="door", instance="front_left"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual( - "//cloud.uprotocol.example.com/body.access/1/door.front_left", - uprotocol_uri, - ) - - def test_build_protocol_uri_from_uri_when_uri_has_r_authority_svc_no_vsn_with_resource_with_instance_n_get_message( - self, - ): - use = UEntity(name="body.access") - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door", instance="front_left"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access//door.front_left", uprotocol_uri) - - def test_build_protocol_uri_from_uri_when_uri_has_r_authority_svc_and_version_with_rsrc_with_i_and_get_message( - self, - ): - use = UEntity(name="body.access", version_major=1) - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door", instance="front_left", message="Door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual( - "//vcu.my_car_vin/body.access/1/door.front_left#Door", - uprotocol_uri, - ) - - def test_build_protocol_uri_from_uri_when_uri_has_r_authority_svc_no_version_with_rsrc_with_i_and_get_message( - self, - ): - use = UEntity(name="body.access") - uuri = UUri( - authority=UAuthority(name="vcu.my_car_vin"), - entity=use, - resource=UResource(name="door", instance="front_left", message="Door"), - ) - uprotocol_uri = LongUriSerializer().serialize(uuri) - self.assertEqual("//vcu.my_car_vin/body.access//door.front_left#Door", uprotocol_uri) - - def test_build_protocol_uri_for_source_part_of_rpc_request_where_source_is_local( - self, - ): - use = UEntity(name="petapp", version_major=1) - resource = UResource(name="rpc", instance="response") - uprotocol_uri = LongUriSerializer().serialize(UUri(entity=use, resource=resource)) - self.assertEqual("/petapp/1/rpc.response", uprotocol_uri) - - def test_build_protocol_uri_for_source_part_of_rpc_request_where_source_is_remote( - self, - ): - u_authority = UAuthority(name="cloud.uprotocol.example.com") - use = UEntity(name="petapp") - resource = UResource(name="rpc", instance="response") - - uprotocol_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("//cloud.uprotocol.example.com/petapp//rpc.response", uprotocol_uri) - - def test_build_protocol_uri_from_uri_parts_when_uri_has_remote_authority_service_and_version_with_resource( - self, - ): - u_authority = UAuthority(name="vcu.my_car_vin") - use = UEntity(name="body.access", version_major=1) - resource = UResource(name="door") - uprotocol_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("//vcu.my_car_vin/body.access/1/door", uprotocol_uri) - - def test_custom_scheme_no_scheme(self): - u_authority = UAuthority(name="vcu.my_car_vin") - use = UEntity(name="body.access", version_major=1) - resource = UResource(name="door") - ucustom_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("//vcu.my_car_vin/body.access/1/door", ucustom_uri) - - def test_parse_local_protocol_uri_with_custom_scheme(self): - uri = "custom:/body.access//door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertFalse(UriValidator.is_remote(uuri.authority)) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertFalse(len(uuri.resource.instance.strip()) == 0) - self.assertEqual("front_left", uuri.resource.instance) - self.assertFalse(len(uuri.resource.message.strip()) == 0) - self.assertEqual("Door", uuri.resource.message) - - def test_parse_remote_protocol_uri_with_custom_scheme(self): - uri = "custom://vcu.vin/body.access//door.front_left#Door" - uri2 = "//vcu.vin/body.access//door.front_left#Door" - uuri = LongUriSerializer().deserialize(uri) - self.assertTrue(UriValidator.is_remote(uuri.authority)) - self.assertEqual("vcu.vin", uuri.authority.name) - self.assertEqual("body.access", uuri.entity.name) - self.assertEqual(0, uuri.entity.version_major) - self.assertEqual("door", uuri.resource.name) - self.assertEqual("front_left", uuri.resource.instance) - self.assertEqual("Door", uuri.resource.message) - self.assertEqual(uri2, LongUriSerializer().serialize(uuri)) - - def test_b_prtcl_uri__uri__uri_rmte_auth_srvc_vsn_rsrc_emp_instc(self): - u_authority = UAuthority(name="vcu.my_car_vin") - use = UEntity(name="body.access", version_major=1) - resource = UResource(name="door", instance="", message="Door") - ucustom_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("//vcu.my_car_vin/body.access/1/door#Door", ucustom_uri) - - def test_b_prtcl_uri__uri__uri_rmte_auth_srvc_vsn_rsrc_emp_msg(self): - u_authority = UAuthority(name="vcu.my_car_vin") - use = UEntity(name="body.access", version_major=1) - resource = UResource(name="door", instance="front_left", message="") - ucustom_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("//vcu.my_car_vin/body.access/1/door.front_left", ucustom_uri) - - def test_b_prtcl_uri__uri__uri_empty_auth_service_ver_w_rsrc(self): - u_authority = UAuthority(name="") - use = UEntity(name="body.access", version_major=1) - resource = UResource(name="door", instance="front_left", message="Door") - ucustom_uri = LongUriSerializer().serialize(UUri(authority=u_authority, entity=use, resource=resource)) - self.assertEqual("/body.access/1/door.front_left#Door", ucustom_uri) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_uri/test_serializer/test_microuriserializer.py b/tests/test_uri/test_serializer/test_microuriserializer.py deleted file mode 100644 index 1b67ea9..0000000 --- a/tests/test_uri/test_serializer/test_microuriserializer.py +++ /dev/null @@ -1,229 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import socket -import unittest - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.microuriserializer import MicroUriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator - - -class TestMicroUriSerializer(unittest.TestCase): - def test_empty(self): - bytes_uuri = MicroUriSerializer().serialize(UUri()) - self.assertEqual(len(bytes_uuri), 0) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(UriValidator.is_empty(uri2)) - - def test_null(self): - bytes_uuri = MicroUriSerializer().serialize(None) - self.assertEqual(len(bytes_uuri), 0) - uri2 = MicroUriSerializer().deserialize(None) - self.assertTrue(UriValidator.is_empty(uri2)) - - def test_serialize_uri(self): - uri = UUri( - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.from_id(19999), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(UriValidator.is_micro_form(uri)) - self.assertTrue(len(bytes_uuri) > 0) - self.assertEqual(uri, uri2) - - def test_serialize_remote_uri_without_address(self): - uri = UUri( - authority=UAuthority(name="vcu.vin"), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.for_rpc_response(), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertTrue(len(bytes_uuri) == 0) - - def test_serialize_uri_missing_ids(self): - uri = UUri( - entity=UEntity(name="hartley"), - resource=UResourceBuilder.for_rpc_response(), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertTrue(len(bytes_uuri) == 0) - - def test_serialize_uri_missing_resource_id(self): - uri = UUri(entity=UEntity(name="hartley")) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertTrue(len(bytes_uuri) == 0) - - def test_deserialize_bad_microuri_length(self): - bad_micro_uuri = bytes([0x1, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - bad_micro_uuri = bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_deserialize_bad_microuri_not_version_1(self): - bad_micro_uuri = bytes([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_deserialize_bad_microuri_not_valid_address_type(self): - bad_micro_uuri = bytes([0x1, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_deserialize_bad_microuri_valid_address_type_invalid_length(self): - bad_micro_uuri = bytes([0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - bad_micro_uuri = bytes([0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - bad_micro_uuri = bytes([0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) - uuri = MicroUriSerializer().deserialize(bad_micro_uuri) - self.assertTrue(UriValidator.is_empty(uuri)) - - def test_serialize_good_ipv4_based_authority(self): - uri = UUri( - authority=UAuthority(ip=socket.inet_pton(socket.AF_INET, "10.0.3.3")), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.for_rpc_request(99), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(len(bytes_uuri) > 0) - self.assertTrue(UriValidator.is_micro_form(uri)) - self.assertTrue(UriValidator.is_micro_form(uri2)) - self.assertEqual(str(uri), str(uri2)) - self.assertTrue(uri == uri2) - - def test_serialize_bad_authority(self): - uri = UUri( - authority=UAuthority(ip=b"123456789"), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.for_rpc_request(99), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(bytes_uuri, bytearray()) - - def test_serialize_good_ipv6_based_authority(self): - uri = UUri( - authority=UAuthority(ip=socket.inet_pton(socket.AF_INET6, "2001:0db8:85a3:0000:0000:8a2e:0370:7334")), - entity=UEntity(id=29999, version_major=254), - resource=UResource(id=19999, name="rpc"), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(UriValidator.is_micro_form(uri)) - self.assertTrue(len(bytes_uuri) > 0) - self.assertTrue(uri == uri2) - - def test_serialize_id_based_authority(self): - size = 13 - byte_array = bytearray(i for i in range(size)) - uri = UUri( - authority=UAuthority(id=bytes(byte_array)), - entity=UEntity(id=29999, version_major=254), - resource=UResource(id=19999, name="rpc"), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(UriValidator.is_micro_form(uri)) - self.assertTrue(len(bytes_uuri) > 0) - self.assertTrue(uri == uri2) - - def test_serialize_bad_length_ip_based_authority(self): - byte_array = bytes([127, 1, 23, 123, 12, 6]) - uri = UUri( - authority=UAuthority(ip=byte_array), - entity=UEntity(id=29999, version_major=254), - resource=UResource(id=19999), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertTrue(len(bytes_uuri) == 0) - - def test_serialize_id_size_255_based_authority(self): - size = 129 - byte_array = bytes(i for i in range(size)) - uri = UUri( - authority=UAuthority(id=byte_array), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.for_rpc_request(99), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(len(bytes_uuri), 9 + size) - uri2 = MicroUriSerializer().deserialize(bytes_uuri) - self.assertTrue(UriValidator.is_micro_form(uri)) - self.assertTrue(uri == uri2) - - def test_serialize_id_out_of_range_2(self): - byte_array = bytearray(258) - for i in range(256): - byte_array[i] = i - byte_array = bytes(byte_array) - uri = UUri( - authority=UAuthority(id=byte_array), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.from_id(19999), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(len(bytes_uuri), 0) - - def test_serialize_resource_id_out_of_range(self): - size = 129 - byte_array = bytes(i for i in range(size)) - uri = UUri( - authority=UAuthority(id=byte_array), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.from_id(65536), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(len(bytes_uuri), 0) - - def test_serialize_entity_id_out_of_range(self): - size = 129 - byte_array = bytes(i for i in range(size)) - uri = UUri( - authority=UAuthority(id=byte_array), - entity=UEntity(id=65536, version_major=254), - resource=UResourceBuilder.from_id(19999), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(len(bytes_uuri), 0) - - def test_serialize_authority_ip_invalid(self): - uri = UUri( - authority=UAuthority(ip=b"123456789"), - entity=UEntity(id=29999, version_major=254), - resource=UResourceBuilder.from_id(19999), - ) - bytes_uuri = MicroUriSerializer().serialize(uri) - self.assertEqual(len(bytes_uuri), 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_uri/test_serializer/test_shorturiserializer.py b/tests/test_uri/test_serializer/test_shorturiserializer.py deleted file mode 100644 index 0edc320..0000000 --- a/tests/test_uri/test_serializer/test_shorturiserializer.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import unittest -from socket import inet_aton - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.shorturiserializer import ShortUriSerializer - - -class ShortUriSerializerTest(unittest.TestCase): - def test_serialize_with_null_uri(self): - str_uri = ShortUriSerializer().serialize(None) - self.assertEqual("", str_uri) - - def test_serialize_with_empty_uri(self): - str_uri = ShortUriSerializer().serialize(UUri()) - self.assertEqual("", str_uri) - - def test_creating_short_uri_serializer(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - resource=UResourceBuilder.for_rpc_response(), - ) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual("/1/1/0", str_uri) - uri2 = ShortUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_creating_short_uri_serializer_with_method(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - resource=UResourceBuilder.for_rpc_request(10), - ) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual("/1/1/10", str_uri) - uri2 = ShortUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_creating_short_uri_serializer_with_topic(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - resource=UResourceBuilder.from_id(20000), - ) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual("/1/1/20000", str_uri) - uri2 = ShortUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_creating_short_uri_serializer_with_authority(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - authority=UAuthority(id=b"19UYA31581L000000"), - resource=UResourceBuilder.from_id(20000), - ) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual("//19UYA31581L000000/1/1/20000", str_uri) - uri2 = ShortUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_creating_short_uri_serializer_with_ip_authority(self): - ip_bytes = inet_aton("192.168.1.100") - uri = UUri( - entity=UEntity(id=1, version_major=1), - authority=UAuthority(ip=ip_bytes), - resource=UResourceBuilder.from_id(20000), - ) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual("//192.168.1.100/1/1/20000", str_uri) - uri2 = ShortUriSerializer().deserialize(str_uri) - self.assertEqual(uri, uri2) - - def test_short_serializing_uri_without_resource(self): - uri = UUri(entity=UEntity(id=1, version_major=1)) - str_uri = ShortUriSerializer().serialize(uri) - self.assertEqual(str_uri, "/1/1") - - def test_short_serializing_uri_with_negative_version_major(self): - with self.assertRaises(ValueError) as context: - UUri( - entity=UEntity(id=1, version_major=-1), - resource=UResourceBuilder.from_id(20000), - ) - self.assertTrue("Value out of range: -1" in str(context.exception)) - - def test_short_deserialize_null_uri(self): - uri = ShortUriSerializer().deserialize(None) - self.assertEqual(uri, UUri()) - - def test_short_deserialize_empty_uri(self): - uri = ShortUriSerializer().deserialize("") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_uri_too_short(self): - uri = ShortUriSerializer().deserialize("1") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_uri_with_scheme_and_authority(self): - uri = ShortUriSerializer().deserialize("up://mypc/1/1/1") - self.assertTrue(uri.authority is not None) - self.assertEqual(uri.authority.id, b"mypc") - self.assertFalse(uri.authority.HasField("name")) - self.assertFalse(uri.authority.HasField("ip")) - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertEqual(uri.entity.version_major, 1) - self.assertTrue(uri.HasField("resource")) - self.assertEqual(uri.resource.id, 1) - - def test_short_deserialize_uri_without_scheme(self): - uri = ShortUriSerializer().deserialize("//mypc/1/1/1") - self.assertTrue(uri.HasField("authority")) - self.assertEqual(uri.authority.id, bytes("mypc", "utf-8")) - self.assertFalse(uri.authority.HasField("name")) - self.assertFalse(uri.authority.HasField("ip")) - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertEqual(uri.entity.version_major, 1) - self.assertTrue(uri.HasField("resource")) - self.assertEqual(uri.resource.id, 1) - - def test_short_deserialize_uri_with_only_authority(self): - uri = ShortUriSerializer().deserialize("//") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_uri_with_scheme_and_only_authority(self): - uri = ShortUriSerializer().deserialize("up://") - self.assertEqual(uri, UUri()) - - def test_short_serialize_with_invalid_ip_address(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - authority=UAuthority(ip=b"34823748273"), - ) - uri_string = ShortUriSerializer().serialize(uri) - self.assertEqual(uri_string, "") - - def test_short_serialize_with_authority_only_name(self): - uri = UUri( - entity=UEntity(id=1, version_major=1), - authority=UAuthority(name="mypc"), - ) - uri_string = ShortUriSerializer().serialize(uri) - self.assertEqual(uri_string, "") - - def test_short_deserialize_local_uri_with_too_many_parts(self): - uri = ShortUriSerializer().deserialize("/1/1/1/1") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_local_uri_with_only_two_parts(self): - uri = ShortUriSerializer().deserialize("/1/1") - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertEqual(uri.entity.version_major, 1) - - def test_short_deserialize_local_uri_with_three_parts(self): - uri = ShortUriSerializer().deserialize("/1") - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertFalse(uri.HasField("resource")) - - def test_short_deserialize_with_blank_authority(self): - uri = ShortUriSerializer().deserialize("///1/1/1") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_with_remote_authority_ip_and_too_many_parts( - self, - ): - uri = ShortUriSerializer().deserialize("//192.168.1.100/1/1/1/1") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_with_remote_authority_ip_and_right_number_of_parts( - self, - ): - uri = ShortUriSerializer().deserialize("//192.168.1.100/1/1/1") - self.assertTrue(uri.authority is not None) - self.assertTrue(uri.authority.HasField("ip")) - self.assertEqual(uri.authority.ip, inet_aton("192.168.1.100")) - self.assertTrue(uri.entity is not None) - self.assertEqual(uri.entity.id, 1) - self.assertEqual(uri.entity.version_major, 1) - self.assertTrue(uri.resource is not None) - self.assertEqual(uri.resource.id, 1) - - def test_short_deserialize_with_remote_authority_ip_address_missing_resource( - self, - ): - uri = ShortUriSerializer().deserialize("//192.168.1.100/1/1") - self.assertTrue(uri.HasField("authority")) - self.assertTrue(uri.authority.HasField("ip")) - self.assertEqual(uri.authority.ip, inet_aton("192.168.1.100")) - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertEqual(uri.entity.version_major, 1) - self.assertFalse(uri.HasField("resource")) - - def test_short_deserialize_with_remote_authority_ip_address_missing_resource_and_version_major( - self, - ): - uri = ShortUriSerializer().deserialize("//192.168.1.100/1") - self.assertTrue(uri.HasField("authority")) - self.assertTrue(uri.authority.HasField("ip")) - self.assertEqual(uri.authority.ip, inet_aton("192.168.1.100")) - self.assertTrue(uri.HasField("entity")) - self.assertEqual(uri.entity.id, 1) - self.assertFalse(uri.entity.HasField("version_major")) - - def test_short_deserialize_with_remote_authority_ip_address_missing_resource_and_version_major_and_ueid( - self, - ): - uri = ShortUriSerializer().deserialize("//192.168.1.100//") - self.assertTrue(uri.HasField("authority")) - self.assertTrue(uri.authority.HasField("ip")) - self.assertEqual(uri.authority.ip, inet_aton("192.168.1.100")) - self.assertFalse(uri.HasField("entity")) - - def test_short_deserialize_with_remote_authority_and_blank_ueversion_and_ueid( - self, - ): - uri = ShortUriSerializer().deserialize("//mypc//1/") - self.assertTrue(uri.HasField("authority")) - self.assertTrue(uri.authority.HasField("id")) - self.assertEqual(uri.authority.id, b"mypc") - self.assertTrue(uri.HasField("entity")) - - def test_short_deserialize_with_remote_authority_and_missing_parts(self): - uri = ShortUriSerializer().deserialize("//mypc") - self.assertTrue(uri.HasField("authority")) - self.assertTrue(uri.authority.HasField("id")) - self.assertEqual(uri.authority.id, b"mypc") - - def test_short_deserialize_with_remote_authority_and_invalid_characters_for_entity_id_and_version( - self, - ): - uri = ShortUriSerializer().deserialize("//mypc/abc/def") - self.assertEqual(uri, UUri()) - - def test_short_deserialize_with_remote_authority_and_invalid_characters_for_resource_id( - self, - ): - uri = ShortUriSerializer().deserialize("//mypc/1/1/abc") - self.assertEqual(uri.resource, UResource()) - - def test_parse_from_string_none(self): - with self.assertRaises(ValueError) as context: - ShortUriSerializer().parse_from_string(None) - self.assertTrue(" Resource must have a command name" in context.exception) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_uri/test_serializer/test_uriserializer.py b/tests/test_uri/test_serializer/test_uriserializer.py index 3ba97bc..a0d1a54 100644 --- a/tests/test_uri/test_serializer/test_uriserializer.py +++ b/tests/test_uri/test_serializer/test_uriserializer.py @@ -1,179 +1,183 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ -import socket import unittest -from google.protobuf.descriptor import FileDescriptor, ServiceDescriptor -from google.protobuf.descriptor_pb2 import ServiceOptions -from google.protobuf.internal.containers import RepeatedCompositeFieldContainer - -from uprotocol.proto.core.usubscription.v3.usubscription_pb2 import DESCRIPTOR as U_SUBSCRIPTION_FILE_DESCRIPTOR -from uprotocol.proto.uprotocol_options_pb2 import UServiceTopic, notification_topic -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uentityfactory import UEntityFactory -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uri.serializer.microuriserializer import MicroUriSerializer -from uprotocol.uri.serializer.shorturiserializer import ShortUriSerializer +from uprotocol.uri.serializer.uriserializer import UriSerializer from uprotocol.uri.validator.urivalidator import UriValidator +from uprotocol.v1.uri_pb2 import UUri class TestUriSerializer(unittest.TestCase): - def get_uresource(self, container: RepeatedCompositeFieldContainer) -> UResource: - if len(container) == 0: - resrc = UResource() - else: - first_topic: UServiceTopic = container[0] - id: int = first_topic.id - name: str = first_topic.name - message: str = first_topic.message - - resrc = UResource() - if id is not None: - resrc.id = id - if name is not None: - resrc.name = name - if message is not None: - resrc.message = message - return resrc - - def check_given_test_data_is_formed_correctly(self, uuri: UUri): - self.assertFalse(UriValidator.is_empty(uuri)) - self.assertTrue(UriValidator.is_micro_form(uuri)) - self.assertTrue(UriValidator.is_long_form(uuri)) - self.assertTrue(UriValidator.is_short_form(uuri)) - - def test_build_resolved_full_information_compare(self): - file_descriptor: FileDescriptor = U_SUBSCRIPTION_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uSubscription"] - options: ServiceOptions = service_descriptor.GetOptions() - container: RepeatedCompositeFieldContainer = options.Extensions[notification_topic] - - uresrc: UResource = self.get_uresource(container) - uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - uuri: UUri = UUri(entity=uentity, resource=uresrc) - - actual_long_uri: str = LongUriSerializer().serialize(uuri) - actual_short_uri: str = ShortUriSerializer().serialize(uuri) - actual_micro_uri: bytes = MicroUriSerializer().serialize(uuri) - - expected_longuri_given_no_uauthority: str = "/core.usubscription/3/SubscriptionChange#Update" - expected_shorturi_given_no_uauthority: str = "/0/3/32768" - expected_micro_uri_given_no_uauthority = bytearray(b"\x01\x00\x80\x00\x00\x00\x03\x00") - - self.check_given_test_data_is_formed_correctly(uuri) - - self.assertEqual(expected_longuri_given_no_uauthority, actual_long_uri) - self.assertEqual(expected_shorturi_given_no_uauthority, actual_short_uri) - self.assertEqual(expected_micro_uri_given_no_uauthority, actual_micro_uri) - - def test_build_resolved_full_information_compare_with_ipv4(self): - file_descriptor: FileDescriptor = U_SUBSCRIPTION_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uSubscription"] - options: ServiceOptions = service_descriptor.GetOptions() - container: RepeatedCompositeFieldContainer = options.Extensions[notification_topic] - - uresrc: UResource = self.get_uresource(container) - uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - - ipv4_address: str = "192.168.1.100" - ipv4_packed_ip: bytes = socket.inet_pton(socket.AF_INET, ipv4_address) - - authority: UAuthority = UAuthority(name="vcu.veh.gm.com", ip=ipv4_packed_ip) - uuri: UUri = UUri(entity=uentity, resource=uresrc, authority=authority) - - self.check_given_test_data_is_formed_correctly(uuri) - - actual_long_uri: str = LongUriSerializer().serialize(uuri) - actual_short_uri: str = ShortUriSerializer().serialize(uuri) - actual_micro_uri: bytes = MicroUriSerializer().serialize(uuri) - - expected_longuri_given_ipv4_uathority: str = "//vcu.veh.gm.com/core.usubscription/3/SubscriptionChange#Update" - expected_shorturi_given_ipv4_uathority: str = "//192.168.1.100/0/3/32768" - expected_microuri_given_ipv4_uathority = bytearray(b"\x01\x01\x80\x00\x00\x00\x03\x00\xc0\xa8\x01d") - - self.assertEqual(expected_longuri_given_ipv4_uathority, actual_long_uri) - self.assertEqual(expected_shorturi_given_ipv4_uathority, actual_short_uri) - self.assertEqual(expected_microuri_given_ipv4_uathority, actual_micro_uri) - - def test_build_resolved_full_information_compare_with_id(self): - file_descriptor: FileDescriptor = U_SUBSCRIPTION_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uSubscription"] - options: ServiceOptions = service_descriptor.GetOptions() - container: RepeatedCompositeFieldContainer = options.Extensions[notification_topic] - - uresrc: UResource = self.get_uresource(container) - uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - - authority: UAuthority = UAuthority(name="1G1YZ23J9P5800001.veh.gm.com", id=b"1G1YZ23J9P5800001") - uuri: UUri = UUri(entity=uentity, resource=uresrc, authority=authority) - - self.check_given_test_data_is_formed_correctly(uuri) - - actual_long_uri: str = LongUriSerializer().serialize(uuri) - actual_short_uri: str = ShortUriSerializer().serialize(uuri) - actual_micro_uri: bytes = MicroUriSerializer().serialize(uuri) - - expected_longuri_given_id_uathority: str = ( - "//1G1YZ23J9P5800001.veh.gm.com/core.usubscription/3/SubscriptionChange#Update" + def test_using_the_serializers(self): + uri = UUri( + authority_name="myAuthority", + ue_id=1, + ue_version_major=2, + resource_id=3, ) - expected_shorturi_given_id_uathority: str = "//1G1YZ23J9P5800001/0/3/32768" - expected_microuri_given_id_uathority = bytearray(b"\x01\x03\x80\x00\x00\x00\x03\x00\x111G1YZ23J9P5800001") - - self.assertEqual(expected_longuri_given_id_uathority, actual_long_uri) - self.assertEqual(expected_shorturi_given_id_uathority, actual_short_uri) - self.assertEqual(expected_microuri_given_id_uathority, actual_micro_uri) - - def test_build_resolved_full_information_compare_with_ipv6(self): - file_descriptor: FileDescriptor = U_SUBSCRIPTION_FILE_DESCRIPTOR - service_descriptor: ServiceDescriptor = file_descriptor.services_by_name["uSubscription"] - options: ServiceOptions = service_descriptor.GetOptions() - container: RepeatedCompositeFieldContainer = options.Extensions[notification_topic] - - uresrc: UResource = self.get_uresource(container) - uentity: UEntity = UEntityFactory.from_proto(service_descriptor) - - # inet_pton(): Convert IP address from its family-specific string format -> a packed, binary format - ipv6_address: str = "2001:db8:85a3:0:0:8a2e:370:7334" - ipv6_packed_ip: bytes = socket.inet_pton(socket.AF_INET6, ipv6_address) - - authority: UAuthority = UAuthority(name="vcu.veh.gm.com", ip=ipv6_packed_ip) - uuri: UUri = UUri(entity=uentity, resource=uresrc, authority=authority) - - self.check_given_test_data_is_formed_correctly(uuri) - - actual_long_uri: str = LongUriSerializer().serialize(uuri) - actual_short_uri: str = ShortUriSerializer().serialize(uuri) - actual_micro_uri: bytes = MicroUriSerializer().serialize(uuri) - - expected_longuri_given_ipv6_uathority: str = "//vcu.veh.gm.com/core.usubscription/3/SubscriptionChange#Update" - expected_shorturi_given_ipv6_uathority: str = "//2001:db8:85a3::8a2e:370:7334/0/3/32768" - expected_microuri_given_ipv6_uathority = bytearray( - b"\x01\x02\x80\x00\x00\x00\x03\x00 \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4" - ) - - self.assertEqual(expected_longuri_given_ipv6_uathority, actual_long_uri) - self.assertEqual(expected_shorturi_given_ipv6_uathority, actual_short_uri) - self.assertEqual(expected_microuri_given_ipv6_uathority, actual_micro_uri) + serialized_uri = UriSerializer.serialize(uri) + self.assertEqual(serialized_uri, "//myAuthority/1/2/3") + + def test_deserializing_a_null_uuri(self): + uri = UriSerializer.deserialize(None) + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_a_empty_uuri(self): + uri = UriSerializer.deserialize("") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_a_blank_uuri(self): + uri = UriSerializer.deserialize(" ") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_a_valid_uri_that_has_scheme(self): + uri = UriSerializer.deserialize("up://myAuthority/1/2/3") + self.assertEqual(uri.authority_name, "myAuthority") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 3) + + def test_deserializing_with_a_valid_uri_that_has_only_scheme(self): + uri = UriSerializer.deserialize("up://") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_a_valid_uuri_with_all_fields(self): + uri = UriSerializer.deserialize("//myAuthority/1/2/3") + self.assertEqual(uri.authority_name, "myAuthority") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 3) + + def test_deserializing_with_only_authority(self): + uri = UriSerializer.deserialize("//myAuthority") + self.assertEqual(uri.authority_name, "myAuthority") + self.assertEqual(uri.ue_id, 0) + self.assertEqual(uri.ue_version_major, 0) + self.assertEqual(uri.resource_id, 0) + + def test_deserializing_authority_ueid(self): + uri = UriSerializer.deserialize("//myAuthority/1") + self.assertEqual(uri.authority_name, "myAuthority") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 0) + self.assertEqual(uri.resource_id, 0) + + def test_deserializing_authority_ueid_ueversion(self): + uri = UriSerializer.deserialize("//myAuthority/1/2") + self.assertEqual(uri.authority_name, "myAuthority") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 0) + + def test_deserializing_a_string_with_invalid_chars(self): + uri = UriSerializer.deserialize("$$") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_names_instead_of_id_for_ueid(self): + uri = UriSerializer.deserialize("//myAuthority/one/2/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_names_instead_of_id_for_ueversion(self): + uri = UriSerializer.deserialize("//myAuthority/1/two/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_names_instead_of_id_for_resource_id(self): + uri = UriSerializer.deserialize("//myAuthority/1/2/three") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_a_string_without_authority(self): + uri = UriSerializer.deserialize("/1/2/3") + self.assertEqual(uri.authority_name, "") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 3) + + def test_deserializing_without_authority_and_resourceid(self): + uri = UriSerializer.deserialize("/1/2") + self.assertEqual(uri.authority_name, "") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 0) + + def test_deserializing_without_authority_resourceid_version_major(self): + uri = UriSerializer.deserialize("/1") + self.assertEqual(uri.authority_name, "") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 0) + self.assertEqual(uri.resource_id, 0) + + def test_deserializing_with_blank_authority(self): + uri = UriSerializer.deserialize("///2") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_all_wildcard_values(self): + uri = UriSerializer.deserialize("//*/FFFF/ff/ffff") + self.assertEqual(uri.authority_name, "*") + self.assertEqual(uri.ue_id, 0xFFFF) + self.assertEqual(uri.ue_version_major, 0xFF) + self.assertEqual(uri.resource_id, 0xFFFF) + + def test_deserializing_with_ueid_out_of_range(self): + uri = UriSerializer.deserialize("/fffffffff/2/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_ueversionmajor_out_of_range(self): + uri = UriSerializer.deserialize("/1/256/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_resourceid_out_of_range(self): + uri = UriSerializer.deserialize("/1/2/65536") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_negative_ueid(self): + uri = UriSerializer.deserialize("/-1/2/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_negative_versionmajor(self): + uri = UriSerializer.deserialize("/1/-2/3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_negative_resourceid(self): + uri = UriSerializer.deserialize("/1/2/-3") + self.assertTrue(UriValidator.is_empty(uri)) + + def test_deserializing_with_wildcard_resourceid(self): + uri = UriSerializer.deserialize("/1/2/ffff") + self.assertEqual(uri.authority_name, "") + self.assertEqual(uri.ue_id, 1) + self.assertEqual(uri.ue_version_major, 2) + self.assertEqual(uri.resource_id, 0xFFFF) + + def test_serializing_an_empty_uri(self): + uri = UUri() + serialized_uri = UriSerializer.serialize(uri) + self.assertEqual(serialized_uri, "") + + def test_serializing_a_none_uri(self): + serialized_uri = UriSerializer.serialize(None) + self.assertEqual(serialized_uri, "") + + def test_serializing_only_authority_ueid(self): + uri = UUri(authority_name="myAuthority", ue_id=1) + serialized_uri = UriSerializer.serialize(uri) + self.assertEqual(serialized_uri, "//myAuthority/1/0/0") + + def test_serializing_only_authority_ueid_version_major(self): + uri = UUri(authority_name="myAuthority", ue_id=1, ue_version_major=2) + serialized_uri = UriSerializer.serialize(uri) + self.assertEqual(serialized_uri, "//myAuthority/1/2/0") if __name__ == "__main__": diff --git a/tests/test_uri/test_validator/test_urivalidator.py b/tests/test_uri/test_validator/test_urivalidator.py index fb71ea2..0a8b227 100644 --- a/tests/test_uri/test_validator/test_urivalidator.py +++ b/tests/test_uri/test_validator/test_urivalidator.py @@ -1,460 +1,60 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ -import json -import os import unittest -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer from uprotocol.uri.validator.urivalidator import UriValidator -from uprotocol.validation.validationresult import ValidationResult +from uprotocol.v1.uri_pb2 import UUri class TestUriValidator(unittest.TestCase): - def test_validate_blank_uri(self): - uri = LongUriSerializer().deserialize(None) - status = UriValidator.validate(uri) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_uri_with_no_entity_get_name(self): - uri = LongUriSerializer().deserialize("//") - status = UriValidator.validate(uri) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_uri_with_get_entity(self): - uri = LongUriSerializer().deserialize("/neelam") - status = UriValidator.validate(uri) - self.assertTrue(ValidationResult.success().__eq__(status)) - - def test_validate_with_malformed_uri(self): - uri = LongUriSerializer().deserialize("neelam") - status = UriValidator.validate(uri) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_with_blank_uentity_name_uri(self): - status = UriValidator.validate(UUri()) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_rpc_method_with_valid_uri(self): - uri = LongUriSerializer().deserialize("/neelam//rpc.echo") - status = UriValidator.validate_rpc_method(uri) - self.assertTrue(ValidationResult.success().__eq__(status)) - - def test_validate_rpc_method_with_invalid_uri(self): - uri = LongUriSerializer().deserialize("/neelam/echo") - status = UriValidator.validate_rpc_method(uri) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_rpc_method_with_malformed_uri(self): - uri = LongUriSerializer().deserialize("neelam") - status = UriValidator.validate_rpc_method(uri) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_rpc_response_with_valid_uri(self): - uri = LongUriSerializer().deserialize("/neelam//rpc.response") - status = UriValidator.validate_rpc_response(uri) - self.assertTrue(ValidationResult.success().__eq__(status)) - - def test_validate_rpc_response_with_malformed_uri(self): - uri = LongUriSerializer().deserialize("neelam") - status = UriValidator.validate_rpc_response(uri) - self.assertTrue(UriValidator.is_empty(uri)) - self.assertEqual("Uri is empty.", status.get_message()) - - def test_validate_rpc_response_with_rpc_type(self): - uri = LongUriSerializer().deserialize("/neelam//dummy.wrong") - status = UriValidator.validate_rpc_response(uri) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid RPC response type.", status.get_message()) - - def test_validate_rpc_response_with_invalid_rpc_response_type(self): - uri = LongUriSerializer().deserialize("/neelam//rpc.wrong") - status = UriValidator.validate_rpc_response(uri) - self.assertTrue(status.is_failure()) - self.assertEqual("Invalid RPC response type.", status.get_message()) - - def test_topic_uri_with_version_when_it_is_valid_remote(self): - uri = "//VCU.MY_CAR_VIN/body.access/1/door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_topic_uri_no_version_when_it_is_valid_remote(self): - uri = "//VCU.MY_CAR_VIN/body.access//door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_topic_uri_with_version_when_it_is_valid_local(self): - uri = "/body.access/1/door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_topic_uri_no_version_when_it_is_valid_local(self): - uri = "/body.access//door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_topic_uri_invalid_when_uri_has_schema_only(self): - uri = ":" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_has_empty_use_name_local(self): - uri = "/" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_is_remote_no_authority(self): - uri = "//" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_is_remote_no_authority_with_use(self): - uri = "///body.access/1/door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_is_missing_use_remote(self): - uri = "//VCU.myvin///door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_is_missing_use_name_remote(self): - uri = "/1/door.front_left#Door" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_topic_uri_invalid_when_uri_is_missing_use_name_local(self): - uri = "//VCU.myvin//1" - status = UriValidator.validate(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_with_version_when_it_is_valid_remote(self): - uri = "//bo.cloud/petapp/1/rpc.response" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_topic_uri_no_version_when_it_is_valid_remote(self): - uri = "//bo.cloud/petapp//rpc.response" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_topic_uri_with_version_when_it_is_valid_local(self): - uri = "/petapp/1/rpc.response" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_topic_uri_no_version_when_it_is_valid_local(self): - uri = "/petapp//rpc.response" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_topic_uri_invalid_when_uri_has_schema_only(self): - uri = ":" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_with_version_when_it_is_not_valid_missing_rpc_response_local(self): - uri = "/petapp/1/dog" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_with_version_when_it_is_not_valid_missing_rpc_response_remote( - self, - ): - uri = "//petapp/1/dog" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_invalid_when_uri_is_remote_no_authority(self): - uri = "//" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_invalid_when_uri_is_remote_no_authority_with_use( - self, - ): - uri = "///body.access/1" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_invalid_when_uri_is_missing_use(self): - uri = "//VCU.myvin" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_invalid_when_uri_is_missing_use_name_remote(self): - uri = "/1" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_topic_uri_invalid_when_uri_is_missing_use_name_local(self): - uri = "//VCU.myvin//1" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_with_version_when_it_is_valid_remote(self): - uri = "//VCU.myvin/body.access/1/rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_method_uri_no_version_when_it_is_valid_remote(self): - uri = "//VCU.myvin/body.access//rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_method_uri_with_version_when_it_is_valid_local(self): - uri = "/body.access/1/rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_method_uri_no_version_when_it_is_valid_local(self): - uri = "/body.access//rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_success) - - def test_rpc_method_uri_invalid_when_uri_has_schema_only(self): - uri = ":" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_with_version_when_it_is_not_valid_not_rpc_method_local( - self, - ): - uri = "/body.access//UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_with_version_when_it_is_not_valid_not_rpc_method_remote( - self, - ): - uri = "//body.access/1/UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_invalid_when_uri_is_remote_no_authority(self): - uri = "//" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_invalid_when_uri_is_remote_no_authority_with_use( - self, - ): - uri = "///body.access/1/rpc.UpdateDoor" - uuri = LongUriSerializer().deserialize(uri) - status = UriValidator.validate_rpc_method(uuri) - self.assertEqual("", str(uuri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_invalid_when_uri_is_remote_missing_authority_remotecase( - self, - ): - uuri = UUri( - entity=UEntity(name="body.access"), - resource=UResource(name="rpc", instance="UpdateDoor"), - authority=UAuthority(), - ) - status = UriValidator.validate_rpc_method(uuri) - self.assertTrue(status.is_failure()) - self.assertEqual("Uri is remote missing u_authority.", status.get_message()) - - def test_rpc_method_uri_invalid_when_uri_is_missing_use(self): - uri = "//VCU.myvin" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_invalid_when_uri_is_missing_use_name_local(self): - uri = "/1/rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_rpc_method_uri_invalid_when_uri_is_missing_use_name_remote(self): - uri = "//VCU.myvin//1/rpc.UpdateDoor" - status = UriValidator.validate_rpc_method(LongUriSerializer().deserialize(uri)) - self.assertTrue(status.is_failure()) - - def test_all_valid_uris(self): - # Access the "validUris" array - valid_uris = self.get_json_object()["validUris"] - for valid_uri in valid_uris: - uuri = LongUriSerializer().deserialize(valid_uri) - status = UriValidator.validate(uuri) - self.assertTrue(status.is_success()) - - def test_all_invalid_uris(self): - # Access the "invalidUris" array - invalid_uris = self.get_json_object()["invalidUris"] - for invalid_uri in invalid_uris: - uuri = LongUriSerializer().deserialize(invalid_uri["uri"]) - status = UriValidator.validate(uuri) - self.assertTrue(status.is_failure()) - self.assertEqual(status.get_message(), invalid_uri["status_message"]) - - def test_all_valid_rpc_uris(self): - valid_rpc_uris = self.get_json_object()["validRpcUris"] - for valid_rpc_uri in valid_rpc_uris: - uuri = LongUriSerializer().deserialize(valid_rpc_uri) - status = UriValidator.validate_rpc_method(uuri) - self.assertTrue(status.is_success) - - def test_all_invalid_rpc_uris(self): - invalid_rpc_uris = self.get_json_object()["invalidRpcUris"] - for invalid_rpc_uri in invalid_rpc_uris: - uuri = LongUriSerializer().deserialize(invalid_rpc_uri["uri"]) - status = UriValidator.validate_rpc_method(uuri) - self.assertTrue(status.is_failure()) - self.assertEqual(status.get_message(), invalid_rpc_uri["status_message"]) - - def test_all_valid_rpc_response_uris(self): - valid_rpc_response_uris = self.get_json_object()["validRpcResponseUris"] - for valid_rpc_response_uri in valid_rpc_response_uris: - uuri = LongUriSerializer().deserialize(valid_rpc_response_uri) - status = UriValidator.validate_rpc_response(uuri) - self.assertTrue(UriValidator.is_rpc_response(uuri)) - self.assertTrue(status.is_success) - - def test_valid_rpc_response_uri(self): - uuri = UUri( - entity=UEntity(name="neelam"), - resource=UResource(name="rpc", id=0, instance="response"), - ) - status = UriValidator.validate_rpc_response(uuri) - self.assertTrue(UriValidator.is_rpc_response(uuri)) - self.assertTrue(status.is_success) - - def test_all_invalid_rpc_response_uris(self): - invalid_rpc_response_uris = self.get_json_object()["invalidRpcResponseUris"] - for invalid_rpc_response_uri in invalid_rpc_response_uris: - uuri = LongUriSerializer().deserialize(invalid_rpc_response_uri) - status = UriValidator.validate_rpc_response(uuri) - self.assertTrue(status.is_failure()) - - def test_invalid_rpc_method_uri(self): - uuri: UUri = UUri( - entity=UEntity(name="hello.world"), - resource=UResource(name="testrpc", instance="SayHello"), - ) - status = UriValidator.validate_rpc_method(uuri) - self.assertFalse(UriValidator.is_rpc_method(uuri)) - self.assertFalse(status.is_success()) - - def test_invalid_rpc_response_uri(self): - uuri: UUri = UUri( - entity=UEntity(name="hartley"), - resource=UResource(name="rpc", id=19999), - ) - status = UriValidator.validate_rpc_response(uuri) - self.assertFalse(UriValidator.is_rpc_response(uuri)) - self.assertFalse(status.is_success()) - - uuri: UUri = UUri( - entity=UEntity(name="hartley"), - resource=UResource(name="testrpc", instance="response"), - ) - status = UriValidator.validate_rpc_response(uuri) - self.assertFalse(UriValidator.is_rpc_response(uuri)) - self.assertFalse(status.is_success()) - - uuri: UUri = UUri( - entity=UEntity(name="hartley"), - resource=UResource(name="testrpc", instance="response"), - ) - status = UriValidator.validate_rpc_response(uuri) - self.assertFalse(UriValidator.is_rpc_response(uuri)) - self.assertFalse(status.is_success()) - - @staticmethod - def get_json_object(): - current_directory = os.getcwd() - json_file_path = os.path.join( - current_directory, - "tests", - "test_uri", - "test_validator", - "uris.json", - ) - - with open(json_file_path, "r") as json_file: - json_data = json.load(json_file) - - return json_data + def test_is_empty_with_null_uri(self): + self.assertTrue(UriValidator.is_empty(None)) - def test_is_rpc_method_with_uresource_and_no_uauthority(self): - self.assertFalse(UriValidator.is_rpc_method(UUri())) + def test_is_empty_with_default_uri(self): + self.assertTrue(UriValidator.is_empty(UUri())) - uri = UUri( - entity=UEntity(name="hartley"), - resource=UResourceBuilder.from_id(0x8000), - ) - self.assertFalse(UriValidator.is_rpc_method(uri)) + def test_is_empty_with_non_empty_uri(self): + self.assertFalse(UriValidator.is_empty(UUri(authority_name="hi"))) def test_is_rpc_method_passing_null_for_uri(self): self.assertFalse(UriValidator.is_rpc_method(None)) - def test_is_rpc_method_passing_null_for_resource(self): - self.assertFalse(UriValidator.is_rpc_method(None)) - - def test_is_rpc_method_for_uresource_without_an_instance(self): - resource = UResource(name="rpc") - self.assertFalse(UriValidator.is_rpc_method(resource)) - - def test_is_rpc_method_for_uresource_with_an_empty_instance(self): - resource = UResource(name="rpc", instance="") - self.assertFalse(UriValidator.is_rpc_method(resource)) + def test_is_rpc_method_passing_null_for_uri(self): + self.assertFalse(UriValidator.is_rpc_method(UUri())) - def test_is_rpc_method_for_uresource_with_id_that_is_less_than_min_topic( - self, - ): - resource = UResource(name="rpc", id=0) - self.assertTrue(UriValidator.is_rpc_method(resource)) + def test_is_rpc_method_for_uresource_id_less_than_min_topic(self): + uri = UUri(resource_id=0x7FFF) + self.assertTrue(UriValidator.is_rpc_method(uri)) - def test_is_rpc_method_for_uresource_with_id_that_is_greater_than_min_topic( - self, - ): - resource = UResource(name="rpc", id=0x8000) - self.assertFalse(UriValidator.is_rpc_method(resource)) + def test_is_rpc_method_for_uresource_id_greater_than_min_topic(self): + uri = UUri(resource_id=0x8000) + self.assertFalse(UriValidator.is_rpc_method(uri)) - def test_is_empty_with_null_uri(self): - self.assertTrue(UriValidator.is_empty(None)) + def test_is_rpc_response_with_null_uri(self): + self.assertFalse(UriValidator.is_rpc_response(None)) - def test_is_resolved_when_uri_is_long_form_only(self): - uri = UUri( - entity=UEntity(name="hartley", version_major=23), - resource=UResource(name="rpc", instance="echo"), - ) - self.assertFalse(UriValidator.is_resolved(uri)) + def test_is_rpc_method_none(self): + self.assertFalse(UriValidator.is_rpc_method(None)) - def test_is_local_when_authority_is_null(self): - self.assertFalse(UriValidator.is_local(None)) + def test_is_rpc_response_with_resourceid_equal_rpcresponseid(self): + uri = UUri(authority_name="hi", resource_id=UriValidator.DEFAULT_RESOURCE_ID) + self.assertTrue(UriValidator.is_rpc_response(uri)) - def test_is_remote_when_authority_is_null(self): - self.assertFalse(UriValidator.is_remote(None)) + def test_is_rpc_response_with_resourceid_greater_rpcresponseid(self): + uri = UUri(resource_id=UriValidator.DEFAULT_RESOURCE_ID + 1) + self.assertFalse(UriValidator.is_rpc_response(uri)) if __name__ == "__main__": diff --git a/tests/test_uri/test_validator/uris.json b/tests/test_uri/test_validator/uris.json deleted file mode 100644 index 2865428..0000000 --- a/tests/test_uri/test_validator/uris.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "validUris": [ - "/hartley", - "/hartley//", - "hartley/0", - "/1", - "/body.access/1", - "/body.access/1/door.front_left#Door", - "//vcu.vin/body.access/1/door.front_left#Door", - "/body.access/1/rpc.OpenWindow", - "/body.access/1/rpc.response" - ], - "invalidUris": [ - { - "uri": "/", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "//", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "//vcu", - "status_message": "Uri is missing uSoftware Entity name.", - "reason": "Missing entity name." - }, - { - "uri": "//vcu.vin/", - "status_message": "Uri is missing uSoftware Entity name.", - "reason": "Missing entity name." - }, - { - "uri": "", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": ":", - "status_message": "Uri is empty.", - "reason": "Contains only schema" - }, - { - "uri": "///", - "status_message": "Uri is empty.", - "reason": "Empty Authority" - }, - { - "uri": "////", - "status_message": "Uri is empty.", - "reason": "Empty Uri" - }, - { - "uri": "1", - "status_message": "Uri is empty.", - "reason": "Invalid Uri, must begin with \"\/\"" - }, - { - "uri": "a", - "status_message": "Uri is empty.", - "reason": "Invalid Uri, must begin with \"\/\"" - } - ], - "validRpcUris": [ - "/petapp/1/rpc.OpenWindow", - "/petapp/1/rpc.response" - ], - "validRpcResponseUris": [ - "/petapp/1/rpc.response" - ], - "invalidRpcResponseUris": [ - "/petapp/1/rpc.OpenWindow" - ], - "invalidRpcUris": [ - { - "uri": "/petapp//", - "reason": "Missing uE version", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp", - "reason": "Missing uE version", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/rpc", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/dummy", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - }, - { - "uri": "/petapp/1/rpc_dummy", - "reason": "Missing RPC Method Name", - "status_message": "Invalid RPC method uri. Uri should be the method to be called, or method from response." - } - ] -} \ No newline at end of file diff --git a/tests/test_uuid/test_factory/test_uuidfactory.py b/tests/test_uuid/test_factory/test_uuidfactory.py index b13aa25..da74638 100644 --- a/tests/test_uuid/test_factory/test_uuidfactory.py +++ b/tests/test_uuid/test_factory/test_uuidfactory.py @@ -1,33 +1,24 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2024 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ import unittest from datetime import datetime, timedelta, timezone -from uprotocol.proto.uuid_pb2 import UUID from uprotocol.uuid.factory.uuidfactory import Factories from uprotocol.uuid.factory.uuidutils import UUIDUtils, Version -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer -from uprotocol.uuid.serializer.microuuidserializer import MicroUuidSerializer +from uprotocol.uuid.serializer.uuidserializer import UuidSerializer +from uprotocol.v1.uuid_pb2 import UUID class TestUUIDFactory(unittest.TestCase): @@ -36,8 +27,7 @@ def test_uuidv8_creation(self): uuid = Factories.UPROTOCOL.create(now) version = UUIDUtils.get_version(uuid) time = UUIDUtils.get_time(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertTrue(UUIDUtils.is_uprotocol(uuid)) @@ -47,23 +37,18 @@ def test_uuidv8_creation(self): self.assertTrue(time) self.assertEqual(time, int(now.timestamp() * 1000)) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) - self.assertNotEqual(uuid1, UUID()) self.assertNotEqual(uuid2, UUID()) - self.assertEqual(uuid, uuid1) self.assertEqual(uuid, uuid2) def test_uuidv8_creation_with_null_instant(self): uuid = Factories.UPROTOCOL.create(None) version = UUIDUtils.get_version(uuid) time = UUIDUtils.get_time(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertTrue(UUIDUtils.is_uprotocol(uuid)) @@ -71,15 +56,11 @@ def test_uuidv8_creation_with_null_instant(self): self.assertFalse(UUIDUtils.is_uuidv6(uuid)) self.assertTrue(version) self.assertTrue(time) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) - self.assertNotEqual(uuid1, UUID()) self.assertNotEqual(uuid2, UUID()) - self.assertEqual(uuid, uuid1) self.assertEqual(uuid, uuid2) def test_uuidv8_overflow(self): @@ -102,31 +83,25 @@ def test_uuidv6_creation_with_instant(self): now = datetime.now() uuid = Factories.UUIDV6.create(now) version = UUIDUtils.get_version(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertTrue(UUIDUtils.is_uuidv6(uuid)) self.assertTrue(UUIDUtils.is_uuid(uuid)) self.assertFalse(UUIDUtils.is_uprotocol(uuid)) self.assertTrue(version) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) - self.assertNotEqual(uuid1, UUID()) self.assertNotEqual(uuid2, UUID()) - self.assertEqual(uuid, uuid1) self.assertEqual(uuid, uuid2) def test_uuidv6_creation_with_null_instant(self): uuid = Factories.UUIDV6.create(None) version = UUIDUtils.get_version(uuid) time = UUIDUtils.get_time(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertTrue(UUIDUtils.is_uuidv6(uuid)) @@ -134,23 +109,18 @@ def test_uuidv6_creation_with_null_instant(self): self.assertTrue(UUIDUtils.is_uuid(uuid)) self.assertTrue(version) self.assertTrue(time) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) - self.assertNotEqual(uuid1, UUID()) self.assertNotEqual(uuid2, UUID()) - self.assertEqual(uuid, uuid1) self.assertEqual(uuid, uuid2) def test_uuid_utils_for_random_uuid(self): - uuid = LongUuidSerializer.instance().deserialize("195f9bd1-526d-4c28-91b1-ff34c8e3632d") + uuid = UuidSerializer.deserialize("195f9bd1-526d-4c28-91b1-ff34c8e3632d") version = UUIDUtils.get_version(uuid) time = UUIDUtils.get_time(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertFalse(UUIDUtils.is_uuidv6(uuid)) @@ -158,23 +128,18 @@ def test_uuid_utils_for_random_uuid(self): self.assertFalse(UUIDUtils.is_uuid(uuid)) self.assertTrue(version) self.assertFalse(time) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) - self.assertNotEqual(uuid1, UUID()) self.assertNotEqual(uuid2, UUID()) - self.assertEqual(uuid, uuid1) self.assertEqual(uuid, uuid2) def test_uuid_utils_for_empty_uuid(self): uuid = UUID() version = UUIDUtils.get_version(uuid) time = UUIDUtils.get_time(uuid) - bytes_data = MicroUuidSerializer.instance().serialize(uuid) - uuid_string = LongUuidSerializer.instance().serialize(uuid) + uuid_string = UuidSerializer.serialize(uuid) self.assertIsNotNone(uuid) self.assertFalse(UUIDUtils.is_uuidv6(uuid)) @@ -182,25 +147,18 @@ def test_uuid_utils_for_empty_uuid(self): self.assertTrue(version) self.assertEqual(version, Version.VERSION_UNKNOWN) self.assertFalse(time) - self.assertGreater(len(bytes_data), 0) self.assertFalse(uuid_string.isspace()) self.assertFalse(UUIDUtils.is_uuidv6(None)) self.assertFalse(UUIDUtils.is_uprotocol(None)) self.assertFalse(UUIDUtils.is_uuid(None)) - uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - - self.assertTrue(uuid1, UUID()) - self.assertEqual(uuid, uuid1) - - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) self.assertTrue(uuid2, UUID()) self.assertEqual(uuid, uuid2) def test_uuid_utils_for_null_uuid(self): self.assertFalse(UUIDUtils.get_version(None)) - self.assertEqual(len(MicroUuidSerializer.instance().serialize(None)), 0) - self.assertEqual(len(LongUuidSerializer.instance().serialize(None)), 0) + self.assertEqual(len(UuidSerializer.serialize(None)), 0) self.assertFalse(UUIDUtils.is_uuidv6(None)) self.assertFalse(UUIDUtils.is_uprotocol(None)) self.assertFalse(UUIDUtils.is_uuid(None)) @@ -210,24 +168,17 @@ def test_uuidutils_from_invalid_uuid(self): uuid = UUID(msb=9 << 12, lsb=0) # Invalid UUID type self.assertFalse(UUIDUtils.get_version(uuid)) self.assertFalse(UUIDUtils.get_time(uuid)) - self.assertTrue(len(MicroUuidSerializer.instance().serialize(uuid)) > 0) - self.assertFalse(LongUuidSerializer.instance().serialize(uuid).isspace()) + self.assertFalse(UuidSerializer.serialize(uuid).isspace()) self.assertFalse(UUIDUtils.is_uuidv6(uuid)) self.assertFalse(UUIDUtils.is_uprotocol(uuid)) self.assertFalse(UUIDUtils.is_uuid(uuid)) self.assertFalse(UUIDUtils.get_time(uuid)) def test_uuidutils_fromstring_with_invalid_string(self): - uuid = LongUuidSerializer.instance().deserialize(None) - self.assertTrue(uuid == UUID()) - uuid1 = LongUuidSerializer.instance().deserialize("") - self.assertTrue(uuid1 == UUID()) - - def test_uuidutils_frombytes_with_invalid_bytes(self): - uuid = MicroUuidSerializer.instance().deserialize(None) - self.assertTrue(uuid == UUID()) - uuid1 = MicroUuidSerializer.instance().deserialize(bytearray()) - self.assertTrue(uuid1 == UUID()) + uuid = UuidSerializer.deserialize(None) + self.assertEqual(uuid, UUID()) + uuid1 = UuidSerializer.deserialize("") + self.assertEqual(uuid1, UUID()) def test_create_uprotocol_uuid_in_the_past(self): now = datetime.now() diff --git a/tests/test_uuid/test_factory/test_uuidutils.py b/tests/test_uuid/test_factory/test_uuidutils.py index 3ada7b3..8fc553b 100644 --- a/tests/test_uuid/test_factory/test_uuidutils.py +++ b/tests/test_uuid/test_factory/test_uuidutils.py @@ -1,22 +1,14 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2024 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ @@ -24,26 +16,15 @@ import time import unittest -from uprotocol.proto.uattributes_pb2 import UAttributes, UPriority -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.transport.builder.uattributesbuilder import UAttributesBuilder from uprotocol.uuid.factory.uuidfactory import Factories from uprotocol.uuid.factory.uuidutils import UUIDUtils +from uprotocol.v1.uuid_pb2 import UUID def create_id(): return Factories.UPROTOCOL.create() -def build_source(): - return UUri( - authority=UAuthority(name="vcu.someVin.veh.steven.gm.com"), - entity=UEntity(name="petapp.steven.gm.com", version_major=1), - resource=UResource(name="door", instance="front_left", message="Door"), - ) - - DELTA = 30 DELAY_MS = 100 DELAY_LONG_MS = 100000 @@ -51,6 +32,10 @@ def build_source(): class TestUUIDUtils(unittest.TestCase): + def test_get_elapsed_time(self): + id_val: UUID = create_id() + self.assertIsNotNone(UUIDUtils.get_elapsed_time(id_val)) + def test_get_elapsed_time_creation_time_unknown(self): self.assertFalse(UUIDUtils.get_elapsed_time(UUID()) is not None) @@ -68,17 +53,6 @@ def test_get_remaining_time_expired(self): time.sleep(DELAY_MS / 1000) self.assertFalse(UUIDUtils.get_remaining_time(id, DELAY_MS - DELTA) is not None) - def test_get_remaining_time_attributes_no_ttl(self): - attributes: UAttributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - self.assertFalse(UUIDUtils.get_remaining_time(attributes) is not None) - - def test_get_remaining_time_attributes_expired(self): - attributes: UAttributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(DELAY_MS - DELTA).build() - ) - time.sleep(DELAY_MS / 1000) - self.assertFalse(UUIDUtils.get_remaining_time(attributes) is not None) - def test_is_expired(self): id: UUID = create_id() self.assertFalse(UUIDUtils.is_expired(id, DELAY_MS - DELTA)) @@ -86,21 +60,9 @@ def test_is_expired(self): self.assertTrue(UUIDUtils.is_expired(id, DELAY_MS - DELTA)) def test_is_expired_no_ttl(self): - id: UUID = create_id() - self.assertFalse(UUIDUtils.is_expired(id, 0)) - self.assertFalse(UUIDUtils.is_expired(id, -1)) - - def test_is_expired_attributes(self): - attributes: UAttributes = ( - UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).with_ttl(DELAY_MS - DELTA).build() - ) - self.assertFalse(UUIDUtils.is_expired(attributes)) - time.sleep(DELAY_MS / 1000) - self.assertTrue(UUIDUtils.is_expired(attributes)) - - def test_is_expired_attributes_no_ttl(self): - attributes: UAttributes = UAttributesBuilder.publish(build_source(), UPriority.UPRIORITY_CS0).build() - self.assertFalse(UUIDUtils.is_expired(attributes)) + id_val: UUID = create_id() + self.assertFalse(UUIDUtils.is_expired(id_val, 0)) + self.assertFalse(UUIDUtils.is_expired(id_val, -1)) def test_get_elapsed_time_invalid_uuid(self): self.assertFalse(UUIDUtils.get_elapsed_time(None) is not None) diff --git a/tests/test_uuid/test_validator/test_uuidvalidator.py b/tests/test_uuid/test_validator/test_uuidvalidator.py index 0a361f1..edf1ba5 100644 --- a/tests/test_uuid/test_validator/test_uuidvalidator.py +++ b/tests/test_uuid/test_validator/test_uuidvalidator.py @@ -1,34 +1,26 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ import unittest from datetime import datetime, timezone -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.proto.uuid_pb2 import UUID from uprotocol.uuid.factory.uuidfactory import Factories from uprotocol.uuid.factory.uuidutils import UUIDUtils -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer -from uprotocol.uuid.validate.uuidvalidator import UuidValidator, Validators +from uprotocol.uuid.serializer.uuidserializer import UuidSerializer +from uprotocol.uuid.validator.uuidvalidator import UuidValidator, Validators +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.uuid_pb2 import UUID from uprotocol.validation.validationresult import ValidationResult @@ -69,7 +61,7 @@ def test_uuidv8_with_invalid_uuids(self): def test_uuidv8_with_invalid_types(self): uuidv6 = Factories.UUIDV6.create() uuid = UUID(msb=0, lsb=0) - uuidv4 = LongUuidSerializer.instance().deserialize("195f9bd1-526d-4c28-91b1-ff34c8e3632d") + uuidv4 = UuidSerializer.deserialize("195f9bd1-526d-4c28-91b1-ff34c8e3632d") validator = Validators.UPROTOCOL.validator() self.assertIsNotNone(validator) diff --git a/uprotocol/cloudevent/README.adoc b/uprotocol/cloudevent/README.adoc deleted file mode 100644 index 28b512e..0000000 --- a/uprotocol/cloudevent/README.adoc +++ /dev/null @@ -1,67 +0,0 @@ -= uProtocol CloudEvents -:toc: -:sectnums: - - -== Overview - -https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l1/cloudevents.adoc[uProtocol CloudEvents] is a common message envelope that could be used to carry way to represent uProtocol transport layer information `UUri` (source), `UPayload`, and `UAttributes`. `CloudEvents` are used by a number of Device-2-Cloud and Cloud-2-Device based transports such as MQTT and HTTP, however it could also be used by any transport (ex. Binder). - - -=== CloudEventFactory -Factory class that builds the various types of CloudEvents for uProtocol (publish, notification, request, response) - -== Examples - -=== Building an uuri -[source,python] ----- - uri = UUri(entity=UEntity(name="body.access"), - resource=UResource(name="door", instance="front_left", message="Door")) - source= LongUriSerializer().serialize(uri) ----- - -=== Build proto payload -[source,python] - ----- -ce_proto = CloudEvent(spec_version="1.0", source="https://example.com", id="hello", type="example.demo", - proto_data=any_pb2.Any()) - -any_obj = any_pb2.Any() -any_obj.Pack(ce_proto) -proto_payload = any_obj - ----- - -=== Build UCloudEvent Attributes -[source,python] - ----- -u_cloud_event_attributes = UCloudEventAttributesBuilder().with_hash("somehash").with_priority( -UPriority.UPRIORITY_CS1).with_ttl(3).build() - ----- - -=== Build publish cloud event -[source,python] - ----- -cloud_event = CloudEventFactory.publish(source, proto_payload, u_cloud_event_attributes) -# test all attributes -assertEqual("1.0", UCloudEvent.get_specversion(cloud_event)) -assertIsNotNone(UCloudEvent.get_id(cloud_event)) -assertEqual(source, UCloudEvent.get_source(cloud_event)) -assertEqual(UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - UCloudEvent.get_type(cloud_event)) -assertNotIn("sink", cloud_event.get_attributes()) -assertEqual("somehash", UCloudEvent.get_hash(cloud_event)) -assertEqual(UPriority.Name(UPriority.UPRIORITY_CS1), UCloudEvent.get_priority(cloud_event)) -assertEqual(3, UCloudEvent.get_ttl(cloud_event)) -assertEqual(proto_payload.SerializeToString(), cloud_event.get_data()) - ----- - - - - diff --git a/uprotocol/cloudevent/__init__.py b/uprotocol/cloudevent/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/cloudevent/cloudevents_pb2.py b/uprotocol/cloudevent/cloudevents_pb2.py deleted file mode 100644 index dc5340b..0000000 --- a/uprotocol/cloudevent/cloudevents_pb2.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: cloudevents.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x63loudevents.proto\x12\x11io.cloudevents.v1\x1a\x19google/protobuf/any.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb0\x04\n\nCloudEvent\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x14\n\x0cspec_version\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x41\n\nattributes\x18\x05 \x03(\x0b\x32-.io.cloudevents.v1.CloudEvent.AttributesEntry\x12\x15\n\x0b\x62inary_data\x18\x06 \x01(\x0cH\x00\x12\x13\n\ttext_data\x18\x07 \x01(\tH\x00\x12*\n\nproto_data\x18\x08 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x1ai\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x45\n\x05value\x18\x02 \x01(\x0b\x32\x36.io.cloudevents.v1.CloudEvent.CloudEventAttributeValue:\x02\x38\x01\x1a\xd3\x01\n\x18\x43loudEventAttributeValue\x12\x14\n\nce_boolean\x18\x01 \x01(\x08H\x00\x12\x14\n\nce_integer\x18\x02 \x01(\x05H\x00\x12\x13\n\tce_string\x18\x03 \x01(\tH\x00\x12\x12\n\x08\x63\x65_bytes\x18\x04 \x01(\x0cH\x00\x12\x10\n\x06\x63\x65_uri\x18\x05 \x01(\tH\x00\x12\x14\n\nce_uri_ref\x18\x06 \x01(\tH\x00\x12\x32\n\x0c\x63\x65_timestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x42\x06\n\x04\x61ttrB\x06\n\x04\x64\x61ta\"@\n\x0f\x43loudEventBatch\x12-\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1d.io.cloudevents.v1.CloudEventB\x8b\x01\n\x17io.cloudevents.v1.protoP\x01Z\x1a\x63loudevents.io/genproto/v1\xaa\x02\x1a\x43loudNative.CloudEvents.V1\xca\x02\x17Io\\CloudEvents\\V1\\Proto\xea\x02\x1aIo::CloudEvents::V1::Protob\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cloudevents_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\n\027io.cloudevents.v1.protoP\001Z\032cloudevents.io/genproto/v1\252\002\032CloudNative.CloudEvents.V1\312\002\027Io\\CloudEvents\\V1\\Proto\352\002\032Io::CloudEvents::V1::Proto' - _globals['_CLOUDEVENT_ATTRIBUTESENTRY']._options = None - _globals['_CLOUDEVENT_ATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_CLOUDEVENT']._serialized_start=101 - _globals['_CLOUDEVENT']._serialized_end=661 - _globals['_CLOUDEVENT_ATTRIBUTESENTRY']._serialized_start=334 - _globals['_CLOUDEVENT_ATTRIBUTESENTRY']._serialized_end=439 - _globals['_CLOUDEVENT_CLOUDEVENTATTRIBUTEVALUE']._serialized_start=442 - _globals['_CLOUDEVENT_CLOUDEVENTATTRIBUTEVALUE']._serialized_end=653 - _globals['_CLOUDEVENTBATCH']._serialized_start=663 - _globals['_CLOUDEVENTBATCH']._serialized_end=727 -# @@protoc_insertion_point(module_scope) \ No newline at end of file diff --git a/uprotocol/cloudevent/datamodel/__init__.py b/uprotocol/cloudevent/datamodel/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/cloudevent/datamodel/ucloudeventattributes.py b/uprotocol/cloudevent/datamodel/ucloudeventattributes.py deleted file mode 100644 index e6e7bdc..0000000 --- a/uprotocol/cloudevent/datamodel/ucloudeventattributes.py +++ /dev/null @@ -1,199 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from uprotocol.proto.uattributes_pb2 import UPriority - - -class UCloudEventAttributes: - """ - Specifies the properties that can configure the UCloudEvent. - """ - - def __init__( - self, - priority: UPriority, - hash_value: str = None, - ttl: int = None, - token: str = None, - traceparent: str = None, - ): - """ - Construct the properties object.

- @param hash_value: an HMAC generated on the data portion of the CloudEvent message using the device key. - @param priority: uProtocol Prioritization classifications. - @param ttl: How long this event should live for after it was generated (in milliseconds). Events without this - attribute (or value is 0) MUST NOT timeout. - @param token: Oauth2 access token to perform the access request defined in the request message. - """ - self.hash = hash_value - self.priority = priority - self.ttl = ttl - self.token = token - self.traceparent = traceparent - - @staticmethod - def empty(): - """ - Static factory method for creating an empty cloud event attributes object, to avoid working with null

- @return: Returns an empty cloud event attributes that indicates that there are no added additional - attributes to configure. - """ - return UCloudEventAttributes(None, None, None, None) - - def is_empty(self): - """ - Indicates that there are no added additional attributes to configure when building a CloudEvent.

- @return: Returns true if this attributes container is an empty container and has no valuable information in - building a CloudEvent. - """ - return ( - (self.hash is None or self.hash.isspace()) - and (self.ttl is None) - and (self.token is None or self.token.isspace()) - and (self.priority is None or self.priority.isspace()) - and (self.traceparent is None or self.traceparent.isspace()) - ) - - def get_hash(self) -> str: - """ - An HMAC generated on the data portion of the CloudEvent message using the device key.

- @return: Returns an Optional hash attribute. - """ - return self.hash if self.hash and self.hash.strip() else None - - def get_priority(self) -> UPriority: - """ - uProtocol Prioritization classifications.

- @return: Returns an Optional priority attribute. - """ - return self.priority - - def get_ttl(self) -> int: - """ - How long this event should live for after it was generated (in milliseconds).

- @return: Returns an Optional time to live attribute. - """ - return self.ttl - - def get_token(self) -> str: - """ - Oauth2 access token to perform the access request defined in the request message.

- @return: Returns an Optional OAuth token attribute. - """ - return self.token if self.token and self.token.strip() else None - - def get_traceparent(self) -> str: - """ - Traceparent of the event. - @return: Returns an optional traceparent attribute. - """ - return self.traceparent if self.traceparent and self.traceparent.strip() else None - - def __eq__(self, other): - if self is other: - return True - if not isinstance(other, UCloudEventAttributes): - return False - return ( - self.hash == other.hash - and self.priority == other.priority - and self.ttl == other.ttl - and self.token == other.token - and self.traceparent == other.traceparent - ) - - def __hash__(self): - return hash((self.hash, self.priority, self.ttl, self.token, self.traceparent)) - - def __str__(self): - traceparent_string = f", traceparent='{self.traceparent}'" if self.traceparent else "" - return ( - f"UCloudEventAttributes{{hash='{self.hash}', priority={self.priority}," - f" ttl={self.ttl}, token='{self.token}'{traceparent_string}}}" - ) - - -class UCloudEventAttributesBuilder: - """ - Builder for constructing the UCloudEventAttributes. - """ - - def __init__(self): - self.hash = None - self.priority = None - self.ttl = None - self.token = None - self.traceparent = None - - def with_hash(self, hash_value: str): - """ - Add an HMAC generated on the data portion of the CloudEvent message using the device key.

- @param hash_value: hash an HMAC generated on the data portion of the CloudEvent message using the device key. - @return: Returns the UCloudEventAttributesBuilder with the configured hash. - """ - self.hash = hash_value - return self - - def with_priority(self, priority: UPriority): - """ - Add a uProtocol Prioritization classifications.

- @param priority: priority uProtocol Prioritization classifications. - @return: Returns the UCloudEventAttributesBuilder with the configured priority. - """ - self.priority = UPriority.Name(priority) - return self - - def with_ttl(self, ttl: int): - """ - Add a time to live which is how long this event should live for after it was generated (in milliseconds). - Events without this attribute (or value is 0) MUST NOT timeout.

- @param ttl: How long this event should live for after it was generated (in milliseconds). Events without this - attribute (or value is 0) MUST NOT timeout. - @return: Returns the UCloudEventAttributesBuilder with the configured time to live. - """ - self.ttl = ttl - return self - - def with_token(self, token: str): - """ - Add an Oauth2 access token to perform the access request defined in the request message.

- @param token: An Oauth2 access token to perform the access request defined in the request message. - @return: Returns the UCloudEventAttributesBuilder with the configured OAuth token. - """ - self.token = token - return self - - def with_traceparent(self, traceparent: str): - """ - An identifier used to correlate observability across related events. - @param traceparent: identifier - @return Returns a traceparent attribute. - """ - self.traceparent = traceparent - return self - - def build(self): - """ - Construct the UCloudEventAttributes from the builder.

- @return: Returns a constructed UProperty. - """ - return UCloudEventAttributes(self.priority, self.hash, self.ttl, self.token, self.traceparent) diff --git a/uprotocol/cloudevent/factory/__init__.py b/uprotocol/cloudevent/factory/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/cloudevent/factory/cloudeventfactory.py b/uprotocol/cloudevent/factory/cloudeventfactory.py deleted file mode 100644 index d725ae1..0000000 --- a/uprotocol/cloudevent/factory/cloudeventfactory.py +++ /dev/null @@ -1,244 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from cloudevents.http import CloudEvent -from google.protobuf import empty_pb2 -from google.protobuf.any_pb2 import Any - -from uprotocol.cloudevent.datamodel.ucloudeventattributes import ( - UCloudEventAttributes, -) -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.proto.uattributes_pb2 import UMessageType -from uprotocol.uuid.factory.uuidfactory import Factories -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer - - -# A factory is a part of the software has methods to generate concrete objects, usually of the same type or -# interface. CloudEvents is a specification for describing events in a common way. We will use CloudEvents to -# formulate all kinds of events (messages) that will be sent to and from devices. The CloudEvent factory knows how -# to generate CloudEvents of the 4 core types: req.v1, res.v1 and pub.v1 -class CloudEventFactory: - PROTOBUF_CONTENT_TYPE = "application/x-protobuf" - - @staticmethod - def request( - application_uri_for_rpc: str, - service_method_uri: str, - request_id: str, - proto_payload: Any, - attributes: UCloudEventAttributes, - ) -> CloudEvent: - """ - Create a CloudEvent for an event for the use case of: RPC Request message. - @param application_uri_for_rpc: The uri for the application requesting the RPC. - @param service_method_uri: The uri for the method to be called on the service Ex. :/body.access/1/rpc.UpdateDoor - @param request_id:The attribute id from the original request - @param proto_payload:Protobuf Any object with the Message command to be executed on the sink service. - @param attributes: Additional attributes such as ttl, hash, priority and token. - @return: Returns an request CloudEvent. - """ - event_id = CloudEventFactory.generate_cloud_event_id() - cloud_event = CloudEventFactory.build_base_cloud_event( - event_id, - application_uri_for_rpc, - proto_payload.SerializeToString(), - proto_payload.DESCRIPTOR.full_name, - attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_REQUEST), - ) - cloud_event.__setitem__("sink", service_method_uri) - cloud_event.__setitem__("reqid", request_id) - - return cloud_event - - @staticmethod - def response( - application_uri_for_rpc: str, - service_method_uri: str, - request_id: str, - proto_payload: Any, - attributes: UCloudEventAttributes, - ) -> CloudEvent: - """ - Create a CloudEvent for an event for the use case of: RPC Response message. - @param application_uri_for_rpc: The destination of the response. The uri for the original application that - requested the RPC and this response is for. - @param service_method_uri: The uri for the method that was called on the service Ex. - :/body.access/1/rpc.UpdateDoor - @param request_id:The cloud event id from the original request cloud event that this response if for. - @param proto_payload: The protobuf serialized response message as defined by the application interface or the - UStatus message containing the details of an error. - @param attributes: Additional attributes such as ttl, hash and priority. - @return: Returns an response CloudEvent. - """ - event_id = CloudEventFactory.generate_cloud_event_id() - cloud_event = CloudEventFactory.build_base_cloud_event( - event_id, - service_method_uri, - proto_payload.SerializeToString(), - proto_payload.DESCRIPTOR.full_name, - attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_RESPONSE), - ) - cloud_event.__setitem__("sink", application_uri_for_rpc) - cloud_event.__setitem__("reqid", request_id) - - return cloud_event - - @staticmethod - def failed_response( - application_uri_for_rpc: str, - service_method_uri: str, - request_id: str, - communication_status: int, - attributes: UCloudEventAttributes, - ) -> CloudEvent: - """ - Create a CloudEvent for an event for the use case of: RPC Response message that failed. - @param application_uri_for_rpc: The destination of the response. The uri for the original application that - requested the RPC and this response is for. - @param service_method_uri: The uri for the method that was called on the service Ex. - :/body.access/1/rpc.UpdateDoor - @param request_id:The cloud event id from the original request cloud event that this response if for. - @param communication_status: A Code value that indicates of a platform communication error while - delivering this CloudEvent. - @param attributes:Additional attributes such as ttl, hash and priority. - @return:Returns an response CloudEvent Response for the use case of RPC Response message that failed. - """ - event_id = CloudEventFactory.generate_cloud_event_id() - # Create an Any message packing an Empty message - empty_proto_payload = Any() - empty_proto_payload.Pack(empty_pb2.Empty()) - cloud_event = CloudEventFactory.build_base_cloud_event( - event_id, - service_method_uri, - empty_proto_payload.SerializeToString(), # Empty payload - "google.protobuf.Empty", - attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_RESPONSE), - ) - cloud_event.__setitem__("sink", application_uri_for_rpc) - cloud_event.__setitem__("reqid", request_id) - cloud_event.__setitem__("commstatus", communication_status) - - return cloud_event - - @staticmethod - def publish(source: str, proto_payload: Any, attributes: UCloudEventAttributes) -> CloudEvent: - """ - Create a CloudEvent for an event for the use case of: Publish generic message. - @param source:The uri of the topic being published. - @param proto_payload:protobuf Any object with the Message to be published. - @param attributes:Additional attributes such as ttl, hash and priority. - @return:Returns a publish CloudEvent. - """ - event_id = CloudEventFactory.generate_cloud_event_id() - cloud_event = CloudEventFactory.build_base_cloud_event( - event_id, - source, - proto_payload.SerializeToString(), - proto_payload.DESCRIPTOR.full_name, - attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - - return cloud_event - - @staticmethod - def notification( - source: str, - sink: str, - proto_payload: Any, - attributes: UCloudEventAttributes, - ) -> CloudEvent: - """ - Create a CloudEvent for an event for the use case of: Publish a notification message. A published event - containing the sink (destination) is often referred to as a notification, it is an event sent to a specific - consumer. - @param source: The uri of the topic being published. - @param sink: The uri of the destination of this notification. - @param proto_payload: protobuf Any object with the Message to be published. - @param attributes: Additional attributes such as ttl, hash and priority. - @return: Returns a publish CloudEvent. - """ - event_id = CloudEventFactory.generate_cloud_event_id() - cloud_event = CloudEventFactory.build_base_cloud_event( - event_id, - source, - proto_payload.SerializeToString(), - proto_payload.DESCRIPTOR.full_name, - attributes, - UCloudEvent.get_event_type(UMessageType.UMESSAGE_TYPE_PUBLISH), - ) - cloud_event.__setitem__("sink", sink) - - return cloud_event - - @staticmethod - def generate_cloud_event_id() -> str: - """ - Generate a UUIDv8 - @return: Returns a UUIDv8 id. - """ - uuid_inst = Factories.UPROTOCOL.create() - return LongUuidSerializer.instance().serialize(uuid_inst) - - @staticmethod - def build_base_cloud_event( - id: str, - source: str, - proto_payload_bytes: bytes, - proto_payload_schema: str, - attributes: UCloudEventAttributes, - type, - ) -> CloudEvent: - """ - Base CloudEvent builder that is the same for all CloudEvent types. - - @param id:Event unique identifier. - @param source: Identifies who is sending this event in the format of a uProtocol URI that can be built from a - UUri object. - @param proto_payload_bytes:The serialized Event data with the content type of "application/x-protobuf". - @param proto_payload_schema:The schema of the proto payload bytes, for example you can use - protoPayload.getTypeUrl() on your service/app object. - @param attributes:Additional cloud event attributes that can be passed in. All attributes are optional and - will be added only if they were configured. - @param type: Type of the cloud event - @return:Returns a CloudEventBuilder that can be additionally configured and then by calling .build() - construct a CloudEvent ready to be serialized and sent to the transport layer. - """ - json_attributes = {"id": id, "source": source, "type": type} - if attributes.get_hash() is not None: - json_attributes["hash"] = attributes.get_hash() - if attributes.get_ttl() is not None: - json_attributes["ttl"] = attributes.get_ttl() - if attributes.get_priority() is not None: - json_attributes["priority"] = attributes.get_priority() - if attributes.get_token() is not None: - json_attributes["token"] = attributes.get_token() - if attributes.get_traceparent() is not None: - json_attributes["traceparent"] = attributes.get_traceparent() - - cloud_event = CloudEvent(json_attributes, proto_payload_bytes) - - return cloud_event diff --git a/uprotocol/cloudevent/factory/ucloudevent.py b/uprotocol/cloudevent/factory/ucloudevent.py deleted file mode 100644 index 8d2a465..0000000 --- a/uprotocol/cloudevent/factory/ucloudevent.py +++ /dev/null @@ -1,625 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import copy -import time -from datetime import datetime, timedelta, timezone - -from cloudevents.http import CloudEvent -from google.protobuf import any_pb2 -from google.protobuf.message import DecodeError - -from uprotocol.proto.uattributes_pb2 import ( - UAttributes, - UMessageType, - UPriority, -) -from uprotocol.proto.umessage_pb2 import UMessage -from uprotocol.proto.upayload_pb2 import UPayload, UPayloadFormat -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uuid.factory.uuidutils import UUIDUtils -from uprotocol.uuid.serializer.longuuidserializer import LongUuidSerializer - - -class UCloudEvent: - """ - Class to extract information from a CloudEvent. - """ - - @staticmethod - def get_source(ce: CloudEvent) -> str: - """ - Extract the source from a cloud event. The source is a mandatory - attribute. The CloudEvent constructor does not allow creating a cloud - event without a source.

- @param ce:CloudEvent with source to be - extracted. - @return:Returns the String value of a CloudEvent source - attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("source", ce) - - @staticmethod - def get_data_content_type(ce: CloudEvent) -> str: - """ - Extract the source from a cloud event. The source is a mandatory - attribute. The CloudEvent constructor does not allow creating a cloud - event without a source.

- @param ce:CloudEvent with source to be - extracted. - @return:Returns the String value of a CloudEvent source - attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("datacontenttype", ce) - - @staticmethod - def get_data_schema(ce: CloudEvent) -> str: - """ - Extract the source from a cloud event. The source is a mandatory - attribute. The CloudEvent constructor does not allow creating a cloud - event without a source.

- @param ce:CloudEvent with source to be - extracted. - @return:Returns the String value of a CloudEvent source - attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("dataschema", ce) - - @staticmethod - def get_type(ce: CloudEvent) -> str: - """ - Extract the type from a cloud event. The source is a mandatory - attribute. The CloudEvent constructor does not allow creating a cloud - event without a type.

- @param ce:CloudEvent with source to be - extracted. - @return:Returns the String value of a CloudEvent type - attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("type", ce) - - @staticmethod - def get_id(ce: CloudEvent) -> str: - """ - Extract the id from a cloud event. The id is a mandatory attribute. The - CloudEvent constructor does not allow creating a cloud event without an - id.

- @param ce:CloudEvent with source to be extracted. - @return:Returns the String value of a CloudEvent id attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("id", ce) - - @staticmethod - def get_specversion(ce: CloudEvent) -> str: - """ - Extract the specversion from a cloud event.

- @param - ce:CloudEvent with source to be extracted. - @return:Returns the String - value of a CloudEvent spec version attribute. - """ - return UCloudEvent.extract_string_value_from_attributes("specversion", ce) - - @staticmethod - def get_sink(ce: CloudEvent) -> str: - """ - Extract the sink from a cloud event. The sink attribute is - optional.

- @param ce:CloudEvent with sink to be extracted. - @return:Returns an Optional String value of a CloudEvent sink attribute - if it exists, otherwise an Optional.empty() is returned. - """ - return UCloudEvent.extract_string_value_from_attributes("sink", ce) - - @staticmethod - def get_request_id(ce: CloudEvent) -> str: - """ - Extract the request id from a cloud event that is a response RPC - CloudEvent. The attribute is optional.

- @param ce: the response - RPC CloudEvent with request id to be extracted. - @return: Returns an - Optional String value of a response RPC CloudEvent request id attribute - if it exists, otherwise an Optional.empty() is returned. - """ - return UCloudEvent.extract_string_value_from_attributes("reqid", ce) - - @staticmethod - def get_hash(ce: CloudEvent) -> str: - """ - Extract the hash attribute from a cloud event. The hash attribute is - optional.

- @param ce: CloudEvent with hash to be extracted. - @return:Returns an Optional String value of a CloudEvent hash attribute - if it exists, otherwise an Optional.empty() is returned. - """ - return UCloudEvent.extract_string_value_from_attributes("hash", ce) - - @staticmethod - def get_priority(ce: CloudEvent) -> str: - """ - Extract the string value of the priority attribute from a cloud event. - The priority attribute is optional.

- @param ce:CloudEvent with - priority to be extracted. - @return:Returns an Optional String value of a - CloudEvent priority attribute if it exists, otherwise an - Optional.empty() is returned. - """ - return UCloudEvent.extract_string_value_from_attributes("priority", ce) - - @staticmethod - def get_ttl(ce: CloudEvent) -> int: - """ - Extract the integer value of the ttl attribute from a cloud event. The - ttl attribute is optional.

- @param ce:CloudEvent with ttl to be - extracted. - @return: Returns an Optional String value of a CloudEvent - ttl attribute if it exists,otherwise an Optional.empty() is returned. - """ - ttl_str = UCloudEvent.extract_string_value_from_attributes("ttl", ce) - return int(ttl_str) if ttl_str is not None else None - - @staticmethod - def get_token(ce: CloudEvent) -> str: - """ - Extract the string value of the token attribute from a cloud event. The - token attribute is optional.

- @param ce: CloudEvent with token - to be extracted. - @return:Returns an Optional String value of a - CloudEvent priority token if it exists, otherwise an Optional.empty() - is returned. - """ - return UCloudEvent.extract_string_value_from_attributes("token", ce) - - @staticmethod - def get_communication_status(ce: CloudEvent) -> UCode: - """ - Extract the integer value of the communication status attribute from a - cloud event. The communication status attribute is optional. If there - was a platform communication error that occurred while delivering this - cloudEvent, it will be indicated in this attribute. If the attribute - does not exist, it is assumed that everything was - UCode.OK_VALUE.

- @param ce: CloudEvent with the platformError to - be extracted. - @return: Returns a UCode value that indicates of a - platform communication error while delivering this CloudEvent or - UCode.OK_VALUE. - """ - try: - comm_status = UCloudEvent.extract_string_value_from_attributes("commstatus", ce) - return int(comm_status) if comm_status is not None else UCode.OK - except Exception: - return UCode.OK - - @staticmethod - def get_traceparent(ce: CloudEvent) -> str: - """ - Extract the string value of the traceparent attribute from a cloud - event. The traceparent attribute is optional. - @param ce: CloudEvent - with traceparent to be extracted. - @return: Returns the string value of - the traceparent if it exists, else returns None. - """ - return UCloudEvent.extract_string_value_from_attributes("traceparent", ce) - - @staticmethod - def has_communication_status_problem(ce: CloudEvent) -> bool: - """ - Indication of a platform communication error that occurred while trying - to deliver the CloudEvent.

- @param ce:CloudEvent to be queried - for a platform delivery error. - @return:returns true if the provided - CloudEvent is marked with having a platform delivery problem. - """ - return UCloudEvent.get_communication_status(ce) != UCode.OK - - @staticmethod - def add_communication_status(ce: CloudEvent, communication_status) -> CloudEvent: - """ - Returns a new CloudEvent from the supplied CloudEvent, with the - platform communication added.

- @param ce:CloudEvent that the - platform delivery error will be added. - @param communication_status:the - platform delivery error UCode to add to the CloudEvent. - @return:Returns - a new CloudEvent from the supplied CloudEvent, with the platform - communication added. - """ - if communication_status is None: - return ce - ce_new = copy.deepcopy(ce) - ce_new.__setitem__("commstatus", communication_status) - return ce_new - - @staticmethod - def get_creation_timestamp(ce: CloudEvent) -> int: - """ - Extract the timestamp from the UUIDV8 CloudEvent Id.

- @param - ce:The CloudEvent with the timestamp to extract. - @return:Return the - timestamp from the UUIDV8 CloudEvent Id or an empty Optional if - timestamp can't be extracted. - """ - cloud_event_id = UCloudEvent.extract_string_value_from_attributes("id", ce) - uuid = LongUuidSerializer.instance().deserialize(cloud_event_id) - - return UUIDUtils.get_time(uuid) if uuid is not None else None - - @staticmethod - def is_expired_by_cloud_event_creation_date(ce: CloudEvent) -> bool: - """ - Calculate if a CloudEvent configured with a creation time and a ttl - attribute is expired. The ttl attribute is a configuration of how long - this event should live for after it was generated (in - milliseconds)

- @param ce:The CloudEvent to inspect for being - expired. - @return:Returns true if the CloudEvent was configured with a - ttl > 0 and a creation time to compare for expiration. - """ - maybe_ttl = UCloudEvent.get_ttl(ce) - if not maybe_ttl or maybe_ttl <= 0: - return False - - cloud_event_creation_time = UCloudEvent.extract_string_value_from_attributes("time", ce) - if cloud_event_creation_time is None: - return False - - now = datetime.now(timezone.utc) - creation_time_plus_ttl = datetime.fromisoformat(cloud_event_creation_time) + timedelta(milliseconds=maybe_ttl) - - return now > creation_time_plus_ttl - - @staticmethod - def is_expired(ce: CloudEvent) -> bool: - """ - Calculate if a CloudEvent configured with UUIDv8 id and a ttl attribute - is expired. The ttl attribute is a configuration of how long this event - should live for after it was generated (in milliseconds).

- @param ce:The CloudEvent to inspect for being expired. - @return:Returns - true if the CloudEvent was configured with a ttl > 0 and UUIDv8 id - to compare for expiration. - """ - maybe_ttl = UCloudEvent.get_ttl(ce) - if not maybe_ttl or maybe_ttl <= 0: - return False - cloud_event_id = UCloudEvent.extract_string_value_from_attributes("id", ce) - - try: - uuid = LongUuidSerializer.instance().deserialize(cloud_event_id) - if uuid is None or uuid == UUID(): - return False - delta = int(round(time.time() * 1000)) - UUIDUtils.get_time(uuid) - except ValueError: - # Invalid UUID, handle accordingly - delta = 0 - return delta >= maybe_ttl - - @staticmethod - def is_cloud_event_id(ce: CloudEvent) -> bool: - """ - Check if a CloudEvent is a valid UUIDv6 or v8 .

- @param ce:The CloudEvent with the id to inspect. - @return: Returns true if the CloudEvent is valid. - """ - cloud_event_id = UCloudEvent.extract_string_value_from_attributes("id", ce) - uuid = LongUuidSerializer.instance().deserialize(cloud_event_id) - - return uuid is not None and UUIDUtils.is_uuid(uuid) - - @staticmethod - def get_payload(ce: CloudEvent) -> any_pb2.Any: - """ - Extract the payload from the CloudEvent as a protobuf Any object. -
An all or nothing error handling strategy is implemented. If - anything goes wrong, an Any.getDefaultInstance() will be - returned.

- @param ce:CloudEvent containing the payload to - extract. - @return:Extracts the payload from a CloudEvent as a Protobuf - Any object. - """ - data = ce.get_data() - if data is None: - return any_pb2.Any() - try: - return any_pb2.Any().FromString(data) - except DecodeError: - return any_pb2.Any() - - @staticmethod - def unpack(ce: CloudEvent, clazz): - """ - Extract the payload from the CloudEvent as a protobuf Message of the - provided class. The protobuf of this message class must be loaded on - the client for this to work.
An all or nothing error handling - strategy is implemented. If anything goes wrong, an empty optional will - be returned.

Example:
Optional<SomeMessage>
-        unpacked = UCloudEvent.unpack(cloudEvent,
-        SomeMessage.class);


- @param ce:CloudEvent containing the - payload to extract. - @param clazz:The class that extends Message that - the payload is extracted into. - @return: Returns a Message payload of - the class type that is provided. - """ - try: - any_obj = UCloudEvent.get_payload(ce) - value = clazz() - value.ParseFromString(any_obj.value) - return value - except DecodeError: - return None - - @staticmethod - def to_string(ce: CloudEvent) -> str: - """ - Function used to pretty print a CloudEvent containing only the id, - source, type and maybe a sink. Used mainly for logging.

- @param - ce:The CloudEvent we want to pretty print. - @return:returns the String - representation of the CloudEvent containing only the id, source, type - and maybe a sink. - """ - if ce is not None: - sink_str = UCloudEvent.get_sink(ce) - sink_str = f", sink='{sink_str}'" if sink_str is not None else "" - id = UCloudEvent.extract_string_value_from_attributes("id", ce) - source = UCloudEvent.extract_string_value_from_attributes("source", ce) - type = UCloudEvent.extract_string_value_from_attributes("type", ce) - return f"CloudEvent{{id='{id}', source='{source}'{sink_str}, type='{type}'}}" - else: - return "null" - - @staticmethod - def extract_string_value_from_attributes(attr_name, ce: CloudEvent) -> str: - """ - Utility for extracting the String value of an attribute.

- @param - attr_name:The name of the CloudEvent attribute. - @param ce:The - CloudEvent containing the data. - @return:the Optional String value of an - attribute matching the attribute name, or an Optional.empty() is the - value does not exist. - """ - - return ce.get_attributes().get(attr_name) - - @staticmethod - def extract_integer_value_from_attributes(attr_name, ce: CloudEvent) -> int: - """ - - Utility for extracting the Integer value of an attribute.

- @param attr_name:The name of the CloudEvent attribute. - @param ce:The - CloudEvent containing the data. - @return:returns the Optional Integer - value of an attribute matching the attribute name,or an - Optional.empty() is the value does not exist. - """ - value = UCloudEvent.extract_string_value_from_attributes(attr_name, ce) - return int(value) if value is not None else None - - @staticmethod - def get_event_type(type): - """ - Get the string representation of the UMessageType. Note: The - UMessageType is determined by the type of the CloudEvent. If the - UMessageType is UMESSAGE_TYPE_NOTIFICATION, we assume the CloudEvent - type is "pub.v1" and the sink is present. - @param type The UMessageType - @return returns the string representation of the UMessageType - """ - return { - UMessageType.UMESSAGE_TYPE_PUBLISH: "pub.v1", - UMessageType.UMESSAGE_TYPE_REQUEST: "req.v1", - UMessageType.UMESSAGE_TYPE_RESPONSE: "res.v1", - UMessageType.UMESSAGE_TYPE_NOTIFICATION: "not.v1", - }.get(type, "") - - @staticmethod - def get_message_type(ce_type): - """ - Get the UMessageType from the string representation. Note: The - UMessageType is determined by the type of the CloudEvent. If the - CloudEvent type is "pub.v1" and the sink is present, the UMessageType - is assumed to be UMESSAGE_TYPE_NOTIFICATION, this is because uProtocol - CloudEvent definition did not have an explicit notification type. - @param cloudEvent The CloudEvent containing the data. - @return returns - the UMessageType - """ - return { - "pub.v1": UMessageType.UMESSAGE_TYPE_PUBLISH, - "req.v1": UMessageType.UMESSAGE_TYPE_REQUEST, - "res.v1": UMessageType.UMESSAGE_TYPE_RESPONSE, - }.get(ce_type, UMessageType.UMESSAGE_TYPE_UNSPECIFIED) - - @staticmethod - def get_content_type_from_upayload_format(payload_format: UPayloadFormat): - """ - Retrieves the string representation of the data content type based on - the provided UPayloadFormat.
This method uses the uProtocol - mimeType custom options declared in upayload.proto. - @param - payload_format The UPayloadFormat enumeration representing the payload - format. - @return The corresponding content type string based on the - payload format. - """ - return { - UPayloadFormat.UPAYLOAD_FORMAT_JSON: "application/json", - UPayloadFormat.UPAYLOAD_FORMAT_RAW: "application/octet-stream", - UPayloadFormat.UPAYLOAD_FORMAT_TEXT: "text/plain", - UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP: "application/x-someip", - UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP_TLV: "application/x-someip_tlv", - }.get(payload_format, "") - - @staticmethod - def get_upayload_format_from_content_type(contenttype: str): - """ - Retrieves the payload format enumeration based on the provided string - representation of the data content type
This method uses the - uProtocol mimeType custom options declared in upayload.proto. - @param - contentType The content type string representing the format of the - payload. - @return The corresponding UPayloadFormat enumeration based on - the content type. - """ - if contenttype is None: - return UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY - - content_type_mapping = { - "application/json": UPayloadFormat.UPAYLOAD_FORMAT_JSON, - "application/octet-stream": UPayloadFormat.UPAYLOAD_FORMAT_RAW, - "text/plain": UPayloadFormat.UPAYLOAD_FORMAT_TEXT, - "application/x-someip": UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP, - "application/x-someip_tlv": UPayloadFormat.UPAYLOAD_FORMAT_SOMEIP_TLV, - } - return content_type_mapping.get(contenttype, UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF) - - @staticmethod - def from_message(message: UMessage) -> CloudEvent: - """ - Get the Cloudevent from the UMessage
- Note: For now, only the value format of - UPayload is supported in the SDK.If the UPayload has a reference, it - needs to be copied to CloudEvent. - @param message The UMessage protobuf containing the data - @return returns the cloud event - """ - - if message is None: - raise ValueError("message cannot be null.") - - attributes = message.attributes - payload = message.payload - - if attributes is None: - attributes = UAttributes() - if payload is None: - payload = UPayload() - - data = bytearray() - json_attributes = { - "id": LongUuidSerializer.instance().serialize(attributes.id), - "source": LongUriSerializer().serialize(message.attributes.source), - "type": UCloudEvent.get_event_type(attributes.type), - } - contenttype = UCloudEvent.get_content_type_from_upayload_format(payload.format) - - if contenttype: - json_attributes["datacontenttype"] = "application/x-protobuf" - - # IMPORTANT: Currently, ONLY the VALUE format is supported in the SDK! - if payload.HasField("value"): - data = payload.value - if attributes.HasField("ttl"): - json_attributes["ttl"] = attributes.ttl - if attributes.priority > 0: - json_attributes["priority"] = UPriority.Name(attributes.priority) - if attributes.HasField("token"): - json_attributes["token"] = attributes.token - if attributes.HasField("sink"): - json_attributes["sink"] = LongUriSerializer().serialize(attributes.sink) - if attributes.HasField("commstatus"): - json_attributes["commstatus"] = attributes.commstatus - if attributes.HasField("reqid"): - json_attributes["reqid"] = LongUuidSerializer.instance().serialize(attributes.reqid) - if attributes.HasField("permission_level"): - json_attributes["plevel"] = attributes.permission_level - if attributes.HasField("traceparent"): - json_attributes["traceparent"] = attributes.traceparent - - cloud_event = CloudEvent(json_attributes, data) - return cloud_event - - @staticmethod - def to_message(event: CloudEvent) -> UMessage: - """ - - Get the UMessage from the cloud event - @param event The CloudEvent containing the data. - @return returns the UMessage - """ - if event is None: - raise ValueError("Cloud Event can't be None") - - payload = UPayload( - format=UCloudEvent.get_upayload_format_from_content_type(UCloudEvent.get_data_content_type(event)), - value=UCloudEvent.get_payload(event).SerializeToString(), - ) - attributes = UAttributes( - id=LongUuidSerializer.instance().deserialize(UCloudEvent.get_id(event)), - type=UCloudEvent.get_message_type(UCloudEvent.get_type(event)), - source=LongUriSerializer().deserialize(UCloudEvent.get_source(event)), - ) - if UCloudEvent.has_communication_status_problem(event): - attributes.commstatus = UCloudEvent.get_communication_status(event) - priority = UCloudEvent.get_priority(event) - - if priority and "UPRIORITY_" not in priority: - priority = "UPRIORITY_" + priority - - if priority is not None: - attributes.priority = UPriority.Value(priority) - - sink = UCloudEvent.get_sink(event) - if sink is not None: - attributes.sink.CopyFrom(LongUriSerializer().deserialize(sink)) - - reqid = UCloudEvent.get_request_id(event) - if reqid is not None: - attributes.reqid.CopyFrom(LongUuidSerializer().deserialize(reqid)) - - ttl = UCloudEvent.get_ttl(event) - if ttl is not None: - attributes.ttl = ttl - - token = UCloudEvent.get_token(event) - if token is not None: - attributes.token = token - - traceparent = UCloudEvent.get_traceparent(event) - if traceparent is not None: - attributes.traceparent = traceparent - - plevel = UCloudEvent.extract_integer_value_from_attributes("plevel", event) - if plevel is not None: - attributes.permission_level = plevel - - return UMessage(attributes=attributes, payload=payload) diff --git a/uprotocol/cloudevent/serialize/__init__.py b/uprotocol/cloudevent/serialize/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/cloudevent/serialize/base64protobufserializer.py b/uprotocol/cloudevent/serialize/base64protobufserializer.py deleted file mode 100644 index 3b70aa8..0000000 --- a/uprotocol/cloudevent/serialize/base64protobufserializer.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import base64 -from builtins import str - -from uprotocol.cloudevent.serialize.cloudeventserializer import CloudEventSerializer - - -class Base64ProtobufSerializer(CloudEventSerializer): - """ - Helper for serializing Base64 protobuf data. - """ - - def deserialize(self, proto_bytes: bytes) -> str: - """ - Deserialize a base64 protobuf payload into a Base64 String.

- - @param proto_bytes: byte[] data - @return: Returns a String from the base64 protobuf payload. - """ - if proto_bytes is None: - return "" - return base64.b64encode(proto_bytes).decode('utf-8') # return base64.b64decode(proto_bytes).decode('utf-8') - - def serialize(self, string_to_serialize: str) -> bytes: - """ - Serialize a String into Base64 format.

- @param string_to_serialize:String to serialize. - @return: Returns the Base64 formatted String as a byte[]. - """ - if string_to_serialize is None: - return bytearray() - return base64.b64decode(string_to_serialize.encode('utf-8')) diff --git a/uprotocol/cloudevent/serialize/cloudeventserializer.py b/uprotocol/cloudevent/serialize/cloudeventserializer.py deleted file mode 100644 index 02f2051..0000000 --- a/uprotocol/cloudevent/serialize/cloudeventserializer.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from abc import ABC, abstractmethod -from typing import Any - - -class CloudEventSerializer(ABC): - @abstractmethod - def serialize(self, cloud_event: Any) -> bytes: - pass - - @abstractmethod - def deserialize(self, bytes_data: bytes) -> Any: - pass diff --git a/uprotocol/cloudevent/serialize/cloudeventserializers.py b/uprotocol/cloudevent/serialize/cloudeventserializers.py deleted file mode 100644 index 0040e06..0000000 --- a/uprotocol/cloudevent/serialize/cloudeventserializers.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from enum import Enum - -from uprotocol.cloudevent.serialize.cloudeventtojsonserializer import CloudEventToJsonSerializer -from uprotocol.cloudevent.serialize.cloudeventtoprotobufserializer import CloudEventToProtobufSerializer - - -class CloudEventSerializers(Enum): - """ - Provides Singleton instances of the CloudEvent Serializers. - """ - - JSON = CloudEventToJsonSerializer() - PROTOBUF = CloudEventToProtobufSerializer() - - def serializer(self): - return self.value diff --git a/uprotocol/cloudevent/serialize/cloudeventtojsonserializer.py b/uprotocol/cloudevent/serialize/cloudeventtojsonserializer.py deleted file mode 100644 index 5b23a9d..0000000 --- a/uprotocol/cloudevent/serialize/cloudeventtojsonserializer.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from cloudevents.conversion import to_json -from cloudevents.http import CloudEvent, from_json - -from uprotocol.cloudevent.serialize.cloudeventserializer import CloudEventSerializer - - -class CloudEventToJsonSerializer(CloudEventSerializer): - """ - CloudEventSerializer to serialize and deserialize CloudEvents to JSON format. - """ - - def serialize(self, ce: CloudEvent) -> bytes: - return to_json(ce) - - def deserialize(self, bytes_data: bytes) -> CloudEvent: - return from_json(bytes_data) diff --git a/uprotocol/cloudevent/serialize/cloudeventtoprotobufserializer.py b/uprotocol/cloudevent/serialize/cloudeventtoprotobufserializer.py deleted file mode 100644 index 702601c..0000000 --- a/uprotocol/cloudevent/serialize/cloudeventtoprotobufserializer.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from cloudevents.http import CloudEvent - -from uprotocol.cloudevent import cloudevents_pb2 -from uprotocol.cloudevent.serialize.cloudeventserializer import ( - CloudEventSerializer, -) - - -# ToDo- convert cloud event to cloudevent proto -class CloudEventToProtobufSerializer(CloudEventSerializer): - """ - CloudEventSerializer to serialize and deserialize CloudEvents to protobuf format. - """ - - def __init__(self): - pass - - def serialize(self, http_event: CloudEvent) -> bytes: - proto_event = cloudevents_pb2.CloudEvent() - # Set required attributes - proto_event.id = http_event["id"] - proto_event.source = http_event["source"] - proto_event.spec_version = http_event["specversion"] - proto_event.type = http_event["type"] - - # Set optional & extension attributes - for key, value in http_event.get_attributes().items(): - if key not in ["specversion", "id", "source", "type", "data"]: - attribute_value = proto_event.attributes[key] - if isinstance(value, bool): - attribute_value.ce_boolean = value - elif isinstance(value, int): - attribute_value.ce_integer = value - elif isinstance(value, str): - attribute_value.ce_string = value - elif isinstance(value, bytes): - attribute_value.ce_bytes = value - - # Set data - data = http_event.get_data() - if isinstance(data, bytes): - proto_event.binary_data = data - elif isinstance(data, str): - proto_event.text_data = data - - return proto_event.SerializeToString() - - def deserialize(self, bytes_data: bytes) -> CloudEvent: - proto_event = cloudevents_pb2.CloudEvent() - proto_event.ParseFromString(bytes_data) - - json_attributes = { - "id": proto_event.id, - "source": proto_event.source, - "type": proto_event.type, - "specversion": proto_event.spec_version, - } - - # Set optional & extension attributes - for key in proto_event.attributes: - if key not in ["specversion", "id", "source", "type", "data"]: - attribute_value = proto_event.attributes[key] - if attribute_value.HasField("ce_boolean"): - json_attributes[key] = attribute_value.ce_boolean - elif attribute_value.HasField("ce_integer"): - json_attributes[key] = attribute_value.ce_integer - elif attribute_value.HasField("ce_string"): - json_attributes[key] = attribute_value.ce_string - elif attribute_value.HasField("ce_bytes"): - json_attributes[key] = attribute_value.ce_bytes - - # Set data - data = bytearray() - if proto_event.HasField("binary_data"): - data = proto_event.binary_data - elif proto_event.HasField("text_data"): - data = proto_event.text_data - - cloud_event = CloudEvent(json_attributes, data) - - return cloud_event diff --git a/uprotocol/cloudevent/validate/__init__.py b/uprotocol/cloudevent/validate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/cloudevent/validate/cloudeventvalidator.py b/uprotocol/cloudevent/validate/cloudeventvalidator.py deleted file mode 100644 index 2b78d76..0000000 --- a/uprotocol/cloudevent/validate/cloudeventvalidator.py +++ /dev/null @@ -1,443 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from abc import ABC, abstractmethod -from enum import Enum - -from cloudevents.http import CloudEvent - -from uprotocol.cloudevent.factory.ucloudevent import UCloudEvent -from uprotocol.proto.uattributes_pb2 import UMessageType -from uprotocol.proto.uri_pb2 import UUri -from uprotocol.uri.serializer.longuriserializer import LongUriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator -from uprotocol.validation.validationresult import ValidationResult - - -class CloudEventValidator(ABC): - @staticmethod - def get_validator(ce: CloudEvent): - """ - Obtain a CloudEventValidator according to the type - attribute in the CloudEvent.

- @param ce:The CloudEvent with the type attribute. - @return:Returns a CloudEventValidator according to the - type attribute in the CloudEvent. - """ - cloud_event_type = ce.get_attributes().get("type") - - if cloud_event_type is None: - return Validators.PUBLISH.validator() - - message_type = UCloudEvent.get_message_type(cloud_event_type) - - if message_type == UMessageType.UMESSAGE_TYPE_RESPONSE: - return Validators.RESPONSE.validator() - elif message_type == UMessageType.UMESSAGE_TYPE_REQUEST: - return Validators.REQUEST.validator() - elif message_type == UMessageType.UMESSAGE_TYPE_NOTIFICATION: - return Validators.NOTIFICATION.validator() - else: - return Validators.PUBLISH.validator() - - def validate(self, ce: CloudEvent) -> ValidationResult: - """ - Validate the CloudEvent. A CloudEventValidator instance is obtained - according to the type attribute on the CloudEvent.

@param - ce:The CloudEvent to validate. @return:Returns a ValidationResult with - success or a ValidationResult with failure containing all the errors - that were found. - """ - self.validate_source(ce) - validation_results = [ - self.validate_version(ce), - self.validate_id(ce), - self.validate_source(ce), - self.validate_type(ce), - self.validate_sink(ce), - ] - - error_messages = [result.get_message() for result in validation_results if not result.is_success()] - error_message = ",".join(error_messages) - - if not error_message: - return ValidationResult.success() - else: - return ValidationResult.failure(",".join(error_messages)) - - @staticmethod - def validate_version(ce: CloudEvent) -> ValidationResult: - return CloudEventValidator.validate_version_spec(ce.get_attributes().get("specversion")) - - @staticmethod - def validate_version_spec(version) -> ValidationResult: - if version == "1.0": - return ValidationResult.success() - else: - return ValidationResult.failure(f"Invalid CloudEvent version [{version}]. CloudEvent version must be 1.0.") - - @staticmethod - def validate_id(ce: CloudEvent) -> ValidationResult: - id = UCloudEvent.extract_string_value_from_attributes("id", ce) - return ( - ValidationResult.success() - if UCloudEvent.is_cloud_event_id(ce) - else ValidationResult.failure(f"Invalid CloudEvent Id [{id}]. CloudEvent Id must be of type UUIDv8.") - ) - - @abstractmethod - def validate_source(self, ce: CloudEvent): - """ - Validate the source value of a cloud event.

- @param ce:The cloud - event containing the source to validate. - @return:Returns the - ValidationResult containing a success or a failure with the error - message. - """ - raise NotImplementedError("Subclasses must implement this method") - - @abstractmethod - def validate_type(self, ce: CloudEvent): - """ - Validate the type value of a cloud event.

- @param ce:The cloud - event containing the type to validate. - @return:Returns the - ValidationResult containing a success or a failure with the error - message. - """ - raise NotImplementedError("Subclasses must implement this method") - - def validate_sink(self, ce: CloudEvent) -> ValidationResult: - """ - Validate the sink value of a cloud event in the default scenario where - the sink attribute is optional.

- @param ce:The cloud event - containing the sink to validate. - @return:Returns the ValidationResult - containing a success or a failure with the error message. - """ - maybe_sink = UCloudEvent.get_sink(ce) - if maybe_sink: - sink = maybe_sink - check_sink = self.validate_u_entity_uri(sink) - if check_sink.is_failure(): - return ValidationResult.failure(f"Invalid CloudEvent sink [{sink}]. {check_sink.get_message()}") - - return ValidationResult.success() - - @staticmethod - def validate_u_entity_uri(uri: str) -> ValidationResult: - """ - Validate an UriPart for an Software Entity must have an authority in - the case of a microRemote uri, and must contain the name of the - USE.

- @param uri:uri string to validate. - @return:Returns the - ValidationResult containing a success or a failure with the error - message. - """ - uri = LongUriSerializer().deserialize(uri) - return CloudEventValidator.validate_u_entity_uri_from_uuri(uri) - - @staticmethod - def validate_u_entity_uri_from_uuri(uri: UUri) -> ValidationResult: - return UriValidator.validate(uri) - - @staticmethod - def validate_topic_uri(uri: str) -> ValidationResult: - """ - Validate a UriPart that is to be used as a topic in publish scenarios - for events such as publish, file and notification.

- @param uri:String UriPart to validate - @return:Returns the - ValidationResult containing a success or a failure with the error - message. - """ - uri = LongUriSerializer().deserialize(uri) - return CloudEventValidator.validate_topic_uri_from_uuri(uri) - - @staticmethod - def validate_topic_uri_from_uuri(uri: UUri) -> ValidationResult: - """ - Validate a UriPart that is to be used as a topic in publish scenarios - for events such as publish, file and notification.

- @param uri: - UriPart to validate. - @return:Returns the ValidationResult containing a - success or a failure with the error message. - """ - validation_result = CloudEventValidator.validate_u_entity_uri_from_uuri(uri) - if validation_result.is_failure(): - return validation_result - - u_resource = uri.resource - if not u_resource.name: - return ValidationResult.failure("UriPart is missing uResource name.") - - if not u_resource.message: - return ValidationResult.failure("UriPart is missing Message information.") - - return ValidationResult.success() - - @staticmethod - def validate_rpc_topic_uri(uri: str) -> ValidationResult: - """ - Validate a UriPart that is meant to be used as the application response - topic for rpc calls.
Used in Request source values and Response - sink values.

- @param uri:String UriPart to validate. - @return:Returns the ValidationResult containing a success or a failure - with the error message. - """ - uri = LongUriSerializer().deserialize(uri) - return CloudEventValidator.validate_rpc_topic_uri_from_uuri(uri) - - @staticmethod - def validate_rpc_topic_uri_from_uuri(uri: UUri) -> ValidationResult: - """ - Validate a UriPart that is meant to be used as the application response - topic for rpc calls.
Used in Request source values and Response - sink values.

- @param uri:UriPart to validate. - @return:Returns - the ValidationResult containing a success or a failure with the error - message. - """ - validation_result = CloudEventValidator.validate_u_entity_uri_from_uuri(uri) - if validation_result.is_failure(): - return ValidationResult.failure( - f"Invalid RPC uri application response topic. {validation_result.get_message()}", - ) - - u_resource = uri.resource - topic = f"{u_resource.name}.{u_resource.instance}" if u_resource.instance else f"{u_resource.name}" - if topic != "rpc.response": - return ValidationResult.failure( - "Invalid RPC uri application response topic. UriPart is missing rpc.response.", - ) - - return ValidationResult.success() - - @staticmethod - def validate_rpc_method(uri: str) -> ValidationResult: - """ - Validate a UriPart that is meant to be used as an RPC method URI. Used in Request sink values and Response - source values.

- @param uri: String UriPart to validate - @return:Returns the ValidationResult containing a success or a failure with the error message. - """ - uuri = LongUriSerializer().deserialize(uri) - validation_result = CloudEventValidator.validate_u_entity_uri_from_uuri(uuri) - if validation_result.is_failure(): - return ValidationResult.failure(f"Invalid RPC method uri. {validation_result.get_message()}") - - if not UriValidator.is_rpc_method(uuri): - return ValidationResult.failure( - "Invalid RPC method uri. UriPart should be the method to be called, or method from response.", - ) - - return ValidationResult.success() - - -class Publish(CloudEventValidator): - """ - Implements Validations for a CloudEvent of type Publish. - """ - - def validate_source(self, cl_event: CloudEvent) -> ValidationResult: - source = cl_event.get_attributes().get("source") - check_source = self.validate_topic_uri(source) - if check_source.is_failure(): - return ValidationResult.failure( - f"Invalid Publish type CloudEvent source [{source}]. {check_source.get_message()}", - ) - - return ValidationResult.success() - - def validate_type(self, cl_event: CloudEvent) -> ValidationResult: - type = cl_event.get_attributes().get("type") - return ( - ValidationResult.success() - if type == "pub.v1" - else ValidationResult.failure( - f"Invalid CloudEvent type [{type}]. CloudEvent of type Publish must have a type of 'pub.v1'", - ) - ) - - def __str__(self) -> str: - return "CloudEventValidator.Publish" - - -class Notification(Publish): - """ - Implements Validations for a CloudEvent of type Publish that behaves as a - Notification, meaning it must have a sink. - """ - - def validate_sink(self, cl_event: CloudEvent) -> ValidationResult: - maybe_sink = UCloudEvent.get_sink(cl_event) - if not maybe_sink: - return ValidationResult.failure( - "Invalid CloudEvent sink. Notification CloudEvent sink must be an uri.", - ) - else: - sink = maybe_sink - check_sink = self.validate_u_entity_uri(sink) - if check_sink.is_failure(): - return ValidationResult.failure( - f"Invalid Notification type CloudEvent sink [{sink}]. {check_sink.get_message()}", - ) - - return ValidationResult.success() - - def validate_source(self, cl_event: CloudEvent) -> ValidationResult: - source = UCloudEvent.get_source(cl_event) - check_source = self.validate_topic_uri(source) - if check_source.is_failure(): - return ValidationResult.failure( - f"Invalid Notification type CloudEvent source [{source}], {check_source.get_message()}", - ) - return ValidationResult.success() - - def validate_type(self, cl_event: CloudEvent) -> ValidationResult: - return ( - ValidationResult.success() - if UCloudEvent.get_type(cl_event) == "not.v1" - else ValidationResult.failure( - f"Invalid CloudEvent type [{UCloudEvent.get_type(cl_event)}]. " - + "CloudEvent of type Notification must have a type of 'not.v1'", - ) - ) - - def __str__(self): - return "CloudEventValidator.Notification" - - -class Request(CloudEventValidator): - """ - Implements Validations for a CloudEvent for RPC Request. - """ - - def validate_source(self, cl_event: CloudEvent) -> ValidationResult: - source = cl_event.get_attributes().get("source") - check_source = self.validate_rpc_topic_uri(source) - if check_source.is_failure(): - return ValidationResult.failure( - f"Invalid RPC Request CloudEvent source [{source}]. {check_source.get_message()}", - ) - return ValidationResult.success() - - def validate_sink(self, cl_event: CloudEvent) -> ValidationResult: - maybe_sink = UCloudEvent.get_sink(cl_event) - if not maybe_sink: - return ValidationResult.failure( - "Invalid RPC Request CloudEvent sink. Request CloudEvent sink must be uri for the method to be called." - ) - else: - sink = maybe_sink - check_sink = self.validate_rpc_method(sink) - if check_sink.is_failure(): - return ValidationResult.failure( - f"Invalid RPC Request CloudEvent sink [{sink}]. {check_sink.get_message()}", - ) - - return ValidationResult.success() - - def validate_type(self, cl_event: CloudEvent) -> ValidationResult: - type = cl_event.get_attributes().get("type") - - return ( - ValidationResult.success() - if type == "req.v1" - else ValidationResult.failure( - f"Invalid CloudEvent type [{type}]. CloudEvent of type Request must have a type of 'req.v1'", - ) - ) - - def __str__(self): - return "CloudEventValidator.Request" - - -class Response(CloudEventValidator): - """ - Implements Validations for a CloudEvent for RPC Response. - """ - - def validate_source(self, cl_event: CloudEvent) -> ValidationResult: - source = cl_event.get_attributes().get("source") - check_source = self.validate_rpc_method(source) - if check_source.is_failure(): - return ValidationResult.failure( - f"Invalid RPC Response CloudEvent source [{source}]. {check_source.get_message()}", - ) - - return ValidationResult.success() - - def validate_sink(self, cl_event) -> ValidationResult: - maybe_sink = UCloudEvent.get_sink(cl_event) - if not maybe_sink: - return ValidationResult.failure( - "Invalid CloudEvent sink. Response CloudEvent sink must be uri the destination of the response.", - ) - else: - sink = maybe_sink - check_sink = self.validate_rpc_topic_uri(sink) - if check_sink.is_failure(): - return ValidationResult.failure( - f"Invalid RPC Response CloudEvent sink [{sink}]. {check_sink.get_message()}", - ) - - return ValidationResult.success() - - def validate_type(self, cl_event: CloudEvent) -> ValidationResult: - type = cl_event.get_attributes().get("type") - - return ( - ValidationResult.success() - if type == "res.v1" - else ValidationResult.failure( - f"Invalid CloudEvent type [{type}]. CloudEvent of type Response must have a type of 'res.v1'", - ) - ) - - def __str__(self): - return "CloudEventValidator.Response" - - -class Validators(Enum): - """ - Enum that hold the implementations of CloudEventValidator according to - type. - """ - - PUBLISH = Publish() - NOTIFICATION = Notification() - REQUEST = Request() - RESPONSE = Response() - - def __init__(self, cloud_event_validator: CloudEventValidator): - self.cloud_event_validator = cloud_event_validator - - def validator(self) -> CloudEventValidator: - return self.cloud_event_validator diff --git a/uprotocol/rpc/README.adoc b/uprotocol/rpc/README.adoc deleted file mode 100644 index eb3a6cb..0000000 --- a/uprotocol/rpc/README.adoc +++ /dev/null @@ -1,8 +0,0 @@ -= uProtocol Rpc Interfaces -:toc: -:sectnums: - -== Overview - -The following module declares the https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/up-l2/rpcclient.adoc[RpcClient interface] defined in uProtocol specification. The interface is used by code generators to build client and service stubs for uServices. - diff --git a/uprotocol/rpc/__init__.py b/uprotocol/rpc/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/uprotocol/rpc/rpcclient.py b/uprotocol/rpc/rpcclient.py deleted file mode 100644 index 17bbf8d..0000000 --- a/uprotocol/rpc/rpcclient.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from abc import ABC, abstractmethod -from concurrent.futures import Future - -from uprotocol.proto.uattributes_pb2 import CallOptions -from uprotocol.proto.upayload_pb2 import UPayload -from uprotocol.proto.uri_pb2 import UUri - - -class RpcClient(ABC): - """ - RpcClient is an interface used by code generators for uProtocol services - defined in proto files such as the core - uProtocol services found in - here - .
The interface provides a clean contract for all transports - to implement to be able to support RPC on their platform.
- Each platform MUST implement this interface.
- """ - - @abstractmethod - def invoke_method(self, method_uri: UUri, request_payload: UPayload, options: CallOptions) -> Future: - """ - API for clients to invoke a method (send an RPC request) and - receive the response (the returned Future UMessage.
- Client will set method to be the URI of the method they want to invoke, - payload to the request message, and attributes - with the various metadata for the method invocation. - @param method_uri The method URI to be invoked, - ex (long form): /example.hello_world/1/rpc.SayHello. - @param requestPayload The request - message to be sent to the server. - @param options RPC method invocation call options, see CallOptions - @return: Returns the CompletableFuture with the result or exception. - """ - pass diff --git a/uprotocol/rpc/rpcmapper.py b/uprotocol/rpc/rpcmapper.py deleted file mode 100644 index ecc7ff8..0000000 --- a/uprotocol/rpc/rpcmapper.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from concurrent.futures import Future - -from google.protobuf import any_pb2 - -from uprotocol.proto.ustatus_pb2 import UCode, UStatus -from uprotocol.rpc.rpcresult import RpcResult - - -class RpcMapper: - """ - RPC Wrapper is an interface that provides static methods to be able to - wrap an RPC request with an RPC Response ( - uP-L2). APIs that return Message assumes that the message is - protobuf serialized com.google.protobuf.Any ( - UMessageFormat.PROTOBUF) and will barf if anything else is passed - """ - - @staticmethod - def map_response(message_future: Future, expected_cls): - """ - Map a response of CompletableFuture<UMessage> from Link - into a CompletableFuture containing the - declared expected return type of the RPC method or an - exception.

- @param response_future:CompletableFuture<UMessage> - response from uTransport. - @param expected_cls:The class name of the declared expected - return type of the RPC method. - @return:Returns a CompletableFuture containing the declared - expected return type of the RPC method or an - exception. - """ - response_future: Future = Future() - - def handle_response(message): - nonlocal response_future - message = message.result() - if not message or not message.HasField("payload"): - response_future.set_exception( - RuntimeError(f"Server returned a null payload. Expected {expected_cls.__name__}") - ) - return response_future - try: - any_message = any_pb2.Any() - any_message.ParseFromString(message.payload.value) - if any_message.Is(expected_cls.DESCRIPTOR): - response_future.set_result(RpcMapper.unpack_payload(any_message, expected_cls)) - else: - response_future.set_exception( - RuntimeError( - f"Unknown payload type [{any_message.type_url}]. Expected [{expected_cls.__name__}]" - ) - ) - - except Exception as e: - response_future.set_exception(RuntimeError(f"{str(e)} [{UStatus.__name__}]")) - - message_future.add_done_callback(handle_response) - - return response_future - - @staticmethod - def map_response_to_result(response_future: Future, expected_cls): - """ - Map a response of CompletableFuture<Any> from Link into a CompletableFuture containing an RpcResult - containing the declared expected return type T, or a UStatus containing any errors.

- @param response_future:CompletableFuture<Any> response from Link. - @param expected_cls:The class name of the declared expected return type of the RPC method. - @return:Returns a CompletableFuture containing an RpcResult containing the declared expected return type T, - or a UStatus containing any errors. - """ - - def handle_response(message): - if message.exception(): - exception = message.exception() - return RpcResult.failure(value=exception, message=str(exception)) - - message = message.result() - if not message or not message.HasField("payload"): - exception = RuntimeError(f"Server returned a null payload. Expected {expected_cls.__name__}") - return RpcResult.failure(value=exception, message=str(exception)) - - try: - any_message = any_pb2.Any() - any_message.ParseFromString(message.payload.value) - - if any_message.Is(expected_cls.DESCRIPTOR): - if expected_cls == UStatus: - return RpcMapper.calculate_status_result(any_message) - else: - return RpcResult.success(RpcMapper.unpack_payload(any_message, expected_cls)) - - if any_message.Is(UStatus.DESCRIPTOR): - return RpcMapper.calculate_status_result(any_message) - except Exception as e: - exception = RuntimeError(f"{str(e)} [{UStatus.__name__}]") - return RpcResult.failure(value=exception, message=str(exception)) - - exception = RuntimeError( - f"Unknown payload type [{any_message.type_url}]. Expected [{expected_cls.DESCRIPTOR.full_name}]" - ) - return RpcResult.failure(value=exception, message=str(exception)) - - result = None # Initialize result - - def callback_wrapper(payload): - nonlocal result - result = handle_response(payload) - - response_future.add_done_callback(callback_wrapper) - return result - - @staticmethod - def calculate_status_result(payload): - status = RpcMapper.unpack_payload(payload, UStatus) - return RpcResult.success(status) if status.code == UCode.OK else RpcResult.failure(status) - - @staticmethod - def unpack_payload(payload, expected_cls): - """ - Unpack a payload of type Any into an object of type T, which is what was packing into the Any - object.

- @param payload:an Any message containing a type of expectedClazz. - @param expected_cls:The class name of the object packed into the Any - @return:Returns an object of type T and of the class name specified, that was packed into the Any - object. - """ - try: - value = expected_cls() - value.ParseFromString(payload.value) - return value - except Exception as e: - raise RuntimeError(f"{str(e)} [{UStatus.__name__}]") from e diff --git a/uprotocol/rpc/rpcresult.py b/uprotocol/rpc/rpcresult.py deleted file mode 100644 index 99c296f..0000000 --- a/uprotocol/rpc/rpcresult.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from abc import ABC, abstractmethod -from typing import Callable, TypeVar, Union - -from uprotocol.proto.ustatus_pb2 import UCode, UStatus - -T = TypeVar("T") - - -class RpcResult(ABC): - """ - Wrapper class for RPC Stub calls. It contains a Success with the type of the RPC call, or a failure with the - UStatus returned by the failed call. - """ - - @abstractmethod - def is_success(self) -> bool: - pass - - @abstractmethod - def is_failure(self) -> bool: - pass - - @abstractmethod - def get_or_else(self, default_value: Callable[[], T]) -> T: - pass - - @abstractmethod - def map(self, f: Callable[[T], T]) -> "RpcResult": - pass - - @abstractmethod - def flat_map(self, f: Callable[[T], "RpcResult"]) -> "RpcResult": - pass - - @abstractmethod - def filter(self, f: Callable[[T], bool]) -> "RpcResult": - pass - - @abstractmethod - def failure_value(self) -> UStatus: - pass - - @abstractmethod - def success_value(self) -> T: - pass - - @staticmethod - def success(value: T) -> "RpcResult": - return Success(value) - - @staticmethod - def failure( - value: Union[ - UStatus, - "Failure", - Exception, - ] = None, - code: UCode = UCode.UNKNOWN, - message: str = "", - ) -> "RpcResult": - return Failure(value, code, message) - - @staticmethod - def flatten(result: "RpcResult") -> "RpcResult": - return result.flat_map(lambda x: x) - - -class Success(RpcResult): - def __init__(self, value: T): - self.value = value - - def is_success(self) -> bool: - return True - - def is_failure(self) -> bool: - return False - - def get_or_else(self, default_value: Callable[[], T]) -> T: - return self.success_value() - - def map(self, f: Callable[[T], T]) -> RpcResult: - try: - return self.success(f(self.success_value())) - except Exception as e: - return self.failure(e) - - def flat_map(self, f: Callable[[T], RpcResult]) -> RpcResult: - try: - return f(self.success_value()) - except Exception as e: - return self.failure(e) - - def filter(self, f: Callable[[T], bool]) -> RpcResult: - try: - return ( - self - if f(self.success_value()) - else self.failure(code=UCode.FAILED_PRECONDITION, message="filtered out") - ) - except Exception as e: - return self.failure(e) - - def failure_value(self) -> UStatus: - raise ValueError("Method failure_value() called on a Success instance") - - def success_value(self) -> T: - return self.value - - def __str__(self) -> str: - return f"Success({self.success_value()})" - - -class Failure(RpcResult): - def __init__( - self, - value: Union[UStatus, "Failure", Exception, None] = None, - code: UCode = UCode.UNKNOWN, - message: str = "", - ): - if isinstance(value, UStatus): - self.value = value - elif isinstance(value, Exception): - self.value = UStatus(code=code, message=str(value)) - elif isinstance(value, Failure): - self.value = value.failure_value() - else: - self.value = UStatus(code=code, message=message) - - def is_success(self) -> bool: - return False - - def is_failure(self) -> bool: - return True - - def get_or_else(self, default_value: Callable[[], T]) -> T: - if callable(default_value): - return default_value() - return default_value - - def map(self, f: Callable[[T], T]) -> RpcResult: - return self.failure(self) - - def flat_map(self, f: Callable[[T], RpcResult]) -> RpcResult: - return self.failure(self.failure_value()) - - def filter(self, f: Callable[[T], bool]) -> RpcResult: - return self.failure(self) - - def failure_value(self) -> UStatus: - return self.value - - def success_value(self) -> T: - raise ValueError("Method success_value() called on a Failure instance") - - def __str__(self) -> str: - return f"Failure({self.value})" diff --git a/uprotocol/transport/builder/uattributesbuilder.py b/uprotocol/transport/builder/uattributesbuilder.py deleted file mode 100644 index 9ae8991..0000000 --- a/uprotocol/transport/builder/uattributesbuilder.py +++ /dev/null @@ -1,273 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from typing import Union - -from multimethod import multimethod - -from uprotocol.proto.uattributes_pb2 import ( - UAttributes, - UMessageType, - UPriority, -) -from uprotocol.proto.uri_pb2 import UUri -from uprotocol.proto.ustatus_pb2 import UCode -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.uuid.factory.uuidfactory import Factories - - -class UAttributesBuilder: - """ - Construct the UAttributesBuilder with the configurations that are - required for every payload transport. - - @param id Unique identifier for the message. - @param type Message type such as Publish a state change, - RPC request or RPC response. - @param priority uProtocol Prioritization classifications. - """ - - def __init__(self, source: UUri, id: UUID, type: UMessageType, priority: UPriority): - self.source = source - self.id = id - self.type = UMessageType.Name(type) - self.priority = priority - self.ttl = None - self.token = None - self.sink = None - self.plevel = None - self.commstatus = None - self.reqid = None - self.traceparent = None - - @staticmethod - def publish(source: UUri, priority: UPriority): - """ - Construct a UAttributesBuilder for a publish message. - @param source Source address of the message. - @param priority The priority of the message. - @return Returns the UAttributesBuilder with the configured priority. - """ - if source is None: - raise ValueError("Source cannot be None.") - if priority is None: - raise ValueError("UPriority cannot be None.") - return UAttributesBuilder( - source, - Factories.UPROTOCOL.create(), - UMessageType.UMESSAGE_TYPE_PUBLISH, - priority, - ) - - @staticmethod - def notification(source: UUri, sink: UUri, priority: UPriority): - """ - Construct a UAttributesBuilder for a notification message. - @param source Source address of the message. - @param sink The destination URI. - @param priority The priority of the message. - @return Returns the UAttributesBuilder with the configured source, - priority and sink. - """ - if source is None: - raise ValueError("Source cannot be None.") - if priority is None: - raise ValueError("UPriority cannot be null.") - if sink is None: - raise ValueError("sink cannot be null.") - return UAttributesBuilder( - source, - Factories.UPROTOCOL.create(), - UMessageType.UMESSAGE_TYPE_NOTIFICATION, - priority, - ).with_sink(sink) - - @staticmethod - def request(source: UUri, sink: UUri, priority: UPriority, ttl: int): - """ - Construct a UAttributesBuilder for a request message. - @param source Source address of the message. - @param sink The destination URI. - @param priority The priority of the message. - @param ttl The time to live in milliseconds. - @return Returns the UAttributesBuilder with the configured - priority, sink and ttl. - """ - if source is None: - raise ValueError("Source cannot be None.") - if priority is None: - raise ValueError("UPriority cannot be null.") - if sink is None: - raise ValueError("sink cannot be null.") - if ttl is None: - raise ValueError("ttl cannot be null.") - - return ( - UAttributesBuilder( - source, - Factories.UPROTOCOL.create(), - UMessageType.UMESSAGE_TYPE_REQUEST, - priority, - ) - .with_ttl(ttl) - .with_sink(sink) - ) - - @multimethod - def response( - source: Union[UUri, None], # noqa: N805 - sink: Union[UUri, None], - priority: Union[int, None], - reqid: Union[UUID, None], - ): - """ - Construct a UAttributesBuilder for a response message. - @param source Source address of the message. - @param sink The destination URI. - @param priority The priority of the message. - @param reqid The original request UUID used to correlate the - response to the request. - @return Returns the UAttributesBuilder with the configured priority, - sink and reqid. - """ - if priority is None: - raise ValueError("UPriority cannot be null.") - if sink is None: - raise ValueError("sink cannot be null.") - if reqid is None: - raise ValueError("reqid cannot be null.") - - return ( - UAttributesBuilder( - source, - Factories.UPROTOCOL.create(), - UMessageType.UMESSAGE_TYPE_RESPONSE, - priority, - ) - .with_sink(sink) - .with_req_id(reqid) - ) - - @multimethod - def response(request: Union[UAttributes, None]): # noqa: N805 - if request is None: - raise ValueError("request cannot be null.") - return UAttributesBuilder.response(request.sink, request.source, request.priority, request.id) - - def with_ttl(self, ttl: int): - """ - Add the time to live in milliseconds. - - @param ttl the time to live in milliseconds. - @return Returns the UAttributesBuilder with the configured ttl. - """ - self.ttl = ttl - return self - - def with_token(self, token: str): - """ - dd the authorization token used for TAP. - - @param token the authorization token used for TAP. - @return Returns the UAttributesBuilder with the configured token. - """ - self.token = token - return self - - def with_sink(self, sink: UUri): - """ - Add the explicit destination URI. - - @param sink the explicit destination URI. - @return Returns the UAttributesBuilder with the configured sink. - """ - self.sink = sink - return self - - def with_permission_level(self, plevel: int): - """ - Add the permission level of the message. - - @param plevel the permission level of the message. - @return Returns the UAttributesBuilder with the configured plevel. - """ - self.plevel = plevel - return self - - def with_comm_status(self, commstatus: UCode): - """ - Add the communication status of the message. - - @param commstatus the communication status of the message. - @return Returns the UAttributesBuilder with the configured commstatus. - """ - self.commstatus = commstatus - return self - - def with_req_id(self, reqid: UUID): - """ - Add the request ID. - - @param reqid the request ID. - @return Returns the UAttributesBuilder with the configured reqid. - """ - self.reqid = reqid - return self - - def with_traceparent(self, traceparent: str): - """ - Add the traceparent. - - @param reqid the traceparent. - @return Returns the UAttributesBuilder with the configured - traceparent. - """ - self.traceparent = traceparent - return self - - def build(self): - """ - Construct the UAttributes from the builder. - - @return Returns a constructed - """ - attributes = UAttributes( - source=self.source, - id=self.id, - type=self.type, - priority=self.priority, - ) - if self.sink is not None: - attributes.sink.CopyFrom(self.sink) - if self.ttl is not None: - attributes.ttl = self.ttl - if self.plevel is not None: - attributes.permission_level = self.plevel - if self.commstatus is not None: - attributes.commstatus = self.commstatus - if self.reqid is not None: - attributes.reqid.CopyFrom(self.reqid) - if self.traceparent is not None: - attributes.traceparent = self.traceparent - if self.token is not None: - attributes.token = self.token - return attributes diff --git a/uprotocol/transport/builder/umessagebuilder.py b/uprotocol/transport/builder/umessagebuilder.py new file mode 100644 index 0000000..be0e167 --- /dev/null +++ b/uprotocol/transport/builder/umessagebuilder.py @@ -0,0 +1,318 @@ +""" +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + +See the NOTICE file(s) distributed with this work for additional +information regarding copyright ownership. + +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at + + http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 +""" + +from google.protobuf.any_pb2 import Any +from google.protobuf.message import Message + +from uprotocol.uuid.factory.uuidfactory import Factories +from uprotocol.v1.uattributes_pb2 import ( + UAttributes, + UMessageType, + UPayloadFormat, + UPriority, +) +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.umessage_pb2 import UMessage +from uprotocol.v1.uri_pb2 import UUri +from uprotocol.v1.uuid_pb2 import UUID + +SOURCE_ERROR = "source cannot be null." +SINK_ERROR = "sink cannot be null." +REQID_ERROR = "reqid cannot be null." +REQUEST_ERROR = "request cannot be null." +TTL_ERROR = "ttl cannot be null." + + +class UMessageBuilder: + """Builder for easy construction of the UAttributes object.""" + + @staticmethod + def publish(source: UUri): + """Construct a UMessageBuilder for a publish message. + + @param source: Source address of the message. + @return Returns the UMessageBuilder with the configured priority. + """ + if source is None: + raise ValueError(SOURCE_ERROR) + return UMessageBuilder( + source, + Factories.UPROTOCOL.create(), + UMessageType.UMESSAGE_TYPE_PUBLISH, + ) + + @staticmethod + def notification(source: UUri, sink: UUri): + """Construct a UMessageBuilder for a notification message. + + @param source Source address of the message. + @param sink The destination URI. + @return Returns the UMessageBuilder with the configured priority + and sink. + """ + if source is None: + raise ValueError(SOURCE_ERROR) + if sink is None: + raise ValueError(SINK_ERROR) + return UMessageBuilder( + source, + Factories.UPROTOCOL.create(), + UMessageType.UMESSAGE_TYPE_NOTIFICATION, + ).with_sink(sink) + + @staticmethod + def request(source: UUri, sink: UUri, ttl: int): + """Construct a UMessageBuilder for a request message. + + @param source Source address of the message. + @param sink The destination URI. + @param ttl The time to live in milliseconds. + @return Returns the UMessageBuilder with the configured priority, + sink and ttl. + """ + if source is None: + raise ValueError(SOURCE_ERROR) + if sink is None: + raise ValueError(SINK_ERROR) + if ttl is None: + raise ValueError(TTL_ERROR) + return ( + UMessageBuilder( + source, + Factories.UPROTOCOL.create(), + UMessageType.UMESSAGE_TYPE_REQUEST, + ) + .with_ttl(ttl) + .with_sink(sink) + ) + + @staticmethod + def response(source: UUri, sink: UUri, reqid: UUID): # noqa: N805 + """Construct a UMessageBuilder for a response message. + + @param source Source address of the message. + @param sink The destination URI. + @param reqid The original request UUID used to correlate the + response to the request. + @return Returns the UMessageBuilder with the configured source, + sink, priority, and reqid. + """ + if source is None: + raise ValueError(SOURCE_ERROR) + if sink is None: + raise ValueError(SINK_ERROR) + if reqid is None: + raise ValueError(REQID_ERROR) + + return ( + UMessageBuilder( + source, + Factories.UPROTOCOL.create(), + UMessageType.UMESSAGE_TYPE_RESPONSE, + ) + .with_sink(sink) + .with_reqid(reqid) + ) + + @staticmethod + def response_for_request(request: UAttributes): # noqa: N805 + """Construct a UMessageBuilder for a response message using an + existing request. + @param request The original request UAttributes used to correlate + the response to the request. + @return Returns the UMessageBuilder with the configured source, sink, + priority, and reqid. + """ + if request is None: + raise ValueError(REQUEST_ERROR) + return ( + UMessageBuilder( + request.sink, + Factories.UPROTOCOL.create(), + UMessageType.UMESSAGE_TYPE_RESPONSE, + ) + .with_priority(request.priority) + .with_sink(request.source) + .with_reqid(request.id) + ) + + def __init__(self, source: UUri, id_val: UUID, type_val: UMessageType): + """Construct the UMessageBuilder with the configurations that are + required for every payload transport. + + @param source Source address of the message. + @param id_val Unique identifier for the message. + @param type_val Message type such as Publish a state change, + RPC request or RPC response. + """ + self.source = source + self.id = id_val + self.type = type_val + self.sink = None + self.priority = None + self.ttl = None + self.plevel = None + self.commstatus = None + self.reqid = None + self.token = None + self.traceparent = None + self.format = None + self.payload = None + + def with_ttl(self, ttl: int): + """Add the time to live in milliseconds. + + @param ttl the time to live in milliseconds. + @return Returns the UMessageBuilder with the configured ttl. + """ + self.ttl = ttl + return self + + def with_token(self, token: str): + """Add the authorization token used for TAP. + + @param token the authorization token used for TAP. + @return Returns the UMessageBuilder with the configured token. + """ + self.token = token + return self + + def with_priority(self, priority: UPriority): + """Add the priority level of the message. + + @param priority the priority level of the message. + @return Returns the UMessageBuilder with the configured priority. + """ + self.priority = priority + return self + + def with_permission_level(self, plevel: int): + """Add the permission level of the message. + + @param plevel the permission level of the message. + @return Returns the UMessageBuilder with the configured plevel. + """ + self.plevel = plevel + return self + + def with_traceparent(self, traceparent: str): + """Add the traceprent. + + @param traceparent the trace parent. + @return Returns the UMessageBuilder with the configured traceparent. + """ + self.traceparent = traceparent + return self + + def with_commstatus(self, commstatus: UCode): + """Add the communication status of the message. + + @param commstatus the communication status of the message. + @return Returns the UMessageBuilder with the configured commstatus. + """ + self.commstatus = commstatus + return self + + def with_reqid(self, reqid: UUID): + """Add the request ID. + + @param reqid the request ID. + @return Returns the UMessageBuilder with the configured reqid. + """ + self.reqid = reqid + return self + + def with_sink(self, sink: UUri): + """Add the explicit destination URI. + + @param sink the explicit destination URI. + @return Returns the UMessageBuilder with the configured sink. + """ + self.sink = sink + return self + + def _build_static(self): + """Construct the UMessage from the builder. + + @return Returns a constructed + """ + message_builder = UMessage() + + attributes_builder = UAttributes(source=self.source, id=self.id, type=self.type) + + priority = self._calculate_priority() + attributes_builder.priority = priority + + if self.sink is not None: + attributes_builder.sink.CopyFrom(self.sink) + if self.ttl is not None: + attributes_builder.ttl = self.ttl + if self.plevel is not None: + attributes_builder.permission_level = self.plevel + if self.commstatus is not None: + attributes_builder.commstatus = self.commstatus + if self.reqid is not None: + attributes_builder.reqid.CopyFrom(self.reqid) + if self.token is not None: + attributes_builder.token = self.token + if self.traceparent is not None: + attributes_builder.traceparent = self.traceparent + if self.format is not None: + attributes_builder.payload_format = self.format + + message_builder.attributes.CopyFrom(attributes_builder) + if self.payload is not None: + message_builder.payload = self.payload + return message_builder + + def build(self, arg1=None, arg2=None): + if arg1 is None and arg2 is None: + return self._build_static() + elif isinstance(arg1, Any) and arg2 is None: + if arg1 is None: + raise ValueError("Any cannot be null.") + self.format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY + self.payload = arg1.SerializeToString() + return self._build_static() + elif isinstance(arg1, Message) and arg2 is None: + if arg1 is None: + raise ValueError("Protobuf Message cannot be null.") + self.format = UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF + self.payload = arg1.SerializeToString() + return self._build_static() + elif isinstance(arg2, bytes): + if arg1 is None: + raise ValueError("Format cannot be null.") + if arg2 is None: + raise ValueError("Payload cannot be null.") + self.format = arg1 + self.payload = arg2 + return self._build_static() + + def _calculate_priority(self): + if self.type in [ + UMessageType.UMESSAGE_TYPE_REQUEST, + UMessageType.UMESSAGE_TYPE_RESPONSE, + ]: + return ( + self.priority + if self.priority is not None and self.priority >= UPriority.UPRIORITY_CS4 + else UPriority.UPRIORITY_CS4 + ) + else: + return ( + self.priority + if self.priority is not None and self.priority >= UPriority.UPRIORITY_CS1 + else UPriority.UPRIORITY_CS1 + ) diff --git a/uprotocol/transport/builder/upayloadbuilder.py b/uprotocol/transport/builder/upayloadbuilder.py deleted file mode 100644 index 73cf780..0000000 --- a/uprotocol/transport/builder/upayloadbuilder.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from typing import Optional, Type - -from google.protobuf.any_pb2 import Any -from google.protobuf.message import Message - -from uprotocol.proto.upayload_pb2 import UPayload, UPayloadFormat - - -class UPayloadBuilder: - @staticmethod - def pack_to_any(message: Message) -> UPayload: - """ - Build a uPayload from google.protobuf.Message by stuffing the message into an Any. - @param message the message to pack - @return the UPayload - """ - any_message = Any() - any_message.Pack(message) - return UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, - value=any_message.SerializeToString(), - ) - - @staticmethod - def pack(message: Message) -> UPayload: - """ - Build a uPayload from google.protobuf.Message using protobuf PayloadFormat. - @param message the message to pack - @return the UPayload - """ - return UPayload( - format=UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF, - value=message.SerializeToString(), - ) - - @staticmethod - def unpack(payload: UPayload, clazz: Type[Message]) -> Optional[Message]: - """ - Unpack a uPayload into a google.protobuf.Message. - @param payload the payload to unpack - @param clazz the class of the message to unpack - @return the unpacked message - """ - if payload is None or payload.value is None: - return None - try: - if payload.format == UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF: - message = clazz() - message.ParseFromString(payload.value) - return message - elif payload.format == UPayloadFormat.UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY: - any_message = Any() - any_message.ParseFromString(payload.value) - message = clazz() - any_message.Unpack(message) - return message - else: - return None - except Exception: - return None diff --git a/uprotocol/transport/ulistener.py b/uprotocol/transport/ulistener.py index be05388..cc188d8 100644 --- a/uprotocol/transport/ulistener.py +++ b/uprotocol/transport/ulistener.py @@ -1,40 +1,30 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ from abc import ABC, abstractmethod -from uprotocol.proto.umessage_pb2 import UMessage +from uprotocol.v1.umessage_pb2 import UMessage class UListener(ABC): - """ - For any implementation that defines some kind of callback or function that will be called to handle incoming + """For any implementation that defines some kind of callback or function that will be called to handle incoming messages. """ @abstractmethod def on_receive(self, umsg: UMessage) -> None: - """ - Method called to handle/process messages.

+ """Method called to handle/process messages.

@param umsg: UMessage to be sent. """ pass diff --git a/uprotocol/transport/utransport.py b/uprotocol/transport/utransport.py index 8536c46..3587004 100644 --- a/uprotocol/transport/utransport.py +++ b/uprotocol/transport/utransport.py @@ -1,56 +1,53 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ from abc import ABC, abstractmethod +from typing import Optional -from uprotocol.proto.umessage_pb2 import UMessage -from uprotocol.proto.uri_pb2 import UUri -from uprotocol.proto.ustatus_pb2 import UStatus from uprotocol.transport.ulistener import UListener +from uprotocol.v1.umessage_pb2 import UMessage +from uprotocol.v1.uri_pb2 import UUri +from uprotocol.v1.ustatus_pb2 import UStatus class UTransport(ABC): - """ - UTransport is the uP-L1 interface that provides a common API for uE developers to send and receive + """UTransport is the uP-L1 interface that provides a common API for uE developers to send and receive messages.
UTransport implementations contain the details for connecting to the underlying transport technology and sending UMessage using the configured technology.
For more information please refer to link """ + TRANSPORT_NULL_ERROR = "Transport cannot be null" + @abstractmethod def send(self, message: UMessage) -> UStatus: - """ - Send a message (in parts) over the transport. + """Send a message (in parts) over the transport. @param message the UMessage to be sent. @return Returns UStatus with UCode set to the status code (successful or failure). """ pass @abstractmethod - def register_listener(self, topic: UUri, listener: UListener) -> UStatus: - """ - Register UListener for UUri topic to be called when a message is received. - @param topic UUri to listen for messages from. - @param listener The UListener that will be execute when the message is + def register_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener: UListener) -> UStatus: + """Register UListener for UUri source and sink filters to be called when + a message is received. + + @param source_filter The UAttributes source address pattern that the + message to receive needs to match. + @param sink_filter The UAttributes sink address pattern that the + message to receive needs to match or None to match messages that do not contain any sink address. + @param listener The UListener that will execute when the message is received on the given UUri. @return Returns UStatus with UCode.OK if the listener is registered correctly, otherwise it returns with the appropriate failure. @@ -58,14 +55,25 @@ def register_listener(self, topic: UUri, listener: UListener) -> UStatus: pass @abstractmethod - def unregister_listener(self, topic: UUri, listener: UListener) -> UStatus: - """ - Unregister UListener for UUri topic. Messages arriving on this topic will - no longer be processed by this listener. - @param topic UUri to the listener was registered for. + def unregister_listener(self, source_filter: UUri, sink_filter: Optional[UUri], listener: UListener) -> UStatus: + """Unregister UListener for UUri source and sink filters. Messages + arriving at this topic will no longer be processed by this listener. + + @param source_filter The UAttributes source address pattern that the + message to receive needs to match. + @param sink_filter The UAttributes sink address pattern that the + message to receive needs to match or None to match messages that do not contain any sink address. @param listener The UListener that will no longer want to be registered to receive messages. @return Returns UStatus with UCode.OK if the listener is unregistered correctly, otherwise it returns with the appropriate failure. """ pass + + @abstractmethod + def get_source(self) -> UUri: + """Get the source URI of the transport. + + @return Returns the source URI of the transport. + """ + pass diff --git a/uprotocol/transport/validate/__init__.py b/uprotocol/transport/validate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_cloudevent/test_datamodel/__init__.py b/uprotocol/transport/validator/__init__.py similarity index 100% rename from tests/test_cloudevent/test_datamodel/__init__.py rename to uprotocol/transport/validator/__init__.py diff --git a/uprotocol/transport/validate/uattributesvalidator.py b/uprotocol/transport/validator/uattributesvalidator.py similarity index 80% rename from uprotocol/transport/validate/uattributesvalidator.py rename to uprotocol/transport/validator/uattributesvalidator.py index f3788f6..c80b14f 100644 --- a/uprotocol/transport/validate/uattributesvalidator.py +++ b/uprotocol/transport/validator/uattributesvalidator.py @@ -1,22 +1,14 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ @@ -24,14 +16,15 @@ from abc import abstractmethod from enum import Enum -from uprotocol.proto.uattributes_pb2 import ( +from uprotocol.uri.validator.urivalidator import UriValidator +from uprotocol.uuid.factory.uuidutils import UUIDUtils +from uprotocol.v1.uattributes_pb2 import ( UAttributes, UMessageType, UPriority, ) -from uprotocol.proto.uri_pb2 import UUri -from uprotocol.uri.validator.urivalidator import UriValidator -from uprotocol.uuid.factory.uuidutils import UUIDUtils +from uprotocol.v1.uri_pb2 import UUri +from uprotocol.v1.uuid_pb2 import UUID from uprotocol.validation.validationresult import ValidationResult @@ -130,25 +123,22 @@ def validate_ttl(attr: UAttributes) -> ValidationResult: """ return ValidationResult.success() - @staticmethod - def validate_sink(attr: UAttributes) -> ValidationResult: + @abstractmethod + def validate_sink(self, attr: UAttributes) -> ValidationResult: """ - Validate the sink UriPart for the default case. If the - UAttributes does not contain a sink then the - ValidationResult - is ok.

+ Validate the sink UriPart. @param attr:UAttributes object containing the sink to validate. @return:Returns a ValidationResult that is success or failed with a failure message. """ - return UriValidator.validate(attr.sink) if attr.HasField("sink") else ValidationResult.success() + pass @staticmethod def validate_priority(attr: UAttributes): """ Validate the priority value to ensure it is one of the known CS values - @param attributes Attributes object containing the + @param attr Attributes object containing the Priority to validate. @return Returns a ValidationResult that is success or failed with a failure message. @@ -178,18 +168,17 @@ def validate_permission_level(attr: UAttributes) -> ValidationResult: @staticmethod def validate_req_id(attr: UAttributes) -> ValidationResult: """ - Validate the correlationId for the default case. If the - UAttributes does not contain a request id then the - ValidationResult is ok.

+ Validate the correlationId for the default case. Only the response message should have a reqid. @param attr:Attributes object containing the request id to validate. @return:Returns a ValidationResult that is success or failed with a failure message. """ - if attr.HasField("reqid") and not UUIDUtils.is_uuid(attr.reqid): - return ValidationResult.failure("Invalid UUID") - else: - return ValidationResult.success() + return ( + ValidationResult.failure("Message should not have a reqid") + if attr.HasField("reqid") + else ValidationResult.success() + ) @staticmethod def validate_id(attr: UAttributes) -> ValidationResult: @@ -239,6 +228,19 @@ def validate_type(self, attributes_value: UAttributes) -> ValidationResult: else (ValidationResult.failure(f"Wrong Attribute Type [{UMessageType.Name(attributes_value.type)}]")) ) + def validate_sink(self, attributes_value: UAttributes) -> ValidationResult: + """ + Validate the sink UriPart for Publish events. Publish should not have a sink. + + @param attributes_value UAttributes object containing the sink to validate. + @return Returns a ValidationResult that is success or failed with a failure message. + """ + return ( + ValidationResult.failure("Sink should not be present") + if attributes_value.HasField("sink") + else ValidationResult.success() + ) + def __str__(self): return "UAttributesValidator.Publish" @@ -275,9 +277,9 @@ def validate_sink(self, attributes_value: UAttributes) -> ValidationResult: failed with a failure message. """ return ( - UriValidator.validate_rpc_method(attributes_value.sink) - if attributes_value.HasField("sink") - else ValidationResult.failure("Missing Sink") + ValidationResult.success() + if UriValidator.is_rpc_method(attributes_value.sink) + else ValidationResult.failure("Invalid Sink Uri") ) def validate_ttl(self, attributes_value: UAttributes) -> ValidationResult: @@ -294,6 +296,19 @@ def validate_ttl(self, attributes_value: UAttributes) -> ValidationResult: return ValidationResult.success() + def validate_priority(self, attributes_value: UAttributes) -> ValidationResult: + """ + Validate the priority value to ensure it is one of the known CS values + + @param attributes_value Attributes object containing the Priority to validate. + @return Returns a {@link ValidationResult} that is success or failed with a failure message. + """ + return ( + ValidationResult.success() + if attributes_value.priority >= UPriority.UPRIORITY_CS4 + else ValidationResult.failure(f"Invalid UPriority [{UPriority.Name(attributes_value.priority)}]") + ) + def __str__(self): return "UAttributesValidator.Request" @@ -331,8 +346,11 @@ def validate_sink(self, attributes_value: UAttributes) -> ValidationResult: """ if not attributes_value.HasField("sink") or attributes_value.sink == UUri(): return ValidationResult.failure("Missing Sink") - result = UriValidator.validate_rpc_response(attributes_value.sink) - return result + return ( + ValidationResult.success() + if UriValidator.is_rpc_response(attributes_value.sink) + else ValidationResult.failure("Invalid Sink Uri") + ) def validate_req_id(self, attributes_value: UAttributes) -> ValidationResult: """ @@ -343,10 +361,24 @@ def validate_req_id(self, attributes_value: UAttributes) -> ValidationResult: @return:Returns a ValidationResult that is success or failed with a failure message. """ + if not attributes_value.HasField("reqid") or attributes_value.reqid == UUID(): + return ValidationResult.failure("Missing correlationId") + if not UUIDUtils.is_uuid(attributes_value.reqid): + return ValidationResult.failure("Invalid correlation UUID") + return ValidationResult.success() + + def validate_priority(self, attributes_value: UAttributes) -> ValidationResult: + """ + Validate the priority value to ensure it is one of the known CS values + + @param attributes_value Attributes object containing the Priority to validate. + @return Returns a ValidationResult that is success or failed with a failure message. + """ + return ( ValidationResult.success() - if attributes_value.reqid and UUIDUtils.is_uuid(attributes_value.reqid) - else ValidationResult.failure("Missing correlationId") + if attributes_value.priority >= UPriority.UPRIORITY_CS4 + else ValidationResult.failure(f"Invalid UPriority [{UPriority.Name(attributes_value.priority)}]") ) def __str__(self): @@ -384,11 +416,13 @@ def validate_sink(self, attributes_value: UAttributes) -> ValidationResult: @return:Returns a ValidationResult that is success or failed with a failure message. """ - if attributes_value is None: - return ValidationResult.failure("UAttributes cannot be null.") if not attributes_value.HasField("sink") or attributes_value.sink == UUri(): return ValidationResult.failure("Missing Sink") - return ValidationResult.success() + return ( + ValidationResult.success() + if UriValidator.is_default_resource_id(attributes_value.sink) + else ValidationResult.failure("Invalid Sink Uri") + ) def __str__(self): return "UAttributesValidator.Notification" diff --git a/uprotocol/uri/factory/uentityfactory.py b/uprotocol/uri/factory/uentityfactory.py deleted file mode 100644 index ceea0cf..0000000 --- a/uprotocol/uri/factory/uentityfactory.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from google.protobuf.descriptor import ServiceDescriptor -from google.protobuf.descriptor_pb2 import ServiceOptions - -from uprotocol.proto.uprotocol_options_pb2 import ( - id, - name, - version_major, - version_minor, -) -from uprotocol.proto.uri_pb2 import UEntity - - -class UEntityFactory: - """ - Factory for creating UEntity objects. - """ - - @staticmethod - def from_proto(service_descriptor: ServiceDescriptor): - if service_descriptor is None: - return UEntity() - - options: ServiceOptions = service_descriptor.GetOptions() - - name_ext: str = options.Extensions[name] - v_major: int = options.Extensions[version_major] - v_minor: int = options.Extensions[version_minor] - id_ext: int = options.Extensions[id] - - uentity = UEntity() - if name_ext is not None: - uentity.name = name_ext - if v_major is not None: - uentity.version_major = v_major - if v_minor is not None: - uentity.version_minor = v_minor - if id_ext is not None: - uentity.id = id_ext - - return uentity diff --git a/uprotocol/uri/factory/uresourcebuilder.py b/uprotocol/uri/factory/uresourcebuilder.py deleted file mode 100644 index 8666847..0000000 --- a/uprotocol/uri/factory/uresourcebuilder.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import re -from typing import Union - -from multimethod import multimethod - -from uprotocol.proto.uri_pb2 import UResource - - -class UResourceBuilder: - MAX_RPC_ID = 1000 - - # The minimum topic ID, below this value are methods. - MIN_TOPIC_ID = 0x8000 - - @staticmethod - def for_rpc_response(): - return UResource(name="rpc", instance="response", id=0) - - @multimethod - def for_rpc_request(method: Union[str, None], id: int = None): # noqa: N805 - uresource = UResource(name="rpc") - if method is not None: - uresource.instance = method - if id is not None: - uresource.id = id - - return uresource - - @multimethod - def for_rpc_request(id: int): # noqa: N805 - return UResourceBuilder.for_rpc_request(None, id) # noqa: N805 - - @staticmethod - def from_id(id): - if id is None: - raise ValueError("id cannot be None") - - return ( - UResourceBuilder.for_rpc_response() - if id == 0 - else (UResourceBuilder.for_rpc_request(id) if id < UResourceBuilder.MIN_TOPIC_ID else UResource(id=id)) - ) - - @staticmethod - def from_uservice_topic(topic): - """ - Build a UResource from a UServiceTopic that is defined in protos and - available from generated stubs. - @param topic The UServiceTopic to build the UResource from. - @return Returns a UResource for an RPC request. - """ - if topic is None: - raise ValueError("topic cannot be None.") - name_and_instance_parts = re.split(r"[\\.]", topic.name) - resource_name = name_and_instance_parts[0] - resource_instance = None if len(name_and_instance_parts) <= 1 else name_and_instance_parts[1] - - resource = UResource(name=resource_name, id=topic.id, message=topic.message) - if resource_instance is not None: - resource.instance = resource_instance - - return resource diff --git a/uprotocol/uri/factory/uri_factory.py b/uprotocol/uri/factory/uri_factory.py new file mode 100644 index 0000000..ed8f6c6 --- /dev/null +++ b/uprotocol/uri/factory/uri_factory.py @@ -0,0 +1,72 @@ +""" +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation + +See the NOTICE file(s) distributed with this work for additional +information regarding copyright ownership. + +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at + + http://www.apache.org/licenses/LICENSE-2.0 + +SPDX-License-Identifier: Apache-2.0 +""" + +from typing import Optional + +from google.protobuf.descriptor import ServiceDescriptor +from google.protobuf.descriptor_pb2 import ServiceOptions + +from uprotocol.uoptions_pb2 import service_id, service_version_major +from uprotocol.v1.uri_pb2 import UUri + + +class UriFactory: + """ + URI Factory that builds URIs from protos + """ + + @staticmethod + def from_proto( + service_descriptor: Optional[ServiceDescriptor], resource_id: int, authority_name: Optional[str] + ) -> UUri: + """ + Builds a URI for a protobuf generated code Service Descriptor. + @param service_descriptor TThe protobuf generated code Service Descriptor. + @param resource_id The resource id. + @param authority_name The authority name. + @return Returns a URI for a protobuf generated code + Service Descriptor. + """ + if service_descriptor is None: + return UUri() + + options: ServiceOptions = service_descriptor.GetOptions() + + version_major: int = options.Extensions[service_version_major] + id_val: int = options.Extensions[service_id] + + uuri = UUri() + if version_major is not None: + uuri.ue_version_major = version_major + if resource_id is not None: + uuri.resource_id = resource_id + if id_val is not None: + uuri.ue_id = id_val + if authority_name is not None: + uuri.authority_name = authority_name + + return uuri + + @staticmethod + def any_func() -> UUri: + """ + Returns a URI with all fields set to 0. + @return Returns a URI with all fields set to 0. + """ + return UUri( + authority_name="*", + ue_id=0xFFFF, + ue_version_major=0xFF, + resource_id=0xFFFF, + ) diff --git a/uprotocol/uri/serializer/ipaddress.py b/uprotocol/uri/serializer/ipaddress.py deleted file mode 100644 index f58747e..0000000 --- a/uprotocol/uri/serializer/ipaddress.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2024 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import socket - - -class IpAddress: - @staticmethod - def to_bytes(ip_address): - if ip_address is None or ip_address.strip() == "": - return b"" - - if IpAddress.is_valid_ipv4_address(ip_address): - return IpAddress.convert_ipv4_to_byte_array(ip_address) - elif IpAddress.is_valid_ipv6_address(ip_address): - return IpAddress.convert_ipv6_to_byte_array(ip_address) - else: - return b"" - - @staticmethod - def is_valid(ip_address): - return ( - (ip_address is not None) - and not ip_address.strip() == "" - and (IpAddress.is_valid_ipv4_address(ip_address) or IpAddress.is_valid_ipv6_address(ip_address)) - ) - - @staticmethod - def is_valid_ipv4_address(ip_address): - try: - return len(ip_address.split(".")) == 4 and socket.inet_pton(socket.AF_INET, ip_address) - except OSError: - return False - - @staticmethod - def convert_ipv4_to_byte_array(ip_address): - return bytes(socket.inet_pton(socket.AF_INET, ip_address)) - - @staticmethod - def is_valid_ipv6_address(ip_address): - try: - return len(ip_address.split(":")) <= 8 and socket.inet_pton(socket.AF_INET6, ip_address) - except OSError: - return False - - @staticmethod - def convert_ipv6_to_byte_array(ip_address): - return bytes(socket.inet_pton(socket.AF_INET6, ip_address)) diff --git a/uprotocol/uri/serializer/longuriserializer.py b/uprotocol/uri/serializer/longuriserializer.py deleted file mode 100644 index 21172e5..0000000 --- a/uprotocol/uri/serializer/longuriserializer.py +++ /dev/null @@ -1,197 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import re - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.serializer.uriserializer import UriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator - - -class LongUriSerializer(UriSerializer): - """ - UUri Serializer that serializes a UUri to a long format string per - - https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc - """ - - def serialize(self, uri: UUri) -> str: - """ - Support for serializing UUri objects into their String format.

- @param uri: UUri object to be serialized to the String format. - @return:Returns the String format of the supplied UUri that can be - used as a sink or a source in a - uProtocol publish communication. - """ - if uri is None or UriValidator.is_empty(uri): - return "" - - sb = [] - - if uri.HasField("authority") and uri.authority.HasField("name") and not uri.authority.name.strip() == "": - sb.append("//") - sb.append(uri.authority.name) - - sb.append("/") - - sb.append(self.build_software_entity_part_of_uri(uri.entity)) - sb.append(self.build_resource_part_of_uri(uri)) - - return re.sub("/+$", "", "".join(sb)) - - @staticmethod - def build_resource_part_of_uri(uuri: UUri) -> str: - if not uuri.HasField("resource"): - return "" - u_resource = uuri.resource - - sb = "/" + u_resource.name - - if u_resource.instance is not None and not u_resource.instance.strip() == "": - sb += "." + u_resource.instance - if u_resource.message is not None and not u_resource.message.strip() == "": - sb += "#" + u_resource.message - - return sb - - @staticmethod - def build_software_entity_part_of_uri(entity: UEntity) -> str: - """ - Create the service part of the uProtocol URI from an - UEntity object.

- @param entity:Software Entity representing a service - or an application. - @return: Returns the String representation of the UEntity - in the uProtocol URI. - """ - sb = str(entity.name.strip()) - sb += "/" - - if entity.version_major > 0: - sb += str(entity.version_major) - - return sb - - def deserialize(self, u_protocol_uri: str) -> UUri: - """ - Deserialize a String into a UUri object.

- @param u_protocol_uri:A long format uProtocol URI. - @return:Returns an UUri data object. - """ - if u_protocol_uri is None or u_protocol_uri.strip() == "": - return UUri() - uri = ( - u_protocol_uri[u_protocol_uri.index(":") + 1 :] - if ":" in u_protocol_uri - else u_protocol_uri.replace("\\", "/") - ) - - is_local = not uri.startswith("//") - uri_parts = LongUriSerializer.remove_empty(uri.split("/")) - number_of_parts_in_uri = len(uri_parts) - - if number_of_parts_in_uri == 0 or number_of_parts_in_uri == 1: - return UUri() - - use_name = "" - use_version = "" - u_resource = None - u_authority = None - - if is_local: - use_name = uri_parts[1] - if number_of_parts_in_uri > 2: - use_version = uri_parts[2] - if number_of_parts_in_uri > 3: - u_resource = self.parse_from_string(uri_parts[3]) - - else: - if uri_parts[2].strip() == "": - return UUri() - u_authority = UAuthority(name=uri_parts[2]) - if len(uri_parts) > 3: - use_name = uri_parts[3] - if number_of_parts_in_uri > 4: - use_version = uri_parts[4] - if number_of_parts_in_uri > 5: - u_resource = self.parse_from_string(uri_parts[5]) - else: - return UUri(authority=u_authority) - - use_version_int = None - try: - if use_version.strip() != "": - use_version_int = int(use_version) - except ValueError: - return UUri() - - u_entity_builder = UEntity(name=use_name) - if use_version_int is not None: - u_entity_builder.version_major = use_version_int - - new_uri = UUri(entity=u_entity_builder) - if u_authority is not None: - new_uri.authority.CopyFrom(u_authority) - - if u_resource is not None: - new_uri.resource.CopyFrom(u_resource) - - return new_uri - - @staticmethod - def parse_from_string(resource_string: str) -> UResource: - """ - Static builder method for creating a UResource using a string - that contains name + instance + message.

- @param resource_string:String that contains the UResource information. - @return:Returns a UResource object. - """ - if resource_string is None or resource_string.strip() == "": - raise ValueError("Resource must have a command name.") - - parts = LongUriSerializer.remove_empty(resource_string.split("#")) - name_and_instance = parts[0] - - name_and_instance_parts = LongUriSerializer.remove_empty(name_and_instance.split(".")) - resource_name = name_and_instance_parts[0] - resource_instance = name_and_instance_parts[1] if len(name_and_instance_parts) > 1 else None - resource_message = parts[1] if len(parts) > 1 else None - - u_resource = UResource(name=resource_name) - if resource_instance is not None: - u_resource.instance = resource_instance - if resource_message is not None: - u_resource.message = resource_message - if "rpc" in resource_name and resource_instance is not None and "response" in resource_instance: - u_resource.id = 0 - - return u_resource - - @staticmethod - def remove_empty(parts): - result = parts[:] - - # Iterate through the list in reverse and remove empty strings - while result and result[-1] == "": - result.pop() - - return result diff --git a/uprotocol/uri/serializer/microuriserializer.py b/uprotocol/uri/serializer/microuriserializer.py deleted file mode 100644 index f083188..0000000 --- a/uprotocol/uri/serializer/microuriserializer.py +++ /dev/null @@ -1,186 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from enum import Enum - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.uriserializer import UriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator - - -class AddressType(Enum): - """ - The type of address used for Micro URI. - """ - - LOCAL = 0 - IPv4 = 1 - IPv6 = 2 - ID = 3 - - @classmethod - def from_value(cls, value): - for addr_type in cls: - if addr_type.value == value: - return addr_type - return None # Return None if no matching enum value is found - - -def keep_8_least_significant_bits(n: int): - # by default, python is little endian - return n & 0xFF - - -class MicroUriSerializer(UriSerializer): - """ - UUri Serializer that serializes a UUri to a byte[] (micro format) per - https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc - """ - - LOCAL_MICRO_URI_LENGTH = 8 - IPV4_MICRO_URI_LENGTH = 12 - IPV6_MICRO_URI_LENGTH = 24 - UP_VERSION = 0x1 - MAX_ID_LENGTH = 255 - - def serialize(self, uri: UUri) -> bytes: - """ - Serialize a UUri into a byte[] following the Micro-URI specifications - for python, bits are unsigned integers [0, 255) - @param uri:The UUri data object. - @return:Returns a byte[] representing the serialized UUri. - """ - - if uri is None or UriValidator.is_empty(uri) or not UriValidator.is_micro_form(uri): - return bytearray() - - maybe_ue_id: int = uri.entity.id - maybe_uresource_id: int = uri.resource.id - - byte_arr: bytearray = bytearray() - - byte_arr.append(self.UP_VERSION) - - address_type: AddressType = AddressType.LOCAL - if uri.authority.HasField("ip"): - length: int = len(uri.authority.ip) - if length == 4: - address_type = AddressType.IPv4 - elif length == 16: - address_type = AddressType.IPv6 - else: - return bytearray() - elif uri.authority.HasField("id"): - address_type = AddressType.ID - - byte_arr.append(address_type.value) - - if maybe_uresource_id > 0xFFFF or maybe_ue_id > 0xFFFF: - return bytearray() - - # URESOURCE_ID - byte_arr.append(keep_8_least_significant_bits(maybe_uresource_id >> 8)) - byte_arr.append(keep_8_least_significant_bits(maybe_uresource_id)) - - # UENTITY_ID - byte_arr.append(keep_8_least_significant_bits(maybe_ue_id >> 8)) - byte_arr.append(keep_8_least_significant_bits(maybe_ue_id)) - - # UE_VERSION - unsigned_value: int = uri.entity.version_major - byte_arr.append(keep_8_least_significant_bits(unsigned_value)) - - # UNUSED - byte_arr.append(0x0) - - # Populating the UAuthority - if address_type != AddressType.LOCAL: - # Write the ID length if the type is ID - if address_type == AddressType.ID: - # If the ID length is greater than the maximum allowed, return an empty byte[] - if len(uri.authority.id) > self.MAX_ID_LENGTH: - return bytearray() - byte_arr.append(keep_8_least_significant_bits(len(uri.authority.id))) - try: - if uri.authority.HasField("ip"): - byte_arr.extend(bytearray(uri.authority.ip)) - elif uri.authority.HasField("id"): - byte_arr.extend(bytearray(uri.authority.id)) - except Exception: - return bytearray() - return byte_arr - - def deserialize(self, micro_uri: bytes) -> UUri: - """ - Deserialize a byte[] into a UUri object.

- @param micro_uri:A byte[] uProtocol micro URI. - @return:Returns an UUri data object from the serialized - format of a microUri. - """ - if micro_uri is None or len(micro_uri) < self.LOCAL_MICRO_URI_LENGTH: - return UUri() - - if micro_uri[0] != self.UP_VERSION: - return UUri() - - u_resource_id = ((micro_uri[2] & 0xFF) << 8) | (micro_uri[3] & 0xFF) - addresstype = AddressType.from_value(micro_uri[1]) - - # Validate Type is found - if addresstype is None: - return UUri() - - # Validate that the micro_uri is the correct length for the type - address_type = addresstype - if address_type == AddressType.LOCAL and len(micro_uri) != self.LOCAL_MICRO_URI_LENGTH: - return UUri() - elif address_type == AddressType.IPv4 and len(micro_uri) != self.IPV4_MICRO_URI_LENGTH: - return UUri() - elif address_type == AddressType.IPv6 and len(micro_uri) != self.IPV6_MICRO_URI_LENGTH: - return UUri() - - # UENTITY_ID - ue_id = ((micro_uri[4] & 0xFF) << 8) | (micro_uri[5] & 0xFF) - - # UE_VERSION - ui_version = micro_uri[6] - - u_authority = None - if address_type in (AddressType.IPv4, AddressType.IPv6): - length = 4 if address_type == AddressType.IPv4 else 16 - data = micro_uri[8 : 8 + length] - u_authority = UAuthority(ip=bytes(data)) - elif address_type == AddressType.ID: - length = micro_uri[8] - u_authority = UAuthority(id=bytes(micro_uri[9 : 9 + length])) - - uri = UUri( - entity=UEntity(id=ue_id, version_major=ui_version), - resource=UResourceBuilder.from_id(u_resource_id), - ) - - if u_authority is not None: - uri.authority.CopyFrom(u_authority) - - return uri diff --git a/uprotocol/uri/serializer/shorturiserializer.py b/uprotocol/uri/serializer/shorturiserializer.py deleted file mode 100644 index 33728fc..0000000 --- a/uprotocol/uri/serializer/shorturiserializer.py +++ /dev/null @@ -1,209 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import re -import socket -from typing import List - -from uprotocol.proto.uri_pb2 import UAuthority, UEntity, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.uri.serializer.ipaddress import IpAddress -from uprotocol.uri.serializer.uriserializer import UriSerializer -from uprotocol.uri.validator.urivalidator import UriValidator - - -def convert_packed_ipaddr_to_string(packed_ipaddr: bytes): - for address_type in [socket.AF_INET, socket.AF_INET6]: - try: - """ - socket.inet_ntop(): - Convert a packed IP address (a bytes-like object - of some number of bytes) - to its standard, family-specific string representation - (for example, '7.10.0.5' or '5aef:2b::8') - """ - return socket.inet_ntop(address_type, packed_ipaddr) - except ValueError: - pass - - raise Exception( - "Could not find correct address family to unpack ip address", - "from bytes to str", - ) - - -class ShortUriSerializer(UriSerializer): - """ - UUri Serializer that serializes a UUri to a Short format string. - """ - - def serialize(self, uri: UUri) -> str: - if uri is None or UriValidator.is_empty(uri): - return "" - string_builder: List[str] = [] - - if uri.HasField("authority"): - authority: UAuthority = uri.authority - if uri.authority.HasField("ip"): - try: - string_builder.append("//") - string_builder.append(convert_packed_ipaddr_to_string(authority.ip)) - except Exception: - return "" - elif uri.authority.HasField("id"): - string_builder.append("//") - string_builder.append(authority.id.decode("utf-8")) - else: - return "" - - string_builder.append("/") - string_builder.append(self.build_software_entity_part_of_uri(uri.entity)) - string_builder.append(self.build_resource_part_of_uri(uri)) - - return re.sub("/+$", "", "".join(string_builder)) - - @staticmethod - def build_resource_part_of_uri(uri: UUri): - if not uri.HasField("resource"): - return "" - resource: UResource = uri.resource - - string_builder: List[str] = ["/"] - string_builder.append(str(resource.id)) - - return "".join(string_builder) - - @staticmethod - def build_software_entity_part_of_uri(entity): - """ - Create the service part of the uProtocol URI from a - software entity object. - @param use Software Entity representing a service or an application. - """ - string_builder: List[str] = [] - string_builder.append(str(entity.id)) - string_builder.append("/") - if entity.version_major > 0: - string_builder.append(str(entity.version_major)) - - return "".join(string_builder) - - def deserialize(self, uprotocol_uri: str) -> UUri: - """ - Deserialize a String into a UUri object. - @param uProtocolUri A short format uProtocol URI. - @return Returns an UUri data object. - """ - if uprotocol_uri is None or uprotocol_uri.strip() == "": - return UUri() - - uri = ( - uprotocol_uri[uprotocol_uri.index(":") + 1 :] if ":" in uprotocol_uri else uprotocol_uri.replace("\\", "/") - ) - - is_local = not uri.startswith("//") - - uri_parts = uri.split("/") - number_of_parts_in_uri = len(uri_parts) - - if number_of_parts_in_uri < 2: - return UUri() - - ue_id = "" - ue_version = "" - - resource = None - authority = None - - if is_local: - ue_id = uri_parts[1] - if number_of_parts_in_uri > 2: - ue_version = uri_parts[2] - - if number_of_parts_in_uri > 3: - resource = ShortUriSerializer.parse_from_string(uri_parts[3]) - - if number_of_parts_in_uri > 4: - return UUri() - else: - if uri_parts[2].strip() == "": - return UUri() - if IpAddress.is_valid(uri_parts[2]): - authority = UAuthority(ip=IpAddress.to_bytes(uri_parts[2])) - else: - authority = UAuthority(id=bytes(uri_parts[2], "utf-8")) - - if len(uri_parts) > 3: - ue_id = uri_parts[3] - if number_of_parts_in_uri > 4: - ue_version = uri_parts[4] - if number_of_parts_in_uri > 5: - resource = ShortUriSerializer.parse_from_string(uri_parts[5]) - if number_of_parts_in_uri > 6: - return UUri() - else: - return UUri(authority=authority) - - ue_version_int = None - ue_id_int = None - - try: - if ue_version.strip() != "": - ue_version_int = int(ue_version) - if ue_id.strip() != "": - ue_id_int = int(ue_id) - except Exception: - return UUri() - - entity = UEntity() - new_uri = UUri() - if ue_id_int is not None: - entity.id = ue_id_int - new_uri.entity.CopyFrom(entity) - if ue_version_int is not None: - entity.version_major = ue_version_int - new_uri.entity.CopyFrom(entity) - - if authority is not None: - new_uri.authority.CopyFrom(authority) - - if resource is not None: - new_uri.resource.CopyFrom(resource) - - return new_uri - - @staticmethod - def parse_from_string(resource_string): - """ - Static factory method for creating a UResource using a string value - @param resource_string String that contains the UResource id. - @return Returns a UResource object. - """ - if resource_string is None: - raise ValueError(" Resource must have a command name") - id = None - try: - id = int(resource_string) - except Exception: - return UResource() - - return UResourceBuilder.from_id(id) diff --git a/uprotocol/uri/serializer/uriserializer.py b/uprotocol/uri/serializer/uriserializer.py index b7150e2..09e47b9 100644 --- a/uprotocol/uri/serializer/uriserializer.py +++ b/uprotocol/uri/serializer/uriserializer.py @@ -1,52 +1,133 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ -from abc import ABC, abstractmethod -from re import T +import re +from typing import Optional -from uprotocol.proto.uri_pb2 import UUri +from uprotocol.uri.validator.urivalidator import UriValidator +from uprotocol.v1.uri_pb2 import UUri -class UriSerializer(ABC): +class UriSerializer: """ UUris are used in transport layers and hence need to be serialized.
Each transport supports different serialization formats. """ - @abstractmethod - def deserialize(self, uri: T) -> UUri: + # The wildcard id for a field. + WILDCARD_ID = 0xFFFF + + @staticmethod + def serialize(uri: Optional[UUri]) -> str: """ - Deserialize from the format to a UUri.

- @param uri:serialized UUri. - @return:Returns a UUri object from the serialized format from the wire. + Support for serializing UUri objects into their String format. + @param uri: UUri object to be serialized to the String format. + @return:Returns the String format of the supplied UUri that can be + used as a sink or a source in a + uProtocol publish communication. """ - pass + if uri is None or UriValidator.is_empty(uri): + return "" + + sb = [] + + if uri.authority_name.strip() != "": + sb.append("//") + sb.append(uri.authority_name) + + sb.append("/") + sb.append(hex(uri.ue_id)[2:].upper()) + sb.append("/") + sb.append(hex(uri.ue_version_major)[2:].upper()) + sb.append("/") + sb.append(hex(uri.resource_id)[2:].upper()) + return re.sub("/+$", "", "".join(sb)) + + @staticmethod + def _build_local_uri(uri_parts, number_of_parts_in_uri): + ue_version = None + ur_id = None + ue_id = int(uri_parts[1], 16) + if number_of_parts_in_uri > 2: + ue_version = int(uri_parts[2], 16) + if number_of_parts_in_uri > 3: + ur_id = int(uri_parts[3], 16) + + return ue_id, ue_version, ur_id + + @staticmethod + def _build_remote_uri(uri_parts, number_of_parts_in_uri): + ue_id = None + ue_version = None + ur_id = None + auth_name = uri_parts[2] + if len(uri_parts) > 3: + ue_id = int(uri_parts[3], 16) + if number_of_parts_in_uri > 4: + ue_version = int(uri_parts[4], 16) + if number_of_parts_in_uri > 5: + ur_id = int(uri_parts[5], 16) + + return auth_name, ue_id, ue_version, ur_id - @abstractmethod - def serialize(self, uri: UUri) -> T: + @staticmethod + def _build_uri(is_local, uri_parts, number_of_parts_in_uri): + auth_name = None + + if is_local: + ue_id, ue_version, ur_id = UriSerializer._build_local_uri(uri_parts, number_of_parts_in_uri) + else: + if uri_parts[2].strip() == "": + return UUri() + auth_name, ue_id, ue_version, ur_id = UriSerializer._build_remote_uri(uri_parts, number_of_parts_in_uri) + return UUri( + authority_name=auth_name, + ue_id=ue_id, + ue_version_major=ue_version, + resource_id=ur_id, + ) + + @staticmethod + def deserialize(uri: Optional[str]) -> UUri: """ - Serialize from a UUri to a specific serialization format.

- @param uri:UUri object to be serialized to the format T. - @return:Returns the UUri in the transport serialized format. + Deserialize from the format to a UUri. + @param uri:serialized UUri. + @return:Returns a UUri object from the serialized format from the wire. """ - pass + if uri is None or uri.strip() == "": + return UUri() + uri = uri[uri.index(":") + 1 :] if ":" in uri else uri.replace("\\", "/") + + is_local = not uri.startswith("//") + uri_parts = uri.split("/") + number_of_parts_in_uri = len(uri_parts) + + if number_of_parts_in_uri in [0, 1]: + return UUri() + + try: + new_uri = UriSerializer._build_uri(is_local, uri_parts, number_of_parts_in_uri) + except ValueError: + return UUri() + + # Ensure that the major version is less than the wildcard + if new_uri.ue_version_major > UriValidator.MAJOR_VERSION_WILDCARD: + return UUri() + + # Ensure that the resource id is less than the wildcard + if new_uri.resource_id > UriSerializer.WILDCARD_ID: + return UUri() + + return new_uri diff --git a/uprotocol/uri/validator/urivalidator.py b/uprotocol/uri/validator/urivalidator.py index eb0e1bb..95e4bb0 100644 --- a/uprotocol/uri/validator/urivalidator.py +++ b/uprotocol/uri/validator/urivalidator.py @@ -1,30 +1,20 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ -from multimethod import multimethod +from typing import Optional -from uprotocol.proto.uri_pb2 import UAuthority, UResource, UUri -from uprotocol.uri.factory.uresourcebuilder import UResourceBuilder -from uprotocol.validation.validationresult import ValidationResult +from uprotocol.v1.uri_pb2 import UUri class UriValidator: @@ -32,65 +22,14 @@ class UriValidator: Class for validating Uris. """ - @staticmethod - def validate(uri: UUri) -> ValidationResult: - """ - Validate a UUri to ensure that it has at least a name - for the uEntity.

- @param uri:UUri to validate. - @return:Returns UStatus containing a success or a failure - with the error message. - """ - if UriValidator.is_empty(uri): - return ValidationResult.failure("Uri is empty.") - - if uri.HasField("authority") and not UriValidator.is_remote(uri.authority): - return ValidationResult.failure("Uri is remote missing u_authority.") + # The minimum publish/notification topic id for a URI. + MIN_TOPIC_ID = 0x8000 - if uri.entity.name.strip() == "": - return ValidationResult.failure("Uri is missing uSoftware Entity name.") + # The Default resource id. + DEFAULT_RESOURCE_ID = 0 - return ValidationResult.success() - - @staticmethod - def validate_rpc_method(uri: UUri) -> ValidationResult: - """ - Validate a UUri that is meant to be used as an RPC method URI. - Used in Request sink values and Response - source values.

- @param uri:UUri to validate. - @return:Returns UStatus containing a success or a failure - with the error message. - """ - status = UriValidator.validate(uri) - if status.is_failure(): - return status - - if not UriValidator.is_rpc_method(uri): - return ValidationResult.failure( - "Invalid RPC method uri. Uri should be the method to be called, or method from response." - ) - - return ValidationResult.success() - - @staticmethod - def validate_rpc_response(uri: UUri) -> ValidationResult: - """ - Validate a UUri that is meant to be used as an RPC response URI. - Used in Request source values and - Response sink values.

- @param uri:UUri to validate. - @return:Returns UStatus containing a success or a failure with - the error message. - """ - status = UriValidator.validate(uri) - if status.is_failure(): - return status - - if not UriValidator.is_rpc_response(uri): - return ValidationResult.failure("Invalid RPC response type.") - - return ValidationResult.success() + # The major version wildcard. + MAJOR_VERSION_WILDCARD = 0xFF @staticmethod def is_empty(uri: UUri) -> bool: @@ -103,8 +42,8 @@ def is_empty(uri: UUri) -> bool: """ return uri is None or uri == UUri() - @multimethod - def is_rpc_method(uri: UUri) -> bool: # noqa: N805 + @staticmethod + def is_rpc_method(uri: Optional[UUri]) -> bool: """ Returns true if URI is of type RPC. A UUri is of type RPC if it contains the word rpc in the resource name @@ -113,133 +52,32 @@ def is_rpc_method(uri: UUri) -> bool: # noqa: N805 @return: Returns true if this resource specifies an RPC method call or RPC response. """ - return uri is not None and UriValidator.is_rpc_method(uri.resource) - - @multimethod - def is_rpc_method(uri: None) -> bool: # noqa: N805 - """ - Returns false if input is None. - @param uri: None - @return Returns false. - """ - return False - - @multimethod - def is_rpc_method(resource: UResource) -> bool: # noqa: N805 - """ - Returns true if Uresource is of type RPC. - @param resource: UResource to check if it is of type RPC method - @return Returns true if URI is of type RPC. - """ return ( - resource is not None - and resource.name == "rpc" - and ( - resource.HasField("instance") - and resource.instance.strip() != "" - or (resource.HasField("id") and resource.id < UResourceBuilder.MIN_TOPIC_ID) - ) + uri is not None + and uri.resource_id != UriValidator.DEFAULT_RESOURCE_ID + and uri.resource_id < UriValidator.MIN_TOPIC_ID ) - @staticmethod - def is_resolved(uri: UUri) -> bool: - return UriValidator.is_long_form(uri) and UriValidator.is_micro_form(uri) - @staticmethod def is_rpc_response(uri: UUri) -> bool: - return uri is not None and uri.resource == UResourceBuilder.for_rpc_response() - - @multimethod - def is_micro_form(uri: UUri) -> bool: # noqa: N805 - """ - Determines if this UUri can be serialized into - a micro form UUri.

- @param uuri: An UUri proto message object - @return:Returns true if this UUri can be serialized into - a micro form UUri. - """ - - return ( - uri is not None - and not UriValidator.is_empty(uri) - and uri.entity.HasField("id") - and uri.resource.HasField("id") - and UriValidator.is_micro_form(uri.authority) - ) - - @multimethod - def is_micro_form(authority: UAuthority) -> bool: # noqa: N805 - """ - check if UAuthority can be represented in micro format. - Micro UAuthorities are local or ones - that contain IP address or IDs. - @param authority UAuthority to check - @return Returns true if UAuthority can be represented in micro format - """ - - return UriValidator.is_local(authority) or (authority.HasField("ip") or (authority.HasField("id"))) - - @multimethod - def is_long_form(uri: UUri) -> bool: # noqa: N805 - """ - Determines if this UUri can be serialized into - a long form UUri.

- @param uuri: An UUri proto message object - @return:Returns true if this UUri can be serialized into - a long form UUri. - """ - - return ( - uri is not None - and not UriValidator.is_empty(uri) - and UriValidator.is_long_form(uri.authority) - and uri.entity.name.strip() != "" - and uri.resource.name.strip() != "" - ) - - @multimethod - def is_long_form(authority: UAuthority) -> bool: # noqa: N805 """ - Returns true if UAuthority is local contains names so - that it can be serialized into long format. - @param authority UAuthority to check - @return Returns true if URI contains names so that - it can be serialized into long format. + @return Returns true if URI is of type RPC response. """ - return authority is not None and ( - UriValidator.is_local(authority) or (authority.HasField("name") and authority.name.strip() != "") - ) + return UriValidator.is_default_resource_id(uri) @staticmethod - def is_local(authority: UAuthority) -> bool: + def is_default_resource_id(uri: UUri) -> bool: """ - Returns true if UAuthority is local meaning there - is no name/ip/id set. - @param authority UAuthority to check if it is local or not - @return Returns true if UAuthority is local meaning the - Authority is not populated with name, ip and id + Returns true if URI is of type RPC response. """ - return (authority is not None) and (authority == UAuthority()) + return not UriValidator.is_empty(uri) and uri.resource_id == UriValidator.DEFAULT_RESOURCE_ID @staticmethod - def is_remote(authority: UAuthority) -> bool: + def is_topic(uri: UUri) -> bool: """ - Returns true if UAuthority is remote meaning - the name and/or ip/id is populated. - @param authority UAuthority to check if it is remote or not - @return Returns true if UAuthority is remote meaning - the name and/or ip/id is populated. - """ - return (authority is not None) and (not authority == UAuthority()) + Returns true if URI is of type Topic used for publish and notifications. - @staticmethod - def is_short_form(uri: UUri) -> bool: - """ - Return True of the UUri is Short form. A UUri that - is micro form (contains numbers) can - also be a Short form Uri. - @param uri {@link UUri} to check - @return Returns true if contains ids can can - be serialized to short format. + @param uri {@link UUri} to check if it is of type Topic + @return Returns true if URI is of type Topic. """ - return UriValidator.is_micro_form(uri) + return not UriValidator.is_empty(uri) and uri.resource_id >= UriValidator.MIN_TOPIC_ID diff --git a/uprotocol/uuid/README.adoc b/uprotocol/uuid/README.adoc index a598900..66cbf7c 100644 --- a/uprotocol/uuid/README.adoc +++ b/uprotocol/uuid/README.adoc @@ -14,7 +14,7 @@ Implementation of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/ version = UUIDUtils.getVersion(uuid) time = UUIDUtils.getTime(uuid) bytes_uuid = MicroUuidSerializer.instance().serialize(uuid) - str_uuid = LongUuidSerializer.instance().serialize(uuid) + str_uuid = UuidSerializer.serialize(uuid) assertTrue(UUIDUtils.isUProtocol(uuid)) assertTrue(UUIDUtils.isuuid(uuid)) @@ -25,7 +25,7 @@ Implementation of https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/ assertFalse(str_uuid.isspace()) uuid1 = MicroUuidSerializer.instance().deserialize(bytes_data) - uuid2 = LongUuidSerializer.instance().deserialize(uuid_string) + uuid2 = UuidSerializer.deserialize(uuid_string) assertNotEqual(uuid1, UUID()) assertNotEqual(uuid2, UUID()) assertEqual(uuid, uuid1) diff --git a/uprotocol/uuid/factory/uuidfactory.py b/uprotocol/uuid/factory/uuidfactory.py index db61f68..bac99bf 100644 --- a/uprotocol/uuid/factory/uuidfactory.py +++ b/uprotocol/uuid/factory/uuidfactory.py @@ -1,31 +1,23 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ import random from datetime import datetime -from uprotocol.proto.uuid_pb2 import UUID from uprotocol.uuid.factory import uuid6 from uprotocol.uuid.factory.uuidutils import UUIDUtils +from uprotocol.v1.uuid_pb2 import UUID class UUIDFactory: diff --git a/uprotocol/uuid/factory/uuidutils.py b/uprotocol/uuid/factory/uuidutils.py index 97370f5..729282e 100644 --- a/uprotocol/uuid/factory/uuidutils.py +++ b/uprotocol/uuid/factory/uuidutils.py @@ -1,22 +1,14 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ @@ -25,11 +17,8 @@ from enum import Enum from typing import Optional, Union -from multimethod import multimethod - -from uprotocol.proto.uattributes_pb2 import UAttributes -from uprotocol.proto.uuid_pb2 import UUID from uprotocol.uuid.factory import PythonUUID +from uprotocol.v1.uuid_pb2 import UUID class Version(Enum): @@ -128,24 +117,24 @@ def is_uuid(uuid_obj: UUID) -> bool: return UUIDUtils.is_uprotocol(uuid_obj) or UUIDUtils.is_uuidv6(uuid_obj) if uuid_obj is not None else False @staticmethod - def get_time(uuid: UUID): + def get_time(uuid_val: UUID): """ Return the number of milliseconds since unix epoch from a passed UUID.

- @param uuid:passed uuid to fetch the time. + @param uuid_val:passed uuid to fetch the time. @return:number of milliseconds since unix epoch or empty if uuid is null. """ time = None - version = UUIDUtils.get_version(uuid) - if uuid is None or version is None: + version = UUIDUtils.get_version(uuid_val) + if uuid_val is None or version is None: return None if version == Version.VERSION_UPROTOCOL: - time = uuid.msb >> 16 + time = uuid_val.msb >> 16 elif version == Version.VERSION_TIME_ORDERED: try: - python_uuid = UUIDUtils.create_pythonuuid_from_eclipseuuid(uuid) + python_uuid = UUIDUtils.create_pythonuuid_from_eclipseuuid(uuid_val) # Convert 100-nanoseconds ticks to milliseconds time = python_uuid.time // 10000 except ValueError: @@ -154,54 +143,41 @@ def get_time(uuid: UUID): return time @staticmethod - def get_elapsed_time(id: UUID): + def get_elapsed_time(id_val: UUID): """ Calculates the elapsed time since the creation of the specified UUID. - @param id The UUID of the object whose creation time + @param id_val The UUID of the object whose creation time needs to be determined. @return The elapsed time in milliseconds, or None if the creation time cannot be determined. """ - creation_time = UUIDUtils.get_time(id) or -1 + creation_time = UUIDUtils.get_time(id_val) or -1 if creation_time < 0: return None now = int(time.time() * 1000) return now - creation_time if now >= creation_time else None - @multimethod - def get_remaining_time(id: Union[UUID, None], ttl: int): # noqa: N805 + @staticmethod + def get_remaining_time(id_val: Union[UUID, None], ttl: int): """ Calculates the remaining time until the expiration of the event identified by the given UUID. - @param id The UUID of the object whose remaining time needs to + @param id_val The UUID of the object whose remaining time needs to be determined. @param ttl The time-to-live (TTL) in milliseconds. @return The remaining time in milliseconds until the event expires, or None if the UUID is null, TTL is non-positive, or the creation time cannot be determined. """ - if id is None or ttl <= 0: + if id_val is None or ttl <= 0: return None - elapsed_time = UUIDUtils.get_elapsed_time(id) + elapsed_time = UUIDUtils.get_elapsed_time(id_val) return ttl - elapsed_time if ttl > elapsed_time else None - @multimethod - def get_remaining_time(attributes: Union[UAttributes, None]): # noqa: N805 - """ - Calculates the remaining time until the expiration of the event - identified by the given UAttributes. - @param attributes The attributes containing information about - the event, including its ID and TTL. - @return The remaining time in milliseconds until the event expires, - or None if the attributes do not contain TTL information or the - creation time cannot be determined. - """ - return UUIDUtils.get_remaining_time(attributes.id, attributes.ttl) if attributes.HasField("ttl") else None - - @multimethod - def is_expired(id: Union[UUID, None], ttl: int): # noqa: N805 + @staticmethod + def is_expired(id: Union[UUID, None], ttl: int): """ Checks if the event identified by the given UUID has expired based on the specified time-to-live (TTL). @@ -214,23 +190,10 @@ def is_expired(id: Union[UUID, None], ttl: int): # noqa: N805 """ return ttl > 0 and UUIDUtils.get_remaining_time(id, ttl) is None - @multimethod - def is_expired(attributes: Union[UAttributes, None]): # noqa: N805 - """ - Checks if the event identified by the given UAttributes has expired. - - @param attributes The attributes containing information about the - event, including its ID and TTL. - @return true if the event has expired, false otherwise.Returns false - if the attributes do not contain TTL - information or creation time cannot be determined. - """ - return attributes.HasField("ttl") and UUIDUtils.is_expired(attributes.id, attributes.ttl) - @staticmethod - def get_msb_lsb(uuid: PythonUUID): + def get_msb_lsb(uuid_val: PythonUUID): # Convert UUID to a 128-bit integer - uuid_int = int(uuid) + uuid_int = int(uuid_val) # Extract most significant bits (first 64 bits) msb = uuid_int >> 64 @@ -241,6 +204,6 @@ def get_msb_lsb(uuid: PythonUUID): return msb, lsb @staticmethod - def create_pythonuuid_from_eclipseuuid(uuid: UUID) -> PythonUUID: - combined_int = (uuid.msb << 64) + uuid.lsb + def create_pythonuuid_from_eclipseuuid(uuid_val: UUID) -> PythonUUID: + combined_int = (uuid_val.msb << 64) + uuid_val.lsb return PythonUUID(int=combined_int) diff --git a/uprotocol/uuid/serializer/longuuidserializer.py b/uprotocol/uuid/serializer/longuuidserializer.py deleted file mode 100644 index a1f4f6e..0000000 --- a/uprotocol/uuid/serializer/longuuidserializer.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.uuid.factory import PythonUUID -from uprotocol.uuid.factory.uuidutils import UUIDUtils -from uprotocol.uuid.serializer.uuidserializer import UuidSerializer - - -class LongUuidSerializer(UuidSerializer): - """ - UUID Serializer implementation used to serialize/deserialize UUIDs to/from - a string - """ - - @staticmethod - def instance(): - return LongUuidSerializer() - - def deserialize(self, string_uuid: str) -> UUID: - """ - Deserialize from the string format to a UUID. - :param string_uuid: Serialized UUID in string format. - :return: Returns a UUID object from the serialized format from the - wire. - """ - if not string_uuid or string_uuid.isspace(): - return UUID() # Return default UUID if string is empty or whitespace - try: - msb, lsb = UUIDUtils.get_msb_lsb(PythonUUID(string_uuid)) - return UUID(msb=msb, lsb=lsb) - except ValueError: - return UUID() # Return default UUID in case of parsing failure - - def serialize(self, uuid: UUID) -> str: - """ - Serialize from a UUID to a string format. - :param uuid: UUID object to be serialized to a string. - :return: Returns the UUID in the string serialized format. - """ - if uuid is None: - return "" - - pythonuuid = UUIDUtils.create_pythonuuid_from_eclipseuuid(uuid) - return str(pythonuuid) if uuid else "" diff --git a/uprotocol/uuid/serializer/microuuidserializer.py b/uprotocol/uuid/serializer/microuuidserializer.py deleted file mode 100644 index 4796915..0000000 --- a/uprotocol/uuid/serializer/microuuidserializer.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation - -See the NOTICE file(s) distributed with this work for additional -information regarding copyright ownership. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE -SPDX-License-Identifier: Apache-2.0 -""" - -import struct - -from uprotocol.proto.uuid_pb2 import UUID -from uprotocol.uuid.factory.uuidutils import UUIDUtils -from uprotocol.uuid.serializer.uuidserializer import UuidSerializer - - -class MicroUuidSerializer(UuidSerializer): - """ - UUID Serializer implementation used to serialize/deserialize UUIDs to/from - bytes - """ - - @staticmethod - def instance(): - return MicroUuidSerializer() - - def deserialize(self, uuid_bytes: bytes) -> UUID: - """ - Deserialize from the bytes format to a UUID. - :param uuid_bytes: Serialized UUID in bytes format. - :return: Returns a UUID object from the serialized format from the - wire. - """ - if not uuid_bytes or len(uuid_bytes) != 16: - return UUID() # Return default UUID if bytes are empty or not 16 bytes - - msb, lsb = struct.unpack(">QQ", uuid_bytes) - return UUID(msb=msb, lsb=lsb) - - def serialize(self, uuid: UUID) -> bytes: - """ - Serialize from a UUID to bytes format. - :param uuid: UUID object to be serialized to bytes. - :return: Returns the UUID in the bytes serialized format. - """ - if uuid is None: - return bytearray() - pythonuuid = UUIDUtils.create_pythonuuid_from_eclipseuuid(uuid) - msb, lsb = divmod(pythonuuid.int, 2**64) - return struct.pack(">QQ", msb, lsb) diff --git a/uprotocol/uuid/serializer/uuidserializer.py b/uprotocol/uuid/serializer/uuidserializer.py index 349f57d..db4ff44 100644 --- a/uprotocol/uuid/serializer/uuidserializer.py +++ b/uprotocol/uuid/serializer/uuidserializer.py @@ -1,54 +1,55 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ -from abc import ABC, abstractmethod -from typing import Generic, TypeVar +from typing import Optional -from uprotocol.proto.uuid_pb2 import UUID +from uprotocol.uuid.factory import PythonUUID +from uprotocol.uuid.factory.uuidutils import UUIDUtils +from uprotocol.v1.uuid_pb2 import UUID -T = TypeVar("T") - -class UuidSerializer(ABC, Generic[T]): +class UuidSerializer: """ UUID Serializer interface used to serialize/deserialize UUIDs to/from either Long (string) or micro (bytes) form """ - @abstractmethod - def deserialize(self, uuid: T) -> UUID: + @staticmethod + def deserialize(string_uuid: Optional[str]) -> UUID: """ - Deserialize from the format to a UUID. - :param uuid: Serialized UUID. - :return: Returns a UUID object from the - serialized format from the wire. + Deserialize from the string format to a UUID. + :param string_uuid: Serialized UUID in string format. + :return: Returns a UUID object from the serialized format from the + wire. """ - pass # Implement your deserialization logic here - - @abstractmethod - def serialize(self, uuid: UUID) -> T: + if not string_uuid or string_uuid.isspace(): + return UUID() # Return default UUID if string is empty or whitespace + try: + msb, lsb = UUIDUtils.get_msb_lsb(PythonUUID(string_uuid)) + return UUID(msb=msb, lsb=lsb) + except ValueError: + return UUID() # Return default UUID in case of parsing failure + + @staticmethod + def serialize(uuid: UUID) -> str: """ - Serialize from a UUID to a specific serialization format. - :param uuid: UUID object to be serialized to the format T. - :return: Returns the UUID in the transport serialized format. + Serialize from a UUID to a string format. + :param uuid: UUID object to be serialized to a string. + :return: Returns the UUID in the string serialized format. """ - pass # Implement your serialization logic here + if uuid is None: + return "" + + pythonuuid = UUIDUtils.create_pythonuuid_from_eclipseuuid(uuid) + return str(pythonuuid) if uuid else "" diff --git a/uprotocol/uuid/validate/__init__.py b/uprotocol/uuid/validate/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_cloudevent/test_factory/__init__.py b/uprotocol/uuid/validator/__init__.py similarity index 100% rename from tests/test_cloudevent/test_factory/__init__.py rename to uprotocol/uuid/validator/__init__.py diff --git a/uprotocol/uuid/validate/uuidvalidator.py b/uprotocol/uuid/validator/uuidvalidator.py similarity index 84% rename from uprotocol/uuid/validate/uuidvalidator.py rename to uprotocol/uuid/validator/uuidvalidator.py index 285b956..c92ef2a 100644 --- a/uprotocol/uuid/validate/uuidvalidator.py +++ b/uprotocol/uuid/validator/uuidvalidator.py @@ -1,31 +1,24 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ from abc import ABC, abstractmethod from enum import Enum -from uprotocol.proto.ustatus_pb2 import UCode, UStatus -from uprotocol.proto.uuid_pb2 import UUID from uprotocol.uuid.factory.uuidutils import UUIDUtils, Version +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.ustatus_pb2 import UStatus +from uprotocol.v1.uuid_pb2 import UUID from uprotocol.validation.validationresult import ValidationResult diff --git a/uprotocol/validation/validationresult.py b/uprotocol/validation/validationresult.py index 97dc537..88c713d 100644 --- a/uprotocol/validation/validationresult.py +++ b/uprotocol/validation/validationresult.py @@ -1,28 +1,21 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2023 Contributors to the -Eclipse Foundation +SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +This program and the accompanying materials are made available under the +terms of the Apache License Version 2.0 which is available at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -SPDX-FileType: SOURCE SPDX-License-Identifier: Apache-2.0 """ from abc import ABC, abstractmethod -from uprotocol.proto.ustatus_pb2 import UCode, UStatus +from uprotocol.v1.ucode_pb2 import UCode +from uprotocol.v1.ustatus_pb2 import UStatus class ValidationResult(ABC):