Skip to content

Commit fe5ba5d

Browse files
authored
Merge pull request #264 from NOAA-GSL/staging
2 parents af144c8 + e4d5fb9 commit fe5ba5d

File tree

7 files changed

+94
-3
lines changed

7 files changed

+94
-3
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "zyra"
3-
version = "0.1.45"
3+
version = "0.1.46"
44
description = "A tool to ingest data from various sources and formats, create imagery or video based on that data, and send the results to various locations for dissemination."
55
authors = ["Eric Hackathorn <eric.j.hackathorn@noaa.gov>"]
66
include = [

src/zyra/api/models/cli_request.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class CLIRunRequest(BaseModel):
2323
"narrate",
2424
"verify",
2525
"run",
26+
"search",
2627
"swarm",
2728
]
2829
command: str = Field(..., description="Subcommand within the selected stage")

src/zyra/api/routers/cli.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,29 @@ def ex(name: str, meta: dict[str, Any]) -> Any:
198198
canonical_groups.append(("decide", parsers_from_register(decide.register_cli)))
199199
canonical_groups.append(("narrate", parsers_from_register(narrate.register_cli)))
200200
canonical_groups.append(("verify", parsers_from_register(verify.register_cli)))
201+
# search / discovery - register_cli takes a full ArgumentParser (like swarm)
202+
import zyra.connectors.discovery as discovery
203+
204+
_search_parser = argparse.ArgumentParser(prog="zyra search")
205+
discovery.register_cli(_search_parser)
206+
_search_cmds: dict[str, argparse.ArgumentParser] = {}
207+
_subparsers_action_type = getattr(argparse, "_SubParsersAction", None)
208+
for _action in getattr(_search_parser, "_actions", []):
209+
if _subparsers_action_type is not None and isinstance(
210+
_action,
211+
_subparsers_action_type, # type: ignore[arg-type]
212+
):
213+
_search_cmds = dict(getattr(_action, "choices", {}))
214+
break
215+
# NOTE:
216+
# - discovery.register_cli registers a full ``zyra search`` CLI, including
217+
# a base command (no subcommand) and one or more subcommands (e.g., ``api``).
218+
# - The API CLI matrix intentionally only exposes the subcommands via
219+
# ``_search_cmds``. As a result, /v1/cli/run with stage="search" supports
220+
# only these subcommands and cannot invoke the base ``zyra search`` form
221+
# (e.g., ``zyra search "tsunami" ...``) without a dedicated executor
222+
# special-case similar to the one used for the ``swarm`` stage.
223+
canonical_groups.append(("search", _search_cmds))
201224
# swarm (single command; attach args directly)
202225
swarm_parser = argparse.ArgumentParser(prog="zyra swarm")
203226
swarm_cli.register_cli(swarm_parser)

src/zyra/api/workers/executor.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,18 @@ def _args_dict_to_argv(stage: str, command: str, args: dict[str, Any]) -> list[s
271271
argv.extend(["--input", str(input_val)])
272272
argv.append(str(path))
273273

274+
# Per-(stage, command) overrides where argparse dest differs from
275+
# the canonical --flag name (e.g., dest="api_query" but flag is --query).
276+
flag_overrides: dict[tuple[str, str], dict[str, str]] = {
277+
("search", "api"): {"api_query": "--query"},
278+
}
279+
overrides = flag_overrides.get((stage, command), {})
280+
274281
# Remaining keys become long flags
275282
for key, value in norm_args.items():
276283
if value is None:
277284
continue
278-
flag = f"--{_to_kebab(key)}"
285+
flag = overrides.get(key, f"--{_to_kebab(key)}")
279286
if isinstance(value, bool):
280287
if value:
281288
argv.append(flag)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
"""Regression test: stage='search' must be accepted by POST /v1/cli/run."""
3+
4+
from __future__ import annotations
5+
6+
from unittest.mock import patch
7+
8+
from fastapi.testclient import TestClient
9+
10+
from zyra.api.server import app
11+
from zyra.api.workers.executor import RunResult
12+
13+
14+
def test_cli_run_accepts_search_stage():
15+
"""POST /v1/cli/run with stage='search' must not return 422 or 400."""
16+
client = TestClient(app)
17+
# Patch run_cli to avoid actually executing the CLI command
18+
with patch(
19+
"zyra.api.routers.cli.run_cli",
20+
return_value=RunResult(stdout="ok", stderr="", exit_code=0),
21+
):
22+
r = client.post(
23+
"/v1/cli/run",
24+
json={
25+
"stage": "search",
26+
"command": "api",
27+
"args": {"url": "https://example.com/api", "api_query": "temperature"},
28+
},
29+
)
30+
assert r.status_code == 200, f"unexpected status: {r.json()}"
31+
body = r.json()
32+
assert body.get("status") == "success"
33+
assert body.get("stdout") == "ok"

tests/cli/test_api_args.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,30 @@ def test_aliases_dest_target_and_src():
5656
assert argv[:2] == ["decimate", "local"]
5757
# path should be positional at the end
5858
assert argv[-1] == "./out.bin"
59+
60+
61+
def test_search_api_query_flag_override():
62+
"""api_query key must emit --query (not --api-query)."""
63+
argv = _args_dict_to_argv(
64+
"search",
65+
"api",
66+
{"url": "https://example.com/api", "api_query": "temperature"},
67+
)
68+
assert argv[:2] == ["search", "api"]
69+
assert "--query" in argv
70+
assert "--api-query" not in argv
71+
i = argv.index("--query")
72+
assert argv[i + 1] == "temperature"
73+
74+
75+
def test_search_api_natural_query_key():
76+
"""Client-friendly 'query' key must also produce --query."""
77+
argv = _args_dict_to_argv(
78+
"search",
79+
"api",
80+
{"url": "https://example.com/api", "query": "temperature"},
81+
)
82+
assert argv[:2] == ["search", "api"]
83+
assert "--query" in argv
84+
i = argv.index("--query")
85+
assert argv[i + 1] == "temperature"

tests/snapshots/openapi_sha256.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2017e2d2238d26852a86f50cd9640d02eb0c71f5ec138c537580cf268466535b
1+
afddec39bddbd2ec4340f6beddf77275ce324618d99b7ff3b9a765071983b569

0 commit comments

Comments
 (0)