truthound-dashboard 1.4.4__py3-none-any.whl → 1.5.0__py3-none-any.whl
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.
- truthound_dashboard/api/alerts.py +75 -86
- truthound_dashboard/api/anomaly.py +7 -13
- truthound_dashboard/api/cross_alerts.py +38 -52
- truthound_dashboard/api/drift.py +49 -59
- truthound_dashboard/api/drift_monitor.py +234 -79
- truthound_dashboard/api/enterprise_sampling.py +498 -0
- truthound_dashboard/api/history.py +57 -5
- truthound_dashboard/api/lineage.py +3 -48
- truthound_dashboard/api/maintenance.py +104 -49
- truthound_dashboard/api/mask.py +1 -2
- truthound_dashboard/api/middleware.py +2 -1
- truthound_dashboard/api/model_monitoring.py +435 -311
- truthound_dashboard/api/notifications.py +227 -191
- truthound_dashboard/api/notifications_advanced.py +21 -20
- truthound_dashboard/api/observability.py +586 -0
- truthound_dashboard/api/plugins.py +2 -433
- truthound_dashboard/api/profile.py +199 -37
- truthound_dashboard/api/quality_reporter.py +701 -0
- truthound_dashboard/api/reports.py +7 -16
- truthound_dashboard/api/router.py +66 -0
- truthound_dashboard/api/rule_suggestions.py +5 -5
- truthound_dashboard/api/scan.py +17 -19
- truthound_dashboard/api/schedules.py +85 -50
- truthound_dashboard/api/schema_evolution.py +6 -6
- truthound_dashboard/api/schema_watcher.py +667 -0
- truthound_dashboard/api/sources.py +98 -27
- truthound_dashboard/api/tiering.py +1323 -0
- truthound_dashboard/api/triggers.py +14 -11
- truthound_dashboard/api/validations.py +12 -11
- truthound_dashboard/api/versioning.py +1 -6
- truthound_dashboard/core/__init__.py +129 -3
- truthound_dashboard/core/actions/__init__.py +62 -0
- truthound_dashboard/core/actions/custom.py +426 -0
- truthound_dashboard/core/actions/notifications.py +910 -0
- truthound_dashboard/core/actions/storage.py +472 -0
- truthound_dashboard/core/actions/webhook.py +281 -0
- truthound_dashboard/core/anomaly.py +262 -67
- truthound_dashboard/core/anomaly_explainer.py +4 -3
- truthound_dashboard/core/backends/__init__.py +67 -0
- truthound_dashboard/core/backends/base.py +299 -0
- truthound_dashboard/core/backends/errors.py +191 -0
- truthound_dashboard/core/backends/factory.py +423 -0
- truthound_dashboard/core/backends/mock_backend.py +451 -0
- truthound_dashboard/core/backends/truthound_backend.py +718 -0
- truthound_dashboard/core/checkpoint/__init__.py +87 -0
- truthound_dashboard/core/checkpoint/adapters.py +814 -0
- truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
- truthound_dashboard/core/checkpoint/runner.py +270 -0
- truthound_dashboard/core/connections.py +437 -10
- truthound_dashboard/core/converters/__init__.py +14 -0
- truthound_dashboard/core/converters/truthound.py +620 -0
- truthound_dashboard/core/cross_alerts.py +540 -320
- truthound_dashboard/core/datasource_factory.py +1672 -0
- truthound_dashboard/core/drift_monitor.py +216 -20
- truthound_dashboard/core/enterprise_sampling.py +1291 -0
- truthound_dashboard/core/interfaces/__init__.py +225 -0
- truthound_dashboard/core/interfaces/actions.py +652 -0
- truthound_dashboard/core/interfaces/base.py +247 -0
- truthound_dashboard/core/interfaces/checkpoint.py +676 -0
- truthound_dashboard/core/interfaces/protocols.py +664 -0
- truthound_dashboard/core/interfaces/reporters.py +650 -0
- truthound_dashboard/core/interfaces/routing.py +646 -0
- truthound_dashboard/core/interfaces/triggers.py +619 -0
- truthound_dashboard/core/lineage.py +407 -71
- truthound_dashboard/core/model_monitoring.py +431 -3
- truthound_dashboard/core/notifications/base.py +4 -0
- truthound_dashboard/core/notifications/channels.py +501 -1203
- truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
- truthound_dashboard/core/notifications/deduplication/service.py +131 -348
- truthound_dashboard/core/notifications/dispatcher.py +202 -11
- truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
- truthound_dashboard/core/notifications/escalation/engine.py +168 -358
- truthound_dashboard/core/notifications/routing/__init__.py +88 -128
- truthound_dashboard/core/notifications/routing/engine.py +90 -317
- truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
- truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
- truthound_dashboard/core/notifications/throttling/builder.py +117 -255
- truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
- truthound_dashboard/core/phase5/collaboration.py +1 -1
- truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
- truthound_dashboard/core/quality_reporter.py +1359 -0
- truthound_dashboard/core/report_history.py +0 -6
- truthound_dashboard/core/reporters/__init__.py +175 -14
- truthound_dashboard/core/reporters/adapters.py +943 -0
- truthound_dashboard/core/reporters/base.py +0 -3
- truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
- truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
- truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
- truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
- truthound_dashboard/core/reporters/compat.py +266 -0
- truthound_dashboard/core/reporters/csv_reporter.py +2 -35
- truthound_dashboard/core/reporters/factory.py +526 -0
- truthound_dashboard/core/reporters/interfaces.py +745 -0
- truthound_dashboard/core/reporters/registry.py +1 -10
- truthound_dashboard/core/scheduler.py +165 -0
- truthound_dashboard/core/schema_evolution.py +3 -3
- truthound_dashboard/core/schema_watcher.py +1528 -0
- truthound_dashboard/core/services.py +595 -76
- truthound_dashboard/core/store_manager.py +810 -0
- truthound_dashboard/core/streaming_anomaly.py +169 -4
- truthound_dashboard/core/tiering.py +1309 -0
- truthound_dashboard/core/triggers/evaluators.py +178 -8
- truthound_dashboard/core/truthound_adapter.py +2620 -197
- truthound_dashboard/core/unified_alerts.py +23 -20
- truthound_dashboard/db/__init__.py +8 -0
- truthound_dashboard/db/database.py +8 -2
- truthound_dashboard/db/models.py +944 -25
- truthound_dashboard/db/repository.py +2 -0
- truthound_dashboard/main.py +11 -0
- truthound_dashboard/schemas/__init__.py +177 -16
- truthound_dashboard/schemas/base.py +44 -23
- truthound_dashboard/schemas/collaboration.py +19 -6
- truthound_dashboard/schemas/cross_alerts.py +19 -3
- truthound_dashboard/schemas/drift.py +61 -55
- truthound_dashboard/schemas/drift_monitor.py +67 -23
- truthound_dashboard/schemas/enterprise_sampling.py +653 -0
- truthound_dashboard/schemas/lineage.py +0 -33
- truthound_dashboard/schemas/mask.py +10 -8
- truthound_dashboard/schemas/model_monitoring.py +89 -10
- truthound_dashboard/schemas/notifications_advanced.py +13 -0
- truthound_dashboard/schemas/observability.py +453 -0
- truthound_dashboard/schemas/plugins.py +0 -280
- truthound_dashboard/schemas/profile.py +154 -247
- truthound_dashboard/schemas/quality_reporter.py +403 -0
- truthound_dashboard/schemas/reports.py +2 -2
- truthound_dashboard/schemas/rule_suggestion.py +8 -1
- truthound_dashboard/schemas/scan.py +4 -24
- truthound_dashboard/schemas/schedule.py +11 -3
- truthound_dashboard/schemas/schema_watcher.py +727 -0
- truthound_dashboard/schemas/source.py +17 -2
- truthound_dashboard/schemas/tiering.py +822 -0
- truthound_dashboard/schemas/triggers.py +16 -0
- truthound_dashboard/schemas/unified_alerts.py +7 -0
- truthound_dashboard/schemas/validation.py +0 -13
- truthound_dashboard/schemas/validators/base.py +41 -21
- truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
- truthound_dashboard/schemas/validators/localization_validators.py +273 -0
- truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
- truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
- truthound_dashboard/schemas/validators/referential_validators.py +312 -0
- truthound_dashboard/schemas/validators/registry.py +93 -8
- truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
- truthound_dashboard/schemas/versioning.py +1 -6
- truthound_dashboard/static/index.html +2 -2
- truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
- truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
- truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
- truthound_dashboard/core/plugins/hooks/manager.py +0 -403
- truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
- truthound_dashboard/core/reporters/junit_reporter.py +0 -233
- truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
- truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
- truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
- truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
- truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
- truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
- truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
- truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
- truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
- truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
- truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
- truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
- truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
- truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
- truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
- truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
- truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
- truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
- truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
- truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
- truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
- truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
- truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
- truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
- truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
- truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
- truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
- truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
- truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
- truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
- truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
- truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
- truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
- truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
- truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
- truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
- truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
- truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
- truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
- truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
- truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
- truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
- truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
- truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
- truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
- truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
- truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
- truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
- truthound_dashboard-1.4.4.dist-info/METADATA +0 -507
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -30,18 +30,39 @@ from urllib.parse import quote_plus
|
|
|
30
30
|
class SourceType(str, Enum):
|
|
31
31
|
"""Supported data source types."""
|
|
32
32
|
|
|
33
|
+
# File-based
|
|
33
34
|
FILE = "file"
|
|
35
|
+
CSV = "csv"
|
|
36
|
+
PARQUET = "parquet"
|
|
37
|
+
JSON = "json"
|
|
38
|
+
NDJSON = "ndjson"
|
|
39
|
+
JSONL = "jsonl"
|
|
40
|
+
|
|
41
|
+
# Core SQL
|
|
34
42
|
POSTGRESQL = "postgresql"
|
|
35
43
|
MYSQL = "mysql"
|
|
36
44
|
SQLITE = "sqlite"
|
|
45
|
+
|
|
46
|
+
# Cloud Data Warehouses
|
|
37
47
|
SNOWFLAKE = "snowflake"
|
|
38
48
|
BIGQUERY = "bigquery"
|
|
39
49
|
REDSHIFT = "redshift"
|
|
40
50
|
DATABRICKS = "databricks"
|
|
51
|
+
|
|
52
|
+
# Enterprise
|
|
41
53
|
ORACLE = "oracle"
|
|
42
54
|
SQLSERVER = "sqlserver"
|
|
55
|
+
|
|
56
|
+
# Big Data
|
|
43
57
|
SPARK = "spark"
|
|
44
58
|
|
|
59
|
+
# NoSQL
|
|
60
|
+
MONGODB = "mongodb"
|
|
61
|
+
ELASTICSEARCH = "elasticsearch"
|
|
62
|
+
|
|
63
|
+
# Streaming
|
|
64
|
+
KAFKA = "kafka"
|
|
65
|
+
|
|
45
66
|
|
|
46
67
|
class FieldType(str, Enum):
|
|
47
68
|
"""Field types for configuration forms."""
|
|
@@ -104,7 +125,7 @@ class SourceTypeDefinition:
|
|
|
104
125
|
name: str
|
|
105
126
|
description: str
|
|
106
127
|
icon: str
|
|
107
|
-
category: Literal["file", "database", "warehouse", "bigdata"]
|
|
128
|
+
category: Literal["file", "database", "warehouse", "bigdata", "nosql", "streaming"]
|
|
108
129
|
fields: list[FieldDefinition]
|
|
109
130
|
docs_url: str = ""
|
|
110
131
|
|
|
@@ -1195,19 +1216,395 @@ class SparkConnectionBuilder(ConnectionBuilder):
|
|
|
1195
1216
|
)
|
|
1196
1217
|
|
|
1197
1218
|
|
|
1219
|
+
class MongoDBConnectionBuilder(ConnectionBuilder):
|
|
1220
|
+
"""Connection builder for MongoDB."""
|
|
1221
|
+
|
|
1222
|
+
source_type = SourceType.MONGODB
|
|
1223
|
+
|
|
1224
|
+
def build(self, config: dict[str, Any]) -> str:
|
|
1225
|
+
"""Build MongoDB connection string."""
|
|
1226
|
+
# Support direct connection string
|
|
1227
|
+
if config.get("connection_string"):
|
|
1228
|
+
return config["connection_string"]
|
|
1229
|
+
|
|
1230
|
+
host = config.get("host", "localhost")
|
|
1231
|
+
port = config.get("port", 27017)
|
|
1232
|
+
database = config.get("database", "")
|
|
1233
|
+
username = config.get("username", "")
|
|
1234
|
+
password = quote_plus(config.get("password", ""))
|
|
1235
|
+
|
|
1236
|
+
# Build connection string
|
|
1237
|
+
if username and password:
|
|
1238
|
+
conn = f"mongodb://{username}:{password}@{host}:{port}/{database}"
|
|
1239
|
+
else:
|
|
1240
|
+
conn = f"mongodb://{host}:{port}/{database}"
|
|
1241
|
+
|
|
1242
|
+
# Add options
|
|
1243
|
+
options = []
|
|
1244
|
+
if config.get("auth_source"):
|
|
1245
|
+
options.append(f"authSource={config['auth_source']}")
|
|
1246
|
+
if config.get("replica_set"):
|
|
1247
|
+
options.append(f"replicaSet={config['replica_set']}")
|
|
1248
|
+
if config.get("ssl"):
|
|
1249
|
+
options.append("ssl=true")
|
|
1250
|
+
|
|
1251
|
+
if options:
|
|
1252
|
+
conn += "?" + "&".join(options)
|
|
1253
|
+
|
|
1254
|
+
return conn
|
|
1255
|
+
|
|
1256
|
+
def validate_config(self, config: dict[str, Any]) -> list[str]:
|
|
1257
|
+
"""Validate MongoDB configuration."""
|
|
1258
|
+
errors = []
|
|
1259
|
+
|
|
1260
|
+
# Either connection_string or host is required
|
|
1261
|
+
if not config.get("connection_string") and not config.get("host"):
|
|
1262
|
+
errors.append("Either connection_string or host is required")
|
|
1263
|
+
|
|
1264
|
+
if not config.get("database"):
|
|
1265
|
+
errors.append("database is required")
|
|
1266
|
+
|
|
1267
|
+
if not config.get("collection"):
|
|
1268
|
+
errors.append("collection is required")
|
|
1269
|
+
|
|
1270
|
+
return errors
|
|
1271
|
+
|
|
1272
|
+
@classmethod
|
|
1273
|
+
def get_definition(cls) -> SourceTypeDefinition:
|
|
1274
|
+
"""Get the source type definition for UI rendering."""
|
|
1275
|
+
return SourceTypeDefinition(
|
|
1276
|
+
type=SourceType.MONGODB.value,
|
|
1277
|
+
name="MongoDB",
|
|
1278
|
+
description="MongoDB document database",
|
|
1279
|
+
icon="database",
|
|
1280
|
+
category="nosql",
|
|
1281
|
+
docs_url="https://www.mongodb.com/docs/",
|
|
1282
|
+
fields=[
|
|
1283
|
+
FieldDefinition(
|
|
1284
|
+
name="connection_string",
|
|
1285
|
+
label="Connection String",
|
|
1286
|
+
placeholder="mongodb://localhost:27017/mydb",
|
|
1287
|
+
description="Full MongoDB connection URI (alternative to individual fields)",
|
|
1288
|
+
),
|
|
1289
|
+
FieldDefinition(
|
|
1290
|
+
name="host",
|
|
1291
|
+
label="Host",
|
|
1292
|
+
placeholder="localhost",
|
|
1293
|
+
description="MongoDB server hostname or IP",
|
|
1294
|
+
),
|
|
1295
|
+
FieldDefinition(
|
|
1296
|
+
name="port",
|
|
1297
|
+
label="Port",
|
|
1298
|
+
type=FieldType.NUMBER,
|
|
1299
|
+
default=27017,
|
|
1300
|
+
min_value=1,
|
|
1301
|
+
max_value=65535,
|
|
1302
|
+
description="MongoDB server port",
|
|
1303
|
+
),
|
|
1304
|
+
FieldDefinition(
|
|
1305
|
+
name="database",
|
|
1306
|
+
label="Database",
|
|
1307
|
+
required=True,
|
|
1308
|
+
placeholder="mydb",
|
|
1309
|
+
description="Database name",
|
|
1310
|
+
),
|
|
1311
|
+
FieldDefinition(
|
|
1312
|
+
name="collection",
|
|
1313
|
+
label="Collection",
|
|
1314
|
+
required=True,
|
|
1315
|
+
placeholder="users",
|
|
1316
|
+
description="Collection name to validate",
|
|
1317
|
+
),
|
|
1318
|
+
FieldDefinition(
|
|
1319
|
+
name="username",
|
|
1320
|
+
label="Username",
|
|
1321
|
+
description="Database username",
|
|
1322
|
+
),
|
|
1323
|
+
FieldDefinition(
|
|
1324
|
+
name="password",
|
|
1325
|
+
label="Password",
|
|
1326
|
+
type=FieldType.PASSWORD,
|
|
1327
|
+
description="Database password",
|
|
1328
|
+
),
|
|
1329
|
+
FieldDefinition(
|
|
1330
|
+
name="auth_source",
|
|
1331
|
+
label="Auth Source",
|
|
1332
|
+
placeholder="admin",
|
|
1333
|
+
default="admin",
|
|
1334
|
+
description="Authentication database",
|
|
1335
|
+
),
|
|
1336
|
+
FieldDefinition(
|
|
1337
|
+
name="replica_set",
|
|
1338
|
+
label="Replica Set",
|
|
1339
|
+
placeholder="rs0",
|
|
1340
|
+
description="Replica set name (for replica set connections)",
|
|
1341
|
+
),
|
|
1342
|
+
FieldDefinition(
|
|
1343
|
+
name="ssl",
|
|
1344
|
+
label="Use SSL/TLS",
|
|
1345
|
+
type=FieldType.BOOLEAN,
|
|
1346
|
+
default=False,
|
|
1347
|
+
description="Enable SSL/TLS connection",
|
|
1348
|
+
),
|
|
1349
|
+
],
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
class ElasticsearchConnectionBuilder(ConnectionBuilder):
|
|
1354
|
+
"""Connection builder for Elasticsearch."""
|
|
1355
|
+
|
|
1356
|
+
source_type = SourceType.ELASTICSEARCH
|
|
1357
|
+
|
|
1358
|
+
def build(self, config: dict[str, Any]) -> str:
|
|
1359
|
+
"""Build Elasticsearch connection URL."""
|
|
1360
|
+
hosts = config.get("hosts", config.get("host", "localhost"))
|
|
1361
|
+
port = config.get("port", 9200)
|
|
1362
|
+
scheme = "https" if config.get("use_ssl") else "http"
|
|
1363
|
+
|
|
1364
|
+
# Handle multiple hosts
|
|
1365
|
+
if isinstance(hosts, list):
|
|
1366
|
+
return ",".join([f"{scheme}://{h}:{port}" for h in hosts])
|
|
1367
|
+
|
|
1368
|
+
return f"{scheme}://{hosts}:{port}"
|
|
1369
|
+
|
|
1370
|
+
def validate_config(self, config: dict[str, Any]) -> list[str]:
|
|
1371
|
+
"""Validate Elasticsearch configuration."""
|
|
1372
|
+
errors = []
|
|
1373
|
+
|
|
1374
|
+
if not config.get("hosts") and not config.get("host"):
|
|
1375
|
+
errors.append("Either hosts or host is required")
|
|
1376
|
+
|
|
1377
|
+
if not config.get("index"):
|
|
1378
|
+
errors.append("index is required")
|
|
1379
|
+
|
|
1380
|
+
return errors
|
|
1381
|
+
|
|
1382
|
+
@classmethod
|
|
1383
|
+
def get_definition(cls) -> SourceTypeDefinition:
|
|
1384
|
+
"""Get the source type definition for UI rendering."""
|
|
1385
|
+
return SourceTypeDefinition(
|
|
1386
|
+
type=SourceType.ELASTICSEARCH.value,
|
|
1387
|
+
name="Elasticsearch",
|
|
1388
|
+
description="Elasticsearch search engine",
|
|
1389
|
+
icon="search",
|
|
1390
|
+
category="nosql",
|
|
1391
|
+
docs_url="https://www.elastic.co/guide/en/elasticsearch/reference/current/",
|
|
1392
|
+
fields=[
|
|
1393
|
+
FieldDefinition(
|
|
1394
|
+
name="host",
|
|
1395
|
+
label="Host",
|
|
1396
|
+
required=True,
|
|
1397
|
+
placeholder="localhost",
|
|
1398
|
+
description="Elasticsearch server hostname or IP",
|
|
1399
|
+
),
|
|
1400
|
+
FieldDefinition(
|
|
1401
|
+
name="port",
|
|
1402
|
+
label="Port",
|
|
1403
|
+
type=FieldType.NUMBER,
|
|
1404
|
+
default=9200,
|
|
1405
|
+
min_value=1,
|
|
1406
|
+
max_value=65535,
|
|
1407
|
+
description="Elasticsearch server port",
|
|
1408
|
+
),
|
|
1409
|
+
FieldDefinition(
|
|
1410
|
+
name="index",
|
|
1411
|
+
label="Index",
|
|
1412
|
+
required=True,
|
|
1413
|
+
placeholder="my_index",
|
|
1414
|
+
description="Index name to validate",
|
|
1415
|
+
),
|
|
1416
|
+
FieldDefinition(
|
|
1417
|
+
name="username",
|
|
1418
|
+
label="Username",
|
|
1419
|
+
description="Elasticsearch username",
|
|
1420
|
+
),
|
|
1421
|
+
FieldDefinition(
|
|
1422
|
+
name="password",
|
|
1423
|
+
label="Password",
|
|
1424
|
+
type=FieldType.PASSWORD,
|
|
1425
|
+
description="Elasticsearch password",
|
|
1426
|
+
),
|
|
1427
|
+
FieldDefinition(
|
|
1428
|
+
name="api_key",
|
|
1429
|
+
label="API Key",
|
|
1430
|
+
type=FieldType.PASSWORD,
|
|
1431
|
+
description="API key for authentication (alternative to username/password)",
|
|
1432
|
+
),
|
|
1433
|
+
FieldDefinition(
|
|
1434
|
+
name="use_ssl",
|
|
1435
|
+
label="Use SSL/TLS",
|
|
1436
|
+
type=FieldType.BOOLEAN,
|
|
1437
|
+
default=True,
|
|
1438
|
+
description="Enable SSL/TLS connection",
|
|
1439
|
+
),
|
|
1440
|
+
FieldDefinition(
|
|
1441
|
+
name="verify_certs",
|
|
1442
|
+
label="Verify Certificates",
|
|
1443
|
+
type=FieldType.BOOLEAN,
|
|
1444
|
+
default=True,
|
|
1445
|
+
description="Verify SSL certificates",
|
|
1446
|
+
),
|
|
1447
|
+
FieldDefinition(
|
|
1448
|
+
name="cloud_id",
|
|
1449
|
+
label="Cloud ID",
|
|
1450
|
+
placeholder="deployment:dXMtd2VzdC0...",
|
|
1451
|
+
description="Elastic Cloud deployment ID (for Elastic Cloud)",
|
|
1452
|
+
),
|
|
1453
|
+
],
|
|
1454
|
+
)
|
|
1455
|
+
|
|
1456
|
+
|
|
1457
|
+
class KafkaConnectionBuilder(ConnectionBuilder):
|
|
1458
|
+
"""Connection builder for Apache Kafka."""
|
|
1459
|
+
|
|
1460
|
+
source_type = SourceType.KAFKA
|
|
1461
|
+
|
|
1462
|
+
def build(self, config: dict[str, Any]) -> str:
|
|
1463
|
+
"""Build Kafka bootstrap servers string."""
|
|
1464
|
+
bootstrap_servers = config.get("bootstrap_servers", "localhost:9092")
|
|
1465
|
+
if isinstance(bootstrap_servers, list):
|
|
1466
|
+
return ",".join(bootstrap_servers)
|
|
1467
|
+
return bootstrap_servers
|
|
1468
|
+
|
|
1469
|
+
def validate_config(self, config: dict[str, Any]) -> list[str]:
|
|
1470
|
+
"""Validate Kafka configuration."""
|
|
1471
|
+
errors = []
|
|
1472
|
+
|
|
1473
|
+
if not config.get("bootstrap_servers"):
|
|
1474
|
+
errors.append("bootstrap_servers is required")
|
|
1475
|
+
|
|
1476
|
+
if not config.get("topic"):
|
|
1477
|
+
errors.append("topic is required")
|
|
1478
|
+
|
|
1479
|
+
return errors
|
|
1480
|
+
|
|
1481
|
+
@classmethod
|
|
1482
|
+
def get_definition(cls) -> SourceTypeDefinition:
|
|
1483
|
+
"""Get the source type definition for UI rendering."""
|
|
1484
|
+
return SourceTypeDefinition(
|
|
1485
|
+
type=SourceType.KAFKA.value,
|
|
1486
|
+
name="Apache Kafka",
|
|
1487
|
+
description="Apache Kafka streaming platform",
|
|
1488
|
+
icon="radio",
|
|
1489
|
+
category="streaming",
|
|
1490
|
+
docs_url="https://kafka.apache.org/documentation/",
|
|
1491
|
+
fields=[
|
|
1492
|
+
FieldDefinition(
|
|
1493
|
+
name="bootstrap_servers",
|
|
1494
|
+
label="Bootstrap Servers",
|
|
1495
|
+
required=True,
|
|
1496
|
+
placeholder="localhost:9092",
|
|
1497
|
+
description="Comma-separated list of Kafka broker addresses",
|
|
1498
|
+
),
|
|
1499
|
+
FieldDefinition(
|
|
1500
|
+
name="topic",
|
|
1501
|
+
label="Topic",
|
|
1502
|
+
required=True,
|
|
1503
|
+
placeholder="my_topic",
|
|
1504
|
+
description="Kafka topic to consume from",
|
|
1505
|
+
),
|
|
1506
|
+
FieldDefinition(
|
|
1507
|
+
name="group_id",
|
|
1508
|
+
label="Consumer Group ID",
|
|
1509
|
+
placeholder="truthound-validator",
|
|
1510
|
+
default="truthound-validator",
|
|
1511
|
+
description="Consumer group identifier",
|
|
1512
|
+
),
|
|
1513
|
+
FieldDefinition(
|
|
1514
|
+
name="auto_offset_reset",
|
|
1515
|
+
label="Auto Offset Reset",
|
|
1516
|
+
type=FieldType.SELECT,
|
|
1517
|
+
options=[
|
|
1518
|
+
{"value": "earliest", "label": "Earliest (from beginning)"},
|
|
1519
|
+
{"value": "latest", "label": "Latest (only new messages)"},
|
|
1520
|
+
],
|
|
1521
|
+
default="earliest",
|
|
1522
|
+
description="Where to start consuming if no offset is stored",
|
|
1523
|
+
),
|
|
1524
|
+
FieldDefinition(
|
|
1525
|
+
name="max_messages",
|
|
1526
|
+
label="Max Messages",
|
|
1527
|
+
type=FieldType.NUMBER,
|
|
1528
|
+
default=10000,
|
|
1529
|
+
min_value=1,
|
|
1530
|
+
max_value=1000000,
|
|
1531
|
+
description="Maximum number of messages to consume per batch",
|
|
1532
|
+
),
|
|
1533
|
+
FieldDefinition(
|
|
1534
|
+
name="security_protocol",
|
|
1535
|
+
label="Security Protocol",
|
|
1536
|
+
type=FieldType.SELECT,
|
|
1537
|
+
options=[
|
|
1538
|
+
{"value": "PLAINTEXT", "label": "Plaintext"},
|
|
1539
|
+
{"value": "SSL", "label": "SSL"},
|
|
1540
|
+
{"value": "SASL_PLAINTEXT", "label": "SASL Plaintext"},
|
|
1541
|
+
{"value": "SASL_SSL", "label": "SASL SSL"},
|
|
1542
|
+
],
|
|
1543
|
+
default="PLAINTEXT",
|
|
1544
|
+
description="Security protocol for broker communication",
|
|
1545
|
+
),
|
|
1546
|
+
FieldDefinition(
|
|
1547
|
+
name="sasl_mechanism",
|
|
1548
|
+
label="SASL Mechanism",
|
|
1549
|
+
type=FieldType.SELECT,
|
|
1550
|
+
options=[
|
|
1551
|
+
{"value": "PLAIN", "label": "PLAIN"},
|
|
1552
|
+
{"value": "SCRAM-SHA-256", "label": "SCRAM-SHA-256"},
|
|
1553
|
+
{"value": "SCRAM-SHA-512", "label": "SCRAM-SHA-512"},
|
|
1554
|
+
{"value": "OAUTHBEARER", "label": "OAuth Bearer"},
|
|
1555
|
+
],
|
|
1556
|
+
default="PLAIN",
|
|
1557
|
+
description="SASL authentication mechanism",
|
|
1558
|
+
depends_on="security_protocol",
|
|
1559
|
+
depends_value="SASL_SSL",
|
|
1560
|
+
),
|
|
1561
|
+
FieldDefinition(
|
|
1562
|
+
name="sasl_username",
|
|
1563
|
+
label="SASL Username",
|
|
1564
|
+
description="SASL authentication username",
|
|
1565
|
+
depends_on="security_protocol",
|
|
1566
|
+
depends_value="SASL_SSL",
|
|
1567
|
+
),
|
|
1568
|
+
FieldDefinition(
|
|
1569
|
+
name="sasl_password",
|
|
1570
|
+
label="SASL Password",
|
|
1571
|
+
type=FieldType.PASSWORD,
|
|
1572
|
+
description="SASL authentication password",
|
|
1573
|
+
depends_on="security_protocol",
|
|
1574
|
+
depends_value="SASL_SSL",
|
|
1575
|
+
),
|
|
1576
|
+
],
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
|
|
1198
1580
|
# Registry of connection builders
|
|
1199
1581
|
CONNECTION_BUILDERS: dict[str, type[ConnectionBuilder]] = {
|
|
1582
|
+
# File-based
|
|
1200
1583
|
SourceType.FILE.value: FileConnectionBuilder,
|
|
1584
|
+
SourceType.CSV.value: FileConnectionBuilder,
|
|
1585
|
+
SourceType.PARQUET.value: FileConnectionBuilder,
|
|
1586
|
+
SourceType.JSON.value: FileConnectionBuilder,
|
|
1587
|
+
SourceType.NDJSON.value: FileConnectionBuilder,
|
|
1588
|
+
SourceType.JSONL.value: FileConnectionBuilder,
|
|
1589
|
+
# Core SQL
|
|
1201
1590
|
SourceType.POSTGRESQL.value: PostgreSQLConnectionBuilder,
|
|
1202
1591
|
SourceType.MYSQL.value: MySQLConnectionBuilder,
|
|
1203
1592
|
SourceType.SQLITE.value: SQLiteConnectionBuilder,
|
|
1593
|
+
# Cloud Data Warehouses
|
|
1204
1594
|
SourceType.SNOWFLAKE.value: SnowflakeConnectionBuilder,
|
|
1205
1595
|
SourceType.BIGQUERY.value: BigQueryConnectionBuilder,
|
|
1206
1596
|
SourceType.REDSHIFT.value: RedshiftConnectionBuilder,
|
|
1207
1597
|
SourceType.DATABRICKS.value: DatabricksConnectionBuilder,
|
|
1598
|
+
# Enterprise
|
|
1208
1599
|
SourceType.ORACLE.value: OracleConnectionBuilder,
|
|
1209
1600
|
SourceType.SQLSERVER.value: SQLServerConnectionBuilder,
|
|
1601
|
+
# Big Data
|
|
1210
1602
|
SourceType.SPARK.value: SparkConnectionBuilder,
|
|
1603
|
+
# NoSQL
|
|
1604
|
+
SourceType.MONGODB.value: MongoDBConnectionBuilder,
|
|
1605
|
+
SourceType.ELASTICSEARCH.value: ElasticsearchConnectionBuilder,
|
|
1606
|
+
# Streaming
|
|
1607
|
+
SourceType.KAFKA.value: KafkaConnectionBuilder,
|
|
1211
1608
|
}
|
|
1212
1609
|
|
|
1213
1610
|
|
|
@@ -1256,6 +1653,10 @@ def build_connection_string(source_type: str, config: dict[str, Any]) -> str:
|
|
|
1256
1653
|
async def test_connection(source_type: str, config: dict[str, Any]) -> dict[str, Any]:
|
|
1257
1654
|
"""Test database connection.
|
|
1258
1655
|
|
|
1656
|
+
Updated for truthound 2.x API:
|
|
1657
|
+
- Uses DataSourceFactory with new datasource API
|
|
1658
|
+
- Delegates to datasource_factory.test_connection for better reuse
|
|
1659
|
+
|
|
1259
1660
|
Args:
|
|
1260
1661
|
source_type: Type of data source.
|
|
1261
1662
|
config: Source-specific configuration.
|
|
@@ -1274,8 +1675,6 @@ async def test_connection(source_type: str, config: dict[str, Any]) -> dict[str,
|
|
|
1274
1675
|
"error": f"Configuration errors: {'; '.join(errors)}",
|
|
1275
1676
|
}
|
|
1276
1677
|
|
|
1277
|
-
connection_string = builder.build(config)
|
|
1278
|
-
|
|
1279
1678
|
if source_type == "file":
|
|
1280
1679
|
# For files, just check if path exists
|
|
1281
1680
|
path = Path(config["path"])
|
|
@@ -1286,15 +1685,39 @@ async def test_connection(source_type: str, config: dict[str, Any]) -> dict[str,
|
|
|
1286
1685
|
"message": f"File exists: {path.name} ({path.stat().st_size:,} bytes)",
|
|
1287
1686
|
}
|
|
1288
1687
|
|
|
1289
|
-
#
|
|
1290
|
-
import
|
|
1688
|
+
# Use new DataSourceFactory test_connection for database sources
|
|
1689
|
+
from .datasource_factory import test_connection as factory_test_connection
|
|
1291
1690
|
|
|
1292
|
-
#
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1691
|
+
# Build SourceConfig-compatible dict
|
|
1692
|
+
full_config = {"type": source_type, **config}
|
|
1693
|
+
|
|
1694
|
+
# Map field names from connections to datasource_factory
|
|
1695
|
+
field_mapping = {
|
|
1696
|
+
"username": "user", # connections uses username, factory uses user
|
|
1297
1697
|
}
|
|
1698
|
+
for old_key, new_key in field_mapping.items():
|
|
1699
|
+
if old_key in full_config and new_key not in full_config:
|
|
1700
|
+
full_config[new_key] = full_config[old_key]
|
|
1701
|
+
|
|
1702
|
+
result = await factory_test_connection(full_config)
|
|
1703
|
+
|
|
1704
|
+
# Convert result format
|
|
1705
|
+
if result["success"]:
|
|
1706
|
+
metadata = result.get("metadata", {})
|
|
1707
|
+
row_count = metadata.get("row_count", "unknown")
|
|
1708
|
+
columns = metadata.get("columns", [])
|
|
1709
|
+
col_count = len(columns) if columns else "unknown"
|
|
1710
|
+
|
|
1711
|
+
return {
|
|
1712
|
+
"success": True,
|
|
1713
|
+
"message": f"Connected! Found {col_count} columns, {row_count} rows",
|
|
1714
|
+
"metadata": metadata,
|
|
1715
|
+
}
|
|
1716
|
+
else:
|
|
1717
|
+
return {
|
|
1718
|
+
"success": False,
|
|
1719
|
+
"error": result.get("message", "Connection failed"),
|
|
1720
|
+
}
|
|
1298
1721
|
|
|
1299
1722
|
except ImportError:
|
|
1300
1723
|
return {"success": False, "error": "truthound package not available"}
|
|
@@ -1331,6 +1754,8 @@ def get_source_type_categories() -> list[dict[str, str]]:
|
|
|
1331
1754
|
{"value": "database", "label": "Databases", "description": "Relational databases"},
|
|
1332
1755
|
{"value": "warehouse", "label": "Data Warehouses", "description": "Cloud data warehouses"},
|
|
1333
1756
|
{"value": "bigdata", "label": "Big Data", "description": "Big data platforms"},
|
|
1757
|
+
{"value": "nosql", "label": "NoSQL", "description": "Document and search databases"},
|
|
1758
|
+
{"value": "streaming", "label": "Streaming", "description": "Streaming data platforms"},
|
|
1334
1759
|
]
|
|
1335
1760
|
|
|
1336
1761
|
|
|
@@ -1345,6 +1770,8 @@ def get_source_types_by_category() -> dict[str, list[dict[str, Any]]]:
|
|
|
1345
1770
|
"database": [],
|
|
1346
1771
|
"warehouse": [],
|
|
1347
1772
|
"bigdata": [],
|
|
1773
|
+
"nosql": [],
|
|
1774
|
+
"streaming": [],
|
|
1348
1775
|
}
|
|
1349
1776
|
|
|
1350
1777
|
for source_type in SourceType:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Result converters for data quality backends.
|
|
2
|
+
|
|
3
|
+
This module provides converters that transform backend-specific result
|
|
4
|
+
objects into dashboard-standard result models.
|
|
5
|
+
|
|
6
|
+
The converter pattern isolates backend-specific code and makes it easy
|
|
7
|
+
to support multiple backends or handle API changes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .truthound import TruthoundResultConverter
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"TruthoundResultConverter",
|
|
14
|
+
]
|