Skip to content

Commit 0f18793

Browse files
Add Subscriptions API suspend and reactivate paths (#1208)
* add subs suspend/reactivate support * update comment about forwarded apierrors * update payloads for bulk suspend/reactivate * type check
1 parent 7e8ffcc commit 0f18793

File tree

6 files changed

+850
-6
lines changed

6 files changed

+850
-6
lines changed

docs/cli/cli-subscriptions.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ To list currently active subscriptions:
157157
planet subscriptions list --status running
158158
```
159159

160+
To list suspended subscriptions:
161+
```sh
162+
planet subscriptions list --status suspended
163+
```
164+
160165
To list subscriptions with the `catalog` source type:
161166
```sh
162167
planet subscriptions list --source-type catalog
@@ -313,6 +318,102 @@ planet subscriptions cancel cb817760-1f07-4ee7-bba6-bcac5346343f
313318
That will stop the subscription from producing any more results, but it will stay in the system so you can
314319
continue to list and get it.
315320

321+
### Suspend Subscription
322+
323+
You can temporarily pause a subscription without canceling it. This is useful when you want to stop processing
324+
and delivery for a period of time but plan to resume later. Suspended subscriptions can be reactivated at any time.
325+
326+
To suspend a single subscription:
327+
328+
```sh
329+
planet subscriptions suspend cb817760-1f07-4ee7-bba6-bcac5346343f
330+
```
331+
332+
You can optionally include details explaining why the subscription is being suspended:
333+
334+
```sh
335+
planet subscriptions suspend cb817760-1f07-4ee7-bba6-bcac5346343f \
336+
--details "Pausing during maintenance window"
337+
```
338+
339+
The suspend command returns the updated subscription JSON, showing the new status.
340+
341+
### Bulk Suspend Subscriptions
342+
343+
The `bulk-suspend` command provides flexible options for suspending multiple subscriptions at once.
344+
345+
To suspend all of your subscriptions (default behavior):
346+
347+
```sh
348+
planet subscriptions bulk-suspend
349+
```
350+
351+
To suspend specific subscriptions:
352+
353+
```sh
354+
planet subscriptions bulk-suspend \
355+
--subscription-ids cb817760-1f07-4ee7-bba6-bcac5346343f,a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6
356+
```
357+
358+
To suspend all subscriptions across your organization (requires organization admin permissions):
359+
360+
```sh
361+
planet subscriptions bulk-suspend --all
362+
```
363+
364+
You can include details explaining the reason for suspension:
365+
366+
```sh
367+
planet subscriptions bulk-suspend --details "System maintenance scheduled"
368+
```
369+
370+
Or combine with specific subscription IDs:
371+
372+
```sh
373+
planet subscriptions bulk-suspend \
374+
--subscription-ids cb817760-1f07-4ee7-bba6-bcac5346343f,a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6 \
375+
--details "Pausing during data migration"
376+
```
377+
378+
**Note:** The `--subscription-ids` and `--all` flags are mutually exclusive. If neither flag is provided,
379+
all of your subscriptions will be suspended.
380+
381+
### Reactivate Subscription
382+
383+
To resume a suspended subscription, use the reactivate command:
384+
385+
```sh
386+
planet subscriptions reactivate cb817760-1f07-4ee7-bba6-bcac5346343f
387+
```
388+
389+
The subscription will resume processing and delivering imagery based on its original configuration.
390+
391+
### Bulk Reactivate Subscriptions
392+
393+
The `bulk-reactivate` command provides flexible options for reactivating multiple suspended subscriptions.
394+
395+
To reactivate all of your suspended subscriptions (default behavior):
396+
397+
```sh
398+
planet subscriptions bulk-reactivate
399+
```
400+
401+
To reactivate specific subscriptions:
402+
403+
```sh
404+
planet subscriptions bulk-reactivate \
405+
--subscription-ids cb817760-1f07-4ee7-bba6-bcac5346343f,a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6
406+
```
407+
408+
To reactivate all suspended subscriptions across your organization (requires organization admin permissions):
409+
410+
```sh
411+
planet subscriptions bulk-reactivate --all
412+
```
413+
414+
**Note:** The `--subscription-ids` and `--all` flags are mutually exclusive. If neither flag is provided,
415+
all of your suspended subscriptions will be reactivated.
416+
316417
## Subscription Request Conveniences
317418

318419
There are a couple of commands that can assist in creating the subscription JSON, used for creation and updates.

planet/cli/subscriptions.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,91 @@ async def cancel_subscription_cmd(ctx, subscription_id, pretty):
280280
_ = await client.cancel_subscription(subscription_id)
281281

282282

283+
@subscriptions.command(name='suspend') # type: ignore
284+
@click.argument('subscription_id')
285+
@click.option('--details',
286+
help='Optional details explaining suspension reason')
287+
@pretty
288+
@click.pass_context
289+
@translate_exceptions
290+
@coro
291+
async def suspend_subscription_cmd(ctx, subscription_id, details, pretty):
292+
"""Suspends a subscription and prints the suspended subscription."""
293+
async with subscriptions_client(ctx) as client:
294+
sub = await client.suspend_subscription(subscription_id, details)
295+
echo_json(sub, pretty)
296+
297+
298+
@subscriptions.command(name='reactivate') # type: ignore
299+
@click.argument('subscription_id')
300+
@pretty
301+
@click.pass_context
302+
@translate_exceptions
303+
@coro
304+
async def reactivate_subscription_cmd(ctx, subscription_id, pretty):
305+
"""Reactivates a subscription."""
306+
async with subscriptions_client(ctx) as client:
307+
_ = await client.reactivate_subscription(subscription_id)
308+
309+
310+
@subscriptions.command(name='bulk-suspend') # type: ignore
311+
@click.option('--subscription-ids',
312+
type=types.CommaSeparatedString(),
313+
help='Comma-separated list of subscription IDs to suspend')
314+
@click.option('--all',
315+
'all_flag',
316+
is_flag=True,
317+
help='Suspend all organization subscriptions (admin only)')
318+
@click.option('--details',
319+
help='Optional details explaining suspension reason')
320+
@pretty
321+
@click.pass_context
322+
@translate_exceptions
323+
@coro
324+
async def bulk_suspend_subscriptions_cmd(ctx,
325+
subscription_ids,
326+
all_flag,
327+
details,
328+
pretty):
329+
"""Suspends multiple subscriptions.
330+
331+
By default (no flags), suspends all of the user's subscriptions.
332+
Use --subscription-ids to suspend specific subscriptions.
333+
Use --all to suspend all organization subscriptions (requires admin).
334+
"""
335+
async with subscriptions_client(ctx) as client:
336+
_ = await client.bulk_suspend_subscriptions(subscription_ids,
337+
details,
338+
all_flag)
339+
340+
341+
@subscriptions.command(name='bulk-reactivate') # type: ignore
342+
@click.option('--subscription-ids',
343+
type=types.CommaSeparatedString(),
344+
help='Comma-separated list of subscription IDs to reactivate')
345+
@click.option('--all',
346+
'all_flag',
347+
is_flag=True,
348+
help='Reactivate all organization subscriptions (admin only)')
349+
@pretty
350+
@click.pass_context
351+
@translate_exceptions
352+
@coro
353+
async def bulk_reactivate_subscriptions_cmd(ctx,
354+
subscription_ids,
355+
all_flag,
356+
pretty):
357+
"""Reactivates multiple subscriptions.
358+
359+
By default (no flags), reactivates all of the user's subscriptions.
360+
Use --subscription-ids to reactivate specific subscriptions.
361+
Use --all to reactivate all organization subscriptions (requires admin).
362+
"""
363+
async with subscriptions_client(ctx) as client:
364+
_ = await client.bulk_reactivate_subscriptions(subscription_ids,
365+
all_flag)
366+
367+
283368
@subscriptions.command(name='update') # type: ignore
284369
@click.argument('subscription_id')
285370
@click.argument('request', type=types.JSON())

planet/clients/subscriptions.py

Lines changed: 168 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,177 @@ async def cancel_subscription(self, subscription_id: str) -> None:
257257
except ClientError: # pragma: no cover
258258
raise
259259

260+
async def suspend_subscription(self,
261+
subscription_id: str,
262+
details: Optional[str] = None) -> dict:
263+
"""Suspend a Subscription.
264+
265+
Args:
266+
subscription_id (str): id of subscription to suspend.
267+
details (str): optional details explaining the reason
268+
for suspension.
269+
270+
Returns:
271+
dict: the suspended subscription.
272+
273+
Raises:
274+
APIError: on an API server error.
275+
ClientError: on a client error.
276+
"""
277+
url = f'{self._base_url}/{subscription_id}/suspend'
278+
json_body = {'details': details} if details is not None else None
279+
280+
try:
281+
resp = await self._session.request(method='POST',
282+
url=url,
283+
json=json_body)
284+
# Forward APIError. We don't strictly need this clause, but it
285+
# makes our intent clear.
286+
except APIError:
287+
raise
288+
except ClientError: # pragma: no cover
289+
raise
290+
else:
291+
return resp.json()
292+
293+
async def reactivate_subscription(self, subscription_id: str) -> None:
294+
"""Reactivate a Subscription.
295+
296+
Args:
297+
subscription_id (str): id of subscription to reactivate.
298+
299+
Returns:
300+
None
301+
302+
Raises:
303+
APIError: on an API server error.
304+
ClientError: on a client error.
305+
"""
306+
url = f'{self._base_url}/{subscription_id}/reactivate'
307+
308+
try:
309+
_ = await self._session.request(method='POST', url=url)
310+
# Forward APIError. We don't strictly need this clause, but it
311+
# makes our intent clear.
312+
except APIError:
313+
raise
314+
except ClientError: # pragma: no cover
315+
raise
316+
317+
async def bulk_suspend_subscriptions(
318+
self,
319+
subscription_ids: Optional[List[str]] = None,
320+
details: Optional[str] = None,
321+
all_subscriptions: bool = False) -> None:
322+
"""Suspend multiple Subscriptions.
323+
324+
This method supports three modes of operation:
325+
326+
1. Suspend specific subscriptions: provide subscription_ids
327+
2. Suspend all user's subscriptions: call with no parameters
328+
3. Suspend all organization subscriptions: set all_subscriptions=True
329+
(organization admin only)
330+
331+
Args:
332+
subscription_ids (List[str]): list of subscription ids
333+
to suspend. If not provided and all_subscriptions is False,
334+
suspends all of the user's subscriptions.
335+
details (str): optional details explaining the reason
336+
for suspension.
337+
all_subscriptions (bool): if True, suspend all
338+
subscriptions for the organization (requires organization
339+
admin permissions). Mutually exclusive with subscription_ids.
340+
341+
Returns:
342+
None
343+
344+
Raises:
345+
ClientError: if both subscription_ids and all_subscriptions are
346+
provided.
347+
APIError: on an API server error.
348+
"""
349+
if subscription_ids and all_subscriptions:
350+
raise ClientError(
351+
'Cannot specify both subscription_ids and all_subscriptions')
352+
353+
url = f'{self._base_url}/suspend'
354+
params = {'user_id': 'all'} if all_subscriptions else None
355+
payload: Dict[str, Any] = {}
356+
if subscription_ids is not None:
357+
payload["subscription_ids"] = subscription_ids
358+
if details is not None:
359+
payload["details"] = details
360+
json_body: Optional[Dict[str, Any]] = payload or None
361+
362+
try:
363+
_ = await self._session.request(method='POST',
364+
url=url,
365+
json=json_body,
366+
params=params)
367+
# Forward APIError. We don't strictly need this clause, but it
368+
# makes our intent clear.
369+
except APIError:
370+
raise
371+
except ClientError: # pragma: no cover
372+
raise
373+
374+
async def bulk_reactivate_subscriptions(
375+
self,
376+
subscription_ids: Optional[List[str]] = None,
377+
all_subscriptions: bool = False) -> None:
378+
"""Reactivate multiple Subscriptions.
379+
380+
This method supports three modes of operation:
381+
382+
1. Reactivate specific subscriptions: provide subscription_ids
383+
2. Reactivate all user's subscriptions: call with no parameters
384+
3. Reactivate all organization subscriptions: set all_subscriptions=True
385+
(organization admin only)
386+
387+
Args:
388+
subscription_ids (List[str]): list of subscription ids
389+
to reactivate. If not provided and all_subscriptions is False,
390+
reactivates all of the user's subscriptions.
391+
all_subscriptions (bool): if True, reactivate all
392+
subscriptions for the organization (requires organization
393+
admin permissions). Mutually exclusive with subscription_ids.
394+
395+
Returns:
396+
None
397+
398+
Raises:
399+
ClientError: if both subscription_ids and all_subscriptions are
400+
provided.
401+
APIError: on an API server error.
402+
"""
403+
if subscription_ids and all_subscriptions:
404+
raise ClientError(
405+
'Cannot specify both subscription_ids and all_subscriptions')
406+
407+
url = f'{self._base_url}/reactivate'
408+
params = {'user_id': 'all'} if all_subscriptions else None
409+
payload: Dict[str, Any] = {}
410+
if subscription_ids is not None:
411+
payload["subscription_ids"] = subscription_ids
412+
json_body: Optional[Dict[str, Any]] = payload or None
413+
414+
try:
415+
_ = await self._session.request(method='POST',
416+
url=url,
417+
json=json_body,
418+
params=params)
419+
# Forward APIError. We don't strictly need this clause, but it
420+
# makes our intent clear.
421+
except APIError:
422+
raise
423+
except ClientError: # pragma: no cover
424+
raise
425+
260426
async def update_subscription(self, subscription_id: str,
261427
request: dict) -> dict:
262428
"""Update (edit) a Subscription via PUT.
263429
264-
Args
430+
Args:
265431
subscription_id (str): id of the subscription to update.
266432
request (dict): subscription content for update, full
267433
payload is required.
@@ -293,7 +459,7 @@ async def patch_subscription(self, subscription_id: str,
293459
request: dict) -> dict:
294460
"""Update (edit) a Subscription via PATCH.
295461
296-
Args
462+
Args:
297463
subscription_id (str): id of the subscription to update.
298464
request (dict): subscription content for update, only
299465
attributes to update are required.

0 commit comments

Comments
 (0)