Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
orders: support user_id query param
  • Loading branch information
adamweiner committed Aug 13, 2025
commit adc451b3aa200a89b114b133eac27be4c7acca6e
10 changes: 10 additions & 0 deletions docs/cli/cli-orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ To list orders with a name containing `xyz`:
planet orders list --name-contains xyz
```

To list orders for all users in your organization (organization admin only):
```sh
planet orders list --user-id all
```

To list orders for a specific user ID (organization admin only):
```sh
planet orders list --user-id 12345
```

#### Sorting

The `list` command also supports sorting the orders on one or more fields: `name`, `created_on`, `state`, and `last_modified`. The sort direction can be specified by appending ` ASC` or ` DESC` to the field name (default is ascending).
Expand Down
5 changes: 5 additions & 0 deletions planet/cli/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ def orders(ctx, base_url):
@click.option(
'--destination-ref',
help="Filter by orders created with the provided destination reference.")
@click.option(
'--user-id',
help="Filter by user ID. Only available to organization admins. Accepts 'all' or a specific user ID.")
@limit
@pretty
async def list(ctx,
Expand All @@ -147,6 +150,7 @@ async def list(ctx,
hosting,
sort_by,
destination_ref,
user_id,
limit,
pretty):
"""List orders
Expand All @@ -167,6 +171,7 @@ async def list(ctx,
hosting=hosting,
sort_by=sort_by,
destination_ref=destination_ref,
user_id=user_id,
limit=limit):
echo_json(o, pretty)

Expand Down
7 changes: 6 additions & 1 deletion planet/clients/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ async def list_orders(
last_modified: Optional[str] = None,
hosting: Optional[bool] = None,
destination_ref: Optional[str] = None,
sort_by: Optional[str] = None) -> AsyncIterator[dict]:
sort_by: Optional[str] = None,
user_id: Optional[Union[str, int]] = None) -> AsyncIterator[dict]:
"""Iterate over the list of stored orders.

By default, order descriptions are sorted by creation date with the last created
Expand Down Expand Up @@ -510,6 +511,8 @@ async def list_orders(
* "name"
* "name DESC"
* "name,state DESC,last_modified"
user_id (str or int): filter by user ID. Only available to organization admins.
Accepts "all" or a specific user ID.

Datetime args (created_on and last_modified) can either be a date-time or an
interval, open or closed. Date and time expressions adhere to RFC 3339. Open
Expand Down Expand Up @@ -547,6 +550,8 @@ async def list_orders(
params["sort_by"] = sort_by
if destination_ref is not None:
params["destination_ref"] = destination_ref
if user_id is not None:
params["user_id"] = user_id
if state:
if state not in ORDER_STATE_SEQUENCE:
raise exceptions.ClientError(
Expand Down
10 changes: 7 additions & 3 deletions planet/sync/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations under
# the License.
"""Functionality for interacting with the orders api"""
from typing import Any, Callable, Dict, Iterator, List, Optional
from typing import Any, Callable, Dict, Iterator, List, Optional, Union

from pathlib import Path
from ..http import Session
Expand Down Expand Up @@ -262,7 +262,8 @@ def list_orders(self,
last_modified: Optional[str] = None,
hosting: Optional[bool] = None,
destination_ref: Optional[str] = None,
sort_by: Optional[str] = None) -> Iterator[dict]:
sort_by: Optional[str] = None,
user_id: Optional[Union[str, int]] = None) -> Iterator[dict]:
"""Iterate over the list of stored orders.

By default, order descriptions are sorted by creation date with the last created
Expand Down Expand Up @@ -296,6 +297,8 @@ def list_orders(self,
* "name"
* "name DESC"
* "name,state DESC,last_modified"
user_id (str or int): filter by user ID. Only available to organization admins.
Accepts "all" or a specific user ID.
limit (int): maximum number of results to return. When set to 0, no
maximum is applied.

Expand All @@ -320,4 +323,5 @@ def list_orders(self,
last_modified,
hosting,
destination_ref,
sort_by))
sort_by,
user_id))
48 changes: 48 additions & 0 deletions tests/integration/test_orders_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,54 @@ async def test_list_orders_filtering_and_sorting(order_descriptions, session):
]


@respx.mock
@pytest.mark.anyio
async def test_list_orders_user_id_filtering(order_descriptions, session):
"""Test user_id parameter filtering for organization admins."""
list_url = TEST_ORDERS_URL + '?source_type=all&user_id=all'

order1, order2, _ = order_descriptions

page1_response = {
"_links": {
"_self": "string"
}, "orders": [order1, order2]
}
mock_resp = httpx.Response(HTTPStatus.OK, json=page1_response)
respx.get(list_url).return_value = mock_resp

cl = OrdersClient(session, base_url=TEST_URL)

# if the value of user_id doesn't get sent as a url parameter,
# the mock will fail and this test will fail
assert [order1, order2] == [
o async for o in cl.list_orders(user_id='all')
]


@respx.mock
def test_list_orders_user_id_sync(order_descriptions, session):
"""Test sync client user_id parameter for organization admins."""
list_url = TEST_ORDERS_URL + '?source_type=all&user_id=all'

order1, order2, _ = order_descriptions

page1_response = {
"_links": {
"_self": "string"
}, "orders": [order1, order2]
}
mock_resp = httpx.Response(HTTPStatus.OK, json=page1_response)
respx.get(list_url).return_value = mock_resp

pl = Planet()
pl.orders._client._base_url = TEST_URL

# if the value of user_id doesn't get sent as a url parameter,
# the mock will fail and this test will fail
assert [order1, order2] == list(pl.orders.list_orders(user_id='all'))


@respx.mock
def test_list_orders_state_success_sync(order_descriptions, session):
list_url = TEST_ORDERS_URL + '?source_type=all&state=failed'
Expand Down
27 changes: 27 additions & 0 deletions tests/integration/test_orders_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,33 @@ def test_cli_orders_list_filtering_and_sorting(invoke, order_descriptions):
assert result.output == sequence + '\n'


@respx.mock
def test_cli_orders_list_user_id(invoke, order_descriptions):
"""Test CLI user_id parameter for organization admins."""
list_url = TEST_ORDERS_URL + '?source_type=all&user_id=all'

order1, order2, _ = order_descriptions

page1_response = {
"_links": {
"_self": "string"
}, "orders": [order1, order2]
}
mock_resp = httpx.Response(HTTPStatus.OK, json=page1_response)
respx.get(list_url).return_value = mock_resp

# if the value of user_id doesn't get sent as a url parameter,
# the mock will fail and this test will fail
result = invoke([
'list',
'--user-id',
'all'
])
assert result.exit_code == 0
sequence = '\n'.join([json.dumps(o) for o in [order1, order2]])
assert result.output == sequence + '\n'


@respx.mock
@pytest.mark.parametrize("limit,limited_list_length", [(None, 100), (0, 102),
(1, 1)])
Expand Down