gaard-api 0.2.1__tar.gz → 0.2.2__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.
- {gaard_api-0.2.1/src/gaard_api.egg-info → gaard_api-0.2.2}/PKG-INFO +19 -6
- {gaard_api-0.2.1 → gaard_api-0.2.2}/pyproject.toml +16 -6
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/database.py +32 -22
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/services.py +78 -14
- gaard_api-0.2.2/src/gaard_api/admin-web/assets/getgaard.svg +87 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin-web/assets/main.js +713 -61
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin-web/assets/styles.css +299 -12
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/admin.py +186 -12
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/analysis.py +10 -10
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/query.py +35 -502
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/cli_commands.py +5 -2
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/core/settings.py +34 -0
- gaard_api-0.2.2/src/gaard_api/extensions.py +125 -0
- gaard_api-0.2.2/src/gaard_api/license.py +899 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/main.py +27 -5
- gaard_api-0.2.2/src/gaard_api/package_updates.py +1007 -0
- gaard_api-0.2.2/src/gaard_api/query_hooks.py +322 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2/src/gaard_api.egg-info}/PKG-INFO +19 -6
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api.egg-info/SOURCES.txt +6 -0
- gaard_api-0.2.2/src/gaard_api.egg-info/requires.txt +34 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_admin_api.py +240 -450
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_api_registry.py +74 -1
- gaard_api-0.2.2/tests/test_license_api.py +425 -0
- gaard_api-0.2.2/tests/test_license_service.py +324 -0
- gaard_api-0.2.1/src/gaard_api/extensions.py +0 -50
- gaard_api-0.2.1/src/gaard_api.egg-info/requires.txt +0 -16
- {gaard_api-0.2.1 → gaard_api-0.2.2}/MANIFEST.in +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/README.md +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/setup.cfg +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/defaults.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/models.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/prompt_runtime.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin/security.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin-web/index.html +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/admin-web/package.json +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/prompts.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api/v1/schema.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/api_registry.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/cli.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/core/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/core/error_handlers.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/core/schema_cache.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/example_data/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/example_data/medical_poc/seed.sql +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/example_database.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/extension_services.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api/server_cli.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api.egg-info/dependency_links.txt +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api.egg-info/entry_points.txt +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/src/gaard_api.egg-info/top_level.txt +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_analysis_api.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_error_handlers.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_prompt_runtime.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_query_error_messages.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_server_cli.py +0 -0
- {gaard_api-0.2.1 → gaard_api-0.2.2}/tests/test_settings.py +0 -0
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gaard-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: GAARD backend web services providing admin interface
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: gaard-core==0.2.
|
|
8
|
-
Requires-Dist: gaard-connectors==0.2.
|
|
9
|
-
Requires-Dist: gaard-llm==0.2.
|
|
10
|
-
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.
|
|
7
|
+
Requires-Dist: gaard-core==0.2.2
|
|
8
|
+
Requires-Dist: gaard-connectors==0.2.2
|
|
9
|
+
Requires-Dist: gaard-llm==0.2.2
|
|
10
|
+
Requires-Dist: gaard-plugin-api<0.3.0,>=0.2.2
|
|
11
11
|
Requires-Dist: fastapi>=0.111.0
|
|
12
12
|
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
13
13
|
Requires-Dist: pydantic>=2.7.0
|
|
14
14
|
Requires-Dist: sqlalchemy>=2.0.0
|
|
15
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: httpx2>=2.5.0
|
|
16
|
+
Provides-Extra: oracle
|
|
17
|
+
Requires-Dist: oracledb>=2.0.0; extra == "oracle"
|
|
18
|
+
Provides-Extra: mssql
|
|
19
|
+
Requires-Dist: pyodbc>=5.0.0; extra == "mssql"
|
|
20
|
+
Provides-Extra: ibm-db2
|
|
21
|
+
Requires-Dist: ibm-db-sa>=0.4.4; extra == "ibm-db2"
|
|
22
|
+
Provides-Extra: teradata
|
|
23
|
+
Requires-Dist: teradatasqlalchemy>=20.0.0.9; extra == "teradata"
|
|
24
|
+
Provides-Extra: all-datasources
|
|
25
|
+
Requires-Dist: oracledb>=2.0.0; extra == "all-datasources"
|
|
26
|
+
Requires-Dist: pyodbc>=5.0.0; extra == "all-datasources"
|
|
27
|
+
Requires-Dist: ibm-db-sa>=0.4.4; extra == "all-datasources"
|
|
28
|
+
Requires-Dist: teradatasqlalchemy>=20.0.0.9; extra == "all-datasources"
|
|
16
29
|
Provides-Extra: dev
|
|
17
30
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
18
31
|
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
@@ -4,24 +4,34 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gaard-api"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "GAARD backend web services providing admin interface"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
|
|
12
12
|
dependencies = [
|
|
13
|
-
"gaard-core==0.2.
|
|
14
|
-
"gaard-connectors==0.2.
|
|
15
|
-
"gaard-llm==0.2.
|
|
16
|
-
"gaard-plugin-api>=0.2.
|
|
13
|
+
"gaard-core==0.2.2",
|
|
14
|
+
"gaard-connectors==0.2.2",
|
|
15
|
+
"gaard-llm==0.2.2",
|
|
16
|
+
"gaard-plugin-api>=0.2.2,<0.3.0",
|
|
17
17
|
"fastapi>=0.111.0",
|
|
18
18
|
"uvicorn[standard]>=0.30.0",
|
|
19
19
|
"pydantic>=2.7.0",
|
|
20
20
|
"sqlalchemy>=2.0.0",
|
|
21
|
-
"
|
|
21
|
+
"httpx2>=2.5.0"
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
+
oracle = ["oracledb>=2.0.0"]
|
|
26
|
+
mssql = ["pyodbc>=5.0.0"]
|
|
27
|
+
ibm_db2 = ["ibm-db-sa>=0.4.4"]
|
|
28
|
+
teradata = ["teradatasqlalchemy>=20.0.0.9"]
|
|
29
|
+
all-datasources = [
|
|
30
|
+
"oracledb>=2.0.0",
|
|
31
|
+
"pyodbc>=5.0.0",
|
|
32
|
+
"ibm-db-sa>=0.4.4",
|
|
33
|
+
"teradatasqlalchemy>=20.0.0.9",
|
|
34
|
+
]
|
|
25
35
|
dev = [
|
|
26
36
|
"pytest>=8.0.0",
|
|
27
37
|
"ruff>=0.5.0",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from collections.abc import Iterator
|
|
2
2
|
import json
|
|
3
|
+
import threading
|
|
3
4
|
|
|
4
5
|
from sqlalchemy import create_engine, delete, select
|
|
5
6
|
from sqlalchemy.engine import Engine
|
|
6
|
-
from sqlalchemy import inspect, text
|
|
7
|
+
from sqlalchemy import inspect, text, Table
|
|
7
8
|
from sqlalchemy.orm import Session, sessionmaker
|
|
8
9
|
|
|
9
10
|
from gaard_api.admin.defaults import DEFAULT_GOVERNANCE_POLICY_CONFIG, DEFAULT_PROMPTS
|
|
@@ -20,10 +21,13 @@ from gaard_api.admin.models import (
|
|
|
20
21
|
from gaard_api.admin.security import hash_password
|
|
21
22
|
from gaard_api.core.settings import settings
|
|
22
23
|
|
|
24
|
+
from typing import cast
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
_engine: Engine | None = None
|
|
25
28
|
_session_factory: sessionmaker[Session] | None = None
|
|
26
29
|
_engine_url: str | None = None
|
|
30
|
+
_init_lock = threading.RLock()
|
|
27
31
|
LEGACY_PROMPT_KEYS = {"investigation_readiness"}
|
|
28
32
|
|
|
29
33
|
|
|
@@ -66,23 +70,24 @@ def create_session() -> Session:
|
|
|
66
70
|
|
|
67
71
|
|
|
68
72
|
def init_metadata_store() -> None:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
with _init_lock:
|
|
74
|
+
engine = get_engine()
|
|
75
|
+
Base.metadata.create_all(engine)
|
|
76
|
+
ensure_data_query_audit_schema(engine)
|
|
77
|
+
ensure_overview_widget_schema(engine)
|
|
78
|
+
|
|
79
|
+
if _session_factory is None:
|
|
80
|
+
raise RuntimeError("Admin metadata session factory is not initialized.")
|
|
81
|
+
|
|
82
|
+
with _session_factory() as session:
|
|
83
|
+
seed_admin_user(session)
|
|
84
|
+
seed_settings(session)
|
|
85
|
+
apply_runtime_settings(session)
|
|
86
|
+
seed_prompts(session)
|
|
87
|
+
seed_datasource_connectors(session)
|
|
88
|
+
seed_overview_widgets(session)
|
|
89
|
+
backfill_data_query_audit_types(session)
|
|
90
|
+
session.commit()
|
|
86
91
|
|
|
87
92
|
|
|
88
93
|
def seed_admin_user(session: Session) -> None:
|
|
@@ -129,7 +134,6 @@ def seed_settings(session: Session) -> None:
|
|
|
129
134
|
),
|
|
130
135
|
"data_query_audit_retention_days": str(settings.gaard_audit_retention_days),
|
|
131
136
|
"schema_cache_ttl_seconds": str(settings.gaard_schema_cache_ttl_seconds),
|
|
132
|
-
"license_edition": "community",
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
for key, value in defaults.items():
|
|
@@ -139,6 +143,9 @@ def seed_settings(session: Session) -> None:
|
|
|
139
143
|
elif setting.updated_by == "system" and setting.value != value:
|
|
140
144
|
setting.value = value
|
|
141
145
|
|
|
146
|
+
if session.get(AdminSetting, "license_edition") is None:
|
|
147
|
+
session.add(AdminSetting(key="license_edition", value="community"))
|
|
148
|
+
|
|
142
149
|
|
|
143
150
|
def apply_runtime_settings(session: Session) -> None:
|
|
144
151
|
schema_cache_ttl = session.get(AdminSetting, "schema_cache_ttl_seconds")
|
|
@@ -374,8 +381,8 @@ def seed_overview_widgets(session: Session) -> None:
|
|
|
374
381
|
existing.active = False
|
|
375
382
|
|
|
376
383
|
if existing is not None and existing.updated_by == "system":
|
|
377
|
-
existing.position = int(item["position"])
|
|
378
|
-
existing.grid_width = int(item["grid_width"])
|
|
384
|
+
existing.position = int(cast(int,item["position"]))
|
|
385
|
+
existing.grid_width = int(cast(int,item["grid_width"]))
|
|
379
386
|
existing.result_mode = str(item["result_mode"])
|
|
380
387
|
|
|
381
388
|
|
|
@@ -416,7 +423,10 @@ def ensure_data_query_audit_schema(engine: Engine) -> None:
|
|
|
416
423
|
)
|
|
417
424
|
|
|
418
425
|
with engine.begin() as connection:
|
|
419
|
-
|
|
426
|
+
|
|
427
|
+
audit_log_table = cast(Table, DataQueryAuditLog.__table__)
|
|
428
|
+
|
|
429
|
+
for index in audit_log_table.indexes:
|
|
420
430
|
index.create(bind=connection, checkfirst=True)
|
|
421
431
|
|
|
422
432
|
|
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from datetime import UTC, datetime, timedelta
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, cast
|
|
7
7
|
|
|
8
8
|
from sqlalchemy.engine import URL
|
|
9
9
|
from sqlalchemy import delete, desc, select
|
|
@@ -139,6 +139,36 @@ def build_sqlalchemy_url_from_connection_config(
|
|
|
139
139
|
query_keys=("charset",),
|
|
140
140
|
)
|
|
141
141
|
|
|
142
|
+
if database_type == "oracle":
|
|
143
|
+
return build_server_database_url(
|
|
144
|
+
drivername="oracle+oracledb",
|
|
145
|
+
connection_config=connection_config,
|
|
146
|
+
default_port=1521,
|
|
147
|
+
query_keys=("service_name",),
|
|
148
|
+
database_required=False,
|
|
149
|
+
required_query_keys=("service_name",),
|
|
150
|
+
required_query_key_labels={"service_name": "service name"},
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if database_type == "mssql":
|
|
154
|
+
return build_server_database_url(
|
|
155
|
+
drivername="mssql+pyodbc",
|
|
156
|
+
connection_config=connection_config,
|
|
157
|
+
default_port=1433,
|
|
158
|
+
query_keys=("driver", "Encrypt", "TrustServerCertificate"),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if database_type == "ibm_db2":
|
|
162
|
+
return build_server_database_url(
|
|
163
|
+
drivername="db2+ibm_db",
|
|
164
|
+
connection_config=connection_config,
|
|
165
|
+
default_port=50000,
|
|
166
|
+
query_keys=(),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if database_type == "teradata":
|
|
170
|
+
return build_teradata_database_url(connection_config)
|
|
171
|
+
|
|
142
172
|
raise ValueError(f"Datasource type {database_type!r} does not support generated URLs.")
|
|
143
173
|
|
|
144
174
|
|
|
@@ -147,6 +177,9 @@ def build_server_database_url(
|
|
|
147
177
|
connection_config: dict[str, Any],
|
|
148
178
|
default_port: int,
|
|
149
179
|
query_keys: tuple[str, ...],
|
|
180
|
+
database_required: bool = True,
|
|
181
|
+
required_query_keys: tuple[str, ...] = (),
|
|
182
|
+
required_query_key_labels: dict[str, str] | None = None,
|
|
150
183
|
) -> str:
|
|
151
184
|
host = str(connection_config.get("host") or "").strip()
|
|
152
185
|
database = str(connection_config.get("database") or "").strip()
|
|
@@ -156,7 +189,7 @@ def build_server_database_url(
|
|
|
156
189
|
|
|
157
190
|
if not host:
|
|
158
191
|
raise ValueError("Datasource host is required.")
|
|
159
|
-
if not database:
|
|
192
|
+
if database_required and not database:
|
|
160
193
|
raise ValueError("Datasource database name is required.")
|
|
161
194
|
if not username:
|
|
162
195
|
raise ValueError("Datasource username is required.")
|
|
@@ -166,6 +199,12 @@ def build_server_database_url(
|
|
|
166
199
|
except (TypeError, ValueError) as exc:
|
|
167
200
|
raise ValueError("Datasource port must be a number.") from exc
|
|
168
201
|
|
|
202
|
+
labels = required_query_key_labels or {}
|
|
203
|
+
for key in required_query_keys:
|
|
204
|
+
if not str(connection_config.get(key) or "").strip():
|
|
205
|
+
label = labels.get(key, key.replace("_", " "))
|
|
206
|
+
raise ValueError(f"Datasource {label} is required.")
|
|
207
|
+
|
|
169
208
|
query = {
|
|
170
209
|
key: str(connection_config.get(key)).strip()
|
|
171
210
|
for key in query_keys
|
|
@@ -179,7 +218,40 @@ def build_server_database_url(
|
|
|
179
218
|
password=password or None,
|
|
180
219
|
host=host,
|
|
181
220
|
port=port,
|
|
182
|
-
database=database,
|
|
221
|
+
database=database or None,
|
|
222
|
+
query=query,
|
|
223
|
+
)
|
|
224
|
+
.render_as_string(hide_password=False)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def build_teradata_database_url(connection_config: dict[str, Any]) -> str:
|
|
229
|
+
host = str(connection_config.get("host") or "").strip()
|
|
230
|
+
username = str(connection_config.get("username") or "").strip()
|
|
231
|
+
password = str(connection_config.get("password") or "")
|
|
232
|
+
dbs_port_value = connection_config.get("dbs_port") or 1025
|
|
233
|
+
|
|
234
|
+
if not host:
|
|
235
|
+
raise ValueError("Datasource host is required.")
|
|
236
|
+
if not username:
|
|
237
|
+
raise ValueError("Datasource username is required.")
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
dbs_port = int(dbs_port_value)
|
|
241
|
+
except (TypeError, ValueError) as exc:
|
|
242
|
+
raise ValueError("Datasource port must be a number.") from exc
|
|
243
|
+
|
|
244
|
+
query = {"dbs_port": str(dbs_port)}
|
|
245
|
+
for key in ("database", "tmode"):
|
|
246
|
+
if connection_config.get(key) not in (None, ""):
|
|
247
|
+
query[key] = str(connection_config.get(key)).strip()
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
URL.create(
|
|
251
|
+
drivername="teradatasql",
|
|
252
|
+
username=username,
|
|
253
|
+
password=password or None,
|
|
254
|
+
host=host,
|
|
183
255
|
query=query,
|
|
184
256
|
)
|
|
185
257
|
.render_as_string(hide_password=False)
|
|
@@ -487,7 +559,7 @@ def set_setting(session: Session, key: str, value: str, actor: str) -> AdminSett
|
|
|
487
559
|
|
|
488
560
|
|
|
489
561
|
def default_governance_policy_config() -> dict[str, Any]:
|
|
490
|
-
return json.loads(json_dumps(DEFAULT_GOVERNANCE_POLICY_CONFIG))
|
|
562
|
+
return cast(dict[str, Any], json.loads(json_dumps(DEFAULT_GOVERNANCE_POLICY_CONFIG)))
|
|
491
563
|
|
|
492
564
|
|
|
493
565
|
def normalize_bool_setting(value: Any, field_name: str) -> bool:
|
|
@@ -2014,17 +2086,9 @@ def set_active_datasource_connector(
|
|
|
2014
2086
|
connector: DatasourceConnector,
|
|
2015
2087
|
actor: str,
|
|
2016
2088
|
) -> None:
|
|
2017
|
-
|
|
2018
|
-
connector.active = False
|
|
2019
|
-
return
|
|
2020
|
-
|
|
2021
|
-
if connector.connector_key != "default":
|
|
2022
|
-
for item in list_datasource_connectors(session):
|
|
2023
|
-
if item.connector_key == "default":
|
|
2024
|
-
item.active = False
|
|
2089
|
+
from gaard_api.extensions import get_query_hook_registry
|
|
2025
2090
|
|
|
2026
|
-
|
|
2027
|
-
connector.updated_by = actor
|
|
2091
|
+
get_query_hook_registry().set_active_datasource_connector(session, connector, actor)
|
|
2028
2092
|
|
|
2029
2093
|
|
|
2030
2094
|
def test_datasource_connection(connector: DatasourceConnector) -> None:
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
version="1.1"
|
|
6
|
+
id="svg1"
|
|
7
|
+
width="1672"
|
|
8
|
+
height="1672"
|
|
9
|
+
viewBox="0 0 1672 1672"
|
|
10
|
+
sodipodi:docname="getgaard.svg"
|
|
11
|
+
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
|
|
12
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
13
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
16
|
+
<defs
|
|
17
|
+
id="defs1" />
|
|
18
|
+
<sodipodi:namedview
|
|
19
|
+
id="namedview1"
|
|
20
|
+
pagecolor="#ffffff"
|
|
21
|
+
bordercolor="#000000"
|
|
22
|
+
borderopacity="0.25"
|
|
23
|
+
inkscape:showpageshadow="2"
|
|
24
|
+
inkscape:pageopacity="0.0"
|
|
25
|
+
inkscape:pagecheckerboard="0"
|
|
26
|
+
inkscape:deskcolor="#d1d1d1"
|
|
27
|
+
inkscape:zoom="0.17088526"
|
|
28
|
+
inkscape:cx="2182.7512"
|
|
29
|
+
inkscape:cy="111.18572"
|
|
30
|
+
inkscape:window-width="1560"
|
|
31
|
+
inkscape:window-height="771"
|
|
32
|
+
inkscape:window-x="0"
|
|
33
|
+
inkscape:window-y="25"
|
|
34
|
+
inkscape:window-maximized="0"
|
|
35
|
+
inkscape:current-layer="g1" />
|
|
36
|
+
<g
|
|
37
|
+
inkscape:groupmode="layer"
|
|
38
|
+
inkscape:label="Image"
|
|
39
|
+
id="g1">
|
|
40
|
+
<g
|
|
41
|
+
id="g6"
|
|
42
|
+
transform="matrix(1.0610508,0,0,1.0610508,-2611.1446,1006.5308)">
|
|
43
|
+
<rect
|
|
44
|
+
style="fill:#111e2f;fill-opacity:1;stroke-width:1.09542"
|
|
45
|
+
id="rect2"
|
|
46
|
+
width="1437.7893"
|
|
47
|
+
height="1437.7893"
|
|
48
|
+
x="2529.908"
|
|
49
|
+
y="-879.6134"
|
|
50
|
+
ry="320.62204" />
|
|
51
|
+
<path
|
|
52
|
+
id="rect1"
|
|
53
|
+
style="fill:#ffffff;stroke-width:1.09542"
|
|
54
|
+
d="m 3234.3408,-626.0861 c -13.6106,0.3309 -25.9368,1.0895 -34.8867,2.3418 -84.1236,11.7709 -151.8696,39.6365 -216.4922,89.0508 -88.4727,67.6513 -151.6819,174.0139 -171.4746,288.5391 -8.8934,51.4587 -8.6698,114.0636 0.5859,163.333996 8.6422,46.004504 21.2085,84.4823042 40.9121,125.273404 23.7832,49.237 50.781,87.8784 88.418,126.5547 91.8576,94.394 218.5332,143.6113 347.0449,134.8359 127.7123,-8.7209 240.4621,-72.1949 316.1836,-177.998 45.9159,-64.1567 74.5299,-138.5546 84.8711,-220.666004 2.9258,-23.231196 4.2525,-75.228896 1.9883,-77.957096 -1.5107,-1.8203 -25.8188,-2.0429 -222.8203,-2.0429 -217.2534,0 -221.1459,0.044 -222.502,2.5781 -0.885,1.6537 -1.3808,21.59 -1.3808,55.5625 0,47.4299 0.2317,53.2391 2.207,55.4218 2.1138,2.3358 8.7633,2.4375 158,2.4376 129.1634,0 155.793,0.305 155.793,1.7832 0,3.2418 -7.4812,26.672 -12.6289,39.5507 -17.8663,44.6984 -41.4198,80.6843 -74.6856,114.1075 -26.441,26.566 -51.6323,44.8176 -82.6855,59.9082 -124.9554,60.7235 -275.5308,28.7986 -373.086,-79.1016 -92.4325,-102.2344 -119.9458,-248.8362 -70.1562,-373.8106 17.3657,-43.5887 43.6592,-83.5871 76.4023,-116.2246 38.482,-38.3578 81.0392,-64.5511 132.1739,-81.3515 48.2572,-15.855 102.913,-19.5453 159.332,-10.7598 6.9666,1.0848 13.6858,2.2858 14.9316,2.6699 1.272,0.392 3.4643,-0.5013 5,-2.0371 2.6697,-2.6697 2.7364,-4.0526 2.7364,-58.0371 v -55.3027 l -3,-1.9356 c -7.5481,-4.8687 -59.9495,-7.7173 -100.7813,-6.7246 z"
|
|
55
|
+
sodipodi:nodetypes="ssssssssssssssssssssssssssscsss" />
|
|
56
|
+
<g
|
|
57
|
+
id="g5"
|
|
58
|
+
transform="translate(2457.7895,-1000.6715)"
|
|
59
|
+
style="fill:#0cc6a6;fill-opacity:1">
|
|
60
|
+
<rect
|
|
61
|
+
style="opacity:1;fill:#0cc6a6;fill-opacity:1;stroke-width:0.938067"
|
|
62
|
+
id="rect3"
|
|
63
|
+
width="72.260895"
|
|
64
|
+
height="134.18539"
|
|
65
|
+
x="936.10278"
|
|
66
|
+
y="636.39392"
|
|
67
|
+
ry="15.974472" />
|
|
68
|
+
<rect
|
|
69
|
+
style="opacity:1;fill:#0cc6a6;fill-opacity:1;stroke-width:1.20121"
|
|
70
|
+
id="rect4"
|
|
71
|
+
width="72.260895"
|
|
72
|
+
height="220.02458"
|
|
73
|
+
x="1046.6084"
|
|
74
|
+
y="550.55475"
|
|
75
|
+
ry="19.286819" />
|
|
76
|
+
<rect
|
|
77
|
+
style="opacity:1;fill:#0cc6a6;fill-opacity:1;stroke-width:1.50056"
|
|
78
|
+
id="rect5"
|
|
79
|
+
width="72.260895"
|
|
80
|
+
height="343.35675"
|
|
81
|
+
x="1159.0873"
|
|
82
|
+
y="427.22256"
|
|
83
|
+
ry="10.289478" />
|
|
84
|
+
</g>
|
|
85
|
+
</g>
|
|
86
|
+
</g>
|
|
87
|
+
</svg>
|