sovaria-cli 0.2.4__tar.gz → 0.2.5__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.
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/PKG-INFO +1 -1
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/db.py +103 -14
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_project_and_commands.py +46 -2
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/pyproject.toml +1 -1
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/.github/workflows/ci.yml +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/.github/workflows/publish.yml +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/.gitignore +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/.python-version +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/Makefile +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/README.md +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/__init__.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/__init__.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/__init__.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/admin.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/api.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/audit.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/auth_product.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/billing.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/chrono.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/context.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/data.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/deploy.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/governance.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/iam.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/init_cmd.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/keys.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/login.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/org.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/policy.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/project.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/realtime.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/scope.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/shell.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/sim.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/storage.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/commands/vector.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/main.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/__init__.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/api_client.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/auth_store.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/config.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/display.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/migrations.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/project_context.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/src/utils/scope_picker.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_auth_store_profiles.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_cli_security.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_cli_smoke_paths.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_config_profiles.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_main_startup.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/apps/cli/tests/test_scope_picker.py +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/pytest.ini +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/requirements-dev.txt +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/requirements.txt +0 -0
- {sovaria_cli-0.2.4 → sovaria_cli-0.2.5}/scripts/cli_sdk_happy_path.sh +0 -0
|
@@ -28,23 +28,118 @@ _LIST_TABLES_SQL = (
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def
|
|
31
|
+
def _begin_tx(
|
|
32
32
|
*,
|
|
33
33
|
profile: str | None,
|
|
34
34
|
org_id: str | None,
|
|
35
35
|
project_id: str | None,
|
|
36
36
|
env_id: str | None,
|
|
37
|
-
) ->
|
|
37
|
+
) -> int:
|
|
38
38
|
result = call_api(
|
|
39
39
|
method="POST",
|
|
40
|
-
path="/api/v1/
|
|
40
|
+
path="/api/v1/data/rel/transaction/begin",
|
|
41
|
+
profile=profile,
|
|
42
|
+
org_id=org_id,
|
|
43
|
+
project_id=project_id,
|
|
44
|
+
env_id=env_id,
|
|
45
|
+
require_scope=True,
|
|
46
|
+
)
|
|
47
|
+
txid = result.get("txid") if isinstance(result, dict) else None
|
|
48
|
+
if not isinstance(txid, int) or txid < 1:
|
|
49
|
+
raise CLIRequestError(f"Invalid txid from transaction begin: {result!r}")
|
|
50
|
+
return txid
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _commit_tx(
|
|
54
|
+
txid: int,
|
|
55
|
+
*,
|
|
56
|
+
profile: str | None,
|
|
57
|
+
org_id: str | None,
|
|
58
|
+
project_id: str | None,
|
|
59
|
+
env_id: str | None,
|
|
60
|
+
) -> None:
|
|
61
|
+
call_api(
|
|
62
|
+
method="POST",
|
|
63
|
+
path="/api/v1/data/rel/transaction/commit",
|
|
41
64
|
profile=profile,
|
|
42
|
-
body={"
|
|
65
|
+
body={"txid": txid},
|
|
43
66
|
org_id=org_id,
|
|
44
67
|
project_id=project_id,
|
|
45
68
|
env_id=env_id,
|
|
46
69
|
require_scope=True,
|
|
47
70
|
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _rollback_tx(
|
|
74
|
+
txid: int,
|
|
75
|
+
*,
|
|
76
|
+
profile: str | None,
|
|
77
|
+
org_id: str | None,
|
|
78
|
+
project_id: str | None,
|
|
79
|
+
env_id: str | None,
|
|
80
|
+
) -> None:
|
|
81
|
+
try:
|
|
82
|
+
call_api(
|
|
83
|
+
method="POST",
|
|
84
|
+
path="/api/v1/data/rel/transaction/rollback",
|
|
85
|
+
profile=profile,
|
|
86
|
+
body={"txid": txid},
|
|
87
|
+
org_id=org_id,
|
|
88
|
+
project_id=project_id,
|
|
89
|
+
env_id=env_id,
|
|
90
|
+
require_scope=True,
|
|
91
|
+
)
|
|
92
|
+
except CLIRequestError:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _run_sql_query(
|
|
97
|
+
sql: str,
|
|
98
|
+
*,
|
|
99
|
+
profile: str | None,
|
|
100
|
+
org_id: str | None,
|
|
101
|
+
project_id: str | None,
|
|
102
|
+
env_id: str | None,
|
|
103
|
+
) -> dict:
|
|
104
|
+
scope = {
|
|
105
|
+
"profile": profile,
|
|
106
|
+
"org_id": org_id,
|
|
107
|
+
"project_id": project_id,
|
|
108
|
+
"env_id": env_id,
|
|
109
|
+
}
|
|
110
|
+
txid = _begin_tx(**scope)
|
|
111
|
+
try:
|
|
112
|
+
result = call_api(
|
|
113
|
+
method="POST",
|
|
114
|
+
path="/api/v1/sql/query",
|
|
115
|
+
profile=profile,
|
|
116
|
+
body={"sql": sql, "txid": txid},
|
|
117
|
+
org_id=org_id,
|
|
118
|
+
project_id=project_id,
|
|
119
|
+
env_id=env_id,
|
|
120
|
+
require_scope=True,
|
|
121
|
+
)
|
|
122
|
+
_commit_tx(txid, **scope)
|
|
123
|
+
return result if isinstance(result, dict) else {"rows": result}
|
|
124
|
+
except Exception:
|
|
125
|
+
_rollback_tx(txid, **scope)
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _fetch_live_tables(
|
|
130
|
+
*,
|
|
131
|
+
profile: str | None,
|
|
132
|
+
org_id: str | None,
|
|
133
|
+
project_id: str | None,
|
|
134
|
+
env_id: str | None,
|
|
135
|
+
) -> list[str]:
|
|
136
|
+
result = _run_sql_query(
|
|
137
|
+
_LIST_TABLES_SQL,
|
|
138
|
+
profile=profile,
|
|
139
|
+
org_id=org_id,
|
|
140
|
+
project_id=project_id,
|
|
141
|
+
env_id=env_id,
|
|
142
|
+
)
|
|
48
143
|
rows = result.get("rows") if isinstance(result, dict) else None
|
|
49
144
|
if not isinstance(rows, list):
|
|
50
145
|
return []
|
|
@@ -154,15 +249,12 @@ def _execute_sql(
|
|
|
154
249
|
env_id: str | None,
|
|
155
250
|
) -> None:
|
|
156
251
|
try:
|
|
157
|
-
result =
|
|
158
|
-
|
|
159
|
-
path="/api/v1/sql/query",
|
|
252
|
+
result = _run_sql_query(
|
|
253
|
+
sql,
|
|
160
254
|
profile=profile,
|
|
161
|
-
body={"sql": sql, "txid": 0},
|
|
162
255
|
org_id=org_id,
|
|
163
256
|
project_id=project_id,
|
|
164
257
|
env_id=env_id,
|
|
165
|
-
require_scope=True,
|
|
166
258
|
)
|
|
167
259
|
except CLIRequestError as exc:
|
|
168
260
|
typer.echo(str(exc), err=True)
|
|
@@ -530,11 +622,8 @@ def migrations_push(
|
|
|
530
622
|
continue
|
|
531
623
|
typer.echo(f"Applying {path.name} …")
|
|
532
624
|
try:
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
path="/api/v1/sql/query",
|
|
536
|
-
body={"sql": sql, "txid": 0},
|
|
537
|
-
require_scope=True,
|
|
625
|
+
_run_sql_query(
|
|
626
|
+
sql,
|
|
538
627
|
**_scope_opts(profile, org_id, project_id, env_id),
|
|
539
628
|
)
|
|
540
629
|
except CLIRequestError as exc:
|
|
@@ -60,10 +60,20 @@ def test_db_tables_uses_information_schema(monkeypatch: pytest.MonkeyPatch) -> N
|
|
|
60
60
|
from apps.cli.src.commands import db
|
|
61
61
|
|
|
62
62
|
captured: dict[str, Any] = {}
|
|
63
|
+
calls: list[tuple[str, str]] = []
|
|
63
64
|
|
|
64
65
|
def _fake_call_api(**kwargs: Any) -> dict[str, Any]:
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
method = str(kwargs.get("method", ""))
|
|
67
|
+
path = str(kwargs.get("path", ""))
|
|
68
|
+
calls.append((method, path))
|
|
69
|
+
if path.endswith("/transaction/begin"):
|
|
70
|
+
return {"txid": 1, "snapshot_txid": 1}
|
|
71
|
+
if path.endswith("/sql/query"):
|
|
72
|
+
captured.update(kwargs)
|
|
73
|
+
return {"rows": [["users"], ["orders"]]}
|
|
74
|
+
if path.endswith("/transaction/commit"):
|
|
75
|
+
return {"ok": True}
|
|
76
|
+
return {}
|
|
67
77
|
|
|
68
78
|
monkeypatch.setattr(db, "call_api", _fake_call_api)
|
|
69
79
|
db.list_tables(profile=None, org_id=None, project_id=None, env_id=None, migrations=False)
|
|
@@ -71,6 +81,9 @@ def test_db_tables_uses_information_schema(monkeypatch: pytest.MonkeyPatch) -> N
|
|
|
71
81
|
assert captured["method"] == "POST"
|
|
72
82
|
assert captured["path"] == "/api/v1/sql/query"
|
|
73
83
|
assert "information_schema.tables" in captured["body"]["sql"]
|
|
84
|
+
assert captured["body"]["txid"] == 1
|
|
85
|
+
assert ("POST", "/api/v1/data/rel/transaction/begin") in calls
|
|
86
|
+
assert ("POST", "/api/v1/data/rel/transaction/commit") in calls
|
|
74
87
|
|
|
75
88
|
|
|
76
89
|
def test_sim_discover_path_smoke(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -211,3 +224,34 @@ def test_storage_buckets_uses_call_api(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
211
224
|
assert captured["path"] == "/api/v1/store/buckets"
|
|
212
225
|
assert captured["require_scope"] is True
|
|
213
226
|
assert any("bucket-a" in line for line in output)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def test_fetch_live_tables_uses_transaction(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
230
|
+
from apps.cli.src.commands import db
|
|
231
|
+
|
|
232
|
+
calls: list[tuple[str, str]] = []
|
|
233
|
+
|
|
234
|
+
def _fake_call_api(**kwargs: Any) -> dict[str, Any]:
|
|
235
|
+
method = str(kwargs.get("method", ""))
|
|
236
|
+
path = str(kwargs.get("path", ""))
|
|
237
|
+
calls.append((method, path))
|
|
238
|
+
if path.endswith("/transaction/begin"):
|
|
239
|
+
return {"txid": 7, "snapshot_txid": 7}
|
|
240
|
+
if path.endswith("/sql/query"):
|
|
241
|
+
return {"rows": [["users"], ["posts"]]}
|
|
242
|
+
if path.endswith("/transaction/commit"):
|
|
243
|
+
return {"ok": True}
|
|
244
|
+
return {}
|
|
245
|
+
|
|
246
|
+
monkeypatch.setattr(db, "call_api", _fake_call_api)
|
|
247
|
+
tables = db._fetch_live_tables(
|
|
248
|
+
profile=None,
|
|
249
|
+
org_id="org-1",
|
|
250
|
+
project_id="proj-1",
|
|
251
|
+
env_id=None,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
assert tables == ["users", "posts"]
|
|
255
|
+
assert ("POST", "/api/v1/data/rel/transaction/begin") in calls
|
|
256
|
+
assert ("POST", "/api/v1/sql/query") in calls
|
|
257
|
+
assert ("POST", "/api/v1/data/rel/transaction/commit") in calls
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|