Skip to content

Commit 03aa51e

Browse files
Merge pull request #1023 from planetlabs/revert-1022-revert-1021-sh-support
Subscriptions API Hosting Support for Sentinel Hub
2 parents 35fddd3 + 891b2bb commit 03aa51e

File tree

5 files changed

+112
-8
lines changed

5 files changed

+112
-8
lines changed

planet/cli/subscriptions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,6 @@ async def list_subscription_results_cmd(ctx,
249249
help='Source JSON. Can be a string, filename, or - for stdin.')
250250
@click.option(
251251
'--delivery',
252-
required=True,
253252
type=types.JSON(),
254253
help=("Delivery configuration, including credentials for a cloud "
255254
"storage provider, to enable cloud delivery of data. Can be a "
@@ -262,6 +261,10 @@ async def list_subscription_results_cmd(ctx,
262261
'--tools',
263262
type=types.JSON(),
264263
help='Toolchain JSON. Can be a string, filename, or - for stdin.')
264+
@click.option(
265+
'--hosting',
266+
type=types.JSON(),
267+
help='Hosting JSON. Can be a string, a filename, or - for stdin.')
265268
@click.option(
266269
'--clip-to-source',
267270
is_flag=True,
@@ -273,6 +276,7 @@ def request(name,
273276
delivery,
274277
notifications,
275278
tools,
279+
hosting,
276280
clip_to_source,
277281
pretty):
278282
"""Generate a subscriptions request.
@@ -287,6 +291,7 @@ def request(name,
287291
delivery,
288292
notifications=notifications,
289293
tools=tools,
294+
hosting=hosting,
290295
clip_to_source=clip_to_source)
291296
echo_json(res, pretty)
292297

planet/subscription_request.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@
4949

5050
def build_request(name: str,
5151
source: Mapping,
52-
delivery: Mapping,
52+
delivery: Optional[Mapping] = None,
5353
notifications: Optional[Mapping] = None,
5454
tools: Optional[List[Mapping]] = None,
55+
hosting: Optional[Mapping] = None,
5556
clip_to_source: Optional[bool] = False) -> dict:
5657
"""Construct a Subscriptions API request.
5758
@@ -65,6 +66,7 @@ def build_request(name: str,
6566
notifications: Specify notifications via email/webhook.
6667
tools: Tools to apply to the products. The order of operation
6768
is determined by the service.
69+
hosting: A hosting destination e.g. Sentinel Hub.
6870
clip_to_source: whether to clip to the source geometry or not
6971
(the default). If True a clip configuration will be added to
7072
the list of requested tools unless an existing clip tool
@@ -106,17 +108,20 @@ def build_request(name: str,
106108
107109
delivery = amazon_s3(ACCESS_KEY_ID, SECRET_ACCESS_KEY, "test", "us-east-1")
108110
111+
hosting = sentinel_hub("2716077c-191e-4e47-9e3f-01c9c429f88d")
112+
109113
subscription_request = build_request(
110-
"test_subscription", source=source, delivery=delivery
114+
"test_subscription", source=source, delivery=delivery, hosting=hosting
111115
)
112116
```
113117
"""
114-
# Because source and delivery are Mappings we must make copies for
118+
# Because source is a Mapping we must make copies for
115119
# the function's return value. dict() shallow copies a Mapping
116120
# and returns a new dict.
117-
details = {
118-
"name": name, "source": dict(source), "delivery": dict(delivery)
119-
}
121+
details = {"name": name, "source": dict(source)}
122+
123+
if delivery:
124+
details['delivery'] = dict(delivery)
120125

121126
if notifications:
122127
details['notifications'] = dict(notifications)
@@ -145,6 +150,9 @@ def build_request(name: str,
145150

146151
details['tools'] = tool_list
147152

153+
if hosting:
154+
details['hosting'] = dict(hosting)
155+
148156
return details
149157

150158

@@ -735,3 +743,24 @@ def cloud_filter_tool(
735743
}
736744

737745
return _tool("cloud_filter", result)
746+
747+
748+
def _hosting(type: str, parameters: dict) -> dict:
749+
return {"type": type, "parameters": parameters}
750+
751+
752+
def sentinel_hub(collection_id: Optional[str]) -> dict:
753+
"""Specify a Sentinel Hub hosting destination.
754+
755+
Requires the user to have a Sentinel Hub account linked with their Planet
756+
account. Subscriptions API will create a new collection to deliver data to
757+
if collection_id is omitted from the request.
758+
759+
Parameters:
760+
collection_id: Sentinel Hub collection
761+
"""
762+
763+
parameters = {}
764+
if collection_id:
765+
parameters['collection_id'] = collection_id
766+
return _hosting("sentinel_hub", parameters)

tests/integration/test_subscriptions_api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,18 @@ async def test_create_subscription_success():
204204
assert sub['name'] == 'test'
205205

206206

207+
@pytest.mark.anyio
208+
@create_mock
209+
async def test_create_subscription_with_hosting_success():
210+
"""Subscription is created, description has the expected items."""
211+
async with Session() as session:
212+
client = SubscriptionsClient(session, base_url=TEST_URL)
213+
sub = await client.create_subscription({
214+
'name': 'test', 'source': 'test', 'hosting': 'yes, please'
215+
})
216+
assert sub['name'] == 'test'
217+
218+
207219
@pytest.mark.anyio
208220
@failing_api_mock
209221
async def test_cancel_subscription_failure():

tests/integration/test_subscriptions_cli.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,16 @@ def test_subscriptions_create_failure(invoke):
9595
# It must be updated when we begin to test against a more strict
9696
# imitation of the Planet Subscriptions API.
9797
GOOD_SUB_REQUEST = {'name': 'lol', 'delivery': True, 'source': 'wut'}
98+
GOOD_SUB_REQUEST_WITH_HOSTING = {
99+
'name': 'lol', 'source': 'wut', 'hosting': True
100+
}
98101

99102

100103
@pytest.mark.parametrize('cmd_arg, runner_input',
101104
[('-', json.dumps(GOOD_SUB_REQUEST)),
102-
(json.dumps(GOOD_SUB_REQUEST), None)])
105+
(json.dumps(GOOD_SUB_REQUEST), None),
106+
('-', json.dumps(GOOD_SUB_REQUEST_WITH_HOSTING)),
107+
(json.dumps(GOOD_SUB_REQUEST_WITH_HOSTING), None)])
103108
@create_mock
104109
def test_subscriptions_create_success(invoke, cmd_arg, runner_input):
105110
"""Subscriptions creation succeeds with a valid subscription request."""

tests/unit/test_subscription_request.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,59 @@ def test_build_request_clip_to_source_failure(geom_geojson):
119119
)
120120

121121

122+
def test_build_request_host_sentinel_hub_with_collection(geom_geojson):
123+
source = {
124+
"type": "catalog",
125+
"parameters": {
126+
"geometry": geom_geojson,
127+
"start_time": "2021-03-01T00:00:00Z",
128+
"end_time": "2023-11-01T00:00:00Z",
129+
"rrule": "FREQ=MONTHLY;BYMONTH=3,4,5,6,7,8,9,10",
130+
"item_types": ["PSScene"],
131+
"asset_types": ["ortho_analytic_4b"]
132+
}
133+
}
134+
135+
hosting = {"type": "sentinel-hub"}
136+
137+
res = subscription_request.build_request('test',
138+
source=source,
139+
hosting=hosting)
140+
141+
expected = {"name": "test", "source": source, "hosting": hosting}
142+
143+
assert res == expected
144+
145+
146+
def test_build_request_host_sentinel_hub_no_collection(geom_geojson):
147+
source = {
148+
"type": "catalog",
149+
"parameters": {
150+
"geometry": geom_geojson,
151+
"start_time": "2021-03-01T00:00:00Z",
152+
"end_time": "2023-11-01T00:00:00Z",
153+
"rrule": "FREQ=MONTHLY;BYMONTH=3,4,5,6,7,8,9,10",
154+
"item_types": ["PSScene"],
155+
"asset_types": ["ortho_analytic_4b"]
156+
}
157+
}
158+
159+
hosting = {
160+
"type": "sentinel-hub",
161+
"parameters": {
162+
"collection_id": "4c9af036-4274-4a97-bf0d-eb2a7853330d"
163+
}
164+
}
165+
166+
res = subscription_request.build_request('test',
167+
source=source,
168+
hosting=hosting)
169+
170+
expected = {"name": "test", "source": source, "hosting": hosting}
171+
172+
assert res == expected
173+
174+
122175
def test_catalog_source_success(geom_geojson):
123176
res = subscription_request.catalog_source(
124177
item_types=["PSScene"],

0 commit comments

Comments
 (0)