entropy-data 0.3.4__tar.gz → 0.3.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.
- {entropy_data-0.3.4 → entropy_data-0.3.5}/CHANGELOG.md +5 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/PKG-INFO +1 -1
- {entropy_data-0.3.4 → entropy_data-0.3.5}/pyproject.toml +1 -1
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/access.py +75 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/semantics.py +46 -0
- entropy_data-0.3.5/tests/commands/test_access_request.py +118 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_semantics.py +75 -1
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.editorconfig +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.github/dependabot.yml +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.github/pull_request_template.md +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.github/workflows/ci.yaml +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.github/workflows/release.yaml +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.gitignore +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/.pre-commit-config.yaml +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/CLAUDE.md +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/Dockerfile +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/LICENSE +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/README.md +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/release +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/__init__.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/__main__.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/cli.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/client.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/__init__.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/api_keys.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/assets.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/certifications.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/connection.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/connectors.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/costs.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/datacontracts.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/dataproducts.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/definitions.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/events.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/example_data.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/git_credentials.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/gitconnections.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/import_export.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/lineage.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/notification_channels.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/organization.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/search.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/settings.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/sourcesystems.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/tags.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/teams.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/test_results.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/usage.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/config.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/output.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/util.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/__init__.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/__init__.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_api_keys.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_assets.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_connection.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_connectors.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_costs.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_datacontracts.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_dataproducts.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_git_credentials.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_gitconnections.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_lineage.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_notification_channels.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_organization.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_settings.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_tags.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_teams.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/commands/test_usage.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/conftest.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/test_client.py +0 -0
- {entropy_data-0.3.4 → entropy_data-0.3.5}/tests/test_config.py +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.3.5]
|
|
6
|
+
|
|
7
|
+
- Add `entropy-data semantics search <namespace> <query> [--kind <kind>] [--limit <n>]` for case-insensitive substring search across concept id, name, and description in a namespace. Implemented client-side over the existing `GET /api/semantics/experimental/namespaces/{ns}/concepts` endpoint.
|
|
8
|
+
- Add `entropy-data access request <data-product-id> <output-port-id> --purpose <text> --consumer-team|--consumer-user|--consumer-dataproduct <id> [--roles <comma-list>] [--id <agreement-id>]` to submit an access request for a provider output port. Wraps `PUT /api/access/{id}` and auto-generates a UUID when `--id` is omitted.
|
|
9
|
+
|
|
5
10
|
## [0.3.4]
|
|
6
11
|
|
|
7
12
|
- Add `entropy-data datacontracts yaml <id>` to fetch a data contract as ODCS YAML (writes to stdout or `--file`). Backed by `GET /api/datacontracts/{id}.yaml`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Access (data usage agreements) commands."""
|
|
2
2
|
|
|
3
|
+
import uuid
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Annotated, Optional
|
|
5
6
|
|
|
@@ -126,3 +127,77 @@ def cancel_access(
|
|
|
126
127
|
print_link(location)
|
|
127
128
|
except Exception as e:
|
|
128
129
|
handle_error(e)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@access_app.command("request")
|
|
133
|
+
def request_access(
|
|
134
|
+
data_product_id: Annotated[str, typer.Argument(help="Provider data product ID.")],
|
|
135
|
+
output_port_id: Annotated[str, typer.Argument(help="Provider output port ID.")],
|
|
136
|
+
purpose: Annotated[str, typer.Option("--purpose", help="Business justification for the access.")] = ...,
|
|
137
|
+
consumer_team: Annotated[
|
|
138
|
+
Optional[str],
|
|
139
|
+
typer.Option("--consumer-team", help="Consumer team ID (request on behalf of a team)."),
|
|
140
|
+
] = None,
|
|
141
|
+
consumer_user: Annotated[
|
|
142
|
+
Optional[str],
|
|
143
|
+
typer.Option("--consumer-user", help="Consumer user ID (request on behalf of a user)."),
|
|
144
|
+
] = None,
|
|
145
|
+
consumer_dataproduct: Annotated[
|
|
146
|
+
Optional[str],
|
|
147
|
+
typer.Option("--consumer-dataproduct", help="Consumer data product ID (cross-product agreement)."),
|
|
148
|
+
] = None,
|
|
149
|
+
roles: Annotated[
|
|
150
|
+
Optional[str],
|
|
151
|
+
typer.Option("--roles", help="Comma-separated list of roles to request (e.g. analyst,data_engineer)."),
|
|
152
|
+
] = None,
|
|
153
|
+
id: Annotated[
|
|
154
|
+
Optional[str],
|
|
155
|
+
typer.Option("--id", help="Agreement ID. Auto-generated UUID if not provided."),
|
|
156
|
+
] = None,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Submit an access request for a provider data product output port.
|
|
159
|
+
|
|
160
|
+
Creates a new access agreement in 'requested' state via PUT /api/access/{id}. Exactly
|
|
161
|
+
one of --consumer-team / --consumer-user / --consumer-dataproduct must be specified.
|
|
162
|
+
"""
|
|
163
|
+
from entropy_data.cli import get_client, handle_error
|
|
164
|
+
from entropy_data.output import error_console
|
|
165
|
+
|
|
166
|
+
consumer_count = sum(c is not None for c in (consumer_team, consumer_user, consumer_dataproduct))
|
|
167
|
+
if consumer_count != 1:
|
|
168
|
+
error_console.print(
|
|
169
|
+
"[red]Error: provide exactly one of --consumer-team, --consumer-user, "
|
|
170
|
+
"or --consumer-dataproduct.[/red]"
|
|
171
|
+
)
|
|
172
|
+
raise SystemExit(2)
|
|
173
|
+
|
|
174
|
+
agreement_id = id or str(uuid.uuid4())
|
|
175
|
+
consumer: dict = {}
|
|
176
|
+
if consumer_team is not None:
|
|
177
|
+
consumer["teamId"] = consumer_team
|
|
178
|
+
if consumer_user is not None:
|
|
179
|
+
consumer["userId"] = consumer_user
|
|
180
|
+
if consumer_dataproduct is not None:
|
|
181
|
+
consumer["dataProductId"] = consumer_dataproduct
|
|
182
|
+
|
|
183
|
+
body: dict = {
|
|
184
|
+
"id": agreement_id,
|
|
185
|
+
"provider": {
|
|
186
|
+
"dataProductId": data_product_id,
|
|
187
|
+
"outputPortId": output_port_id,
|
|
188
|
+
},
|
|
189
|
+
"consumer": consumer,
|
|
190
|
+
"info": {
|
|
191
|
+
"purpose": purpose,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
if roles:
|
|
195
|
+
body["roles"] = [r.strip() for r in roles.split(",") if r.strip()]
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
client = get_client()
|
|
199
|
+
location = client.put_resource(RESOURCE_PATH, agreement_id, body)
|
|
200
|
+
print_success(f"Access request '{agreement_id}' submitted.")
|
|
201
|
+
print_link(location)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
handle_error(e)
|
|
@@ -257,6 +257,52 @@ def delete_relationship(
|
|
|
257
257
|
handle_error(e)
|
|
258
258
|
|
|
259
259
|
|
|
260
|
+
@semantics_app.command("search")
|
|
261
|
+
def search_concepts(
|
|
262
|
+
namespace: Annotated[str, typer.Argument(help="Namespace ID.")],
|
|
263
|
+
query: Annotated[str, typer.Argument(help="Search query (case-insensitive substring).")],
|
|
264
|
+
kind: Annotated[
|
|
265
|
+
Optional[str],
|
|
266
|
+
typer.Option("--kind", help="Filter by kind: entity, metric, group, shared_property, property."),
|
|
267
|
+
] = None,
|
|
268
|
+
limit: Annotated[int, typer.Option("--limit", help="Max results.")] = 50,
|
|
269
|
+
output: Annotated[Optional[OutputFormat], typer.Option("--output", "-o", help="Output format.")] = None,
|
|
270
|
+
) -> None:
|
|
271
|
+
"""Search concepts in a namespace by case-insensitive substring against id, name, description.
|
|
272
|
+
|
|
273
|
+
Implemented client-side: fetches all concepts in the namespace via the experimental list
|
|
274
|
+
endpoint, then filters in memory. The platform has no dedicated search endpoint.
|
|
275
|
+
"""
|
|
276
|
+
from entropy_data.cli import get_client, get_output_format, handle_error
|
|
277
|
+
from entropy_data.output import error_console
|
|
278
|
+
|
|
279
|
+
fmt = output or get_output_format()
|
|
280
|
+
q = query.strip().lower()
|
|
281
|
+
if not q:
|
|
282
|
+
error_console.print("[red]Error: query must not be empty.[/red]")
|
|
283
|
+
raise SystemExit(2)
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
client = get_client()
|
|
287
|
+
all_concepts, _ = client.list_resources(_concepts_path(namespace))
|
|
288
|
+
|
|
289
|
+
def matches(c: dict) -> bool:
|
|
290
|
+
haystack = " ".join(
|
|
291
|
+
[
|
|
292
|
+
c.get("id") or "",
|
|
293
|
+
c.get("name") or "",
|
|
294
|
+
c.get("description") or "",
|
|
295
|
+
]
|
|
296
|
+
).lower()
|
|
297
|
+
return q in haystack
|
|
298
|
+
|
|
299
|
+
filtered = [c for c in all_concepts if matches(c) and (kind is None or c.get("kind") == kind)]
|
|
300
|
+
truncated = filtered[: max(1, limit)]
|
|
301
|
+
print_resource_list(truncated, "semantic-concepts", fmt)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
handle_error(e)
|
|
304
|
+
|
|
305
|
+
|
|
260
306
|
semantics_app.add_typer(namespaces_app, name="namespaces", help="Manage semantic namespaces.")
|
|
261
307
|
semantics_app.add_typer(concepts_app, name="concepts", help="Manage semantic concepts.")
|
|
262
308
|
semantics_app.add_typer(relationships_app, name="relationships", help="Manage semantic relationships.")
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Tests for the `access request` command."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
import responses
|
|
7
|
+
from typer.testing import CliRunner
|
|
8
|
+
|
|
9
|
+
import entropy_data.config as cfg
|
|
10
|
+
from entropy_data.cli import app
|
|
11
|
+
|
|
12
|
+
runner = CliRunner()
|
|
13
|
+
BASE_URL = "https://api.entropy-data.com"
|
|
14
|
+
|
|
15
|
+
UUID_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _setup(monkeypatch, tmp_path):
|
|
19
|
+
monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "config.toml")
|
|
20
|
+
monkeypatch.setenv("ENTROPY_DATA_API_KEY", "test-key")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@responses.activate
|
|
24
|
+
def test_request_with_consumer_team(monkeypatch, tmp_path):
|
|
25
|
+
_setup(monkeypatch, tmp_path)
|
|
26
|
+
captured: dict = {}
|
|
27
|
+
|
|
28
|
+
def _capture(request):
|
|
29
|
+
captured["body"] = json.loads(request.body)
|
|
30
|
+
return (200, {}, "")
|
|
31
|
+
|
|
32
|
+
responses.add_callback(responses.PUT, re.compile(rf"{BASE_URL}/api/access/.+"), callback=_capture)
|
|
33
|
+
|
|
34
|
+
result = runner.invoke(
|
|
35
|
+
app,
|
|
36
|
+
[
|
|
37
|
+
"access",
|
|
38
|
+
"request",
|
|
39
|
+
"dp_account_master",
|
|
40
|
+
"accounts",
|
|
41
|
+
"--purpose",
|
|
42
|
+
"Building churn model.",
|
|
43
|
+
"--consumer-team",
|
|
44
|
+
"customer-success",
|
|
45
|
+
],
|
|
46
|
+
)
|
|
47
|
+
assert result.exit_code == 0
|
|
48
|
+
assert "submitted" in result.output
|
|
49
|
+
body = captured["body"]
|
|
50
|
+
assert body["provider"] == {"dataProductId": "dp_account_master", "outputPortId": "accounts"}
|
|
51
|
+
assert body["consumer"] == {"teamId": "customer-success"}
|
|
52
|
+
assert body["info"] == {"purpose": "Building churn model."}
|
|
53
|
+
assert UUID_RE.match(body["id"])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@responses.activate
|
|
57
|
+
def test_request_with_explicit_id_and_roles(monkeypatch, tmp_path):
|
|
58
|
+
_setup(monkeypatch, tmp_path)
|
|
59
|
+
captured: dict = {}
|
|
60
|
+
|
|
61
|
+
def _capture(request):
|
|
62
|
+
captured["body"] = json.loads(request.body)
|
|
63
|
+
return (200, {}, "")
|
|
64
|
+
|
|
65
|
+
responses.add_callback(responses.PUT, f"{BASE_URL}/api/access/req-001", callback=_capture)
|
|
66
|
+
|
|
67
|
+
result = runner.invoke(
|
|
68
|
+
app,
|
|
69
|
+
[
|
|
70
|
+
"access",
|
|
71
|
+
"request",
|
|
72
|
+
"dp_a",
|
|
73
|
+
"op_a",
|
|
74
|
+
"--purpose",
|
|
75
|
+
"x",
|
|
76
|
+
"--consumer-user",
|
|
77
|
+
"alice@example.com",
|
|
78
|
+
"--roles",
|
|
79
|
+
"analyst, data_engineer",
|
|
80
|
+
"--id",
|
|
81
|
+
"req-001",
|
|
82
|
+
],
|
|
83
|
+
)
|
|
84
|
+
assert result.exit_code == 0
|
|
85
|
+
body = captured["body"]
|
|
86
|
+
assert body["id"] == "req-001"
|
|
87
|
+
assert body["consumer"] == {"userId": "alice@example.com"}
|
|
88
|
+
assert body["roles"] == ["analyst", "data_engineer"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_request_missing_consumer(monkeypatch, tmp_path):
|
|
92
|
+
_setup(monkeypatch, tmp_path)
|
|
93
|
+
result = runner.invoke(
|
|
94
|
+
app,
|
|
95
|
+
["access", "request", "dp_a", "op_a", "--purpose", "x"],
|
|
96
|
+
)
|
|
97
|
+
assert result.exit_code == 2
|
|
98
|
+
assert "consumer" in result.output.lower()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_request_multiple_consumers(monkeypatch, tmp_path):
|
|
102
|
+
_setup(monkeypatch, tmp_path)
|
|
103
|
+
result = runner.invoke(
|
|
104
|
+
app,
|
|
105
|
+
[
|
|
106
|
+
"access",
|
|
107
|
+
"request",
|
|
108
|
+
"dp_a",
|
|
109
|
+
"op_a",
|
|
110
|
+
"--purpose",
|
|
111
|
+
"x",
|
|
112
|
+
"--consumer-team",
|
|
113
|
+
"t1",
|
|
114
|
+
"--consumer-user",
|
|
115
|
+
"u1",
|
|
116
|
+
],
|
|
117
|
+
)
|
|
118
|
+
assert result.exit_code == 2
|
|
@@ -24,6 +24,27 @@ CONCEPT = {
|
|
|
24
24
|
"status": "active",
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
CONCEPTS_FOR_SEARCH = [
|
|
28
|
+
{
|
|
29
|
+
"id": "customer",
|
|
30
|
+
"name": "Customer",
|
|
31
|
+
"kind": "entity",
|
|
32
|
+
"description": "A person or organization that purchases.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "account",
|
|
36
|
+
"name": "Account",
|
|
37
|
+
"kind": "entity",
|
|
38
|
+
"description": "An ongoing relationship with a customer.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "mrr",
|
|
42
|
+
"name": "Monthly Recurring Revenue",
|
|
43
|
+
"kind": "metric",
|
|
44
|
+
"description": "Monthly revenue from active subscriptions.",
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
|
|
27
48
|
RELATIONSHIP = {
|
|
28
49
|
"id": "customer-has-orders",
|
|
29
50
|
"name": "Customer has Orders",
|
|
@@ -216,7 +237,60 @@ def test_semantics_relationships_delete(monkeypatch, tmp_path):
|
|
|
216
237
|
["semantics", "relationships", "delete", "main", "customer-has-orders"],
|
|
217
238
|
)
|
|
218
239
|
assert result.exit_code == 0
|
|
219
|
-
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# Search
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@responses.activate
|
|
246
|
+
def test_semantics_search_substring(monkeypatch, tmp_path):
|
|
247
|
+
monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "config.toml")
|
|
248
|
+
monkeypatch.setenv("ENTROPY_DATA_API_KEY", "test-key")
|
|
249
|
+
responses.add(responses.GET, f"{NS_URL}/main/concepts", json=CONCEPTS_FOR_SEARCH, status=200)
|
|
250
|
+
result = runner.invoke(
|
|
251
|
+
app, ["semantics", "search", "main", "customer", "--output", "json"]
|
|
252
|
+
)
|
|
253
|
+
assert result.exit_code == 0
|
|
254
|
+
data = json.loads(result.output)
|
|
255
|
+
ids = {c["id"] for c in data}
|
|
256
|
+
# 'customer' matches both the customer concept and the account description
|
|
257
|
+
assert ids == {"customer", "account"}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@responses.activate
|
|
261
|
+
def test_semantics_search_kind_filter(monkeypatch, tmp_path):
|
|
262
|
+
monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "config.toml")
|
|
263
|
+
monkeypatch.setenv("ENTROPY_DATA_API_KEY", "test-key")
|
|
264
|
+
responses.add(responses.GET, f"{NS_URL}/main/concepts", json=CONCEPTS_FOR_SEARCH, status=200)
|
|
265
|
+
result = runner.invoke(
|
|
266
|
+
app,
|
|
267
|
+
["semantics", "search", "main", "revenue", "--kind", "metric", "--output", "json"],
|
|
268
|
+
)
|
|
269
|
+
assert result.exit_code == 0
|
|
270
|
+
data = json.loads(result.output)
|
|
271
|
+
assert len(data) == 1
|
|
272
|
+
assert data[0]["id"] == "mrr"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@responses.activate
|
|
276
|
+
def test_semantics_search_limit(monkeypatch, tmp_path):
|
|
277
|
+
monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "config.toml")
|
|
278
|
+
monkeypatch.setenv("ENTROPY_DATA_API_KEY", "test-key")
|
|
279
|
+
responses.add(responses.GET, f"{NS_URL}/main/concepts", json=CONCEPTS_FOR_SEARCH, status=200)
|
|
280
|
+
result = runner.invoke(
|
|
281
|
+
app,
|
|
282
|
+
["semantics", "search", "main", "a", "--limit", "1", "--output", "json"],
|
|
283
|
+
)
|
|
284
|
+
assert result.exit_code == 0
|
|
285
|
+
data = json.loads(result.output)
|
|
286
|
+
assert len(data) == 1
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_semantics_search_empty_query(monkeypatch, tmp_path):
|
|
290
|
+
monkeypatch.setattr(cfg, "CONFIG_FILE", tmp_path / "config.toml")
|
|
291
|
+
monkeypatch.setenv("ENTROPY_DATA_API_KEY", "test-key")
|
|
292
|
+
result = runner.invoke(app, ["semantics", "search", "main", " "])
|
|
293
|
+
assert result.exit_code == 2
|
|
220
294
|
|
|
221
295
|
|
|
222
296
|
def test_semantics_help():
|
|
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
|
{entropy_data-0.3.4 → entropy_data-0.3.5}/src/entropy_data/commands/notification_channels.py
RENAMED
|
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
|