Skip to content

Commit dc51fcd

Browse files
authored
remove product bundles json from repo, and fetch it dynamically, add helper function to get valid item types and bundles in cli (#1094)
* get product bundles from orders endpoint, don't test every bundle and item type for maintainability * remove unused var * use lazy loading for bundles spec * tmp * remove skip validation, clean up error for user * clean up * remove PSOrthoTile from tests, replace with PSScene, mock bundles spec in async tests * update tests to use a mocked bundles spec, clean up linting/formatting * mock spec in data api cli tests * use real bundle name in mock * print to debug test failing in ci * print to debug test failing in ci * move fake bundles spec into conftest to avoid collision * add commands for show-bundles and show-item-types in cli with tests * remove 'show' from cli commands, rework error message * reformat cache dict * mock call to spec url in the fixture
1 parent 57add4c commit dc51fcd

19 files changed

+527
-1162
lines changed

design-docs/CLI-Data.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ with a utc offset using a file to specify the filter.
737737

738738
```
739739
$ planet data stats --utc-offset +1:00 \
740-
PSScene,PSOrthoTile day filter-desc.json
740+
PSScene,REOrthoTile day filter-desc.json
741741
742742
<unedited API response json>
743743
```

planet/cli/data.py

+26-14
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
SEARCH_SORT_DEFAULT,
2828
STATS_INTERVAL)
2929

30-
from planet.specs import (get_data_item_types,
31-
validate_data_item_type,
32-
SpecificationException)
30+
from planet.specs import (FetchBundlesSpecError,
31+
get_item_types,
32+
SpecificationException,
33+
validate_data_item_type)
3334

3435
from . import types
3536
from .cmds import coro, translate_exceptions
@@ -38,9 +39,6 @@
3839
from .session import CliSession
3940
from .validators import check_geom
4041

41-
valid_item_string = "Valid entries for ITEM_TYPES: " + "|".join(
42-
get_data_item_types())
43-
4442

4543
@asynccontextmanager
4644
async def data_client(ctx):
@@ -79,6 +77,8 @@ def check_item_types(ctx, param, item_types) -> Optional[List[dict]]:
7977
return item_types
8078
except SpecificationException as e:
8179
raise click.BadParameter(str(e))
80+
except FetchBundlesSpecError as e:
81+
raise click.ClickException(str(e))
8282

8383

8484
def check_item_type(ctx, param, item_type) -> Optional[List[dict]]:
@@ -88,12 +88,14 @@ def check_item_type(ctx, param, item_type) -> Optional[List[dict]]:
8888
validate_data_item_type(item_type)
8989
except SpecificationException as e:
9090
raise click.BadParameter(str(e))
91+
except FetchBundlesSpecError as e:
92+
raise click.ClickException(str(e))
9193

9294
return item_type
9395

9496

