airbyte-internal-ops 0.3.0__py3-none-any.whl → 0.4.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.
- {airbyte_internal_ops-0.3.0.dist-info → airbyte_internal_ops-0.4.0.dist-info}/METADATA +1 -1
- {airbyte_internal_ops-0.3.0.dist-info → airbyte_internal_ops-0.4.0.dist-info}/RECORD +14 -14
- airbyte_ops_mcp/connection_config_retriever/audit_logging.py +4 -8
- airbyte_ops_mcp/gcp_auth.py +142 -49
- airbyte_ops_mcp/gcp_logs/error_lookup.py +6 -5
- airbyte_ops_mcp/mcp/prerelease.py +48 -11
- airbyte_ops_mcp/mcp/prod_db_queries.py +478 -8
- airbyte_ops_mcp/mcp/regression_tests.py +16 -3
- airbyte_ops_mcp/prod_db_access/queries.py +150 -1
- airbyte_ops_mcp/prod_db_access/sql.py +407 -0
- airbyte_ops_mcp/regression_tests/connection_secret_retriever.py +0 -4
- airbyte_ops_mcp/regression_tests/connector_runner.py +8 -4
- {airbyte_internal_ops-0.3.0.dist-info → airbyte_internal_ops-0.4.0.dist-info}/WHEEL +0 -0
- {airbyte_internal_ops-0.3.0.dist-info → airbyte_internal_ops-0.4.0.dist-info}/entry_points.txt +0 -0
|
@@ -26,9 +26,17 @@ from airbyte_ops_mcp.prod_db_access.sql import (
|
|
|
26
26
|
SELECT_CONNECTIONS_BY_DESTINATION_CONNECTOR_AND_ORG,
|
|
27
27
|
SELECT_CONNECTOR_VERSIONS,
|
|
28
28
|
SELECT_DATAPLANES_LIST,
|
|
29
|
+
SELECT_DESTINATION_CONNECTION_STATS,
|
|
29
30
|
SELECT_FAILED_SYNC_ATTEMPTS_FOR_CONNECTOR,
|
|
30
31
|
SELECT_NEW_CONNECTOR_RELEASES,
|
|
31
32
|
SELECT_ORG_WORKSPACES,
|
|
33
|
+
SELECT_RECENT_FAILED_SYNCS_FOR_DESTINATION_CONNECTOR,
|
|
34
|
+
SELECT_RECENT_FAILED_SYNCS_FOR_SOURCE_CONNECTOR,
|
|
35
|
+
SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_DESTINATION_CONNECTOR,
|
|
36
|
+
SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_SOURCE_CONNECTOR,
|
|
37
|
+
SELECT_RECENT_SYNCS_FOR_DESTINATION_CONNECTOR,
|
|
38
|
+
SELECT_RECENT_SYNCS_FOR_SOURCE_CONNECTOR,
|
|
39
|
+
SELECT_SOURCE_CONNECTION_STATS,
|
|
32
40
|
SELECT_SUCCESSFUL_SYNCS_FOR_VERSION,
|
|
33
41
|
SELECT_SYNC_RESULTS_FOR_VERSION,
|
|
34
42
|
SELECT_WORKSPACE_INFO,
|
|
@@ -227,7 +235,7 @@ def query_actors_pinned_to_version(
|
|
|
227
235
|
)
|
|
228
236
|
|
|
229
237
|
|
|
230
|
-
def
|
|
238
|
+
def query_syncs_for_version_pinned_connector(
|
|
231
239
|
connector_version_id: str,
|
|
232
240
|
days: int = 7,
|
|
233
241
|
limit: int = 100,
|
|
@@ -320,6 +328,81 @@ def query_failed_sync_attempts_for_connector(
|
|
|
320
328
|
return results
|
|
321
329
|
|
|
322
330
|
|
|
331
|
+
def query_recent_syncs_for_connector(
|
|
332
|
+
connector_definition_id: str,
|
|
333
|
+
is_destination: bool = False,
|
|
334
|
+
status_filter: str = "all",
|
|
335
|
+
organization_id: str | None = None,
|
|
336
|
+
days: int = 7,
|
|
337
|
+
limit: int = 100,
|
|
338
|
+
*,
|
|
339
|
+
gsm_client: secretmanager.SecretManagerServiceClient | None = None,
|
|
340
|
+
) -> list[dict[str, Any]]:
|
|
341
|
+
"""Query recent sync jobs for ALL actors using a connector definition.
|
|
342
|
+
|
|
343
|
+
Finds all actors with the given actor_definition_id and returns their sync jobs,
|
|
344
|
+
regardless of whether they have explicit version pins. Filters out deleted actors,
|
|
345
|
+
deleted workspaces, and deprecated connections.
|
|
346
|
+
|
|
347
|
+
This is useful for finding healthy connections with recent successful syncs,
|
|
348
|
+
or for investigating connector issues across all users.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
connector_definition_id: Connector definition UUID to filter by
|
|
352
|
+
is_destination: If True, query destination connectors; if False, query sources
|
|
353
|
+
status_filter: Filter by job status - "all", "succeeded", or "failed"
|
|
354
|
+
organization_id: Optional organization UUID to filter results by (post-query filter)
|
|
355
|
+
days: Number of days to look back (default: 7)
|
|
356
|
+
limit: Maximum number of results (default: 100)
|
|
357
|
+
gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
List of sync job records with workspace info and optional pin context
|
|
361
|
+
"""
|
|
362
|
+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
|
363
|
+
|
|
364
|
+
# Select the appropriate query based on connector type and status filter
|
|
365
|
+
if is_destination:
|
|
366
|
+
if status_filter == "succeeded":
|
|
367
|
+
query = SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_DESTINATION_CONNECTOR
|
|
368
|
+
query_name = "SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_DESTINATION_CONNECTOR"
|
|
369
|
+
elif status_filter == "failed":
|
|
370
|
+
query = SELECT_RECENT_FAILED_SYNCS_FOR_DESTINATION_CONNECTOR
|
|
371
|
+
query_name = "SELECT_RECENT_FAILED_SYNCS_FOR_DESTINATION_CONNECTOR"
|
|
372
|
+
else:
|
|
373
|
+
query = SELECT_RECENT_SYNCS_FOR_DESTINATION_CONNECTOR
|
|
374
|
+
query_name = "SELECT_RECENT_SYNCS_FOR_DESTINATION_CONNECTOR"
|
|
375
|
+
else:
|
|
376
|
+
if status_filter == "succeeded":
|
|
377
|
+
query = SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_SOURCE_CONNECTOR
|
|
378
|
+
query_name = "SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_SOURCE_CONNECTOR"
|
|
379
|
+
elif status_filter == "failed":
|
|
380
|
+
query = SELECT_RECENT_FAILED_SYNCS_FOR_SOURCE_CONNECTOR
|
|
381
|
+
query_name = "SELECT_RECENT_FAILED_SYNCS_FOR_SOURCE_CONNECTOR"
|
|
382
|
+
else:
|
|
383
|
+
query = SELECT_RECENT_SYNCS_FOR_SOURCE_CONNECTOR
|
|
384
|
+
query_name = "SELECT_RECENT_SYNCS_FOR_SOURCE_CONNECTOR"
|
|
385
|
+
|
|
386
|
+
results = _run_sql_query(
|
|
387
|
+
query,
|
|
388
|
+
parameters={
|
|
389
|
+
"connector_definition_id": connector_definition_id,
|
|
390
|
+
"cutoff_date": cutoff_date,
|
|
391
|
+
"limit": limit,
|
|
392
|
+
},
|
|
393
|
+
query_name=query_name,
|
|
394
|
+
gsm_client=gsm_client,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Post-query filter by organization_id if provided
|
|
398
|
+
if organization_id is not None:
|
|
399
|
+
results = [
|
|
400
|
+
r for r in results if str(r.get("organization_id")) == organization_id
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
return results
|
|
404
|
+
|
|
405
|
+
|
|
323
406
|
def query_dataplanes_list(
|
|
324
407
|
*,
|
|
325
408
|
gsm_client: secretmanager.SecretManagerServiceClient | None = None,
|
|
@@ -416,3 +499,69 @@ def query_workspaces_by_email_domain(
|
|
|
416
499
|
query_name="SELECT_WORKSPACES_BY_EMAIL_DOMAIN",
|
|
417
500
|
gsm_client=gsm_client,
|
|
418
501
|
)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def query_source_connection_stats(
|
|
505
|
+
connector_definition_id: str,
|
|
506
|
+
days: int = 7,
|
|
507
|
+
*,
|
|
508
|
+
gsm_client: secretmanager.SecretManagerServiceClient | None = None,
|
|
509
|
+
) -> list[dict[str, Any]]:
|
|
510
|
+
"""Query aggregate connection stats for a SOURCE connector.
|
|
511
|
+
|
|
512
|
+
Returns counts of connections grouped by pinned version, including:
|
|
513
|
+
- Total, enabled, and active connection counts
|
|
514
|
+
- Pinned vs unpinned breakdown
|
|
515
|
+
- Latest attempt status breakdown (succeeded, failed, cancelled, running, unknown)
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
connector_definition_id: Source connector definition UUID
|
|
519
|
+
days: Number of days to look back for "active" connections (default: 7)
|
|
520
|
+
gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
List of dicts with aggregate counts grouped by pinned_version_id
|
|
524
|
+
"""
|
|
525
|
+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
|
526
|
+
return _run_sql_query(
|
|
527
|
+
SELECT_SOURCE_CONNECTION_STATS,
|
|
528
|
+
parameters={
|
|
529
|
+
"connector_definition_id": connector_definition_id,
|
|
530
|
+
"cutoff_date": cutoff_date,
|
|
531
|
+
},
|
|
532
|
+
query_name="SELECT_SOURCE_CONNECTION_STATS",
|
|
533
|
+
gsm_client=gsm_client,
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def query_destination_connection_stats(
|
|
538
|
+
connector_definition_id: str,
|
|
539
|
+
days: int = 7,
|
|
540
|
+
*,
|
|
541
|
+
gsm_client: secretmanager.SecretManagerServiceClient | None = None,
|
|
542
|
+
) -> list[dict[str, Any]]:
|
|
543
|
+
"""Query aggregate connection stats for a DESTINATION connector.
|
|
544
|
+
|
|
545
|
+
Returns counts of connections grouped by pinned version, including:
|
|
546
|
+
- Total, enabled, and active connection counts
|
|
547
|
+
- Pinned vs unpinned breakdown
|
|
548
|
+
- Latest attempt status breakdown (succeeded, failed, cancelled, running, unknown)
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
connector_definition_id: Destination connector definition UUID
|
|
552
|
+
days: Number of days to look back for "active" connections (default: 7)
|
|
553
|
+
gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
List of dicts with aggregate counts grouped by pinned_version_id
|
|
557
|
+
"""
|
|
558
|
+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
|
559
|
+
return _run_sql_query(
|
|
560
|
+
SELECT_DESTINATION_CONNECTION_STATS,
|
|
561
|
+
parameters={
|
|
562
|
+
"connector_definition_id": connector_definition_id,
|
|
563
|
+
"cutoff_date": cutoff_date,
|
|
564
|
+
},
|
|
565
|
+
query_name="SELECT_DESTINATION_CONNECTION_STATS",
|
|
566
|
+
gsm_client=gsm_client,
|
|
567
|
+
)
|
|
@@ -360,6 +360,305 @@ SELECT_SUCCESSFUL_SYNCS_FOR_VERSION = sqlalchemy.text(
|
|
|
360
360
|
"""
|
|
361
361
|
)
|
|
362
362
|
|
|
363
|
+
# Get recent sync results for ALL actors using a SOURCE connector definition.
|
|
364
|
+
# Finds all actors with the given actor_definition_id and returns their sync attempts,
|
|
365
|
+
# regardless of whether they have explicit version pins.
|
|
366
|
+
# Query starts from jobs table to leverage indexed columns.
|
|
367
|
+
# The LEFT JOIN to scoped_configuration provides pin context when available (pin_origin_type,
|
|
368
|
+
# pin_origin, pinned_version_id will be NULL for unpinned actors).
|
|
369
|
+
# Status filtering ('all', 'succeeded', 'failed') is handled at the application layer by
|
|
370
|
+
# selecting among different SQL query constants; this query returns all statuses.
|
|
371
|
+
SELECT_RECENT_SYNCS_FOR_SOURCE_CONNECTOR = sqlalchemy.text(
|
|
372
|
+
"""
|
|
373
|
+
SELECT
|
|
374
|
+
jobs.id AS job_id,
|
|
375
|
+
jobs.scope AS connection_id,
|
|
376
|
+
jobs.status AS job_status,
|
|
377
|
+
jobs.started_at AS job_started_at,
|
|
378
|
+
jobs.updated_at AS job_updated_at,
|
|
379
|
+
connection.name AS connection_name,
|
|
380
|
+
actor.id AS actor_id,
|
|
381
|
+
actor.name AS actor_name,
|
|
382
|
+
actor.actor_definition_id,
|
|
383
|
+
actor.tombstone AS actor_tombstone,
|
|
384
|
+
workspace.id AS workspace_id,
|
|
385
|
+
workspace.name AS workspace_name,
|
|
386
|
+
workspace.organization_id,
|
|
387
|
+
workspace.dataplane_group_id,
|
|
388
|
+
dataplane_group.name AS dataplane_name,
|
|
389
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
390
|
+
scoped_configuration.origin AS pin_origin,
|
|
391
|
+
scoped_configuration.value AS pinned_version_id
|
|
392
|
+
FROM jobs
|
|
393
|
+
JOIN connection
|
|
394
|
+
ON jobs.scope = connection.id::text
|
|
395
|
+
AND connection.status != 'deprecated'
|
|
396
|
+
JOIN actor
|
|
397
|
+
ON connection.source_id = actor.id
|
|
398
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
399
|
+
AND actor.tombstone = false
|
|
400
|
+
JOIN workspace
|
|
401
|
+
ON actor.workspace_id = workspace.id
|
|
402
|
+
AND workspace.tombstone = false
|
|
403
|
+
LEFT JOIN dataplane_group
|
|
404
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
405
|
+
LEFT JOIN scoped_configuration
|
|
406
|
+
ON scoped_configuration.scope_id = actor.id
|
|
407
|
+
AND scoped_configuration.key = 'connector_version'
|
|
408
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
409
|
+
WHERE
|
|
410
|
+
jobs.config_type = 'sync'
|
|
411
|
+
AND jobs.updated_at >= :cutoff_date
|
|
412
|
+
ORDER BY
|
|
413
|
+
jobs.updated_at DESC
|
|
414
|
+
LIMIT :limit
|
|
415
|
+
"""
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
# Same as above but filtered to only successful syncs
|
|
419
|
+
SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_SOURCE_CONNECTOR = sqlalchemy.text(
|
|
420
|
+
"""
|
|
421
|
+
SELECT
|
|
422
|
+
jobs.id AS job_id,
|
|
423
|
+
jobs.scope AS connection_id,
|
|
424
|
+
jobs.status AS job_status,
|
|
425
|
+
jobs.started_at AS job_started_at,
|
|
426
|
+
jobs.updated_at AS job_updated_at,
|
|
427
|
+
connection.name AS connection_name,
|
|
428
|
+
actor.id AS actor_id,
|
|
429
|
+
actor.name AS actor_name,
|
|
430
|
+
actor.actor_definition_id,
|
|
431
|
+
actor.tombstone AS actor_tombstone,
|
|
432
|
+
workspace.id AS workspace_id,
|
|
433
|
+
workspace.name AS workspace_name,
|
|
434
|
+
workspace.organization_id,
|
|
435
|
+
workspace.dataplane_group_id,
|
|
436
|
+
dataplane_group.name AS dataplane_name,
|
|
437
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
438
|
+
scoped_configuration.origin AS pin_origin,
|
|
439
|
+
scoped_configuration.value AS pinned_version_id
|
|
440
|
+
FROM jobs
|
|
441
|
+
JOIN connection
|
|
442
|
+
ON jobs.scope = connection.id::text
|
|
443
|
+
AND connection.status != 'deprecated'
|
|
444
|
+
JOIN actor
|
|
445
|
+
ON connection.source_id = actor.id
|
|
446
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
447
|
+
AND actor.tombstone = false
|
|
448
|
+
JOIN workspace
|
|
449
|
+
ON actor.workspace_id = workspace.id
|
|
450
|
+
AND workspace.tombstone = false
|
|
451
|
+
LEFT JOIN dataplane_group
|
|
452
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
453
|
+
LEFT JOIN scoped_configuration
|
|
454
|
+
ON scoped_configuration.scope_id = actor.id
|
|
455
|
+
AND scoped_configuration.key = 'connector_version'
|
|
456
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
457
|
+
WHERE
|
|
458
|
+
jobs.config_type = 'sync'
|
|
459
|
+
AND jobs.status = 'succeeded'
|
|
460
|
+
AND jobs.updated_at >= :cutoff_date
|
|
461
|
+
ORDER BY
|
|
462
|
+
jobs.updated_at DESC
|
|
463
|
+
LIMIT :limit
|
|
464
|
+
"""
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Same as above but filtered to only failed syncs
|
|
468
|
+
SELECT_RECENT_FAILED_SYNCS_FOR_SOURCE_CONNECTOR = sqlalchemy.text(
|
|
469
|
+
"""
|
|
470
|
+
SELECT
|
|
471
|
+
jobs.id AS job_id,
|
|
472
|
+
jobs.scope AS connection_id,
|
|
473
|
+
jobs.status AS job_status,
|
|
474
|
+
jobs.started_at AS job_started_at,
|
|
475
|
+
jobs.updated_at AS job_updated_at,
|
|
476
|
+
connection.name AS connection_name,
|
|
477
|
+
actor.id AS actor_id,
|
|
478
|
+
actor.name AS actor_name,
|
|
479
|
+
actor.actor_definition_id,
|
|
480
|
+
actor.tombstone AS actor_tombstone,
|
|
481
|
+
workspace.id AS workspace_id,
|
|
482
|
+
workspace.name AS workspace_name,
|
|
483
|
+
workspace.organization_id,
|
|
484
|
+
workspace.dataplane_group_id,
|
|
485
|
+
dataplane_group.name AS dataplane_name,
|
|
486
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
487
|
+
scoped_configuration.origin AS pin_origin,
|
|
488
|
+
scoped_configuration.value AS pinned_version_id
|
|
489
|
+
FROM jobs
|
|
490
|
+
JOIN connection
|
|
491
|
+
ON jobs.scope = connection.id::text
|
|
492
|
+
AND connection.status != 'deprecated'
|
|
493
|
+
JOIN actor
|
|
494
|
+
ON connection.source_id = actor.id
|
|
495
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
496
|
+
AND actor.tombstone = false
|
|
497
|
+
JOIN workspace
|
|
498
|
+
ON actor.workspace_id = workspace.id
|
|
499
|
+
AND workspace.tombstone = false
|
|
500
|
+
LEFT JOIN dataplane_group
|
|
501
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
502
|
+
LEFT JOIN scoped_configuration
|
|
503
|
+
ON scoped_configuration.scope_id = actor.id
|
|
504
|
+
AND scoped_configuration.key = 'connector_version'
|
|
505
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
506
|
+
WHERE
|
|
507
|
+
jobs.config_type = 'sync'
|
|
508
|
+
AND jobs.status = 'failed'
|
|
509
|
+
AND jobs.updated_at >= :cutoff_date
|
|
510
|
+
ORDER BY
|
|
511
|
+
jobs.updated_at DESC
|
|
512
|
+
LIMIT :limit
|
|
513
|
+
"""
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
# Get recent sync results for ALL actors using a DESTINATION connector definition.
|
|
517
|
+
SELECT_RECENT_SYNCS_FOR_DESTINATION_CONNECTOR = sqlalchemy.text(
|
|
518
|
+
"""
|
|
519
|
+
SELECT
|
|
520
|
+
jobs.id AS job_id,
|
|
521
|
+
jobs.scope AS connection_id,
|
|
522
|
+
jobs.status AS job_status,
|
|
523
|
+
jobs.started_at AS job_started_at,
|
|
524
|
+
jobs.updated_at AS job_updated_at,
|
|
525
|
+
connection.name AS connection_name,
|
|
526
|
+
actor.id AS actor_id,
|
|
527
|
+
actor.name AS actor_name,
|
|
528
|
+
actor.actor_definition_id,
|
|
529
|
+
actor.tombstone AS actor_tombstone,
|
|
530
|
+
workspace.id AS workspace_id,
|
|
531
|
+
workspace.name AS workspace_name,
|
|
532
|
+
workspace.organization_id,
|
|
533
|
+
workspace.dataplane_group_id,
|
|
534
|
+
dataplane_group.name AS dataplane_name,
|
|
535
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
536
|
+
scoped_configuration.origin AS pin_origin,
|
|
537
|
+
scoped_configuration.value AS pinned_version_id
|
|
538
|
+
FROM jobs
|
|
539
|
+
JOIN connection
|
|
540
|
+
ON jobs.scope = connection.id::text
|
|
541
|
+
AND connection.status != 'deprecated'
|
|
542
|
+
JOIN actor
|
|
543
|
+
ON connection.destination_id = actor.id
|
|
544
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
545
|
+
AND actor.tombstone = false
|
|
546
|
+
JOIN workspace
|
|
547
|
+
ON actor.workspace_id = workspace.id
|
|
548
|
+
AND workspace.tombstone = false
|
|
549
|
+
LEFT JOIN dataplane_group
|
|
550
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
551
|
+
LEFT JOIN scoped_configuration
|
|
552
|
+
ON scoped_configuration.scope_id = actor.id
|
|
553
|
+
AND scoped_configuration.key = 'connector_version'
|
|
554
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
555
|
+
WHERE
|
|
556
|
+
jobs.config_type = 'sync'
|
|
557
|
+
AND jobs.updated_at >= :cutoff_date
|
|
558
|
+
ORDER BY
|
|
559
|
+
jobs.updated_at DESC
|
|
560
|
+
LIMIT :limit
|
|
561
|
+
"""
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Same as above but filtered to only successful syncs
|
|
565
|
+
SELECT_RECENT_SUCCESSFUL_SYNCS_FOR_DESTINATION_CONNECTOR = sqlalchemy.text(
|
|
566
|
+
"""
|
|
567
|
+
SELECT
|
|
568
|
+
jobs.id AS job_id,
|
|
569
|
+
jobs.scope AS connection_id,
|
|
570
|
+
jobs.status AS job_status,
|
|
571
|
+
jobs.started_at AS job_started_at,
|
|
572
|
+
jobs.updated_at AS job_updated_at,
|
|
573
|
+
connection.name AS connection_name,
|
|
574
|
+
actor.id AS actor_id,
|
|
575
|
+
actor.name AS actor_name,
|
|
576
|
+
actor.actor_definition_id,
|
|
577
|
+
actor.tombstone AS actor_tombstone,
|
|
578
|
+
workspace.id AS workspace_id,
|
|
579
|
+
workspace.name AS workspace_name,
|
|
580
|
+
workspace.organization_id,
|
|
581
|
+
workspace.dataplane_group_id,
|
|
582
|
+
dataplane_group.name AS dataplane_name,
|
|
583
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
584
|
+
scoped_configuration.origin AS pin_origin,
|
|
585
|
+
scoped_configuration.value AS pinned_version_id
|
|
586
|
+
FROM jobs
|
|
587
|
+
JOIN connection
|
|
588
|
+
ON jobs.scope = connection.id::text
|
|
589
|
+
AND connection.status != 'deprecated'
|
|
590
|
+
JOIN actor
|
|
591
|
+
ON connection.destination_id = actor.id
|
|
592
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
593
|
+
AND actor.tombstone = false
|
|
594
|
+
JOIN workspace
|
|
595
|
+
ON actor.workspace_id = workspace.id
|
|
596
|
+
AND workspace.tombstone = false
|
|
597
|
+
LEFT JOIN dataplane_group
|
|
598
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
599
|
+
LEFT JOIN scoped_configuration
|
|
600
|
+
ON scoped_configuration.scope_id = actor.id
|
|
601
|
+
AND scoped_configuration.key = 'connector_version'
|
|
602
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
603
|
+
WHERE
|
|
604
|
+
jobs.config_type = 'sync'
|
|
605
|
+
AND jobs.status = 'succeeded'
|
|
606
|
+
AND jobs.updated_at >= :cutoff_date
|
|
607
|
+
ORDER BY
|
|
608
|
+
jobs.updated_at DESC
|
|
609
|
+
LIMIT :limit
|
|
610
|
+
"""
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
# Same as above but filtered to only failed syncs
|
|
614
|
+
SELECT_RECENT_FAILED_SYNCS_FOR_DESTINATION_CONNECTOR = sqlalchemy.text(
|
|
615
|
+
"""
|
|
616
|
+
SELECT
|
|
617
|
+
jobs.id AS job_id,
|
|
618
|
+
jobs.scope AS connection_id,
|
|
619
|
+
jobs.status AS job_status,
|
|
620
|
+
jobs.started_at AS job_started_at,
|
|
621
|
+
jobs.updated_at AS job_updated_at,
|
|
622
|
+
connection.name AS connection_name,
|
|
623
|
+
actor.id AS actor_id,
|
|
624
|
+
actor.name AS actor_name,
|
|
625
|
+
actor.actor_definition_id,
|
|
626
|
+
actor.tombstone AS actor_tombstone,
|
|
627
|
+
workspace.id AS workspace_id,
|
|
628
|
+
workspace.name AS workspace_name,
|
|
629
|
+
workspace.organization_id,
|
|
630
|
+
workspace.dataplane_group_id,
|
|
631
|
+
dataplane_group.name AS dataplane_name,
|
|
632
|
+
scoped_configuration.origin_type AS pin_origin_type,
|
|
633
|
+
scoped_configuration.origin AS pin_origin,
|
|
634
|
+
scoped_configuration.value AS pinned_version_id
|
|
635
|
+
FROM jobs
|
|
636
|
+
JOIN connection
|
|
637
|
+
ON jobs.scope = connection.id::text
|
|
638
|
+
AND connection.status != 'deprecated'
|
|
639
|
+
JOIN actor
|
|
640
|
+
ON connection.destination_id = actor.id
|
|
641
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
642
|
+
AND actor.tombstone = false
|
|
643
|
+
JOIN workspace
|
|
644
|
+
ON actor.workspace_id = workspace.id
|
|
645
|
+
AND workspace.tombstone = false
|
|
646
|
+
LEFT JOIN dataplane_group
|
|
647
|
+
ON workspace.dataplane_group_id = dataplane_group.id
|
|
648
|
+
LEFT JOIN scoped_configuration
|
|
649
|
+
ON scoped_configuration.scope_id = actor.id
|
|
650
|
+
AND scoped_configuration.key = 'connector_version'
|
|
651
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
652
|
+
WHERE
|
|
653
|
+
jobs.config_type = 'sync'
|
|
654
|
+
AND jobs.status = 'failed'
|
|
655
|
+
AND jobs.updated_at >= :cutoff_date
|
|
656
|
+
ORDER BY
|
|
657
|
+
jobs.updated_at DESC
|
|
658
|
+
LIMIT :limit
|
|
659
|
+
"""
|
|
660
|
+
)
|
|
661
|
+
|
|
363
662
|
# Get failed attempt results for ALL actors using a connector definition.
|
|
364
663
|
# Finds all actors with the given actor_definition_id and returns their failed sync attempts,
|
|
365
664
|
# regardless of whether they have explicit version pins.
|
|
@@ -527,3 +826,111 @@ SELECT_WORKSPACES_BY_EMAIL_DOMAIN = sqlalchemy.text(
|
|
|
527
826
|
LIMIT :limit
|
|
528
827
|
"""
|
|
529
828
|
)
|
|
829
|
+
|
|
830
|
+
# =============================================================================
|
|
831
|
+
# Connector Connection Stats Queries (Aggregate Counts)
|
|
832
|
+
# =============================================================================
|
|
833
|
+
|
|
834
|
+
# Count connections by SOURCE connector with latest attempt status breakdown
|
|
835
|
+
# Groups by pinned version and provides counts of succeeded/failed/other attempts
|
|
836
|
+
# Uses a CTE to get the latest attempt per connection, then aggregates
|
|
837
|
+
SELECT_SOURCE_CONNECTION_STATS = sqlalchemy.text(
|
|
838
|
+
"""
|
|
839
|
+
WITH latest_attempts AS (
|
|
840
|
+
SELECT DISTINCT ON (connection.id)
|
|
841
|
+
connection.id AS connection_id,
|
|
842
|
+
connection.status AS connection_status,
|
|
843
|
+
scoped_configuration.value AS pinned_version_id,
|
|
844
|
+
attempts.status::text AS latest_attempt_status
|
|
845
|
+
FROM connection
|
|
846
|
+
JOIN actor
|
|
847
|
+
ON connection.source_id = actor.id
|
|
848
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
849
|
+
AND actor.tombstone = false
|
|
850
|
+
JOIN workspace
|
|
851
|
+
ON actor.workspace_id = workspace.id
|
|
852
|
+
AND workspace.tombstone = false
|
|
853
|
+
LEFT JOIN scoped_configuration
|
|
854
|
+
ON scoped_configuration.scope_id = actor.id
|
|
855
|
+
AND scoped_configuration.key = 'connector_version'
|
|
856
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
857
|
+
LEFT JOIN jobs
|
|
858
|
+
ON jobs.scope = connection.id::text
|
|
859
|
+
AND jobs.config_type = 'sync'
|
|
860
|
+
AND jobs.updated_at >= :cutoff_date
|
|
861
|
+
LEFT JOIN attempts
|
|
862
|
+
ON attempts.job_id = jobs.id
|
|
863
|
+
WHERE
|
|
864
|
+
connection.status != 'deprecated'
|
|
865
|
+
ORDER BY
|
|
866
|
+
connection.id,
|
|
867
|
+
attempts.ended_at DESC NULLS LAST
|
|
868
|
+
)
|
|
869
|
+
SELECT
|
|
870
|
+
pinned_version_id,
|
|
871
|
+
COUNT(*) AS total_connections,
|
|
872
|
+
COUNT(*) FILTER (WHERE connection_status = 'active') AS enabled_connections,
|
|
873
|
+
COUNT(*) FILTER (WHERE latest_attempt_status IS NOT NULL) AS active_connections,
|
|
874
|
+
COUNT(*) FILTER (WHERE pinned_version_id IS NOT NULL) AS pinned_connections,
|
|
875
|
+
COUNT(*) FILTER (WHERE pinned_version_id IS NULL) AS unpinned_connections,
|
|
876
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'succeeded') AS succeeded_connections,
|
|
877
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'failed') AS failed_connections,
|
|
878
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'cancelled') AS cancelled_connections,
|
|
879
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'running') AS running_connections,
|
|
880
|
+
COUNT(*) FILTER (WHERE latest_attempt_status IS NULL) AS unknown_connections
|
|
881
|
+
FROM latest_attempts
|
|
882
|
+
GROUP BY pinned_version_id
|
|
883
|
+
ORDER BY total_connections DESC
|
|
884
|
+
"""
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
# Count connections by DESTINATION connector with latest attempt status breakdown
|
|
888
|
+
SELECT_DESTINATION_CONNECTION_STATS = sqlalchemy.text(
|
|
889
|
+
"""
|
|
890
|
+
WITH latest_attempts AS (
|
|
891
|
+
SELECT DISTINCT ON (connection.id)
|
|
892
|
+
connection.id AS connection_id,
|
|
893
|
+
connection.status AS connection_status,
|
|
894
|
+
scoped_configuration.value AS pinned_version_id,
|
|
895
|
+
attempts.status::text AS latest_attempt_status
|
|
896
|
+
FROM connection
|
|
897
|
+
JOIN actor
|
|
898
|
+
ON connection.destination_id = actor.id
|
|
899
|
+
AND actor.actor_definition_id = :connector_definition_id
|
|
900
|
+
AND actor.tombstone = false
|
|
901
|
+
JOIN workspace
|
|
902
|
+
ON actor.workspace_id = workspace.id
|
|
903
|
+
AND workspace.tombstone = false
|
|
904
|
+
LEFT JOIN scoped_configuration
|
|
905
|
+
ON scoped_configuration.scope_id = actor.id
|
|
906
|
+
AND scoped_configuration.key = 'connector_version'
|
|
907
|
+
AND scoped_configuration.scope_type = 'actor'
|
|
908
|
+
LEFT JOIN jobs
|
|
909
|
+
ON jobs.scope = connection.id::text
|
|
910
|
+
AND jobs.config_type = 'sync'
|
|
911
|
+
AND jobs.updated_at >= :cutoff_date
|
|
912
|
+
LEFT JOIN attempts
|
|
913
|
+
ON attempts.job_id = jobs.id
|
|
914
|
+
WHERE
|
|
915
|
+
connection.status != 'deprecated'
|
|
916
|
+
ORDER BY
|
|
917
|
+
connection.id,
|
|
918
|
+
attempts.ended_at DESC NULLS LAST
|
|
919
|
+
)
|
|
920
|
+
SELECT
|
|
921
|
+
pinned_version_id,
|
|
922
|
+
COUNT(*) AS total_connections,
|
|
923
|
+
COUNT(*) FILTER (WHERE connection_status = 'active') AS enabled_connections,
|
|
924
|
+
COUNT(*) FILTER (WHERE latest_attempt_status IS NOT NULL) AS active_connections,
|
|
925
|
+
COUNT(*) FILTER (WHERE pinned_version_id IS NOT NULL) AS pinned_connections,
|
|
926
|
+
COUNT(*) FILTER (WHERE pinned_version_id IS NULL) AS unpinned_connections,
|
|
927
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'succeeded') AS succeeded_connections,
|
|
928
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'failed') AS failed_connections,
|
|
929
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'cancelled') AS cancelled_connections,
|
|
930
|
+
COUNT(*) FILTER (WHERE latest_attempt_status = 'running') AS running_connections,
|
|
931
|
+
COUNT(*) FILTER (WHERE latest_attempt_status IS NULL) AS unknown_connections
|
|
932
|
+
FROM latest_attempts
|
|
933
|
+
GROUP BY pinned_version_id
|
|
934
|
+
ORDER BY total_connections DESC
|
|
935
|
+
"""
|
|
936
|
+
)
|
|
@@ -39,7 +39,6 @@ from airbyte_ops_mcp.connection_config_retriever import (
|
|
|
39
39
|
ConnectionObject,
|
|
40
40
|
retrieve_objects,
|
|
41
41
|
)
|
|
42
|
-
from airbyte_ops_mcp.gcp_auth import ensure_adc_credentials
|
|
43
42
|
|
|
44
43
|
if TYPE_CHECKING:
|
|
45
44
|
from airbyte_ops_mcp.regression_tests.connection_fetcher import ConnectionData
|
|
@@ -85,9 +84,6 @@ def retrieve_unmasked_config(
|
|
|
85
84
|
Returns:
|
|
86
85
|
The unmasked source config dict, or None if retrieval fails.
|
|
87
86
|
"""
|
|
88
|
-
# Ensure GCP credentials are available (supports GCP_PROD_DB_ACCESS_CREDENTIALS fallback)
|
|
89
|
-
ensure_adc_credentials()
|
|
90
|
-
|
|
91
87
|
# Only request the source config - that's all we need for secrets
|
|
92
88
|
requested_objects = [ConnectionObject.SOURCE_CONFIG]
|
|
93
89
|
|
|
@@ -105,16 +105,19 @@ class ConnectorRunner:
|
|
|
105
105
|
if self.config is not None:
|
|
106
106
|
config_path = temp_dir / self.CONFIG_FILE
|
|
107
107
|
config_path.write_text(json.dumps(self.config))
|
|
108
|
+
config_path.chmod(0o666)
|
|
108
109
|
self.logger.debug(f"Wrote config to {config_path}")
|
|
109
110
|
|
|
110
111
|
if self.configured_catalog is not None:
|
|
111
112
|
catalog_path = temp_dir / self.CATALOG_FILE
|
|
112
113
|
catalog_path.write_text(self.configured_catalog.json())
|
|
114
|
+
catalog_path.chmod(0o666)
|
|
113
115
|
self.logger.debug(f"Wrote catalog to {catalog_path}")
|
|
114
116
|
|
|
115
117
|
if self.state is not None:
|
|
116
118
|
state_path = temp_dir / self.STATE_FILE
|
|
117
119
|
state_path.write_text(json.dumps(self.state))
|
|
120
|
+
state_path.chmod(0o666)
|
|
118
121
|
self.logger.debug(f"Wrote state to {state_path}")
|
|
119
122
|
|
|
120
123
|
def _build_docker_command(self, temp_dir: Path) -> list[str]:
|
|
@@ -135,7 +138,7 @@ class ConnectorRunner:
|
|
|
135
138
|
"--name",
|
|
136
139
|
container_name,
|
|
137
140
|
"-v",
|
|
138
|
-
f"{temp_dir}:{self.DATA_DIR}
|
|
141
|
+
f"{temp_dir}:{self.DATA_DIR}",
|
|
139
142
|
]
|
|
140
143
|
|
|
141
144
|
if self.proxy_url:
|
|
@@ -168,9 +171,10 @@ class ConnectorRunner:
|
|
|
168
171
|
|
|
169
172
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
170
173
|
temp_path = Path(temp_dir)
|
|
171
|
-
# Make temp directory world-
|
|
172
|
-
# Many connector images run as non-root users (e.g., 'airbyte' user)
|
|
173
|
-
|
|
174
|
+
# Make temp directory world-writable so non-root container users can read/write
|
|
175
|
+
# Many connector images run as non-root users (e.g., 'airbyte' user) with
|
|
176
|
+
# different UIDs than the host user, so they need write access for config migration
|
|
177
|
+
temp_path.chmod(0o777)
|
|
174
178
|
self._prepare_data_directory(temp_path)
|
|
175
179
|
|
|
176
180
|
docker_cmd = self._build_docker_command(temp_path)
|
|
File without changes
|
{airbyte_internal_ops-0.3.0.dist-info → airbyte_internal_ops-0.4.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|