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.
Files changed (76) hide show
  1. {phlo_api-0.3.0 → phlo_api-0.4.0}/PKG-INFO +1 -1
  2. {phlo_api-0.3.0 → phlo_api-0.4.0}/pyproject.toml +1 -1
  3. phlo_api-0.4.0/src/phlo_api/Dockerfile +21 -0
  4. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/main.py +1 -0
  5. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/dagster.py +6 -5
  6. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/loki.py +7 -3
  7. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/nessie.py +6 -5
  8. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/quality.py +7 -3
  9. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/trino.py +11 -9
  10. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2.py +281 -226
  11. phlo_api-0.4.0/src/phlo_api/observatory_api/v2_cache.py +37 -0
  12. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_capabilities.py +6 -0
  13. phlo_api-0.4.0/src/phlo_api/observatory_api/v2_saved_queries.py +94 -0
  14. phlo_api-0.4.0/src/phlo_api/observatory_api/v2_search.py +124 -0
  15. phlo_api-0.4.0/src/phlo_api/observatory_api/v2_services.py +438 -0
  16. phlo_api-0.4.0/src/phlo_api/observatory_api/v2_workflow_wizard.py +1135 -0
  17. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/service.yaml +11 -4
  18. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/PKG-INFO +1 -1
  19. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/SOURCES.txt +9 -0
  20. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_authentication_api.py +48 -0
  21. phlo_api-0.4.0/tests/test_cors.py +21 -0
  22. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_integration_api.py +16 -0
  23. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_api.py +94 -0
  24. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_capabilities.py +1 -0
  25. phlo_api-0.4.0/tests/test_observatory_v2_read_models.py +76 -0
  26. phlo_api-0.4.0/tests/test_observatory_v2_workflow_wizard.py +291 -0
  27. phlo_api-0.4.0/tests/test_service_url_resolution.py +55 -0
  28. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_trino_defaults.py +1 -0
  29. phlo_api-0.3.0/src/phlo_api/Dockerfile +0 -15
  30. {phlo_api-0.3.0 → phlo_api-0.4.0}/README.md +0 -0
  31. {phlo_api-0.3.0 → phlo_api-0.4.0}/setup.cfg +0 -0
  32. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/__init__.py +0 -0
  33. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/__init__.py +0 -0
  34. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/authentication.py +0 -0
  35. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/authorization.py +0 -0
  36. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/maintenance.py +0 -0
  37. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/api/observability.py +0 -0
  38. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/__init__.py +0 -0
  39. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/contributing.py +0 -0
  40. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/extension_settings.py +0 -0
  41. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/extensions.py +0 -0
  42. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/iceberg.py +0 -0
  43. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/lineage.py +0 -0
  44. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/search.py +0 -0
  45. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/settings.py +0 -0
  46. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/trino_sql.py +0 -0
  47. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_actions.py +0 -0
  48. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_catalog.py +0 -0
  49. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_governance.py +0 -0
  50. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_metadata.py +0 -0
  51. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_models.py +0 -0
  52. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_observability.py +0 -0
  53. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_products.py +0 -0
  54. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_runs.py +0 -0
  55. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/observatory_api/v2_storage.py +0 -0
  56. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/plugin.py +0 -0
  57. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api/regulated_surface_adapter.py +0 -0
  58. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/dependency_links.txt +0 -0
  59. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/entry_points.txt +0 -0
  60. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/requires.txt +0 -0
  61. {phlo_api-0.3.0 → phlo_api-0.4.0}/src/phlo_api.egg-info/top_level.txt +0 -0
  62. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_api_backend_endpoints.py +0 -0
  63. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_api_import_boundaries.py +0 -0
  64. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_authorization_api.py +0 -0
  65. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_lineage_api.py +0 -0
  66. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_loki_api.py +0 -0
  67. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_maintenance_api.py +0 -0
  68. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observability_api.py +0 -0
  69. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_actions.py +0 -0
  70. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_catalog.py +0 -0
  71. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_observability.py +0 -0
  72. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_storage.py +0 -0
  73. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_observatory_v2_surfaces.py +0 -0
  74. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_rest_api.py +0 -0
  75. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_service_correlation_runtime.py +0 -0
  76. {phlo_api-0.3.0 → phlo_api-0.4.0}/tests/test_trino_sql.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phlo-api
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Phlo API - Backend service exposing Phlo internals to Observatory
5
5
  Author-email: Phlo Team <team@phlo.dev>
6
6
  License: MIT
@@ -19,7 +19,7 @@ dependencies = [
19
19
  description = "Phlo API - Backend service exposing Phlo internals to Observatory"
20
20
  name = "phlo-api"
21
21
  requires-python = ">=3.11"
22
- version = "0.3.0"
22
+ version = "0.4.0"
23
23
 
24
24
  [[project.authors]]
25
25
  email = "team@phlo.dev"
@@ -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 = os.environ.get("DAGSTER_GRAPHQL_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 os.environ.get("LOKI_URL", DEFAULT_LOKI_URL)
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 = os.environ.get("NESSIE_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 os.environ.get("DAGSTER_GRAPHQL_URL", DEFAULT_DAGSTER_URL)
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
- os.environ.get(_DEFAULT_QUERY_ENGINE_ENV) or _DEFAULT_TRINO_CAPABILITY_NAME,
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 = os.environ.get(_QUERY_ENGINE_URL_ENV) or os.environ.get("TRINO_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 = os.environ.get("PHLO_QUERY_CATALOG") or os.environ.get("TRINO_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 = os.environ.get("PHLO_DEFAULT_REF") or os.environ.get("NESSIE_DEFAULT_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 = os.environ.get(_DISCOVERY_SCHEMAS_ENV)
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: