Skip to content

Commit

Permalink
Merge pull request #1043 from planetlabs/orders-hosting
Browse files Browse the repository at this point in the history
Add hosting parameter support for Orders API
  • Loading branch information
pl-gideon authored Jun 4, 2024
2 parents 162cc04 + ef53d61 commit a314d93
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 40 deletions.
30 changes: 30 additions & 0 deletions docs/cli/cli-orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
47 changes: 15 additions & 32 deletions docs/cli/cli-subscriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
40 changes: 38 additions & 2 deletions planet/cli/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -205,16 +206,40 @@ 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: str, pretty):
async def create(ctx, request, pretty, **kwargs):
"""Create an order.
This command outputs the created order description, optionally
pretty-printed.
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)

Expand Down Expand Up @@ -281,6 +306,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,
Expand All @@ -295,6 +327,8 @@ async def request(ctx,
single_archive,
delivery,
stac,
hosting,
collection_id,
pretty):
"""Generate an order request.
Expand Down Expand Up @@ -337,6 +371,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)
4 changes: 2 additions & 2 deletions planet/cli/subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 17 additions & 1 deletion planet/order_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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


Expand Down Expand Up @@ -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}
4 changes: 1 addition & 3 deletions planet/subscription_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
62 changes: 62 additions & 0 deletions tests/integration/test_orders_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
12 changes: 12 additions & 0 deletions tests/unit/test_order_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit a314d93

Please sign in to comment.