current-cli 0.1.35__tar.gz → 0.1.36__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: current-cli
3
- Version: 0.1.35
3
+ Version: 0.1.36
4
4
  Summary: Command line interface for the Current API, generated from its OpenAPI schema at runtime
5
5
  Author-email: Orin <your-email@example.com>
6
6
  License-Expression: MIT
@@ -16,9 +16,14 @@ def _make_callback(op: Operation):
16
16
  for param in op.path_params:
17
17
  path = path.replace("{" + param.name + "}", str(kwargs.pop(param.name)))
18
18
 
19
- params: dict[str, str] = {}
19
+ params: dict[str, str | list[str]] = {}
20
20
  for param in op.query_params:
21
21
  value = kwargs.pop(param.name, None)
22
+ # click gives a repeatable option an empty tuple, not None, when
23
+ # it is absent; treat that as "not given" so it never becomes an
24
+ # empty query key.
25
+ if param.is_array:
26
+ value = list(value) if value else None
22
27
  if value is None and param.name == "workspace":
23
28
  value = config.get("default_workspace")
24
29
  if value is None:
@@ -48,6 +53,7 @@ def _make_command(op: Operation) -> click.Command:
48
53
  click.Option(
49
54
  [f"--{param.name.replace('_', '-')}"],
50
55
  required=False,
56
+ multiple=param.is_array,
51
57
  help=param.description or None,
52
58
  )
53
59
  )
@@ -21,6 +21,9 @@ class Parameter:
21
21
  location: str # "path" or "query"
22
22
  required: bool
23
23
  description: str
24
+ # An array query param is sent as the repeated key the API reads with
25
+ # getlist (?fields=a&fields=b), so its option has to be repeatable too.
26
+ is_array: bool = False
24
27
 
25
28
 
26
29
  @dataclass(frozen=True)
@@ -90,6 +93,7 @@ def extract_operations(spec: dict) -> list[Operation]:
90
93
  location=raw["in"],
91
94
  required=raw.get("required", False),
92
95
  description=raw.get("description", ""),
96
+ is_array=raw.get("schema", {}).get("type") == "array",
93
97
  )
94
98
  if param.location == "path":
95
99
  path_params.append(param)
@@ -1841,12 +1841,17 @@ paths:
1841
1841
  get:
1842
1842
  operationId: projects_projects_inventory_retrieve
1843
1843
  description: |-
1844
- Page current field values, optionally narrowed to named keys.
1844
+ Page current field values, optionally narrowed to named keys and
1845
+ to projects whose values match.
1845
1846
 
1846
1847
  The full project list serializes every field of every project —
1847
1848
  megabytes at production scale. This page is the compact census a
1848
1849
  chat agent can scan: identity plus the keys it asked for, no
1849
- timeline, no provenance. See state.list_projects.
1850
+ timeline, no provenance.
1851
+
1852
+ Filter here rather than page-and-sift: a caller that reads every
1853
+ page and picks the matches by eye miscounts, and "total" under a
1854
+ filter is the count it was really after. See state.list_projects.
1850
1855
  parameters:
1851
1856
  - in: query
1852
1857
  name: fields
@@ -1855,6 +1860,16 @@ paths:
1855
1860
  items:
1856
1861
  type: string
1857
1862
  description: Field keys to keep; omit to return every current value
1863
+ - in: query
1864
+ name: filter
1865
+ schema:
1866
+ type: array
1867
+ items:
1868
+ type: string
1869
+ description: Keep only projects whose current value matches, as 'key:value'
1870
+ (for example 'status:Active'). Repeat for more fields; repeating one key
1871
+ accepts any of its values. An option matches by label, value, or ID. 'total'
1872
+ counts the matching projects.
1858
1873
  - in: query
1859
1874
  name: limit
1860
1875
  schema:
@@ -1865,6 +1880,14 @@ paths:
1865
1880
  schema:
1866
1881
  type: integer
1867
1882
  description: Number of projects to skip (default 0)
1883
+ - in: query
1884
+ name: values
1885
+ schema:
1886
+ type: string
1887
+ enum:
1888
+ - ids
1889
+ - labels
1890
+ description: Read option values as labels (default) or as option IDs
1868
1891
  - in: query
1869
1892
  name: workspace
1870
1893
  schema:
@@ -617,19 +617,26 @@ Options:
617
617
 
618
618
  ### current projects inventory retrieve
619
619
 
620
- Page current field values, optionally narrowed to named keys.
620
+ Page current field values, optionally narrowed to named keys and
621
+ to projects whose values match.
621
622
 
622
623
  The full project list serializes every field of every project —
623
624
  megabytes at production scale. This page is the compact census a
624
625
  chat agent can scan: identity plus the keys it asked for, no
625
- timeline, no provenance. See state.list_projects.
626
+ timeline, no provenance.
627
+
628
+ Filter here rather than page-and-sift: a caller that reads every
629
+ page and picks the matches by eye miscounts, and "total" under a
630
+ filter is the count it was really after. See state.list_projects.
626
631
 
627
632
  GET /api/projects/projects/inventory/
628
633
 
629
634
  Options:
630
635
  - `--fields` - Field keys to keep; omit to return every current value
636
+ - `--filter` - Keep only projects whose current value matches, as 'key:value' (for example 'status:Active'). Repeat for more fields; repeating one key accepts any of its values. An option matches by label, value, or ID. 'total' counts the matching projects.
631
637
  - `--limit` - Max projects per page (default 100, max 200)
632
638
  - `--offset` - Number of projects to skip (default 0)
639
+ - `--values` - Read option values as labels (default) or as option IDs
633
640
  - `--workspace` - Workspace UUID
634
641
 
635
642
  ### current projects list
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "current-cli"
3
- version = "0.1.35"
3
+ version = "0.1.36"
4
4
  description = "Command line interface for the Current API, generated from its OpenAPI schema at runtime"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -60,6 +60,40 @@ def test_create_sends_json_body(cli, runner, transport_factory):
60
60
  assert json.loads(request.content) == {"name": "Order beams", "workspace": "w1"}
61
61
 
62
62
 
63
+ def test_array_query_param_repeats(cli, runner, transport_factory):
64
+ """An array param must repeat its key, the shape the API reads with
65
+ getlist — one AND-ed filter per --filter."""
66
+ transport = transport_factory(ok({}))
67
+
68
+ result = runner.invoke(
69
+ cli,
70
+ [
71
+ "projects",
72
+ "inventory",
73
+ "retrieve",
74
+ "--workspace",
75
+ "ws-uuid",
76
+ "--filter",
77
+ "status:Active",
78
+ "--filter",
79
+ "site_type:Garden Style",
80
+ ],
81
+ )
82
+
83
+ assert result.exit_code == 0, result.output
84
+ params = transport.requests[0].url.params
85
+ assert params.get_list("filter") == ["status:Active", "site_type:Garden Style"]
86
+
87
+
88
+ def test_array_query_param_is_absent_when_not_given(cli, runner, transport_factory):
89
+ transport = transport_factory(ok({}))
90
+
91
+ result = runner.invoke(cli, ["projects", "inventory", "retrieve", "--workspace", "ws-uuid"])
92
+
93
+ assert result.exit_code == 0, result.output
94
+ assert "filter" not in transport.requests[0].url.params
95
+
96
+
63
97
  def test_default_workspace_fallback(cli, runner, transport_factory):
64
98
  config.set_value("default_workspace", "default-ws")
65
99
  transport = transport_factory(ok({}))
File without changes
File without changes