diff --git a/monitoring/uss_qualifier/resources/README.md b/monitoring/uss_qualifier/resources/README.md index e53d100d7a..e3eb1cad78 100644 --- a/monitoring/uss_qualifier/resources/README.md +++ b/monitoring/uss_qualifier/resources/README.md @@ -33,13 +33,13 @@ Resources for a given test configuration are all declared in a single global res 3. Every type of test resource must define how to create an instance of the test resource from an instance of the resource specification. -## Resource modifiers +## Resource-modifying resources -A `ResourceModifier` is a resource that wraps another resource and produces variants of it based on an integer index. This is useful when a test scenario needs multiple unique-but-related instances of a resource (e.g., distinct flights derived from a single base flight). +A `ResourceModifyingResource` is a resource capable of spawning other resources by modifying a template/base resource according to a desired key, such as an index. This is useful when a test scenario needs multiple unique-but-related instances of a resource (e.g., distinct flights derived from a single base flight). -To use a `ResourceModifier`: +To use a `ResourceModifyingResource`: 1. Declare it like any other resource, with its `base_resource` dependency pointing to the resource to be modified. -2. When need, call `adjust(index)` to obtain a modified copy of the base resource. Different `index` values produce different (unique) variants; the same `index` produces equivalent results. +2. When a variant of the base/template resource is needed, call `modify(key)` to obtain a modified copy of the base resource. Different `key` values generally produce different variants; the same `key` should produce equivalent results. -The base resource itself remains available as `base_resource` on the modifier. +The base/template resource itself remains available as `base_resource` on the modifier. diff --git a/monitoring/uss_qualifier/resources/dev/__init__.py b/monitoring/uss_qualifier/resources/dev/__init__.py index d666e7f23e..7b572b03de 100644 --- a/monitoring/uss_qualifier/resources/dev/__init__.py +++ b/monitoring/uss_qualifier/resources/dev/__init__.py @@ -1,4 +1,6 @@ from .noop import NoOpResource as NoOpResource from .test_exclusions import TestExclusionsResource as TestExclusionsResource -from .test_modifier import TestModifierModifierResource as TestModifierModifierResource -from .test_modifier import TestModifierResource as TestModifierResource +from .test_modifier import ( + NumberGeneratorModifierResource as NumberGeneratorModifierResource, +) +from .test_modifier import NumberGeneratorResource as NumberGeneratorResource diff --git a/monitoring/uss_qualifier/resources/dev/test_modifier.py b/monitoring/uss_qualifier/resources/dev/test_modifier.py index ba0e53fa16..7fef8ff338 100644 --- a/monitoring/uss_qualifier/resources/dev/test_modifier.py +++ b/monitoring/uss_qualifier/resources/dev/test_modifier.py @@ -1,20 +1,24 @@ from implicitdict import ImplicitDict -from monitoring.uss_qualifier.resources.resource import Resource, ResourceModifier +from monitoring.uss_qualifier.resources.resource import ( + Resource, + ResourceProvidingResource, + SupportedKeysNotSpecifiedError, +) -class TestModifierSpecification(ImplicitDict): +class NumberGeneratorSpecification(ImplicitDict): base_id: int -class TestModifierResource(Resource[TestModifierSpecification]): - """TestModifierResource is a simple resource returing 10 number, starting from base_id. Used for unit tests.""" +class NumberGeneratorResource(Resource[NumberGeneratorSpecification]): + """A simple resource returing 10 numbers, starting from base_id. Used for unit tests.""" - _spec: TestModifierSpecification + _spec: NumberGeneratorSpecification def __init__( self, - specification: TestModifierSpecification, + specification: NumberGeneratorSpecification, resource_origin: str, ): super().__init__(specification, resource_origin) @@ -24,22 +28,45 @@ def build_ids(self) -> list[int]: return list(range(self._spec.base_id, self._spec.base_id + 10)) -class TestModifierModifierSpecification(ImplicitDict): +class NumberGeneratorModifierSpecification(ImplicitDict): shift_interval: int -class TestModifierModifierResource( - ResourceModifier[TestModifierModifierSpecification, TestModifierResource] +class NumberGeneratorModifierResource( + ResourceProvidingResource[ + NumberGeneratorModifierSpecification, NumberGeneratorResource + ] ): - """Modifier for a TestModifierResource. Used for unit tests.""" + """Modifier for a NumberGeneratorResource. Used for unit tests.""" - def adjust(self, index: int) -> TestModifierResource: + _spec: NumberGeneratorModifierSpecification + base_resource: NumberGeneratorResource - # 'Clone' the resource with new specs - return TestModifierResource( - TestModifierSpecification( + def __init__( + self, + specification: NumberGeneratorModifierSpecification, + resource_origin: str, + base_resource: NumberGeneratorResource, + ): + super().__init__(specification, resource_origin) + self._spec = specification + self.base_resource = base_resource + + def _modified_resource_origin(self, index: int) -> str: + return f"Modification {index} of {self.base_resource.resource_origin} by {self.resource_origin}" + + def provide_resource_for(self, **kwargs) -> NumberGeneratorResource: + if "index" not in kwargs: + raise SupportedKeysNotSpecifiedError("index not specified") + index = kwargs["index"] + if not isinstance(index, int): + raise SupportedKeysNotSpecifiedError("index is not an int") + + # 'Clone' the base resource with new specs + return NumberGeneratorResource( + NumberGeneratorSpecification( base_id=self.base_resource._spec.base_id + self._spec.shift_interval * index, ), - resource_origin=self.base_resource.resource_origin, + resource_origin=self._modified_resource_origin(index), ) diff --git a/monitoring/uss_qualifier/resources/resource.py b/monitoring/uss_qualifier/resources/resource.py index 5089c5a2ad..1f1d6c9391 100644 --- a/monitoring/uss_qualifier/resources/resource.py +++ b/monitoring/uss_qualifier/resources/resource.py @@ -43,34 +43,26 @@ def is_type(self, resource_type: str) -> bool: ResourceType = TypeVar("ResourceType", bound=Resource) -class ResourceModifier[SpecificationType: ImplicitDict, ResourceType]( - Resource[SpecificationType], ABC -): - """A specifc type of resources that can return adjusted an resource that shall unique based on a specifc 'index'. - The underlying resource shall be a dependency named 'base_resource'. +class SupportedKeysNotSpecifiedError(ValueError): + """Error when a ResourceProvidingResource is asked to provide_resource_for, but the supported key(s) are not specified correctly.""" - Concrete subclass must implement 'adjust' as needed. - """ + pass - _spec: SpecificationType - base_resource: ResourceType - def __init__( - self, - specification: SpecificationType, - resource_origin: str, - base_resource: ResourceType, - ): - super().__init__(specification, resource_origin) - self._spec = specification - self.base_resource = base_resource +class ResourceProvidingResource[ + SpecificationType: ImplicitDict, + ResourceType: Resource, +](Resource[SpecificationType], ABC): + """Resource capable of spawning ResourceType resources according to a desired key, such as an index.""" @abstractmethod - def adjust(self, index: int) -> ResourceType: - """ - Return a new instance of the base resource, modified to be unique based on 'index' value. - """ - pass + def provide_resource_for(self, **kwargs) -> ResourceType: + """Provide a resource corresponding with the provided key(s) (e.g., index, USS pair, etc).""" + raise NotImplementedError() + + def _provided_resource_origin(self, key_name: str) -> str: + """Method that should generally be used to describe the origin of a resource provided by this resource.""" + return f"Resource for {key_name} provided by {self.resource_origin}" class MissingResourceError(ValueError): diff --git a/monitoring/uss_qualifier/resources/resources_test.py b/monitoring/uss_qualifier/resources/resources_test.py index c80bff6cbc..476d2d43f2 100644 --- a/monitoring/uss_qualifier/resources/resources_test.py +++ b/monitoring/uss_qualifier/resources/resources_test.py @@ -1,76 +1,89 @@ import unittest +import pytest + from monitoring.uss_qualifier.resources.definitions import ( ResourceDeclaration, ResourceID, ) from monitoring.uss_qualifier.resources.dev.test_modifier import ( - TestModifierModifierSpecification, - TestModifierSpecification, + NumberGeneratorModifierSpecification, + NumberGeneratorSpecification, +) +from monitoring.uss_qualifier.resources.resource import ( + SupportedKeysNotSpecifiedError, + create_resources, ) -from monitoring.uss_qualifier.resources.resource import create_resources -class TestResourceModifier(unittest.TestCase): - def _build_test_modifier_declaration( +class TestModifierResource(unittest.TestCase): + def _build_number_generator_declaration( self, base_id ) -> dict[ResourceID, ResourceDeclaration]: return { - "test": ResourceDeclaration( - resource_type="resources.dev.TestModifierResource", - specification=TestModifierSpecification(base_id=base_id), + "number_generator": ResourceDeclaration( + resource_type="resources.dev.NumberGeneratorResource", + specification=NumberGeneratorSpecification(base_id=base_id), ) } - def _build_test_modifier_modifier_declaration( + def _build_modifier_declaration( self, base_id, shift_interval ) -> dict[ResourceID, ResourceDeclaration]: return { - "test": self._build_test_modifier_declaration(base_id)["test"], - "test_modifier": ResourceDeclaration( - resource_type="resources.dev.TestModifierModifierResource", - specification=TestModifierModifierSpecification( + "number_generator": self._build_number_generator_declaration(base_id)[ + "number_generator" + ], + "modifier": ResourceDeclaration( + resource_type="resources.dev.NumberGeneratorModifierResource", + specification=NumberGeneratorModifierSpecification( shift_interval=shift_interval ), dependencies={ - "base_resource": "test", + "base_resource": "number_generator", }, ), } def test_base_resource(self): """Test basic usage of the resource""" - declaration = self._build_test_modifier_declaration(42) + declaration = self._build_number_generator_declaration(42) - resources = create_resources(declaration, "test", True) - assert "test" in resources + resources = create_resources(declaration, "unittest", True) + assert "number_generator" in resources - resource = resources["test"] + resource = resources["number_generator"] assert resource.build_ids() == [42, 43, 44, 45, 46, 47, 48, 49, 50, 51] def test_base_resource_base_id(self): """Test that base id works as expected""" - declaration = self._build_test_modifier_declaration(52) + declaration = self._build_number_generator_declaration(52) - resources = create_resources(declaration, "test", True) - assert "test" in resources + resources = create_resources(declaration, "unittest", True) + assert "number_generator" in resources - resource = resources["test"] + resource = resources["number_generator"] assert resource.build_ids() == [52, 53, 54, 55, 56, 57, 58, 59, 60, 61] def test_modifier_resource(self): - """Test basic usage of the resource modifier""" - declaration = self._build_test_modifier_modifier_declaration(42, 10) + """Test basic usage of the resource modifier resource""" + declaration = self._build_modifier_declaration(42, 10) + + resources = create_resources(declaration, "unittest", True) + assert "modifier" in resources + + resource = resources["modifier"] - resources = create_resources(declaration, "test", True) - assert "test_modifier" in resources + with pytest.raises(SupportedKeysNotSpecifiedError): + resource.provide_resource_for(key=0) - resource = resources["test_modifier"] + with pytest.raises(SupportedKeysNotSpecifiedError): + resource.provide_resource_for(index="foo") - assert resource.adjust(0).build_ids() == [ + assert resource.provide_resource_for(index=0).build_ids() == [ 42, 43, 44, @@ -82,7 +95,7 @@ def test_modifier_resource(self): 50, 51, ] - assert resource.adjust(1).build_ids() == [ + assert resource.provide_resource_for(index=1).build_ids() == [ 52, 53, 54, @@ -97,14 +110,14 @@ def test_modifier_resource(self): def test_modifier_resource_shift(self): """Test shift usage of the resource modifier""" - declaration = self._build_test_modifier_modifier_declaration(42, 20) + declaration = self._build_modifier_declaration(42, 20) - resources = create_resources(declaration, "test", True) - assert "test_modifier" in resources + resources = create_resources(declaration, "unittest", True) + assert "modifier" in resources - resource = resources["test_modifier"] + resource = resources["modifier"] - assert resource.adjust(0).build_ids() == [ + assert resource.provide_resource_for(index=0).build_ids() == [ 42, 43, 44, @@ -116,7 +129,7 @@ def test_modifier_resource_shift(self): 50, 51, ] - assert resource.adjust(1).build_ids() == [ + assert resource.provide_resource_for(index=1).build_ids() == [ 62, 63, 64, diff --git a/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierModifierSpecification.json b/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorModifierSpecification.json similarity index 66% rename from schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierModifierSpecification.json rename to schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorModifierSpecification.json index 24a9d04c65..a767c59bda 100644 --- a/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierModifierSpecification.json +++ b/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorModifierSpecification.json @@ -1,7 +1,7 @@ { - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierModifierSpecification.json", + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorModifierSpecification.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.uss_qualifier.resources.dev.test_modifier.TestModifierModifierSpecification, as defined in monitoring/uss_qualifier/resources/dev/test_modifier.py", + "description": "monitoring.uss_qualifier.resources.dev.test_modifier.NumberGeneratorModifierSpecification, as defined in monitoring/uss_qualifier/resources/dev/test_modifier.py", "properties": { "$ref": { "description": "Path to content that replaces the $ref", diff --git a/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierSpecification.json b/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorSpecification.json similarity index 67% rename from schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierSpecification.json rename to schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorSpecification.json index 1824bdad7e..b1f66f3b5e 100644 --- a/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierSpecification.json +++ b/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorSpecification.json @@ -1,7 +1,7 @@ { - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/TestModifierSpecification.json", + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/dev/test_modifier/NumberGeneratorSpecification.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.uss_qualifier.resources.dev.test_modifier.TestModifierSpecification, as defined in monitoring/uss_qualifier/resources/dev/test_modifier.py", + "description": "monitoring.uss_qualifier.resources.dev.test_modifier.NumberGeneratorSpecification, as defined in monitoring/uss_qualifier/resources/dev/test_modifier.py", "properties": { "$ref": { "description": "Path to content that replaces the $ref",