def test_parse_model_missing_attribute_raises_type_error(): @dataclass(frozen=True) class TestModel: model_id: int model_lists: Any data = {'ModelID': 1} with pytest.raises(TypeError): parsing_helpers.parse_model(data, TestModel)
def test_parse_model(): @dataclass(frozen=True) class TestModel: model_id: int model_name: str model_score: float touched_by: str created_date: datetime version: int = None data = { 'ModelID': 1, 'ModelName': 'model1', 'ModelScore': .97, 'TouchedBy': 'signal', 'CreatedDate': '2010-01-01T01:00:00' } parsed = parsing_helpers.parse_model(data, TestModel) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, model_name='model1', model_score=.97, touched_by='signal', created_date=datetime(2010, 1, 1, 1, 0, 0, tzinfo=timezone.utc))
def get_multiple(connection: Connection, relative_url: str, cls: Type[TModel], query_string: Optional[QueryString] = None, rename_keys: Optional[Dict[str, str]] = None) \ -> Tuple[TModel, ...]: """Get a multiple objects from the API. Make a get request to the specified URL to retrieve a sequence of results and return a list of objects of the provided class instantiated with the retrieved data. If the API responds with an empty sequence an empty list is returned. Args: connection: The connection object to use to make the appropriate get request to the API. relative_url: The relative URL to make the request to. cls: The class to instantiate the object for the retrieved data. query_string: Query parameters for the request. rename_keys: Key names to rename to match model attribute names, used when an automated translation of the name from CapsWords to snake_case is to sufficient. Renaming must provide the name in CapsWords. """ response = connection._make_get_request(relative_url, query_string=query_string) response.raise_for_status() data = response.json() return tuple(parse_model(d, cls, rename_keys=rename_keys) for d in data)
def get_single(connection: Connection, relative_url: str, cls: Type[TModel], query_string: Optional[QueryString] = None, rename_keys: Optional[Dict[str, str]] = None) \ -> Optional[TModel]: """Get a single object from the API. Make a get request to the specified URL and return an object of the provided class instantiated with the retrieved data. If the API responds with a "Not Found" status code, return None. Args: connection: The connection object to use to make the appropriate get request to the API. relative_url: The relative URL to make the request to. cls: The class to instantiate the object for the retrieved data. query_string: Query parameters for the request. rename_keys: Key names to rename to match model attribute names, used when an automated translation of the name from CapsWords to snake_case is to sufficient. Renaming must provide the name in CapsWords. Returns: An object of the provided class instantiated with the data retrieved from the specified URL, or None if the API responds with a "Not Found" status code. """ response = connection._make_get_request(relative_url, query_string=query_string) if response.status_code == requests.codes.not_found: return None response.raise_for_status() data = response.json() return parse_model(data, cls, rename_keys=rename_keys)
def test_parse_model_field(value: Union[str, int, float, bool, None], field_type: Type, expected: Union[str, int, float, bool, None, datetime, List[int], List[str], Tuple[int, ...], Tuple[str, ...]]) \ -> None: transformed = parsing_helpers.parse_model(value, field_type) assert type(transformed) == type(expected) assert transformed == expected
def test_parse_model_rename_key_extra_attribute_ignored(): @dataclass(frozen=True) class TestModel: model_id: int data = {'ModelID': 1} rename_keys = {'NAME': 'model_name'} parsed = parsing_helpers.parse_model(data, TestModel, rename_keys) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1)
def test_parse_model_default_factory(): @dataclass(frozen=True) class TestModel: model_id: int model_lists: List = field(default_factory=list) data = {'ModelID': 1} parsed = parsing_helpers.parse_model(data, TestModel) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, model_lists=[])
def test_parse_model_default(): @dataclass(frozen=True) class TestModel: model_id: int model_name: str = 'a' data = {'ModelID': 1} parsed = parsing_helpers.parse_model(data, TestModel) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, model_name='a')
def test_parse_model_rename_key(): @dataclass(frozen=True) class TestModel: model_id: int model_name: str data = {'ModelID': 1, 'NAME': 'model1'} rename_keys = {'NAME': 'model_name'} parsed = parsing_helpers.parse_model(data, TestModel, rename_keys) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, model_name='model1')
def test_parse_nested_model(): @dataclass(frozen=True) class TestNestedModel: model_id: int @dataclass(frozen=True) class TestModel: model_id: int nested_model: TestNestedModel data = {'ModelID': 1, 'nested_model': {'ModelID': 3}} parsed = parsing_helpers.parse_model(data, TestModel) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, nested_model=TestNestedModel(3))
def test_parse_model_extra_attributes_are_ignored(): @dataclass(frozen=True) class TestModel: model_id: int model_name: str data = { 'ModelID': 1, 'ModelName': 'model1', 'ModelScore': .97, 'TouchedBy': 'signal', 'CreatedDate': '2010-01-01' } parsed = parsing_helpers.parse_model(data, TestModel) assert isinstance(parsed, TestModel) assert parsed == TestModel(model_id=1, model_name='model1')
def get_imos( self, vessel_filter: Optional[VesselFilter] = None ) -> Tuple[Vessel, ...]: """Retrieves available vessel types. Args: vessel_filter: A filter used to find specific vessel . If not specified, returns all available vessels . Returns: A tuple of available vessels that match the filter. """ response = self.__connection._make_get_request( "voyages-api/v2/filters/availableVessels") response.raise_for_status() vessels = (parse_model(c, Vessel) for c in response.json()) vessel_filter = vessel_filter or VesselFilter() return tuple(vessel_filter._apply(vessels))
def get_vessel_classes( self, class_filter: Optional[VesselClassFilter] = None ) -> Tuple[VesselClass, ...]: """Retrieves available vessel classes. Args: class_filter: A filter used to find specific vessel classes. If not specified, returns all available vessel classes. Returns: A tuple of available vessel classes that match the filter. """ response = self.__connection._make_get_request( "voyages-api/v2/filters/availableVesselClasses") response.raise_for_status() classes = (parse_model(c, VesselClass) for c in response.json()) class_filter = class_filter or VesselClassFilter() return tuple(class_filter._apply(classes))
def test_parse_model_field_raises_error( value: Union[str, int, float, bool, None], field_type: Type, expected_error: Type[BaseException]) -> None: with pytest.raises(expected_error): parsing_helpers.parse_model(value, field_type)