phlo-api 0.3.0__tar.gz → 0.4.0__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.
- {phlo_api-0.3.0 → phlo_api-0.4.0}/PKG-INFO +1 -1
- {phlo_api-0.3.0 → phlo_api-0.4.0}/pyproject.toml +1 -1
- phlo_api-0.4.0/src/phlo_api/Dockerfile +21 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/main.py +1 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/dagster.py +6 -5
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/loki.py +7 -3
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/nessie.py +6 -5
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/quality.py +7 -3
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/trino.py +11 -9
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2.py +281 -226
- phlo_api-0.4.0/src/phlo_api/observatory_api/v2_cache.py +37 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_capabilities.py +6 -0
- phlo_api-0.4.0/src/phlo_api/observatory_api/v2_saved_queries.py +94 -0
- phlo_api-0.4.0/src/phlo_api/observatory_api/v2_search.py +124 -0
- phlo_api-0.4.0/src/phlo_api/observatory_api/v2_services.py +438 -0
- phlo_api-0.4.0/src/phlo_api/observatory_api/v2_workflow_wizard.py +1135 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/service.yaml +11 -4
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/PKG-INFO +1 -1
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/SOURCES.txt +9 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_authentication_api.py +48 -0
- phlo_api-0.4.0/tests/test_cors.py +21 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_integration_api.py +16 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_api.py +94 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_capabilities.py +1 -0
- phlo_api-0.4.0/tests/test_observatory_v2_read_models.py +76 -0
- phlo_api-0.4.0/tests/test_observatory_v2_workflow_wizard.py +291 -0
- phlo_api-0.4.0/tests/test_service_url_resolution.py +55 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_trino_defaults.py +1 -0
- phlo_api-0.3.0/src/phlo_api/Dockerfile +0 -15
- {phlo_api-0.3.0 → phlo_api-0.4.0}/README.md +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/setup.cfg +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/__init__.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/__init__.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/authentication.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/authorization.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/maintenance.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/observability.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/__init__.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/contributing.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/extension_settings.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/extensions.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/iceberg.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/lineage.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/search.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/settings.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/trino_sql.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_actions.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_catalog.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_governance.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_metadata.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_models.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_observability.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_products.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_runs.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_storage.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/plugin.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/regulated_surface_adapter.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/dependency_links.txt +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/entry_points.txt +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/requires.txt +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/top_level.txt +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_api_backend_endpoints.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_api_import_boundaries.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_authorization_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_lineage_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_loki_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_maintenance_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observability_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_actions.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_catalog.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_observability.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_storage.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_surfaces.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_rest_api.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_service_correlation_runtime.py +0 -0
- {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_trino_sql.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install the same PyPI package versions that generated this stack. Source
|
|
6
|
+
# checkouts still copy the local phlo_api package below, but PyPI installs only
|
|
7
|
+
# have this package directory as Docker build context.
|
|
8
|
+
ARG PHLO_VERSION=
|
|
9
|
+
ARG PHLO_API_VERSION=
|
|
10
|
+
RUN set -eux; \
|
|
11
|
+
if [ -n "$PHLO_VERSION" ]; then pip install --no-cache-dir "phlo==$PHLO_VERSION"; else pip install --no-cache-dir phlo; fi; \
|
|
12
|
+
if [ -n "$PHLO_API_VERSION" ]; then pip install --no-cache-dir "phlo-api==$PHLO_API_VERSION"; else pip install --no-cache-dir phlo-api; fi
|
|
13
|
+
|
|
14
|
+
# Copy the API package so `phlo_api.main` is importable.
|
|
15
|
+
COPY . /app/phlo_api
|
|
16
|
+
|
|
17
|
+
ENV PYTHONPATH=/app
|
|
18
|
+
|
|
19
|
+
EXPOSE 4000
|
|
20
|
+
|
|
21
|
+
CMD ["python", "-m", "uvicorn", "phlo_api.main:app", "--host", "0.0.0.0", "--port", "4000"]
|
|
@@ -50,6 +50,7 @@ require_regulated_validation(runtime=app)
|
|
|
50
50
|
_cors_origins_raw = os.environ.get(
|
|
51
51
|
"PHLO_API_CORS_ORIGINS",
|
|
52
52
|
"http://localhost:3000,http://127.0.0.1:3000,"
|
|
53
|
+
"http://localhost:3001,http://127.0.0.1:3001,"
|
|
53
54
|
"http://localhost:3005,http://127.0.0.1:3005,"
|
|
54
55
|
"http://localhost:4000,http://127.0.0.1:4000",
|
|
55
56
|
)
|
|
@@ -31,7 +31,6 @@ Example:
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
-
import os
|
|
35
34
|
import time
|
|
36
35
|
from datetime import datetime
|
|
37
36
|
from typing import Any
|
|
@@ -40,6 +39,8 @@ import httpx
|
|
|
40
39
|
from fastapi import APIRouter, Query
|
|
41
40
|
from pydantic import BaseModel
|
|
42
41
|
|
|
42
|
+
from phlo.config.env import project_env_value
|
|
43
|
+
from phlo.config.network import resolve_url
|
|
43
44
|
from phlo.logging import get_bound_correlation_context, get_logger
|
|
44
45
|
from phlo.security.service_identity import build_service_headers
|
|
45
46
|
from phlo_api.observatory_api.quality import fetch_quality_snapshot
|
|
@@ -64,12 +65,12 @@ def resolve_dagster_url(override: str | None = None) -> str:
|
|
|
64
65
|
None: No exceptions raised directly.
|
|
65
66
|
|
|
66
67
|
"""
|
|
67
|
-
env_url =
|
|
68
|
+
env_url = project_env_value("DAGSTER_GRAPHQL_URL")
|
|
68
69
|
if override and override.strip():
|
|
69
70
|
if env_url and override.strip() == "http://localhost:3000/graphql":
|
|
70
|
-
return env_url
|
|
71
|
-
return override
|
|
72
|
-
return env_url or DEFAULT_DAGSTER_URL
|
|
71
|
+
return resolve_url(env_url, port_env_var="DAGSTER_PORT")
|
|
72
|
+
return resolve_url(override, port_env_var="DAGSTER_PORT")
|
|
73
|
+
return resolve_url(env_url or DEFAULT_DAGSTER_URL, port_env_var="DAGSTER_PORT")
|
|
73
74
|
|
|
74
75
|
|
|
75
76
|
# --- GraphQL Queries ---
|
|
@@ -29,7 +29,6 @@ Example:
|
|
|
29
29
|
from __future__ import annotations
|
|
30
30
|
|
|
31
31
|
import json
|
|
32
|
-
import os
|
|
33
32
|
from datetime import datetime, timedelta
|
|
34
33
|
from typing import Any, Literal
|
|
35
34
|
|
|
@@ -37,6 +36,8 @@ import httpx
|
|
|
37
36
|
from fastapi import APIRouter, Query
|
|
38
37
|
from pydantic import BaseModel
|
|
39
38
|
|
|
39
|
+
from phlo.config.env import project_env_value
|
|
40
|
+
from phlo.config.network import resolve_url
|
|
40
41
|
from phlo.logging import get_logger
|
|
41
42
|
|
|
42
43
|
logger = get_logger(__name__)
|
|
@@ -59,8 +60,11 @@ def resolve_loki_url(override: str | None = None) -> str:
|
|
|
59
60
|
|
|
60
61
|
"""
|
|
61
62
|
if override and override.strip():
|
|
62
|
-
return override
|
|
63
|
-
return
|
|
63
|
+
return resolve_url(override, port_env_var="LOKI_PORT")
|
|
64
|
+
return resolve_url(
|
|
65
|
+
project_env_value("LOKI_URL", DEFAULT_LOKI_URL) or DEFAULT_LOKI_URL,
|
|
66
|
+
port_env_var="LOKI_PORT",
|
|
67
|
+
)
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
# --- Pydantic Models ---
|
|
@@ -33,7 +33,6 @@ Example:
|
|
|
33
33
|
|
|
34
34
|
from __future__ import annotations
|
|
35
35
|
|
|
36
|
-
import os
|
|
37
36
|
from typing import Any, Literal
|
|
38
37
|
from urllib.parse import quote
|
|
39
38
|
|
|
@@ -41,6 +40,8 @@ import httpx
|
|
|
41
40
|
from fastapi import APIRouter, Query
|
|
42
41
|
from pydantic import BaseModel
|
|
43
42
|
|
|
43
|
+
from phlo.config.env import project_env_value
|
|
44
|
+
from phlo.config.network import resolve_url
|
|
44
45
|
from phlo.logging import get_logger
|
|
45
46
|
|
|
46
47
|
logger = get_logger(__name__)
|
|
@@ -60,12 +61,12 @@ def resolve_nessie_url(override: str | None = None) -> str:
|
|
|
60
61
|
Nessie URL from override, environment, or default.
|
|
61
62
|
|
|
62
63
|
"""
|
|
63
|
-
env_url =
|
|
64
|
+
env_url = project_env_value("NESSIE_URL")
|
|
64
65
|
if override and override.strip():
|
|
65
66
|
if env_url and override.strip() == "http://localhost:19120/api/v2":
|
|
66
|
-
return env_url
|
|
67
|
-
return override
|
|
68
|
-
return env_url or DEFAULT_NESSIE_URL
|
|
67
|
+
return resolve_url(env_url, port_env_var="NESSIE_PORT")
|
|
68
|
+
return resolve_url(override, port_env_var="NESSIE_PORT")
|
|
69
|
+
return resolve_url(env_url or DEFAULT_NESSIE_URL, port_env_var="NESSIE_PORT")
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
# --- Pydantic Models ---
|
|
@@ -28,7 +28,6 @@ Example:
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
|
|
30
30
|
import json
|
|
31
|
-
import os
|
|
32
31
|
from datetime import datetime, timezone
|
|
33
32
|
from typing import Any, Literal
|
|
34
33
|
|
|
@@ -36,6 +35,8 @@ import httpx
|
|
|
36
35
|
from fastapi import APIRouter, Query
|
|
37
36
|
from pydantic import BaseModel
|
|
38
37
|
|
|
38
|
+
from phlo.config.env import project_env_value
|
|
39
|
+
from phlo.config.network import resolve_url
|
|
39
40
|
from phlo.logging import get_logger
|
|
40
41
|
|
|
41
42
|
logger = get_logger(__name__)
|
|
@@ -56,8 +57,11 @@ def resolve_dagster_url(override: str | None = None) -> str:
|
|
|
56
57
|
|
|
57
58
|
"""
|
|
58
59
|
if override and override.strip():
|
|
59
|
-
return override
|
|
60
|
-
return
|
|
60
|
+
return resolve_url(override, port_env_var="DAGSTER_PORT")
|
|
61
|
+
return resolve_url(
|
|
62
|
+
project_env_value("DAGSTER_GRAPHQL_URL", DEFAULT_DAGSTER_URL) or DEFAULT_DAGSTER_URL,
|
|
63
|
+
port_env_var="DAGSTER_PORT",
|
|
64
|
+
)
|
|
61
65
|
|
|
62
66
|
|
|
63
67
|
# --- GraphQL Queries ---
|
|
@@ -45,6 +45,8 @@ from pydantic import BaseModel, Field
|
|
|
45
45
|
|
|
46
46
|
from phlo.capabilities import resolve_capability
|
|
47
47
|
from phlo.capabilities.discovery import discover_capabilities
|
|
48
|
+
from phlo.config.env import project_env_value
|
|
49
|
+
from phlo.config.network import resolve_url
|
|
48
50
|
from phlo.logging import get_bound_correlation_context, get_logger
|
|
49
51
|
from phlo_api.api.authorization import check_dataset_query, check_dataset_read
|
|
50
52
|
from phlo_api.observatory_api.trino_sql import (
|
|
@@ -107,7 +109,7 @@ def _resolve_query_engine() -> Any | None:
|
|
|
107
109
|
discover_capabilities()
|
|
108
110
|
return resolve_capability(
|
|
109
111
|
"query_engine",
|
|
110
|
-
|
|
112
|
+
project_env_value(_DEFAULT_QUERY_ENGINE_ENV) or _DEFAULT_TRINO_CAPABILITY_NAME,
|
|
111
113
|
)
|
|
112
114
|
|
|
113
115
|
|
|
@@ -124,18 +126,18 @@ def resolve_trino_url(override: str | None = None) -> str:
|
|
|
124
126
|
RuntimeError: If no query-engine URL is configured.
|
|
125
127
|
|
|
126
128
|
"""
|
|
127
|
-
env_url =
|
|
129
|
+
env_url = project_env_value(_QUERY_ENGINE_URL_ENV) or project_env_value("TRINO_URL")
|
|
128
130
|
if override and override.strip():
|
|
129
|
-
return override
|
|
131
|
+
return resolve_url(override, port_env_var="TRINO_PORT")
|
|
130
132
|
if env_url:
|
|
131
|
-
return env_url
|
|
133
|
+
return resolve_url(env_url, port_env_var="TRINO_PORT")
|
|
132
134
|
|
|
133
135
|
resolution = _resolve_query_engine()
|
|
134
136
|
if resolution is not None:
|
|
135
137
|
for key in ("url", "http_url", "endpoint"):
|
|
136
138
|
value = resolution.metadata.get(key)
|
|
137
139
|
if isinstance(value, str) and value:
|
|
138
|
-
return value
|
|
140
|
+
return resolve_url(value, port_env_var="TRINO_PORT")
|
|
139
141
|
|
|
140
142
|
host = resolution.metadata.get("host")
|
|
141
143
|
port = resolution.metadata.get("port")
|
|
@@ -143,7 +145,7 @@ def resolve_trino_url(override: str | None = None) -> str:
|
|
|
143
145
|
resolution.metadata.get("scheme") or resolution.metadata.get("http_scheme") or "http"
|
|
144
146
|
)
|
|
145
147
|
if isinstance(host, str) and host and port is not None:
|
|
146
|
-
return f"{scheme}://{host}:{port}"
|
|
148
|
+
return resolve_url(f"{scheme}://{host}:{port}", port_env_var="TRINO_PORT")
|
|
147
149
|
|
|
148
150
|
raise RuntimeError(
|
|
149
151
|
"No query-engine URL is configured. Set PHLO_QUERY_ENGINE_URL or TRINO_URL, "
|
|
@@ -164,7 +166,7 @@ def resolve_default_catalog() -> str:
|
|
|
164
166
|
RuntimeError: If no default catalog is configured.
|
|
165
167
|
|
|
166
168
|
"""
|
|
167
|
-
env_catalog =
|
|
169
|
+
env_catalog = project_env_value("PHLO_QUERY_CATALOG") or project_env_value("TRINO_CATALOG")
|
|
168
170
|
if env_catalog:
|
|
169
171
|
return env_catalog
|
|
170
172
|
|
|
@@ -193,7 +195,7 @@ def resolve_default_ref() -> str:
|
|
|
193
195
|
RuntimeError: If no default ref is configured.
|
|
194
196
|
|
|
195
197
|
"""
|
|
196
|
-
env_ref =
|
|
198
|
+
env_ref = project_env_value("PHLO_DEFAULT_REF") or project_env_value("NESSIE_DEFAULT_REF")
|
|
197
199
|
if env_ref:
|
|
198
200
|
return env_ref
|
|
199
201
|
|
|
@@ -229,7 +231,7 @@ def resolve_table_discovery_schemas(
|
|
|
229
231
|
if preferred_schema and preferred_schema.strip():
|
|
230
232
|
return [preferred_schema.strip()]
|
|
231
233
|
|
|
232
|
-
env_schemas =
|
|
234
|
+
env_schemas = project_env_value(_DISCOVERY_SCHEMAS_ENV)
|
|
233
235
|
if env_schemas:
|
|
234
236
|
schemas = [schema.strip() for schema in env_schemas.split(",") if schema.strip()]
|
|
235
237
|
if schemas:
|