From 988ffe45cf6b33b707d1781de9de3b718e3c2e52 Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Thu, 23 May 2024 14:54:18 -0600 Subject: [PATCH 1/6] Fix code example in subscription build_request documentation --- planet/subscription_request.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/planet/subscription_request.py b/planet/subscription_request.py index 5d773ca0..c7a73812 100644 --- a/planet/subscription_request.py +++ b/planet/subscription_request.py @@ -86,9 +86,8 @@ def build_request(name: str, ClientError: when a valid Subscriptions API request can't be constructed. - Examples: + Example:: - ```python from datetime import datetime from planet.subscription_request import build_request, catalog_source, amazon_s3 @@ -114,7 +113,6 @@ def build_request(name: str, subscription_request = build_request( "test_subscription", source=source, delivery=delivery, hosting=hosting ) - ``` """ # Because source is a Mapping we must make copies for # the function's return value. dict() shallow copies a Mapping From f6f3002bf542dbc4c94a4f5167dad3c0c7414f0d Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Tue, 28 May 2024 09:55:07 -0600 Subject: [PATCH 2/6] Add hosting parameter for Orders API to CLI / SDK --- planet/cli/orders.py | 28 ++++++++++++- planet/cli/subscriptions.py | 4 +- planet/order_request.py | 18 +++++++- tests/integration/test_orders_cli.py | 62 ++++++++++++++++++++++++++++ tests/unit/test_order_request.py | 12 ++++++ 5 files changed, 119 insertions(+), 5 deletions(-) diff --git a/planet/cli/orders.py b/planet/cli/orders.py index 6a6b5111..a7777ca3 100644 --- a/planet/cli/orders.py +++ b/planet/cli/orders.py @@ -22,6 +22,7 @@ from planet import OrdersClient # allow mocking from . import types from .cmds import coro, translate_exceptions +from ..order_request import sentinel_hub from .io import echo_json from .options import limit, pretty from .session import CliSession @@ -206,7 +207,7 @@ async def download(ctx, order_id, overwrite, directory, checksum): @coro @click.argument("request", type=types.JSON()) @pretty -async def create(ctx, request: str, pretty): +async def create(ctx, request, pretty, **kwargs): """Create an order. This command outputs the created order description, optionally @@ -214,7 +215,19 @@ async def create(ctx, request: str, pretty): REQUEST is the full description of the order to be created. It must be JSON and can be specified a json string, filename, or '-' for stdin. + + Other flag options are hosting and collection_id. The hosting flag + specifies the hosting type, and the collection_id flag specifies the + collection ID for Sentinel Hub. If the collection_id is omitted, a new + collection will be created. """ + + hosting = kwargs.get('hosting') + collection_id = kwargs.get('collection_id') + + if hosting == "sentinel_hub": + request["hosting"] = sentinel_hub(collection_id) + async with orders_client(ctx) as cl: order = await cl.create_order(request) @@ -281,6 +294,13 @@ async def create(ctx, request: str, pretty): help="""Include or exclude metadata in SpatioTemporal Asset Catalog (STAC) format. Not specifying either defaults to including it (--stac), except for orders with google_earth_engine delivery""") +@click.option('--hosting', + type=click.Choice(['sentinel_hub']), + help='Hosting for data delivery. ' + 'Currently, only "sentinel_hub" is supported.') +@click.option('--collection_id', + help='Collection ID for Sentinel Hub hosting. ' + 'If omitted, a new collection will be created.') @pretty async def request(ctx, item_type, @@ -295,6 +315,8 @@ async def request(ctx, single_archive, delivery, stac, + hosting, + collection_id, pretty): """Generate an order request. @@ -337,6 +359,8 @@ async def request(ctx, delivery=delivery, notifications=notifications, tools=tools, - stac=stac_json) + stac=stac_json, + hosting=hosting, + collection_id=collection_id) echo_json(request, pretty) diff --git a/planet/cli/subscriptions.py b/planet/cli/subscriptions.py index 01eec56e..f22f11fa 100644 --- a/planet/cli/subscriptions.py +++ b/planet/cli/subscriptions.py @@ -101,7 +101,7 @@ async def list_subscriptions_cmd(ctx, status, limit, pretty): ) @click.option("--collection-id", default=None, - help='Collection ID for Sentinel Hub.' + help='Collection ID for Sentinel Hub hosting. ' 'If omitted, a new collection will be created.') @pretty @click.pass_context @@ -302,7 +302,7 @@ async def list_subscription_results_cmd(ctx, help="Clip to the source geometry without specifying a clip tool.") @click.option("--collection-id", default=None, - help='Collection ID for Sentinel Hub.' + help='Collection ID for Sentinel Hub hosting. ' 'If omitted, a new collection will be created.') @pretty def request(name, diff --git a/planet/order_request.py b/planet/order_request.py index 074d7bdd..8228c2e5 100644 --- a/planet/order_request.py +++ b/planet/order_request.py @@ -30,7 +30,9 @@ def build_request(name: str, notifications: Optional[dict] = None, order_type: Optional[str] = None, tools: Optional[List[dict]] = None, - stac: Optional[dict] = None) -> dict: + stac: Optional[dict] = None, + hosting: Optional[str] = None, + collection_id: Optional[str] = None) -> dict: """Prepare an order request. ```python @@ -65,6 +67,9 @@ def build_request(name: str, order_type: Accept a partial order, indicated by 'partial'. tools: Tools to apply to the products. Order defines the toolchain order of operatations. + stac: Include STAC metadata. + hosting: A hosting destination. E.g. Sentinel Hub. + collection_id: A Sentinel Hub collection ID. Raises: planet.specs.SpecificationException: If order_type is not a valid @@ -91,6 +96,9 @@ def build_request(name: str, if stac: details['metadata'] = stac + if hosting == 'sentinel_hub': + details['hosting'] = sentinel_hub(collection_id) + return details @@ -534,3 +542,11 @@ def band_math_tool(b1: str, # e.g. {"b1": "b1", "b2":"arctan(b1)"} if b1 and b2 are specified parameters = dict((k, v) for k, v in locals().items() if v) return _tool('bandmath', parameters) + + +def sentinel_hub(collection_id: Optional[str] = None) -> dict: + """Specify a Sentinel Hub hosting destination.""" + params = {} + if collection_id: + params['collection_id'] = collection_id + return {'sentinel_hub': params} diff --git a/tests/integration/test_orders_cli.py b/tests/integration/test_orders_cli.py index 7bc057ba..c873877a 100644 --- a/tests/integration/test_orders_cli.py +++ b/tests/integration/test_orders_cli.py @@ -768,3 +768,65 @@ def test_cli_orders_request_no_stac(invoke): }] } assert order_request == json.loads(result.output) + + +@respx.mock +def test_cli_orders_request_hosting_sentinel_hub(invoke, stac_json): + + result = invoke([ + 'request', + '--item-type=PSScene', + '--bundle=visual', + '--name=test', + '20220325_131639_20_2402', + '--hosting=sentinel_hub', + ]) + + order_request = { + "name": + "test", + "products": [{ + "item_ids": ["20220325_131639_20_2402"], + "item_type": "PSScene", + "product_bundle": "visual", + }], + "metadata": + stac_json, + "hosting": { + "sentinel_hub": {} + } + } + assert order_request == json.loads(result.output) + + +@respx.mock +def test_cli_orders_request_hosting_sentinel_hub_collection_id( + invoke, stac_json): + + result = invoke([ + 'request', + '--item-type=PSScene', + '--bundle=visual', + '--name=test', + '20220325_131639_20_2402', + '--hosting=sentinel_hub', + '--collection_id=1234' + ]) + + order_request = { + "name": + "test", + "products": [{ + "item_ids": ["20220325_131639_20_2402"], + "item_type": "PSScene", + "product_bundle": "visual", + }], + "metadata": + stac_json, + "hosting": { + "sentinel_hub": { + "collection_id": "1234" + } + } + } + assert order_request == json.loads(result.output) diff --git a/tests/unit/test_order_request.py b/tests/unit/test_order_request.py index 46a01c48..b3f0f8a2 100644 --- a/tests/unit/test_order_request.py +++ b/tests/unit/test_order_request.py @@ -329,3 +329,15 @@ def test_no_archive_items_without_type(): assert "archive_type" not in delivery_config assert "archive_filename" not in delivery_config assert "single_archive" not in delivery_config + + +def test_sentinel_hub(): + sh_config = order_request.sentinel_hub() + expected = {'sentinel_hub': {}} + assert sh_config == expected + + +def test_sentinel_hub_collection_id(): + sh_config = order_request.sentinel_hub("1234") + expected = {'sentinel_hub': {'collection_id': "1234"}} + assert sh_config == expected From 28f2216e8fbaea5c0aa5104f4909d4be7982d978 Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Tue, 4 Jun 2024 13:05:45 -0600 Subject: [PATCH 3/6] Reformat e.g. in docstring --- 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 8228c2e5..ad2166fa 100644 --- a/planet/order_request.py +++ b/planet/order_request.py @@ -68,7 +68,7 @@ def build_request(name: str, tools: Tools to apply to the products. Order defines the toolchain order of operatations. stac: Include STAC metadata. - hosting: A hosting destination. E.g. Sentinel Hub. + hosting: A hosting destination (e.g. Sentinel Hub). collection_id: A Sentinel Hub collection ID. Raises: From 1d67e113fb5cd888ea64335c420f02a778a0f8b2 Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Tue, 4 Jun 2024 15:07:09 -0600 Subject: [PATCH 4/6] Add documentation for hosting parameter to cli-subscriptions.md --- docs/cli/cli-orders.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/cli/cli-orders.md b/docs/cli/cli-orders.md index c9a1e248..ba11f07c 100644 --- a/docs/cli/cli-orders.md +++ b/docs/cli/cli-orders.md @@ -176,6 +176,26 @@ planet orders request \ *New in version 2.1* +#### Sentinel Hub Hosting + +You can deliver your orders directly to Sentinel Hub using the hosting options. + +``` +planet orders request \ + --item-type PSScene \ + --bundle analytic_sr_udm2 \ + --name 'My First Order' \ + 20220605_124027_64_242b \ + --hosting sentinel_hub \ + --collection_id ba8f7274-aacc-425e-8a38-e21517bfbeff +``` + +- The --hosting option is optional and currently supports sentinel_hub as its only value. +- The --collection_id is also optional. If you decide to use this, ensure that the order request and the collection have matching bands. If you're unsure, allow the system to create a new collection for you by omitting the --collection_id option. This will ensure the newly set-up collection is configured correctly, and you can subsequently add items to this collection as needed. + +For more information on Sentinel Hub hosting, see the [Orders API documentation](https://developers.planet.com/apis/orders/delivery/#delivery-to-sentinel-hub-collection) and the [Linking Planet User to Sentinel Hub User +](https://support.planet.com/hc/en-us/articles/16550358397469-Linking-Planet-User-to-Sentinel-Hub-User) support post. + ### Save an Order Request The above command just prints out the necessary JSON to create an order. To actually use it you can @@ -236,6 +256,16 @@ Note the default output will be a bit more 'flat' - if you'd like the above form command-line just use `jq` as above: `planet orders create request-1.json | jq` (but remember if you run that command again it will create a second order). +#### Sentinel Hub Hosting + +For convenience, `planet orders create` accepts the same `--hosting` and `--collection_id` options that [`planet orders request`](#sentinel-hub-hosting) does. + +```sh +planet orders create request-1.json \ + --hosting sentinel_hub \ + --collection_id ba8f7274-aacc-425e-8a38-e21517bfbeff +``` + ### Create Request and Order in One Call Using a unix command called a 'pipe', which looks like `|`, you can skip the step of saving to disk, From db7ff5763459c3b6a321d188241a05153d1de7dc Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Tue, 4 Jun 2024 16:10:24 -0600 Subject: [PATCH 5/6] Move and correct hosting documentation in cli-subscriptions.md --- docs/cli/cli-subscriptions.md | 47 +++++++++++------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/docs/cli/cli-subscriptions.md b/docs/cli/cli-subscriptions.md index af5101fc..c13cdb61 100644 --- a/docs/cli/cli-subscriptions.md +++ b/docs/cli/cli-subscriptions.md @@ -120,17 +120,6 @@ planet subscriptions create my-subscription.json !!!note "Note" The above command assumes that you’ve saved the subscriptions JSON as `my-subscription.json` and that you’ve replaced the delivery information with your own bucket and credentials. -#### Create a Subscription with Hosting and Collection ID - -In addition to the basic subscription creation process, you can now specify hosting options and a collection ID directly in the create command. - -```sh -planet subscriptions create my-subscription.json --hosting sentinel_hub --collection_id ba8f7274-aacc-425e-8a38-e21517bfbeff -``` - -- The --hosting option is optional and currently supports sentinel_hub as its only value. -- The --collection_id is also optional. If you decide to use this, ensure that the subscription request and the collection have matching bands. If you're unsure, allow the system to create a new collection for you by omitting the --collection_id option. This will ensure the newly set-up collection is configured correctly, and you can subsequently add items to this collection as needed. - ### List Subscriptions Now that you’ve got a subscription working you can make use of the other commands. @@ -487,42 +476,36 @@ The main documentation page also has the parameters for Google Cloud, AWS and Or ### Subscriptions Request -When creating a new subscription, you can include hosting options directly using the --hosting and --collection-id flags. - -- The --hosting option is optional and currently supports sentinel_hub as its only value. -- The --collection_id is also optional. If you decide to use this, ensure that the subscription request and the collection have matching bands. If you're unsure, allow the system to create a new collection for you by omitting the --collection_id option. This will ensure the newly set-up collection is configured correctly, and you can subsequently add items to this collection as needed. -- You may also input --hosting as a JSON file. The file should be formatted: - -```json -"hosting": { - "parameters": { - "collection_id": "4bdef85c-3f50-4006-a713-2350da665f80" - }, - "type": "sentinel_hub" -}, -``` - Once you’ve got all your sub-blocks of JSON saved you’re ready to make a complete subscriptions request with the `subscriptions request` command: +The above will print it nicely out so you can see the full request. You can write it out +as a file, or pipe it directly into `subscriptions create` or `subscriptions update`: + ```sh planet subscriptions request \ --name 'First Subscription' \ --source request-catalog.json \ --tools tools.json \ --delivery cloud-delivery.json \ - --hosting sentinel_hub \ - -- collection_id 4bdef85c-3f50-4006-a713-2350da665f80 \ - --pretty + | planet subscriptions create - ``` -The above will print it nicely out so you can see the full request. You can write it out -as a file, or pipe it directly into `subscriptions create` or `subscriptions update`: +#### Sentinel Hub Hosting + +When creating a new subscription, you can include hosting options directly using the --hosting and --collection-id flags. + +- The --hosting option is optional and currently supports sentinel_hub as its only value. +- The --collection_id is also optional. If you decide to use this, ensure that the subscription request and the collection have matching bands. If you're unsure, allow the system to create a new collection for you by omitting the --collection_id option. This will ensure the newly set-up collection is configured correctly, and you can subsequently add items to this collection as needed. +- You may also input --hosting as a JSON file. The file should be formatted: ```sh planet subscriptions request \ --name 'First Subscription' \ --source request-catalog.json \ --tools tools.json \ - --delivery cloud-delivery.json \ + --hosting sentinel_hub \ | planet subscriptions create - ``` + +For more information on Sentinel Hub hosting, see the [Subscriptions API documentation](https://developers.planet.com/docs/subscriptions/delivery/#delivery-to-sentinel-hub-collection) and the [Linking Planet User to Sentinel Hub User +](https://support.planet.com/hc/en-us/articles/16550358397469-Linking-Planet-User-to-Sentinel-Hub-User) support post. \ No newline at end of file From ef53d61428e0de3ebcb4d5c95a5e52f8147d4cb8 Mon Sep 17 00:00:00 2001 From: Gideon Richter Date: Tue, 4 Jun 2024 16:27:12 -0600 Subject: [PATCH 6/6] Add hosting and collection_id parameters to planet orders create command --- planet/cli/orders.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/planet/cli/orders.py b/planet/cli/orders.py index a7777ca3..db7734ec 100644 --- a/planet/cli/orders.py +++ b/planet/cli/orders.py @@ -206,6 +206,18 @@ async def download(ctx, order_id, overwrite, directory, checksum): @translate_exceptions @coro @click.argument("request", type=types.JSON()) +@click.option( + "--hosting", + type=click.Choice([ + "sentinel_hub", + ]), + default=None, + help='Hosting type. Currently, only "sentinel_hub" is supported.', +) +@click.option("--collection-id", + default=None, + help='Collection ID for Sentinel Hub hosting. ' + 'If omitted, a new collection will be created.') @pretty async def create(ctx, request, pretty, **kwargs): """Create an order.