gaard-api 0.2.12__tar.gz → 0.2.13__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.12/src/gaard_api.egg-info → gaard_api-0.2.13}/PKG-INFO +1 -1
- {gaard_api-0.2.12 → gaard_api-0.2.13}/pyproject.toml +1 -1
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/services.py +1 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/main.js +13 -12
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/admin.py +63 -22
- {gaard_api-0.2.12 → gaard_api-0.2.13/src/gaard_api.egg-info}/PKG-INFO +1 -1
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_admin_api.py +44 -11
- {gaard_api-0.2.12 → gaard_api-0.2.13}/MANIFEST.in +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/README.md +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/setup.cfg +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/database.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/database_initial.sql +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/database_legacy_updates.sql +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/database_updates.sql +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/defaults.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/migration_runner.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/models.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/prompt_runtime.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin/security.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/getgaard.svg +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/identity.js +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/styles.css +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack-all.js +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack.min.css +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/index.html +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/admin-web/package.json +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/analysis.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/dashboards.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/prompts.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/query.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api/v1/schema.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/api_registry.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/auth_dependencies.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/auth_hooks.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/cli.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/cli_commands.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/conversations.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/core/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/core/error_handlers.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/core/schema_cache.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/core/settings.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/example_data/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/example_data/medical_poc/seed.sql +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/example_database.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/extension_services.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/extensions.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/license.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/main.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/package_updates.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/py.typed +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/query_hooks.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/server_cli.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/siem.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api/tls_http.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api.egg-info/SOURCES.txt +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api.egg-info/dependency_links.txt +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api.egg-info/entry_points.txt +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api.egg-info/requires.txt +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/src/gaard_api.egg-info/top_level.txt +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_analysis_api.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_api_cli.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_api_registry.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_conversation_api.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_database_migrations.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_error_handlers.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_license_api.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_license_service.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_prompt_runtime.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_query_error_messages.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_server_cli.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_settings.py +0 -0
- {gaard_api-0.2.12 → gaard_api-0.2.13}/tests/test_tls_http.py +0 -0
|
@@ -2229,6 +2229,7 @@ def build_table_settings(
|
|
|
2229
2229
|
tables[table.name] = {
|
|
2230
2230
|
"selected": bool(existing.get("selected", True)),
|
|
2231
2231
|
"description": str(existing.get("description", "")),
|
|
2232
|
+
"view_description": str(existing.get("view_description", "")),
|
|
2232
2233
|
"primary_key_prompt": str(existing.get("primary_key_prompt", "")),
|
|
2233
2234
|
"foreign_key_prompt": str(existing.get("foreign_key_prompt", "")),
|
|
2234
2235
|
"join_logic": str(existing.get("join_logic", "")),
|
|
@@ -100,7 +100,7 @@ var state = {
|
|
|
100
100
|
loadedName: "",
|
|
101
101
|
loadedSql: "",
|
|
102
102
|
name: "",
|
|
103
|
-
|
|
103
|
+
view_description: "",
|
|
104
104
|
sql: "",
|
|
105
105
|
error: "",
|
|
106
106
|
success: "",
|
|
@@ -1926,7 +1926,7 @@ function renderDatasourceViewDialog() {
|
|
|
1926
1926
|
</div>
|
|
1927
1927
|
<form id="datasource-view-form" class="form-grid">
|
|
1928
1928
|
<label>View name<input name="name" value="${escapeHtml(dialog.name || "")}" autocomplete="off" /></label>
|
|
1929
|
-
<label>View description<textarea name="
|
|
1929
|
+
<label>View description<textarea name="view_description">${escapeHtml(dialog.view_description || "")}</textarea></label>
|
|
1930
1930
|
<label>SQL<textarea name="sql" class="textarea-small" spellcheck="false">${escapeHtml(dialog.sql || "")}</textarea></label>
|
|
1931
1931
|
${busy === "load" ? `<p class="muted"><span class="spinner" aria-hidden="true"></span> Loading view definition.</p>` : ""}
|
|
1932
1932
|
${dialog.error ? `<p class="error">${escapeHtml(dialog.error)}</p>` : ""}
|
|
@@ -1976,6 +1976,7 @@ function getDatasourceSchemaDraftTables(rawTables, tableSettings) {
|
|
|
1976
1976
|
state.datasourceSchemaDraftTables[table.name] = {
|
|
1977
1977
|
selected: settings.selected !== false,
|
|
1978
1978
|
description: settings.description || "",
|
|
1979
|
+
view_description: settings.view_description || "",
|
|
1979
1980
|
primary_key_prompt: settings.primary_key_prompt || "",
|
|
1980
1981
|
foreign_key_prompt: settings.foreign_key_prompt || "",
|
|
1981
1982
|
join_logic: settings.join_logic || ""
|
|
@@ -3340,7 +3341,7 @@ function defaultDatasourceViewDialog() {
|
|
|
3340
3341
|
loadedName: "",
|
|
3341
3342
|
loadedSql: "",
|
|
3342
3343
|
name: "",
|
|
3343
|
-
|
|
3344
|
+
view_description: "",
|
|
3344
3345
|
sql: "",
|
|
3345
3346
|
error: "",
|
|
3346
3347
|
success: "",
|
|
@@ -3367,8 +3368,7 @@ async function openEditDatasourceViewDialog(event) {
|
|
|
3367
3368
|
const rawTables = state.datasourceSchema?.item?.raw_schema?.tables || [];
|
|
3368
3369
|
const tableSettings = state.datasourceSchema?.item?.table_settings?.tables || {};
|
|
3369
3370
|
const draftSettings = getDatasourceSchemaDraftTables(rawTables, tableSettings)[viewName] || {};
|
|
3370
|
-
const
|
|
3371
|
-
const draftDescription = hasDraftDescription ? draftSettings.description || "" : "";
|
|
3371
|
+
const draftViewDescription = draftSettings.view_description || "";
|
|
3372
3372
|
state.datasourceViewDialog = {
|
|
3373
3373
|
...defaultDatasourceViewDialog(),
|
|
3374
3374
|
open: true,
|
|
@@ -3376,14 +3376,14 @@ async function openEditDatasourceViewDialog(event) {
|
|
|
3376
3376
|
originalName: viewName,
|
|
3377
3377
|
loadedName: viewName,
|
|
3378
3378
|
name: viewName,
|
|
3379
|
-
|
|
3379
|
+
view_description: draftViewDescription,
|
|
3380
3380
|
busy: "load"
|
|
3381
3381
|
};
|
|
3382
3382
|
render();
|
|
3383
3383
|
try {
|
|
3384
3384
|
const result = await api(`/api/v1/admin/datasources/${selected.id}/schema/views/${encodeURIComponent(viewName)}`);
|
|
3385
3385
|
const item = result.item || {};
|
|
3386
|
-
const
|
|
3386
|
+
const viewDescription = draftViewDescription || item.view_description || item.description || "";
|
|
3387
3387
|
state.datasourceViewDialog = {
|
|
3388
3388
|
...state.datasourceViewDialog,
|
|
3389
3389
|
mode: "edit",
|
|
@@ -3391,7 +3391,7 @@ async function openEditDatasourceViewDialog(event) {
|
|
|
3391
3391
|
loadedName: item.name || viewName,
|
|
3392
3392
|
loadedSql: item.sql || "",
|
|
3393
3393
|
name: item.name || viewName,
|
|
3394
|
-
|
|
3394
|
+
view_description: viewDescription,
|
|
3395
3395
|
sql: item.sql || "",
|
|
3396
3396
|
busy: "",
|
|
3397
3397
|
error: "",
|
|
@@ -3420,14 +3420,14 @@ function readDatasourceViewDialogPayload() {
|
|
|
3420
3420
|
const dialog = state.datasourceViewDialog || {};
|
|
3421
3421
|
return {
|
|
3422
3422
|
name: String(dialog.name || "").trim(),
|
|
3423
|
-
|
|
3423
|
+
view_description: String(dialog.view_description || ""),
|
|
3424
3424
|
sql: String(dialog.sql || "")
|
|
3425
3425
|
};
|
|
3426
3426
|
}
|
|
3427
3427
|
const data = new FormData(form);
|
|
3428
3428
|
return {
|
|
3429
3429
|
name: String(data.get("name") || "").trim(),
|
|
3430
|
-
|
|
3430
|
+
view_description: String(data.get("view_description") || ""),
|
|
3431
3431
|
sql: String(data.get("sql") || "")
|
|
3432
3432
|
};
|
|
3433
3433
|
}
|
|
@@ -3464,7 +3464,7 @@ async function generateDatasourceViewSql() {
|
|
|
3464
3464
|
try {
|
|
3465
3465
|
const result = await api(`/api/v1/admin/datasources/${selected.id}/schema/views/generate-sql`, {
|
|
3466
3466
|
method: "POST",
|
|
3467
|
-
body: JSON.stringify({ name: payload.name,
|
|
3467
|
+
body: JSON.stringify({ name: payload.name, view_description: payload.view_description })
|
|
3468
3468
|
});
|
|
3469
3469
|
state.datasourceViewDialog = {
|
|
3470
3470
|
...state.datasourceViewDialog,
|
|
@@ -3546,7 +3546,7 @@ async function saveDatasourceView(event) {
|
|
|
3546
3546
|
const result = canSaveSettingsOnly
|
|
3547
3547
|
? await api(`/api/v1/admin/datasources/${selected.id}/schema/views/${encodeURIComponent(dialog.originalName || payload.name)}`, {
|
|
3548
3548
|
method: "PUT",
|
|
3549
|
-
body: JSON.stringify({
|
|
3549
|
+
body: JSON.stringify({ view_description: payload.view_description })
|
|
3550
3550
|
})
|
|
3551
3551
|
: dialog.mode === "edit"
|
|
3552
3552
|
? await api(`/api/v1/admin/datasources/${selected.id}/schema/views/${encodeURIComponent(dialog.originalName || payload.name)}/execute`, {
|
|
@@ -3689,6 +3689,7 @@ async function saveDatasourceSchema(event) {
|
|
|
3689
3689
|
tables[table.name] = {
|
|
3690
3690
|
selected: draft.selected !== false,
|
|
3691
3691
|
description: draft.description || "",
|
|
3692
|
+
view_description: draft.view_description || "",
|
|
3692
3693
|
primary_key_prompt: draft.primary_key_prompt || "",
|
|
3693
3694
|
foreign_key_prompt: draft.foreign_key_prompt || "",
|
|
3694
3695
|
join_logic: draft.join_logic || ""
|
|
@@ -363,22 +363,33 @@ class DatasourceSchemaTableSettingsRequest(BaseModel):
|
|
|
363
363
|
class DatasourceViewSqlGenerationRequest(BaseModel):
|
|
364
364
|
name: str = Field(min_length=1, max_length=128)
|
|
365
365
|
description: str = Field(default="", max_length=20000)
|
|
366
|
+
view_description: str | None = Field(default=None, max_length=20000)
|
|
366
367
|
|
|
367
368
|
|
|
368
369
|
class DatasourceViewExecuteRequest(BaseModel):
|
|
369
370
|
name: str = Field(min_length=1, max_length=128)
|
|
370
371
|
description: str = Field(default="", max_length=20000)
|
|
372
|
+
view_description: str | None = Field(default=None, max_length=20000)
|
|
371
373
|
sql: str = Field(min_length=1, max_length=200000)
|
|
372
374
|
|
|
373
375
|
|
|
374
376
|
class DatasourceViewUpdateRequest(BaseModel):
|
|
375
377
|
name: str = Field(min_length=1, max_length=128)
|
|
376
378
|
description: str = Field(default="", max_length=20000)
|
|
379
|
+
view_description: str | None = Field(default=None, max_length=20000)
|
|
377
380
|
sql: str = Field(min_length=1, max_length=200000)
|
|
378
381
|
|
|
379
382
|
|
|
380
383
|
class DatasourceViewSettingsRequest(BaseModel):
|
|
381
384
|
description: str = Field(default="", max_length=20000)
|
|
385
|
+
view_description: str | None = Field(default=None, max_length=20000)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def request_view_description(request: Any) -> str:
|
|
389
|
+
view_description = getattr(request, "view_description", None)
|
|
390
|
+
if view_description is not None:
|
|
391
|
+
return view_description
|
|
392
|
+
return str(getattr(request, "description", ""))
|
|
382
393
|
|
|
383
394
|
|
|
384
395
|
class BusinessLogicSuggestionUpdateRequest(BaseModel):
|
|
@@ -3248,6 +3259,27 @@ def datasource_view_description_from_cache(
|
|
|
3248
3259
|
connector: DatasourceConnector,
|
|
3249
3260
|
view_name: str,
|
|
3250
3261
|
actor: str,
|
|
3262
|
+
) -> str:
|
|
3263
|
+
cache = get_datasource_schema_cache(session, connector.id)
|
|
3264
|
+
if cache is None:
|
|
3265
|
+
cache = introspect_datasource_connector(session, connector, actor)
|
|
3266
|
+
settings = json_loads(cache.table_settings_json)
|
|
3267
|
+
tables = settings.get("tables", {})
|
|
3268
|
+
direct = tables.get(view_name, {})
|
|
3269
|
+
if direct:
|
|
3270
|
+
return str(direct.get("view_description", direct.get("description", "")))
|
|
3271
|
+
lowered = view_name.lower()
|
|
3272
|
+
for name, item in tables.items():
|
|
3273
|
+
if str(name).lower() == lowered:
|
|
3274
|
+
return str(item.get("view_description", item.get("description", "")))
|
|
3275
|
+
return ""
|
|
3276
|
+
|
|
3277
|
+
|
|
3278
|
+
def datasource_view_logic_from_cache(
|
|
3279
|
+
session: Session,
|
|
3280
|
+
connector: DatasourceConnector,
|
|
3281
|
+
view_name: str,
|
|
3282
|
+
actor: str,
|
|
3251
3283
|
) -> str:
|
|
3252
3284
|
cache = get_datasource_schema_cache(session, connector.id)
|
|
3253
3285
|
if cache is None:
|
|
@@ -3282,14 +3314,23 @@ def get_datasource_view_definition(
|
|
|
3282
3314
|
finally:
|
|
3283
3315
|
engine.dispose()
|
|
3284
3316
|
|
|
3317
|
+
view_description = datasource_view_description_from_cache(
|
|
3318
|
+
session,
|
|
3319
|
+
connector,
|
|
3320
|
+
existing_name,
|
|
3321
|
+
actor,
|
|
3322
|
+
)
|
|
3323
|
+
datasource_logic = datasource_view_logic_from_cache(
|
|
3324
|
+
session,
|
|
3325
|
+
connector,
|
|
3326
|
+
existing_name,
|
|
3327
|
+
actor,
|
|
3328
|
+
)
|
|
3285
3329
|
return {
|
|
3286
3330
|
"name": existing_name,
|
|
3287
|
-
"description":
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
existing_name,
|
|
3291
|
-
actor,
|
|
3292
|
-
),
|
|
3331
|
+
"description": view_description,
|
|
3332
|
+
"view_description": view_description,
|
|
3333
|
+
"datasource_logic": datasource_logic,
|
|
3293
3334
|
"sql": view_sql_for_editor(connector, existing_name, definition),
|
|
3294
3335
|
}
|
|
3295
3336
|
|
|
@@ -3298,7 +3339,7 @@ def save_datasource_view_settings_to_cache(
|
|
|
3298
3339
|
session: Session,
|
|
3299
3340
|
cache: DatasourceSchemaCache,
|
|
3300
3341
|
view_name: str,
|
|
3301
|
-
|
|
3342
|
+
view_description: str,
|
|
3302
3343
|
actor: str,
|
|
3303
3344
|
) -> DatasourceSchemaCache:
|
|
3304
3345
|
schema = json_loads(cache.schema_json)
|
|
@@ -3315,7 +3356,7 @@ def save_datasource_view_settings_to_cache(
|
|
|
3315
3356
|
table_settings = settings.setdefault("tables", {})
|
|
3316
3357
|
existing = table_settings.setdefault(actual_view_name, {})
|
|
3317
3358
|
existing["selected"] = True
|
|
3318
|
-
existing["
|
|
3359
|
+
existing["view_description"] = view_description.strip()
|
|
3319
3360
|
return update_schema_table_settings(
|
|
3320
3361
|
session=session,
|
|
3321
3362
|
cache=cache,
|
|
@@ -3328,7 +3369,7 @@ def execute_datasource_view_sql(
|
|
|
3328
3369
|
session: Session,
|
|
3329
3370
|
connector: DatasourceConnector,
|
|
3330
3371
|
view_name: str,
|
|
3331
|
-
|
|
3372
|
+
view_description: str,
|
|
3332
3373
|
sql: str,
|
|
3333
3374
|
actor: str,
|
|
3334
3375
|
) -> DatasourceSchemaCache:
|
|
@@ -3352,7 +3393,7 @@ def execute_datasource_view_sql(
|
|
|
3352
3393
|
session,
|
|
3353
3394
|
cache,
|
|
3354
3395
|
normalized_name,
|
|
3355
|
-
|
|
3396
|
+
view_description,
|
|
3356
3397
|
actor,
|
|
3357
3398
|
)
|
|
3358
3399
|
|
|
@@ -3361,7 +3402,7 @@ def save_datasource_view_settings(
|
|
|
3361
3402
|
session: Session,
|
|
3362
3403
|
connector: DatasourceConnector,
|
|
3363
3404
|
view_name: str,
|
|
3364
|
-
|
|
3405
|
+
view_description: str,
|
|
3365
3406
|
actor: str,
|
|
3366
3407
|
) -> DatasourceSchemaCache:
|
|
3367
3408
|
normalized_name = normalize_datasource_view_name(view_name)
|
|
@@ -3372,7 +3413,7 @@ def save_datasource_view_settings(
|
|
|
3372
3413
|
session,
|
|
3373
3414
|
cache,
|
|
3374
3415
|
normalized_name,
|
|
3375
|
-
|
|
3416
|
+
view_description,
|
|
3376
3417
|
actor,
|
|
3377
3418
|
)
|
|
3378
3419
|
|
|
@@ -3382,7 +3423,7 @@ def update_datasource_view_sql(
|
|
|
3382
3423
|
connector: DatasourceConnector,
|
|
3383
3424
|
current_view_name: str,
|
|
3384
3425
|
next_view_name: str,
|
|
3385
|
-
|
|
3426
|
+
view_description: str,
|
|
3386
3427
|
sql: str,
|
|
3387
3428
|
actor: str,
|
|
3388
3429
|
) -> DatasourceSchemaCache:
|
|
@@ -3416,7 +3457,7 @@ def update_datasource_view_sql(
|
|
|
3416
3457
|
session,
|
|
3417
3458
|
cache,
|
|
3418
3459
|
next_name,
|
|
3419
|
-
|
|
3460
|
+
view_description,
|
|
3420
3461
|
actor,
|
|
3421
3462
|
)
|
|
3422
3463
|
|
|
@@ -3448,11 +3489,11 @@ def generate_datasource_view_sql(
|
|
|
3448
3489
|
session: Session,
|
|
3449
3490
|
connector: DatasourceConnector,
|
|
3450
3491
|
view_name: str,
|
|
3451
|
-
|
|
3492
|
+
view_description: str,
|
|
3452
3493
|
actor: str,
|
|
3453
3494
|
) -> str:
|
|
3454
3495
|
normalized_name = normalize_datasource_view_name(view_name)
|
|
3455
|
-
if not
|
|
3496
|
+
if not view_description.strip():
|
|
3456
3497
|
raise ValueError("View description is required to generate SQL.")
|
|
3457
3498
|
|
|
3458
3499
|
cache = get_or_create_datasource_schema_cache(session, connector, actor)
|
|
@@ -3464,8 +3505,8 @@ def generate_datasource_view_sql(
|
|
|
3464
3505
|
query_request = QueryRequest(
|
|
3465
3506
|
question=(
|
|
3466
3507
|
f"Create a SQL SELECT statement for a database view named {normalized_name}. "
|
|
3467
|
-
"The SELECT must implement this
|
|
3468
|
-
f"{
|
|
3508
|
+
"The SELECT must implement this view description:\n"
|
|
3509
|
+
f"{view_description.strip()}\n\n"
|
|
3469
3510
|
"Return only one SELECT query. Do not create, drop, alter, insert, update or delete data."
|
|
3470
3511
|
),
|
|
3471
3512
|
datasource_id=connector.connector_key,
|
|
@@ -3615,7 +3656,7 @@ def generate_datasource_view_sql_preview(
|
|
|
3615
3656
|
session=session,
|
|
3616
3657
|
connector=connector,
|
|
3617
3658
|
view_name=request.name,
|
|
3618
|
-
|
|
3659
|
+
view_description=request_view_description(request),
|
|
3619
3660
|
actor=user.username,
|
|
3620
3661
|
)
|
|
3621
3662
|
except (
|
|
@@ -3656,7 +3697,7 @@ def execute_datasource_view_sql_endpoint(
|
|
|
3656
3697
|
session=session,
|
|
3657
3698
|
connector=connector,
|
|
3658
3699
|
view_name=request.name,
|
|
3659
|
-
|
|
3700
|
+
view_description=request_view_description(request),
|
|
3660
3701
|
sql=request.sql,
|
|
3661
3702
|
actor=user.username,
|
|
3662
3703
|
)
|
|
@@ -3705,7 +3746,7 @@ def update_datasource_view_sql_endpoint(
|
|
|
3705
3746
|
connector=connector,
|
|
3706
3747
|
current_view_name=view_name,
|
|
3707
3748
|
next_view_name=request.name,
|
|
3708
|
-
|
|
3749
|
+
view_description=request_view_description(request),
|
|
3709
3750
|
sql=request.sql,
|
|
3710
3751
|
actor=user.username,
|
|
3711
3752
|
)
|
|
@@ -3756,7 +3797,7 @@ def save_datasource_view_settings_endpoint(
|
|
|
3756
3797
|
session=session,
|
|
3757
3798
|
connector=connector,
|
|
3758
3799
|
view_name=view_name,
|
|
3759
|
-
|
|
3800
|
+
view_description=request_view_description(request),
|
|
3760
3801
|
actor=user.username,
|
|
3761
3802
|
)
|
|
3762
3803
|
except (SQLAlchemyError, ValueError) as exc:
|
|
@@ -3668,7 +3668,7 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3668
3668
|
headers={"Authorization": f"Bearer {token}"},
|
|
3669
3669
|
json={
|
|
3670
3670
|
"name": "v_generated_active_patients",
|
|
3671
|
-
"
|
|
3671
|
+
"view_description": "Active patients count.",
|
|
3672
3672
|
},
|
|
3673
3673
|
)
|
|
3674
3674
|
|
|
@@ -3682,7 +3682,7 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3682
3682
|
headers={"Authorization": f"Bearer {token}"},
|
|
3683
3683
|
json={
|
|
3684
3684
|
"name": "v_active_patients",
|
|
3685
|
-
"
|
|
3685
|
+
"view_description": "Active patient names.",
|
|
3686
3686
|
"sql": (
|
|
3687
3687
|
"CREATE VIEW v_active_patients AS "
|
|
3688
3688
|
"SELECT id, first_name, last_name FROM patients WHERE status = 'active'"
|
|
@@ -3697,10 +3697,12 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3697
3697
|
for table in created["raw_schema"]["tables"]
|
|
3698
3698
|
)
|
|
3699
3699
|
assert (
|
|
3700
|
-
created["table_settings"]["tables"]["v_active_patients"]["
|
|
3700
|
+
created["table_settings"]["tables"]["v_active_patients"]["view_description"]
|
|
3701
3701
|
== "Active patient names."
|
|
3702
3702
|
)
|
|
3703
|
+
assert created["table_settings"]["tables"]["v_active_patients"]["description"] == ""
|
|
3703
3704
|
assert "View: v_active_patients" in created["formatted_schema"]
|
|
3705
|
+
assert "Active patient names." not in created["formatted_schema"]
|
|
3704
3706
|
|
|
3705
3707
|
definition_response = admin_client.get(
|
|
3706
3708
|
f"/api/v1/admin/datasources/{datasource['id']}/schema/views/v_active_patients",
|
|
@@ -3710,7 +3712,8 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3710
3712
|
assert definition_response.status_code == 200
|
|
3711
3713
|
definition = definition_response.json()["item"]
|
|
3712
3714
|
assert definition["name"] == "v_active_patients"
|
|
3713
|
-
assert definition["
|
|
3715
|
+
assert definition["view_description"] == "Active patient names."
|
|
3716
|
+
assert definition["datasource_logic"] == ""
|
|
3714
3717
|
assert definition["sql"].startswith("SELECT")
|
|
3715
3718
|
|
|
3716
3719
|
duplicate_response = admin_client.post(
|
|
@@ -3718,7 +3721,7 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3718
3721
|
headers={"Authorization": f"Bearer {token}"},
|
|
3719
3722
|
json={
|
|
3720
3723
|
"name": "v_active_patients",
|
|
3721
|
-
"
|
|
3724
|
+
"view_description": "Duplicate.",
|
|
3722
3725
|
"sql": "SELECT id FROM patients",
|
|
3723
3726
|
},
|
|
3724
3727
|
)
|
|
@@ -3731,7 +3734,7 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3731
3734
|
headers={"Authorization": f"Bearer {token}"},
|
|
3732
3735
|
json={
|
|
3733
3736
|
"name": "v_current_patients",
|
|
3734
|
-
"
|
|
3737
|
+
"view_description": "Renamed active patient view.",
|
|
3735
3738
|
"sql": "SELECT id, first_name FROM patients WHERE status = 'active'",
|
|
3736
3739
|
},
|
|
3737
3740
|
)
|
|
@@ -3747,23 +3750,53 @@ def test_datasource_schema_views_can_be_created_saved_and_deleted(
|
|
|
3747
3750
|
for table in edited["raw_schema"]["tables"]
|
|
3748
3751
|
)
|
|
3749
3752
|
assert (
|
|
3750
|
-
edited["table_settings"]["tables"]["v_current_patients"]["
|
|
3753
|
+
edited["table_settings"]["tables"]["v_current_patients"]["view_description"]
|
|
3751
3754
|
== "Renamed active patient view."
|
|
3752
3755
|
)
|
|
3756
|
+
assert edited["table_settings"]["tables"]["v_current_patients"]["description"] == ""
|
|
3753
3757
|
|
|
3754
3758
|
save_response = admin_client.put(
|
|
3755
3759
|
f"/api/v1/admin/datasources/{datasource['id']}/schema/views/v_current_patients",
|
|
3756
3760
|
headers={"Authorization": f"Bearer {token}"},
|
|
3757
|
-
json={"
|
|
3761
|
+
json={"view_description": "Updated active patient view definition."},
|
|
3758
3762
|
)
|
|
3759
3763
|
|
|
3760
3764
|
assert save_response.status_code == 200
|
|
3761
3765
|
saved = save_response.json()["item"]
|
|
3762
3766
|
assert (
|
|
3763
|
-
saved["table_settings"]["tables"]["v_current_patients"]["
|
|
3764
|
-
== "Updated active patient view
|
|
3767
|
+
saved["table_settings"]["tables"]["v_current_patients"]["view_description"]
|
|
3768
|
+
== "Updated active patient view definition."
|
|
3765
3769
|
)
|
|
3766
|
-
assert "
|
|
3770
|
+
assert saved["table_settings"]["tables"]["v_current_patients"]["description"] == ""
|
|
3771
|
+
assert "Updated active patient view definition." not in saved["formatted_schema"]
|
|
3772
|
+
|
|
3773
|
+
logic_response = admin_client.put(
|
|
3774
|
+
f"/api/v1/admin/datasources/{datasource['id']}/schema/tables",
|
|
3775
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
3776
|
+
json={
|
|
3777
|
+
"tables": {
|
|
3778
|
+
"v_current_patients": {
|
|
3779
|
+
"selected": True,
|
|
3780
|
+
"description": "Use this view for active patient lookup.",
|
|
3781
|
+
"view_description": "Updated active patient view definition.",
|
|
3782
|
+
"join_logic": "",
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
},
|
|
3786
|
+
)
|
|
3787
|
+
|
|
3788
|
+
assert logic_response.status_code == 200
|
|
3789
|
+
logic_saved = logic_response.json()["item"]
|
|
3790
|
+
assert (
|
|
3791
|
+
logic_saved["table_settings"]["tables"]["v_current_patients"]["description"]
|
|
3792
|
+
== "Use this view for active patient lookup."
|
|
3793
|
+
)
|
|
3794
|
+
assert (
|
|
3795
|
+
logic_saved["table_settings"]["tables"]["v_current_patients"]["view_description"]
|
|
3796
|
+
== "Updated active patient view definition."
|
|
3797
|
+
)
|
|
3798
|
+
assert "Use this view for active patient lookup." in logic_saved["formatted_schema"]
|
|
3799
|
+
assert "Updated active patient view definition." not in logic_saved["formatted_schema"]
|
|
3767
3800
|
|
|
3768
3801
|
delete_response = admin_client.delete(
|
|
3769
3802
|
f"/api/v1/admin/datasources/{datasource['id']}/schema/views/v_current_patients",
|
|
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
|
|
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
|