From 1b58ed7b69c8847845fe248fb0324e57c10e95ea Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Thu, 18 Aug 2022 10:45:36 -0400 Subject: [PATCH 1/6] Reversed the order of validating item type and bundles. --- planet/order_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planet/order_request.py b/planet/order_request.py index 911927bc6..102e5ef6f 100644 --- a/planet/order_request.py +++ b/planet/order_request.py @@ -113,8 +113,8 @@ def product(item_ids: List[str], are not valid bundles or if item_type is not valid for the given bundle or fallback bundle. ''' + item_type = specs.validate_item_type(item_type, product_bundle) validated_product_bundle = specs.validate_bundle(product_bundle) - item_type = specs.validate_item_type(item_type, validated_product_bundle) if fallback_bundle is not None: validated_fallback_bundle = specs.validate_bundle(fallback_bundle) From 441adcb0eb298da6200a6d4c4e6a1515e72097e4 Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Thu, 18 Aug 2022 10:47:03 -0400 Subject: [PATCH 2/6] Reversed the order of getting item types and validating bundles. Added Exception to get_item_types, for test. --- planet/specs.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/planet/specs.py b/planet/specs.py index 0f8c1550b..3db0090de 100644 --- a/planet/specs.py +++ b/planet/specs.py @@ -48,8 +48,8 @@ def validate_bundle(bundle): def validate_item_type(item_type, bundle): - bundle = validate_bundle(bundle) supported = get_item_types(bundle) + bundle = validate_bundle(bundle) return _validate_field(item_type, supported, 'item_type') @@ -111,15 +111,18 @@ def get_item_types(product_bundle=None): '''If given product bundle, get specific item types supported by Orders API. Otherwise, get all item types supported by Orders API.''' spec = _get_product_bundle_spec() - if product_bundle: - item_types = spec['bundles'][product_bundle]['assets'].keys() - else: - product_bundle = get_product_bundles() - all_item_types = [] - for bundle in product_bundle: - all_item_types += [*spec['bundles'][bundle]['assets'].keys()] - item_types = set(all_item_types) - return item_types + try: + if product_bundle: + item_types = spec['bundles'][product_bundle]['assets'].keys() + else: + product_bundle = get_product_bundles() + all_item_types = [] + for bundle in product_bundle: + all_item_types += [*spec['bundles'][bundle]['assets'].keys()] + item_types = set(all_item_types) + return item_types + except KeyError: + raise SpecificationException() def _get_product_bundle_spec(): From 614ebe5d0d7edd7d34b052ec81e266d757cbdacc Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Fri, 19 Aug 2022 09:36:00 -0400 Subject: [PATCH 3/6] Removed unnecessary function call. Made get_item_types more pythonic and consistent. Raised a more verbose exception. --- planet/specs.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/planet/specs.py b/planet/specs.py index 3db0090de..46ea191ca 100644 --- a/planet/specs.py +++ b/planet/specs.py @@ -15,7 +15,7 @@ """Functionality for validating against the Planet API specification.""" import json import logging - +import itertools from .constants import DATA_DIR PRODUCT_BUNDLE_SPEC_NAME = 'orders_product_bundle_2022_02_02.json' @@ -49,7 +49,6 @@ def validate_bundle(bundle): def validate_item_type(item_type, bundle): supported = get_item_types(bundle) - bundle = validate_bundle(bundle) return _validate_field(item_type, supported, 'item_type') @@ -111,18 +110,19 @@ def get_item_types(product_bundle=None): '''If given product bundle, get specific item types supported by Orders API. Otherwise, get all item types supported by Orders API.''' spec = _get_product_bundle_spec() + try: if product_bundle: - item_types = spec['bundles'][product_bundle]['assets'].keys() + item_types = set(spec['bundles'][product_bundle]['assets'].keys()) else: - product_bundle = get_product_bundles() - all_item_types = [] - for bundle in product_bundle: - all_item_types += [*spec['bundles'][bundle]['assets'].keys()] - item_types = set(all_item_types) - return item_types + item_types = set( + itertools.chain.from_iterable( + spec['bundles'][bundle]['assets'].keys() + for bundle in get_product_bundles())) except KeyError: - raise SpecificationException() + raise validate_bundle(product_bundle) + + return item_types def _get_product_bundle_spec(): From fe600d4ed4d5bcabe2a0a2ef4e8066c58afe8e5c Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Mon, 22 Aug 2022 10:56:17 -0400 Subject: [PATCH 4/6] Added message to SpecificationException class. Re-added bundle validation in validate_item_type. --- planet/specs.py | 50 ++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/planet/specs.py b/planet/specs.py index 46ea191ca..9c08a0bb8 100644 --- a/planet/specs.py +++ b/planet/specs.py @@ -37,18 +37,32 @@ LOGGER = logging.getLogger(__name__) -class SpecificationException(Exception): +class NoMatchException(Exception): '''No match was found''' pass +class SpecificationException(Exception): + '''No match was found''' + + def __init__(self, value, supported, field_name): + self.value = value + self.supported = supported + self.field_name = field_name + self.opts = ', '.join(["'" + s + "'" for s in supported]) + + def __str__(self): + return f'{self.field_name} - \'{self.value}\' is not one of {self.opts}.' + + def validate_bundle(bundle): supported = get_product_bundles() return _validate_field(bundle, supported, 'product_bundle') def validate_item_type(item_type, bundle): - supported = get_item_types(bundle) + validated_bundle = validate_bundle(bundle) + supported = get_item_types(validated_bundle) return _validate_field(item_type, supported, 'item_type') @@ -72,20 +86,13 @@ def validate_file_format(file_format): def _validate_field(value, supported, field_name): try: - value = get_match(value, supported) + value = get_match(value, supported, field_name) except (NoMatchException): - opts = ', '.join(["'" + s + "'" for s in supported]) - msg = f'{field_name} - \'{value}\' is not one of {opts}.' - raise SpecificationException(msg) + raise SpecificationException(value, supported, field_name) return value -class NoMatchException(Exception): - '''No match was found''' - pass - - -def get_match(test_entry, spec_entries): +def get_match(test_entry, spec_entries, field_name): '''Find and return matching spec entry regardless of capitalization. This is helpful for working with the API spec, where the capitalization @@ -95,7 +102,7 @@ def get_match(test_entry, spec_entries): match = next(e for e in spec_entries if e.lower() == test_entry.lower()) except (StopIteration): - raise NoMatchException('{test_entry} should be one of {spec_entries}') + raise SpecificationException(test_entry, spec_entries, field_name) return match @@ -111,16 +118,13 @@ def get_item_types(product_bundle=None): API. Otherwise, get all item types supported by Orders API.''' spec = _get_product_bundle_spec() - try: - if product_bundle: - item_types = set(spec['bundles'][product_bundle]['assets'].keys()) - else: - item_types = set( - itertools.chain.from_iterable( - spec['bundles'][bundle]['assets'].keys() - for bundle in get_product_bundles())) - except KeyError: - raise validate_bundle(product_bundle) + if product_bundle: + item_types = set(spec['bundles'][product_bundle]['assets'].keys()) + else: + item_types = set( + itertools.chain.from_iterable( + spec['bundles'][bundle]['assets'].keys() + for bundle in get_product_bundles())) return item_types From f366e5cd5cc23d4c5437e537d3f98cba7e4a52f1 Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Mon, 22 Aug 2022 10:56:50 -0400 Subject: [PATCH 5/6] Added field name to get_match tests. --- tests/unit/test_specs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_specs.py b/tests/unit/test_specs.py index 21b4e5717..e7c17bf73 100644 --- a/tests/unit/test_specs.py +++ b/tests/unit/test_specs.py @@ -46,10 +46,11 @@ def test_get_type_match(): spec_list = ['Locket', 'drop', 'DEER'] test_entry = 'locket' - assert 'Locket' == specs.get_match(test_entry, spec_list) + field_name = 'field_name' + assert 'Locket' == specs.get_match(test_entry, spec_list, field_name) - with pytest.raises(specs.NoMatchException): - specs.get_match('a', ['b']) + with pytest.raises(specs.SpecificationException): + specs.get_match('a', ['b'], field_name) def test_validate_bundle_supported(): From d44ad3b4d58ca3a7dd5e122f154d588e2ec1f360 Mon Sep 17 00:00:00 2001 From: Kevin Lacaille Date: Mon, 22 Aug 2022 11:27:45 -0400 Subject: [PATCH 6/6] Validate bundle before item type. --- planet/order_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planet/order_request.py b/planet/order_request.py index 102e5ef6f..911927bc6 100644 --- a/planet/order_request.py +++ b/planet/order_request.py @@ -113,8 +113,8 @@ def product(item_ids: List[str], are not valid bundles or if item_type is not valid for the given bundle or fallback bundle. ''' - item_type = specs.validate_item_type(item_type, product_bundle) validated_product_bundle = specs.validate_bundle(product_bundle) + item_type = specs.validate_item_type(item_type, validated_product_bundle) if fallback_bundle is not None: validated_fallback_bundle = specs.validate_bundle(fallback_bundle)