9597
def check_search_id(ctx, param, search_id) -> str:
96-
"""Ensure search id is a valix hex string"""
98+
"""Ensure search id is a valid hex string"""
9799
try:
98100
_ = DataClient._check_search_id(search_id)
99101
except exceptions.ClientError as e:
@@ -274,7 +276,7 @@ def filter(ctx,
274276
echo_json(filt, pretty)
275277

276278

277-
@data.command(epilog=valid_item_string) # type: ignore
279+
@data.command() # type: ignore
278280
@click.pass_context
279281
@translate_exceptions
280282
@coro
@@ -320,7 +322,7 @@ async def search(ctx, item_types, geom, filter, limit, name, sort, pretty):
320322
echo_json(item, pretty)
321323

322324

323-
@data.command(epilog=valid_item_string) # type: ignore
325+
@data.command() # type: ignore
324326
@click.pass_context
325327
@translate_exceptions
326328
@coro
@@ -417,7 +419,7 @@ async def search_run(ctx, search_id, sort, limit, pretty):
417419
echo_json(item, pretty)
418420

419421

420-
@data.command(epilog=valid_item_string) # type: ignore
422+
@data.command() # type: ignore
421423
@click.pass_context
422424
@translate_exceptions
423425
@coro
@@ -477,7 +479,7 @@ async def search_delete(ctx, search_id):
477479
await cl.delete_search(search_id)
478480

479481

480-
@data.command(epilog=valid_item_string) # type: ignore
482+
@data.command() # type: ignore
481483
@click.pass_context
482484
@translate_exceptions
483485
@coro
@@ -526,7 +528,7 @@ async def search_update(ctx,
526528
echo_json(items, pretty)
527529

528530

529-
@data.command(epilog=valid_item_string) # type: ignore
531+
@data.command() # type: ignore
530532
@click.pass_context
531533
@translate_exceptions
532534
@coro
@@ -588,7 +590,7 @@ async def asset_download(ctx,
588590
cl.validate_checksum(asset, path)
589591

590592

591-
@data.command(epilog=valid_item_string) # type: ignore
593+
@data.command() # type: ignore
592594
@click.pass_context
593595
@translate_exceptions
594596
@coro
@@ -602,7 +604,7 @@ async def asset_activate(ctx, item_type, item_id, asset_type):
602604
await cl.activate_asset(asset)
603605

604606

605-
@data.command(epilog=valid_item_string) # type: ignore
607+
@data.command() # type: ignore
606608
@click.pass_context
607609
@translate_exceptions
608610
@coro
@@ -652,3 +654,13 @@ async def asset_wait(ctx, item_type, item_id, asset_type, delay, max_attempts):
652654

653655
# TODO: search_run()".
654656
# TODO: item_get()".
657+
658+
659+
@data.command() # type: ignore
660+
@click.pass_context
661+
@translate_exceptions
662+
def item_types(ctx):
663+
"""Show valid item types."""
664+
click.echo("Valid item types:")
665+
for it in get_item_types():
666+
click.echo(f"- {it}")

planet/cli/orders.py

+63-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from contextlib import asynccontextmanager
1616
import logging
1717
from pathlib import Path
18+
from typing import List, Optional
1819

1920
import click
2021

@@ -26,10 +27,47 @@
2627
from .io import echo_json
2728
from .options import limit, pretty
2829
from .session import CliSession
30+
from ..specs import (FetchBundlesSpecError,
31+
get_bundle_names,
32+
get_item_types,
33+
SpecificationException,
34+
validate_bundle,
35+
validate_data_item_type)
2936

3037
LOGGER = logging.getLogger(__name__)
3138

3239

40+
def check_item_type(ctx, param, item_type):
41+
"""Validates the item type provided by comparing it to all supported
42+
item types."""
43+
try:
44+
validate_data_item_type(item_type)
45+
return item_type
46+
except SpecificationException as e:
47+
raise click.BadParameter(str(e))
48+
except FetchBundlesSpecError as e:
49+
raise click.ClickException(str(e))
50+
51+
52+
def check_bundle(ctx, param, bundle) -> Optional[List[dict]]:
53+
"""Validates the bundle provided by comparing it to all supported
54+
bundles."""
55+
56+
# Get the value of item_type from the context
57+
item_type = ctx.params.get('item_type')
58+
if not item_type:
59+
raise click.BadParameter("Item type is required to validate a bundle.")
60+
61+
try:
62+
validate_bundle(item_type, bundle)
63+
except SpecificationException as e:
64+
raise click.BadParameter(str(e))
65+
except FetchBundlesSpecError as e:
66+
raise click.ClickException(str(e))
67+
68+
return bundle
69+
70+
3371
@asynccontextmanager
3472
async def orders_client(ctx):
3573
base_url = ctx.obj['BASE_URL']
@@ -314,13 +352,13 @@ async def create(ctx, request, pretty, **kwargs):
314352
@click.option('--item-type',
315353
required=True,
316354
help='Item type for requested item ids.',
317-
type=click.Choice(planet.specs.get_item_types(),
318-
case_sensitive=False))
355+
type=str,
356+
callback=check_item_type)
319357
@click.option('--bundle',
320358
required=True,
321-
help='Asset type for the item.',
322-
type=click.Choice(planet.specs.get_product_bundles(),
323-
case_sensitive=False))
359+
help='Bundle type for the item.',
360+
type=str,
361+
callback=check_bundle)
324362
@click.option('--name',
325363
required=True,
326364
help='Order name. Does not need to be unique.',
@@ -444,3 +482,23 @@ async def request(ctx,
444482
create_configuration=create_configuration)
445483

446484
echo_json(request, pretty)
485+
486+
487+
@orders.command() # type: ignore
488+
@click.pass_context
489+
@translate_exceptions
490+
def item_types(ctx):
491+
"""Show valid item types for ordering."""
492+
click.echo("Valid item types:")
493+
for it in get_item_types():
494+
click.echo(f"- {it}")
495+
496+
497+
@orders.command() # type: ignore
498+
@click.pass_context
499+
@translate_exceptions
500+
def bundles(ctx):
501+
"""Show valid bundle names for ordering."""
502+
click.echo("Valid bundles:")
503+
for it in get_bundle_names():
504+
click.echo(f"- {it}")

planet/cli/subscriptions.py

+15-17
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@
1212
from planet.clients.subscriptions import SubscriptionsClient
1313
from .. import subscription_request
1414
from ..subscription_request import sentinel_hub
15-
from ..specs import get_item_types, validate_item_type, SpecificationException
15+
from ..specs import FetchBundlesSpecError, get_item_types, SpecificationException, validate_item_type
1616
from .validators import check_geom
1717

18-
ALL_ITEM_TYPES = get_item_types()
19-
valid_item_string = "Valid entries for ITEM_TYPES: " + "|".join(ALL_ITEM_TYPES)
20-
2118

2219
def check_item_types(ctx, param, item_types) -> Optional[List[dict]]:
2320
"""Validates each item types provided by comparing them to all supported
@@ -28,17 +25,8 @@ def check_item_types(ctx, param, item_types) -> Optional[List[dict]]:
2825
return item_types
2926
except SpecificationException as e:
3027
raise click.BadParameter(str(e))
31-
32-
33-
def check_item_type(ctx, param, item_type) -> Optional[List[dict]]:
34-
"""Validates the item type provided by comparing it to all supported
35-
item types."""
36-
try:
37-
validate_item_type(item_type)
38-
except SpecificationException as e:
39-
raise click.BadParameter(str(e))
40-
41-
return item_type
28+
except FetchBundlesSpecError as e:
29+
raise click.ClickException(str(e))
4230

4331

4432
@asynccontextmanager
@@ -418,7 +406,7 @@ def request(name,
418406
echo_json(res, pretty)
419407

420408

421-
@subscriptions.command(epilog=valid_item_string) # type: ignore
409+
@subscriptions.command() # type: ignore
422410
@translate_exceptions
423411
@click.option('--item-types',
424412
required=True,
@@ -496,7 +484,7 @@ def request_catalog(item_types,
496484
@click.option(
497485
'--var-id',
498486
required=True,
499-
help='A Planetary Variable ID. See documenation for all available IDs.')
487+
help='A Planetary Variable ID. See documentation for all available IDs.')
500488
@click.option(
501489
'--geometry',
502490
required=True,
@@ -528,3 +516,13 @@ def request_pv(var_type, var_id, geometry, start_time, end_time, pretty):
528516
end_time=end_time,
529517
)
530518
echo_json(res, pretty)
519+
520+
521+
@subscriptions.command() # type: ignore
522+
@click.pass_context
523+
@translate_exceptions
524+
def item_types(ctx):
525+
"""Show valid item types for catalog subscriptions."""
526+
click.echo("Valid item types:")
527+
for it in get_item_types():
528+
click.echo(f"- {it}")

planet/clients/orders.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
ORDERS_PATH = '/orders/v2'
3333
BULK_PATH = '/bulk/orders/v2'
3434

35-
# Order states https://developers.planet.com/docs/orders/ordering/#order-states
35+
# Order states https://developers.planet.com/apis/orders/states/
3636
# this is in order of state progression except for final states
3737
ORDER_STATE_SEQUENCE = \
3838
('queued', 'running', 'failed', 'success', 'partial', 'cancelled')

planet/data/README.md

-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
# data
22
This directory contains static external resources used by the codebase.
33

4-
## Orders Spec
5-
6-
### Files
7-
8-
* `orders_product_bundle_2024-08-12.json`
9-
10-
### Description
11-
12-
This file was downloaded from the Planet
13-
[Product Bundles Reference](https://developers.planet.com/docs/orders/product-bundles-reference/)
14-
page and is stored without any adjustments.
15-
164
## GeoJSON Schemas
175

186
### Files

0 commit comments

Comments
 (0)