airbyte-internal-ops 0.4.2__py3-none-any.whl → 0.5.1__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.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/METADATA +2 -1
- {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/RECORD +21 -129
- airbyte_ops_mcp/cli/cloud.py +31 -2
- airbyte_ops_mcp/cloud_admin/api_client.py +506 -33
- airbyte_ops_mcp/cloud_admin/models.py +56 -0
- airbyte_ops_mcp/constants.py +58 -0
- airbyte_ops_mcp/{_legacy/airbyte_ci/metadata_service/docker_hub.py → docker_hub.py} +16 -10
- airbyte_ops_mcp/mcp/cloud_connector_versions.py +491 -10
- airbyte_ops_mcp/mcp/prerelease.py +5 -44
- airbyte_ops_mcp/mcp/prod_db_queries.py +128 -4
- airbyte_ops_mcp/mcp/regression_tests.py +10 -5
- airbyte_ops_mcp/{_legacy/airbyte_ci/metadata_service/validators/metadata_validator.py → metadata_validator.py} +18 -12
- airbyte_ops_mcp/prod_db_access/queries.py +51 -0
- airbyte_ops_mcp/prod_db_access/sql.py +76 -0
- airbyte_ops_mcp/regression_tests/ci_output.py +8 -4
- airbyte_ops_mcp/regression_tests/connection_fetcher.py +16 -5
- airbyte_ops_mcp/regression_tests/http_metrics.py +21 -2
- airbyte_ops_mcp/regression_tests/models.py +7 -1
- airbyte_ops_mcp/telemetry.py +162 -0
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/.gitignore +0 -1
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/README.md +0 -420
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/__init__.py +0 -2
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/__init__.py +0 -1
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/__init__.py +0 -8
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/base_backend.py +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/duckdb_backend.py +0 -87
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/file_backend.py +0 -165
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/connection_objects_retrieval.py +0 -377
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/connector_runner.py +0 -247
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/errors.py +0 -7
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/evaluation_modes.py +0 -25
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/hacks.py +0 -23
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/json_schema_helper.py +0 -384
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/mitm_addons.py +0 -37
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/models.py +0 -595
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/proxy.py +0 -207
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/secret_access.py +0 -47
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/segment_tracking.py +0 -45
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/utils.py +0 -214
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/conftest.py.disabled +0 -751
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/consts.py +0 -4
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/poetry.lock +0 -4480
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/pytest.ini +0 -9
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/__init__.py +0 -1
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_check.py +0 -61
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_discover.py +0 -117
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_read.py +0 -627
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_spec.py +0 -43
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/report.py +0 -542
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/stash_keys.py +0 -38
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/__init__.py +0 -0
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/private_details.html.j2 +0 -305
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/report.html.j2 +0 -515
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/utils.py +0 -187
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/__init__.py +0 -0
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_check.py +0 -61
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_discover.py +0 -217
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_read.py +0 -177
- airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_spec.py +0 -631
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/README.md +0 -91
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/bin/bundle-schemas.js +0 -48
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/bin/generate-metadata-models.sh +0 -36
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ActorDefinitionResourceRequirements.py +0 -54
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/AirbyteInternal.py +0 -22
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/AllowedHosts.py +0 -18
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorBreakingChanges.py +0 -65
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorBuildOptions.py +0 -15
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorIPCOptions.py +0 -25
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetadataDefinitionV0.json +0 -897
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetadataDefinitionV0.py +0 -478
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetrics.py +0 -24
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorPackageInfo.py +0 -12
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryDestinationDefinition.py +0 -407
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryReleases.py +0 -406
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistrySourceDefinition.py +0 -407
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryV0.py +0 -413
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorReleases.py +0 -98
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorTestSuiteOptions.py +0 -58
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/GeneratedFields.py +0 -62
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/GitInfo.py +0 -31
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/JobType.py +0 -23
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/NormalizationDestinationDefinitionConfig.py +0 -24
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RegistryOverrides.py +0 -111
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ReleaseStage.py +0 -15
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RemoteRegistries.py +0 -23
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ResourceRequirements.py +0 -18
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RolloutConfiguration.py +0 -29
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/Secret.py +0 -34
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SecretStore.py +0 -22
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SourceFileInfo.py +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SuggestedStreams.py +0 -18
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SupportLevel.py +0 -15
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/TestConnections.py +0 -14
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/__init__.py +0 -31
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/airbyte-connector-metadata-schema.json +0 -0
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ActorDefinitionResourceRequirements.yaml +0 -30
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/AirbyteInternal.yaml +0 -32
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/AllowedHosts.yaml +0 -13
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorBreakingChanges.yaml +0 -65
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorBuildOptions.yaml +0 -10
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorIPCOptions.yaml +0 -29
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorMetadataDefinitionV0.yaml +0 -172
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorMetrics.yaml +0 -30
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorPackageInfo.yaml +0 -9
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryDestinationDefinition.yaml +0 -90
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryReleases.yaml +0 -35
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistrySourceDefinition.yaml +0 -92
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryV0.yaml +0 -18
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorReleases.yaml +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorTestSuiteOptions.yaml +0 -28
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/GeneratedFields.yaml +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/GitInfo.yaml +0 -21
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/JobType.yaml +0 -14
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/NormalizationDestinationDefinitionConfig.yaml +0 -21
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RegistryOverrides.yaml +0 -38
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ReleaseStage.yaml +0 -11
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RemoteRegistries.yaml +0 -25
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ResourceRequirements.yaml +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RolloutConfiguration.yaml +0 -29
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/Secret.yaml +0 -19
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SecretStore.yaml +0 -16
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SourceFileInfo.yaml +0 -17
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SuggestedStreams.yaml +0 -13
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SupportLevel.yaml +0 -10
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/TestConnections.yaml +0 -17
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/package-lock.json +0 -62
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/package.json +0 -12
- airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/transform.py +0 -71
- {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/WHEEL +0 -0
- {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/entry_points.txt +0 -0
|
@@ -15,6 +15,9 @@ from airbyte import constants
|
|
|
15
15
|
from airbyte.exceptions import PyAirbyteInputError
|
|
16
16
|
|
|
17
17
|
from airbyte_ops_mcp import constants as ops_constants
|
|
18
|
+
from airbyte_ops_mcp.mcp.prod_db_queries import (
|
|
19
|
+
_resolve_canonical_name_to_definition_id,
|
|
20
|
+
)
|
|
18
21
|
|
|
19
22
|
# Internal enums for scoped configuration API "magic strings"
|
|
20
23
|
# These values caused issues during development and are now centralized here
|
|
@@ -129,7 +132,7 @@ def _get_access_token(
|
|
|
129
132
|
|
|
130
133
|
def get_user_id_by_email(
|
|
131
134
|
email: str,
|
|
132
|
-
|
|
135
|
+
config_api_root: str,
|
|
133
136
|
client_id: str | None = None,
|
|
134
137
|
client_secret: str | None = None,
|
|
135
138
|
bearer_token: str | None = None,
|
|
@@ -138,7 +141,7 @@ def get_user_id_by_email(
|
|
|
138
141
|
|
|
139
142
|
Args:
|
|
140
143
|
email: The user's email address
|
|
141
|
-
|
|
144
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
142
145
|
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
143
146
|
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
144
147
|
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
@@ -151,7 +154,7 @@ def get_user_id_by_email(
|
|
|
151
154
|
"""
|
|
152
155
|
access_token = _get_access_token(client_id, client_secret, bearer_token)
|
|
153
156
|
|
|
154
|
-
endpoint = f"{
|
|
157
|
+
endpoint = f"{config_api_root}/users/list_instance_admin"
|
|
155
158
|
response = requests.post(
|
|
156
159
|
endpoint,
|
|
157
160
|
json={},
|
|
@@ -193,7 +196,7 @@ def resolve_connector_version_id(
|
|
|
193
196
|
actor_definition_id: str,
|
|
194
197
|
connector_type: Literal["source", "destination"],
|
|
195
198
|
version: str,
|
|
196
|
-
|
|
199
|
+
config_api_root: str,
|
|
197
200
|
client_id: str | None = None,
|
|
198
201
|
client_secret: str | None = None,
|
|
199
202
|
bearer_token: str | None = None,
|
|
@@ -204,7 +207,7 @@ def resolve_connector_version_id(
|
|
|
204
207
|
actor_definition_id: The actor definition ID
|
|
205
208
|
connector_type: Either "source" or "destination"
|
|
206
209
|
version: The version string (e.g., "0.1.47-preview.abe7cb4")
|
|
207
|
-
|
|
210
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
208
211
|
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
209
212
|
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
210
213
|
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
@@ -217,7 +220,7 @@ def resolve_connector_version_id(
|
|
|
217
220
|
"""
|
|
218
221
|
access_token = _get_access_token(client_id, client_secret, bearer_token)
|
|
219
222
|
|
|
220
|
-
endpoint = f"{
|
|
223
|
+
endpoint = f"{config_api_root}/actor_definition_versions/resolve"
|
|
221
224
|
payload = {
|
|
222
225
|
"actorDefinitionId": actor_definition_id,
|
|
223
226
|
"actorType": connector_type,
|
|
@@ -267,7 +270,7 @@ def _get_scoped_configuration_context(
|
|
|
267
270
|
actor_definition_id: str,
|
|
268
271
|
scope_type: _ScopeType,
|
|
269
272
|
scope_id: str,
|
|
270
|
-
|
|
273
|
+
config_api_root: str,
|
|
271
274
|
access_token: str,
|
|
272
275
|
) -> dict[str, Any] | None:
|
|
273
276
|
"""Get the active scoped configuration for a single scope level.
|
|
@@ -279,7 +282,7 @@ def _get_scoped_configuration_context(
|
|
|
279
282
|
actor_definition_id: The actor definition ID for the connector
|
|
280
283
|
scope_type: The scope type enum (ACTOR, WORKSPACE, or ORGANIZATION)
|
|
281
284
|
scope_id: The ID for the scope (connector_id, workspace_id, or organization_id)
|
|
282
|
-
|
|
285
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
283
286
|
access_token: Pre-authenticated access token
|
|
284
287
|
|
|
285
288
|
Returns:
|
|
@@ -288,7 +291,7 @@ def _get_scoped_configuration_context(
|
|
|
288
291
|
Raises:
|
|
289
292
|
PyAirbyteInputError: If the API request fails
|
|
290
293
|
"""
|
|
291
|
-
endpoint = f"{
|
|
294
|
+
endpoint = f"{config_api_root}/scoped_configuration/get_context"
|
|
292
295
|
context_payload = {
|
|
293
296
|
"config_key": _ScopedConfigKey.CONNECTOR_VERSION.value,
|
|
294
297
|
"resource_type": _ResourceType.ACTOR_DEFINITION.value,
|
|
@@ -330,7 +333,7 @@ def get_all_scoped_configuration_contexts(
|
|
|
330
333
|
actor_definition_id: str,
|
|
331
334
|
workspace_id: str,
|
|
332
335
|
organization_id: str,
|
|
333
|
-
|
|
336
|
+
config_api_root: str,
|
|
334
337
|
client_id: str | None = None,
|
|
335
338
|
client_secret: str | None = None,
|
|
336
339
|
bearer_token: str | None = None,
|
|
@@ -346,7 +349,7 @@ def get_all_scoped_configuration_contexts(
|
|
|
346
349
|
actor_definition_id: The actor definition ID for the connector
|
|
347
350
|
workspace_id: The workspace ID (required - must always check workspace scope)
|
|
348
351
|
organization_id: The organization ID (required - must always check org scope)
|
|
349
|
-
|
|
352
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
350
353
|
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
351
354
|
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
352
355
|
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
@@ -378,7 +381,7 @@ def get_all_scoped_configuration_contexts(
|
|
|
378
381
|
actor_definition_id=actor_definition_id,
|
|
379
382
|
scope_type=scope_type_enum,
|
|
380
383
|
scope_id=scope_id,
|
|
381
|
-
|
|
384
|
+
config_api_root=config_api_root,
|
|
382
385
|
access_token=access_token,
|
|
383
386
|
)
|
|
384
387
|
if active_config:
|
|
@@ -390,7 +393,7 @@ def get_all_scoped_configuration_contexts(
|
|
|
390
393
|
def get_connector_version(
|
|
391
394
|
connector_id: str,
|
|
392
395
|
connector_type: Literal["source", "destination"],
|
|
393
|
-
|
|
396
|
+
config_api_root: str,
|
|
394
397
|
client_id: str | None = None,
|
|
395
398
|
client_secret: str | None = None,
|
|
396
399
|
bearer_token: str | None = None,
|
|
@@ -405,7 +408,7 @@ def get_connector_version(
|
|
|
405
408
|
Args:
|
|
406
409
|
connector_id: The ID of the deployed connector (source or destination)
|
|
407
410
|
connector_type: Either "source" or "destination"
|
|
408
|
-
|
|
411
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
409
412
|
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
410
413
|
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
411
414
|
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
@@ -424,13 +427,13 @@ def get_connector_version(
|
|
|
424
427
|
access_token = _get_access_token(client_id, client_secret, bearer_token)
|
|
425
428
|
|
|
426
429
|
# Determine endpoint based on connector type
|
|
427
|
-
#
|
|
430
|
+
# config_api_root already includes /v1
|
|
428
431
|
if connector_type == "source":
|
|
429
|
-
endpoint = f"{
|
|
432
|
+
endpoint = f"{config_api_root}/actor_definition_versions/get_for_source"
|
|
430
433
|
payload = {"sourceId": connector_id}
|
|
431
434
|
definition_id_key = "sourceDefinitionId"
|
|
432
435
|
else:
|
|
433
|
-
endpoint = f"{
|
|
436
|
+
endpoint = f"{config_api_root}/actor_definition_versions/get_for_destination"
|
|
434
437
|
payload = {"destinationId": connector_id}
|
|
435
438
|
definition_id_key = "destinationDefinitionId"
|
|
436
439
|
|
|
@@ -453,7 +456,7 @@ def get_connector_version(
|
|
|
453
456
|
"connector_type": connector_type,
|
|
454
457
|
"endpoint": endpoint,
|
|
455
458
|
"payload": payload,
|
|
456
|
-
"
|
|
459
|
+
"config_api_root": config_api_root,
|
|
457
460
|
"status_code": response.status_code,
|
|
458
461
|
"response": response.text,
|
|
459
462
|
},
|
|
@@ -471,7 +474,7 @@ def get_connector_version(
|
|
|
471
474
|
# If workspace_id is provided, also get detailed scoped configuration context
|
|
472
475
|
if workspace_id:
|
|
473
476
|
# Get actor_definition_id from the connector info
|
|
474
|
-
get_endpoint = f"{
|
|
477
|
+
get_endpoint = f"{config_api_root}/{connector_type}s/get"
|
|
475
478
|
get_payload: dict[str, str] = {f"{connector_type}Id": connector_id}
|
|
476
479
|
|
|
477
480
|
get_response = requests.post(
|
|
@@ -491,7 +494,7 @@ def get_connector_version(
|
|
|
491
494
|
|
|
492
495
|
if actor_definition_id:
|
|
493
496
|
# Get organization_id from workspace
|
|
494
|
-
workspace_endpoint = f"{
|
|
497
|
+
workspace_endpoint = f"{config_api_root}/workspaces/get"
|
|
495
498
|
workspace_response = requests.post(
|
|
496
499
|
workspace_endpoint,
|
|
497
500
|
json={"workspaceId": workspace_id},
|
|
@@ -514,7 +517,7 @@ def get_connector_version(
|
|
|
514
517
|
actor_definition_id=actor_definition_id,
|
|
515
518
|
workspace_id=workspace_id,
|
|
516
519
|
organization_id=organization_id,
|
|
517
|
-
|
|
520
|
+
config_api_root=config_api_root,
|
|
518
521
|
bearer_token=access_token,
|
|
519
522
|
)
|
|
520
523
|
|
|
@@ -524,7 +527,7 @@ def get_connector_version(
|
|
|
524
527
|
def set_connector_version_override(
|
|
525
528
|
connector_id: str,
|
|
526
529
|
connector_type: Literal["source", "destination"],
|
|
527
|
-
|
|
530
|
+
config_api_root: str,
|
|
528
531
|
client_id: str | None = None,
|
|
529
532
|
client_secret: str | None = None,
|
|
530
533
|
workspace_id: str | None = None,
|
|
@@ -551,7 +554,7 @@ def set_connector_version_override(
|
|
|
551
554
|
Args:
|
|
552
555
|
connector_id: The ID of the deployed connector
|
|
553
556
|
connector_type: Either "source" or "destination"
|
|
554
|
-
|
|
557
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
555
558
|
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
556
559
|
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
557
560
|
workspace_id: The workspace ID
|
|
@@ -587,7 +590,7 @@ def set_connector_version_override(
|
|
|
587
590
|
if unset:
|
|
588
591
|
# To unset, we need to delete the scoped configuration
|
|
589
592
|
# First, get the actor_definition_id for the connector
|
|
590
|
-
get_endpoint: str = f"{
|
|
593
|
+
get_endpoint: str = f"{config_api_root}/{connector_type}s/get"
|
|
591
594
|
get_payload: dict[str, str] = {f"{connector_type}Id": connector_id}
|
|
592
595
|
definition_id_key = f"{connector_type}DefinitionId"
|
|
593
596
|
|
|
@@ -620,7 +623,7 @@ def set_connector_version_override(
|
|
|
620
623
|
actor_definition_id=actor_definition_id,
|
|
621
624
|
scope_type=_ScopeType.ACTOR,
|
|
622
625
|
scope_id=connector_id,
|
|
623
|
-
|
|
626
|
+
config_api_root=config_api_root,
|
|
624
627
|
access_token=access_token,
|
|
625
628
|
)
|
|
626
629
|
|
|
@@ -629,7 +632,7 @@ def set_connector_version_override(
|
|
|
629
632
|
return False
|
|
630
633
|
|
|
631
634
|
# Delete the active configuration
|
|
632
|
-
delete_endpoint = f"{
|
|
635
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
633
636
|
delete_payload = {"scopedConfigurationId": active_config["id"]}
|
|
634
637
|
|
|
635
638
|
response = requests.post(
|
|
@@ -659,7 +662,7 @@ def set_connector_version_override(
|
|
|
659
662
|
else:
|
|
660
663
|
# Set a new override
|
|
661
664
|
# First, get the actor_definition_id for the connector
|
|
662
|
-
get_endpoint = f"{
|
|
665
|
+
get_endpoint = f"{config_api_root}/{connector_type}s/get"
|
|
663
666
|
get_payload: dict[str, str] = {f"{connector_type}Id": connector_id}
|
|
664
667
|
definition_id_key = f"{connector_type}DefinitionId"
|
|
665
668
|
|
|
@@ -692,7 +695,7 @@ def set_connector_version_override(
|
|
|
692
695
|
actor_definition_id=actor_definition_id,
|
|
693
696
|
connector_type=connector_type,
|
|
694
697
|
version=version,
|
|
695
|
-
|
|
698
|
+
config_api_root=config_api_root,
|
|
696
699
|
client_id=client_id,
|
|
697
700
|
client_secret=client_secret,
|
|
698
701
|
bearer_token=bearer_token,
|
|
@@ -700,7 +703,7 @@ def set_connector_version_override(
|
|
|
700
703
|
|
|
701
704
|
# Get organization_id from workspace info for comprehensive scope checking
|
|
702
705
|
# This is REQUIRED - we must always check all three scopes
|
|
703
|
-
workspace_endpoint = f"{
|
|
706
|
+
workspace_endpoint = f"{config_api_root}/workspaces/get"
|
|
704
707
|
workspace_payload = {"workspaceId": workspace_id}
|
|
705
708
|
workspace_response = requests.post(
|
|
706
709
|
workspace_endpoint,
|
|
@@ -742,7 +745,7 @@ def set_connector_version_override(
|
|
|
742
745
|
actor_definition_id=actor_definition_id,
|
|
743
746
|
workspace_id=workspace_id,
|
|
744
747
|
organization_id=organization_id,
|
|
745
|
-
|
|
748
|
+
config_api_root=config_api_root,
|
|
746
749
|
client_id=client_id,
|
|
747
750
|
client_secret=client_secret,
|
|
748
751
|
bearer_token=bearer_token,
|
|
@@ -833,7 +836,7 @@ def set_connector_version_override(
|
|
|
833
836
|
},
|
|
834
837
|
)
|
|
835
838
|
|
|
836
|
-
delete_endpoint = f"{
|
|
839
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
837
840
|
delete_payload = {"scopedConfigurationId": actor_config["id"]}
|
|
838
841
|
|
|
839
842
|
delete_response = requests.post(
|
|
@@ -886,14 +889,14 @@ def set_connector_version_override(
|
|
|
886
889
|
)
|
|
887
890
|
origin = get_user_id_by_email(
|
|
888
891
|
email=user_email,
|
|
889
|
-
|
|
892
|
+
config_api_root=config_api_root,
|
|
890
893
|
client_id=client_id,
|
|
891
894
|
client_secret=client_secret,
|
|
892
895
|
bearer_token=bearer_token,
|
|
893
896
|
)
|
|
894
897
|
|
|
895
898
|
# Create the override with correct schema
|
|
896
|
-
endpoint = f"{
|
|
899
|
+
endpoint = f"{config_api_root}/scoped_configuration/create"
|
|
897
900
|
|
|
898
901
|
# Build payload with explicit string values for auditability
|
|
899
902
|
# (enum.value ensures we log exactly what we send)
|
|
@@ -959,3 +962,473 @@ def set_connector_version_override(
|
|
|
959
962
|
)
|
|
960
963
|
|
|
961
964
|
return True
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
def set_workspace_connector_version_override(
|
|
968
|
+
workspace_id: str,
|
|
969
|
+
connector_name: str,
|
|
970
|
+
connector_type: Literal["source", "destination"],
|
|
971
|
+
config_api_root: str,
|
|
972
|
+
client_id: str | None = None,
|
|
973
|
+
client_secret: str | None = None,
|
|
974
|
+
version: str | None = None,
|
|
975
|
+
unset: bool = False,
|
|
976
|
+
override_reason: str | None = None,
|
|
977
|
+
override_reason_reference_url: str | None = None,
|
|
978
|
+
user_email: str | None = None,
|
|
979
|
+
bearer_token: str | None = None,
|
|
980
|
+
) -> bool:
|
|
981
|
+
"""Set or clear a workspace-level version override for a connector type.
|
|
982
|
+
|
|
983
|
+
This pins ALL instances of a connector type within a workspace to a specific version.
|
|
984
|
+
For example, pinning 'source-github' at workspace level means all GitHub sources
|
|
985
|
+
in that workspace will use the pinned version.
|
|
986
|
+
|
|
987
|
+
Args:
|
|
988
|
+
workspace_id: The workspace ID
|
|
989
|
+
connector_name: The connector name (e.g., 'source-github')
|
|
990
|
+
connector_type: Either "source" or "destination"
|
|
991
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
992
|
+
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
993
|
+
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
994
|
+
version: The version to pin to (e.g., "0.1.0"), or None to unset
|
|
995
|
+
unset: If True, removes any existing override
|
|
996
|
+
override_reason: Required when setting. Explanation for the override
|
|
997
|
+
override_reason_reference_url: Optional URL with more context
|
|
998
|
+
user_email: Email of user creating the override
|
|
999
|
+
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
1000
|
+
|
|
1001
|
+
Returns:
|
|
1002
|
+
True if operation succeeded, False if no override existed (unset only)
|
|
1003
|
+
|
|
1004
|
+
Raises:
|
|
1005
|
+
PyAirbyteInputError: If the API request fails or parameters are invalid
|
|
1006
|
+
"""
|
|
1007
|
+
# Input validation
|
|
1008
|
+
if (version is None) == (not unset):
|
|
1009
|
+
raise PyAirbyteInputError(
|
|
1010
|
+
message="Must specify EXACTLY ONE of version (to set) OR unset=True (to clear), but not both",
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
if not unset and (not override_reason or len(override_reason.strip()) < 10):
|
|
1014
|
+
raise PyAirbyteInputError(
|
|
1015
|
+
message="override_reason is required when setting a version and must be at least 10 characters",
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
access_token = _get_access_token(client_id, client_secret, bearer_token)
|
|
1019
|
+
|
|
1020
|
+
# Resolve connector name to actor_definition_id using the shared registry lookup
|
|
1021
|
+
actor_definition_id = _resolve_canonical_name_to_definition_id(connector_name)
|
|
1022
|
+
|
|
1023
|
+
if unset:
|
|
1024
|
+
# Get the existing workspace-level configuration
|
|
1025
|
+
active_config = _get_scoped_configuration_context(
|
|
1026
|
+
actor_definition_id=actor_definition_id,
|
|
1027
|
+
scope_type=_ScopeType.WORKSPACE,
|
|
1028
|
+
scope_id=workspace_id,
|
|
1029
|
+
config_api_root=config_api_root,
|
|
1030
|
+
access_token=access_token,
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
if not active_config:
|
|
1034
|
+
return False
|
|
1035
|
+
|
|
1036
|
+
# Verify this is actually a workspace-scoped config (not inherited from org)
|
|
1037
|
+
api_scope_type = active_config.get("scope_type", "").lower()
|
|
1038
|
+
api_scope_id = active_config.get("scope_id", "")
|
|
1039
|
+
if api_scope_type != _ScopeType.WORKSPACE.value or api_scope_id != workspace_id:
|
|
1040
|
+
raise PyAirbyteInputError(
|
|
1041
|
+
message=f"Cannot delete: the active config is not workspace-scoped. "
|
|
1042
|
+
f"Expected scope_type='{_ScopeType.WORKSPACE.value}' and scope_id='{workspace_id}', "
|
|
1043
|
+
f"but got scope_type='{api_scope_type}' and scope_id='{api_scope_id}'. "
|
|
1044
|
+
f"This may be an inherited config from organization level.",
|
|
1045
|
+
context={
|
|
1046
|
+
"workspace_id": workspace_id,
|
|
1047
|
+
"expected_scope_type": _ScopeType.WORKSPACE.value,
|
|
1048
|
+
"actual_scope_type": api_scope_type,
|
|
1049
|
+
"actual_scope_id": api_scope_id,
|
|
1050
|
+
"full_config": active_config,
|
|
1051
|
+
},
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
# Delete the configuration
|
|
1055
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
1056
|
+
delete_payload = {"scopedConfigurationId": active_config["id"]}
|
|
1057
|
+
|
|
1058
|
+
response = requests.post(
|
|
1059
|
+
delete_endpoint,
|
|
1060
|
+
json=delete_payload,
|
|
1061
|
+
headers={
|
|
1062
|
+
"Authorization": f"Bearer {access_token}",
|
|
1063
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1064
|
+
"Content-Type": "application/json",
|
|
1065
|
+
},
|
|
1066
|
+
timeout=30,
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
if response.status_code not in (200, 204):
|
|
1070
|
+
raise PyAirbyteInputError(
|
|
1071
|
+
message=f"Failed to delete workspace version override: {response.status_code} {response.text}",
|
|
1072
|
+
context={
|
|
1073
|
+
"delete_endpoint": delete_endpoint,
|
|
1074
|
+
"config_id": active_config["id"],
|
|
1075
|
+
"status_code": response.status_code,
|
|
1076
|
+
"response": response.text,
|
|
1077
|
+
},
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
return True
|
|
1081
|
+
|
|
1082
|
+
# Set a new workspace-level override
|
|
1083
|
+
# Resolve version string to version ID
|
|
1084
|
+
version_id = resolve_connector_version_id(
|
|
1085
|
+
actor_definition_id=actor_definition_id,
|
|
1086
|
+
connector_type=connector_type,
|
|
1087
|
+
version=version,
|
|
1088
|
+
config_api_root=config_api_root,
|
|
1089
|
+
bearer_token=access_token,
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
# Check for existing workspace-level configuration
|
|
1093
|
+
existing_config = _get_scoped_configuration_context(
|
|
1094
|
+
actor_definition_id=actor_definition_id,
|
|
1095
|
+
scope_type=_ScopeType.WORKSPACE,
|
|
1096
|
+
scope_id=workspace_id,
|
|
1097
|
+
config_api_root=config_api_root,
|
|
1098
|
+
access_token=access_token,
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
if existing_config:
|
|
1102
|
+
existing_version_id = existing_config.get("value")
|
|
1103
|
+
existing_version_name = existing_config.get("value_name", "unknown")
|
|
1104
|
+
|
|
1105
|
+
# If already pinned to the same version, no action needed
|
|
1106
|
+
if existing_version_id == version_id:
|
|
1107
|
+
raise PyAirbyteInputError(
|
|
1108
|
+
message=f"Workspace is already pinned to version {existing_version_name} for {connector_name}. "
|
|
1109
|
+
f"Use unset=True first if you want to re-pin to a different version.",
|
|
1110
|
+
context={
|
|
1111
|
+
"workspace_id": workspace_id,
|
|
1112
|
+
"connector_name": connector_name,
|
|
1113
|
+
"existing_version": existing_version_name,
|
|
1114
|
+
"requested_version": version,
|
|
1115
|
+
},
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
# Verify this is a workspace-scoped config before deleting
|
|
1119
|
+
api_scope_type = existing_config.get("scope_type", "").lower()
|
|
1120
|
+
api_scope_id = existing_config.get("scope_id", "")
|
|
1121
|
+
if (
|
|
1122
|
+
api_scope_type == _ScopeType.WORKSPACE.value
|
|
1123
|
+
and api_scope_id == workspace_id
|
|
1124
|
+
):
|
|
1125
|
+
# Delete existing workspace-level config before creating new one
|
|
1126
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
1127
|
+
delete_payload = {"scopedConfigurationId": existing_config["id"]}
|
|
1128
|
+
|
|
1129
|
+
delete_response = requests.post(
|
|
1130
|
+
delete_endpoint,
|
|
1131
|
+
json=delete_payload,
|
|
1132
|
+
headers={
|
|
1133
|
+
"Authorization": f"Bearer {access_token}",
|
|
1134
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1135
|
+
"Content-Type": "application/json",
|
|
1136
|
+
},
|
|
1137
|
+
timeout=30,
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
if delete_response.status_code not in (200, 204):
|
|
1141
|
+
raise PyAirbyteInputError(
|
|
1142
|
+
message=f"Failed to delete existing workspace version override: "
|
|
1143
|
+
f"{delete_response.status_code} {delete_response.text}",
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
# Get user ID from email
|
|
1147
|
+
if not user_email:
|
|
1148
|
+
raise PyAirbyteInputError(
|
|
1149
|
+
message="user_email is required to set a version override",
|
|
1150
|
+
)
|
|
1151
|
+
origin = get_user_id_by_email(
|
|
1152
|
+
email=user_email,
|
|
1153
|
+
config_api_root=config_api_root,
|
|
1154
|
+
bearer_token=access_token,
|
|
1155
|
+
)
|
|
1156
|
+
|
|
1157
|
+
# Create the override
|
|
1158
|
+
endpoint = f"{config_api_root}/scoped_configuration/create"
|
|
1159
|
+
payload: dict[str, Any] = {
|
|
1160
|
+
"config_key": _ScopedConfigKey.CONNECTOR_VERSION.value,
|
|
1161
|
+
"resource_type": _ResourceType.ACTOR_DEFINITION.value,
|
|
1162
|
+
"resource_id": actor_definition_id,
|
|
1163
|
+
"scope_type": _ScopeType.WORKSPACE.value,
|
|
1164
|
+
"scope_id": workspace_id,
|
|
1165
|
+
"value": version_id,
|
|
1166
|
+
"description": override_reason,
|
|
1167
|
+
"origin_type": _OriginType.USER.value,
|
|
1168
|
+
"origin": origin,
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if override_reason_reference_url:
|
|
1172
|
+
payload["reference_url"] = override_reason_reference_url
|
|
1173
|
+
|
|
1174
|
+
response = requests.post(
|
|
1175
|
+
endpoint,
|
|
1176
|
+
json=payload,
|
|
1177
|
+
headers={
|
|
1178
|
+
"Authorization": f"Bearer {access_token}",
|
|
1179
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1180
|
+
"Content-Type": "application/json",
|
|
1181
|
+
},
|
|
1182
|
+
timeout=30,
|
|
1183
|
+
)
|
|
1184
|
+
|
|
1185
|
+
if response.status_code not in (200, 201):
|
|
1186
|
+
raise PyAirbyteInputError(
|
|
1187
|
+
message=f"Failed to set workspace version override: {response.status_code} {response.text}",
|
|
1188
|
+
context={
|
|
1189
|
+
"workspace_id": workspace_id,
|
|
1190
|
+
"connector_name": connector_name,
|
|
1191
|
+
"version": version,
|
|
1192
|
+
"endpoint": endpoint,
|
|
1193
|
+
"status_code": response.status_code,
|
|
1194
|
+
"response": response.text,
|
|
1195
|
+
},
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
return True
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
def set_organization_connector_version_override(
|
|
1202
|
+
organization_id: str,
|
|
1203
|
+
connector_name: str,
|
|
1204
|
+
connector_type: Literal["source", "destination"],
|
|
1205
|
+
config_api_root: str,
|
|
1206
|
+
client_id: str | None = None,
|
|
1207
|
+
client_secret: str | None = None,
|
|
1208
|
+
version: str | None = None,
|
|
1209
|
+
unset: bool = False,
|
|
1210
|
+
override_reason: str | None = None,
|
|
1211
|
+
override_reason_reference_url: str | None = None,
|
|
1212
|
+
user_email: str | None = None,
|
|
1213
|
+
bearer_token: str | None = None,
|
|
1214
|
+
) -> bool:
|
|
1215
|
+
"""Set or clear an organization-level version override for a connector type.
|
|
1216
|
+
|
|
1217
|
+
This pins ALL instances of a connector type across an entire organization to a
|
|
1218
|
+
specific version. For example, pinning 'source-github' at organization level means
|
|
1219
|
+
all GitHub sources in all workspaces within that organization will use the pinned version.
|
|
1220
|
+
|
|
1221
|
+
Args:
|
|
1222
|
+
organization_id: The organization ID
|
|
1223
|
+
connector_name: The connector name (e.g., 'source-github')
|
|
1224
|
+
connector_type: Either "source" or "destination"
|
|
1225
|
+
config_api_root: The Config API root URL (e.g., CLOUD_CONFIG_API_ROOT)
|
|
1226
|
+
client_id: The Airbyte Cloud client ID (required if no bearer_token)
|
|
1227
|
+
client_secret: The Airbyte Cloud client secret (required if no bearer_token)
|
|
1228
|
+
version: The version to pin to (e.g., "0.1.0"), or None to unset
|
|
1229
|
+
unset: If True, removes any existing override
|
|
1230
|
+
override_reason: Required when setting. Explanation for the override
|
|
1231
|
+
override_reason_reference_url: Optional URL with more context
|
|
1232
|
+
user_email: Email of user creating the override
|
|
1233
|
+
bearer_token: Pre-existing bearer token (takes precedence over client credentials)
|
|
1234
|
+
|
|
1235
|
+
Returns:
|
|
1236
|
+
True if operation succeeded, False if no override existed (unset only)
|
|
1237
|
+
|
|
1238
|
+
Raises:
|
|
1239
|
+
PyAirbyteInputError: If the API request fails or parameters are invalid
|
|
1240
|
+
"""
|
|
1241
|
+
# Input validation
|
|
1242
|
+
if (version is None) == (not unset):
|
|
1243
|
+
raise PyAirbyteInputError(
|
|
1244
|
+
message="Must specify EXACTLY ONE of version (to set) OR unset=True (to clear), but not both",
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
if not unset and (not override_reason or len(override_reason.strip()) < 10):
|
|
1248
|
+
raise PyAirbyteInputError(
|
|
1249
|
+
message="override_reason is required when setting a version and must be at least 10 characters",
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
access_token = _get_access_token(client_id, client_secret, bearer_token)
|
|
1253
|
+
|
|
1254
|
+
# Resolve connector name to actor_definition_id using the shared registry lookup
|
|
1255
|
+
actor_definition_id = _resolve_canonical_name_to_definition_id(connector_name)
|
|
1256
|
+
|
|
1257
|
+
if unset:
|
|
1258
|
+
# Get the existing organization-level configuration
|
|
1259
|
+
active_config = _get_scoped_configuration_context(
|
|
1260
|
+
actor_definition_id=actor_definition_id,
|
|
1261
|
+
scope_type=_ScopeType.ORGANIZATION,
|
|
1262
|
+
scope_id=organization_id,
|
|
1263
|
+
config_api_root=config_api_root,
|
|
1264
|
+
access_token=access_token,
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
if not active_config:
|
|
1268
|
+
return False
|
|
1269
|
+
|
|
1270
|
+
# Verify this is actually an organization-scoped config
|
|
1271
|
+
api_scope_type = active_config.get("scope_type", "").lower()
|
|
1272
|
+
api_scope_id = active_config.get("scope_id", "")
|
|
1273
|
+
if (
|
|
1274
|
+
api_scope_type != _ScopeType.ORGANIZATION.value
|
|
1275
|
+
or api_scope_id != organization_id
|
|
1276
|
+
):
|
|
1277
|
+
raise PyAirbyteInputError(
|
|
1278
|
+
message=f"Cannot delete: the active config is not organization-scoped. "
|
|
1279
|
+
f"Expected scope_type='{_ScopeType.ORGANIZATION.value}' and scope_id='{organization_id}', "
|
|
1280
|
+
f"but got scope_type='{api_scope_type}' and scope_id='{api_scope_id}'.",
|
|
1281
|
+
context={
|
|
1282
|
+
"organization_id": organization_id,
|
|
1283
|
+
"expected_scope_type": _ScopeType.ORGANIZATION.value,
|
|
1284
|
+
"actual_scope_type": api_scope_type,
|
|
1285
|
+
"actual_scope_id": api_scope_id,
|
|
1286
|
+
"full_config": active_config,
|
|
1287
|
+
},
|
|
1288
|
+
)
|
|
1289
|
+
|
|
1290
|
+
# Delete the configuration
|
|
1291
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
1292
|
+
delete_payload = {"scopedConfigurationId": active_config["id"]}
|
|
1293
|
+
|
|
1294
|
+
response = requests.post(
|
|
1295
|
+
delete_endpoint,
|
|
1296
|
+
json=delete_payload,
|
|
1297
|
+
headers={
|
|
1298
|
+
"Authorization": f"Bearer {access_token}",
|
|
1299
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1300
|
+
"Content-Type": "application/json",
|
|
1301
|
+
},
|
|
1302
|
+
timeout=30,
|
|
1303
|
+
)
|
|
1304
|
+
|
|
1305
|
+
if response.status_code not in (200, 204):
|
|
1306
|
+
raise PyAirbyteInputError(
|
|
1307
|
+
message=f"Failed to delete organization version override: {response.status_code} {response.text}",
|
|
1308
|
+
context={
|
|
1309
|
+
"delete_endpoint": delete_endpoint,
|
|
1310
|
+
"config_id": active_config["id"],
|
|
1311
|
+
"status_code": response.status_code,
|
|
1312
|
+
"response": response.text,
|
|
1313
|
+
},
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
return True
|
|
1317
|
+
|
|
1318
|
+
# Set a new organization-level override
|
|
1319
|
+
# Resolve version string to version ID
|
|
1320
|
+
version_id = resolve_connector_version_id(
|
|
1321
|
+
actor_definition_id=actor_definition_id,
|
|
1322
|
+
connector_type=connector_type,
|
|
1323
|
+
version=version,
|
|
1324
|
+
config_api_root=config_api_root,
|
|
1325
|
+
bearer_token=access_token,
|
|
1326
|
+
)
|
|
1327
|
+
|
|
1328
|
+
# Check for existing organization-level configuration
|
|
1329
|
+
existing_config = _get_scoped_configuration_context(
|
|
1330
|
+
actor_definition_id=actor_definition_id,
|
|
1331
|
+
scope_type=_ScopeType.ORGANIZATION,
|
|
1332
|
+
scope_id=organization_id,
|
|
1333
|
+
config_api_root=config_api_root,
|
|
1334
|
+
access_token=access_token,
|
|
1335
|
+
)
|
|
1336
|
+
|
|
1337
|
+
if existing_config:
|
|
1338
|
+
existing_version_id = existing_config.get("value")
|
|
1339
|
+
existing_version_name = existing_config.get("value_name", "unknown")
|
|
1340
|
+
|
|
1341
|
+
# If already pinned to the same version, no action needed
|
|
1342
|
+
if existing_version_id == version_id:
|
|
1343
|
+
raise PyAirbyteInputError(
|
|
1344
|
+
message=f"Organization is already pinned to version {existing_version_name} for {connector_name}. "
|
|
1345
|
+
f"Use unset=True first if you want to re-pin to a different version.",
|
|
1346
|
+
context={
|
|
1347
|
+
"organization_id": organization_id,
|
|
1348
|
+
"connector_name": connector_name,
|
|
1349
|
+
"existing_version": existing_version_name,
|
|
1350
|
+
"requested_version": version,
|
|
1351
|
+
},
|
|
1352
|
+
)
|
|
1353
|
+
|
|
1354
|
+
# Verify this is an organization-scoped config before deleting
|
|
1355
|
+
api_scope_type = existing_config.get("scope_type", "").lower()
|
|
1356
|
+
api_scope_id = existing_config.get("scope_id", "")
|
|
1357
|
+
if (
|
|
1358
|
+
api_scope_type == _ScopeType.ORGANIZATION.value
|
|
1359
|
+
and api_scope_id == organization_id
|
|
1360
|
+
):
|
|
1361
|
+
# Delete existing organization-level config before creating new one
|
|
1362
|
+
delete_endpoint = f"{config_api_root}/scoped_configuration/delete"
|
|
1363
|
+
delete_payload = {"scopedConfigurationId": existing_config["id"]}
|
|
1364
|
+
|
|
1365
|
+
delete_response = requests.post(
|
|
1366
|
+
delete_endpoint,
|
|
1367
|
+
json=delete_payload,
|
|
1368
|
+
headers={
|
|
1369
|
+
"Authorization": f"Bearer {access_token}",
|
|
1370
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1371
|
+
"Content-Type": "application/json",
|
|
1372
|
+
},
|
|
1373
|
+
timeout=30,
|
|
1374
|
+
)
|
|
1375
|
+
|
|
1376
|
+
if delete_response.status_code not in (200, 204):
|
|
1377
|
+
raise PyAirbyteInputError(
|
|
1378
|
+
message=f"Failed to delete existing organization version override: "
|
|
1379
|
+
f"{delete_response.status_code} {delete_response.text}",
|
|
1380
|
+
)
|
|
1381
|
+
|
|
1382
|
+
# Get user ID from email
|
|
1383
|
+
if not user_email:
|
|
1384
|
+
raise PyAirbyteInputError(
|
|
1385
|
+
message="user_email is required to set a version override",
|
|
1386
|
+
)
|
|
1387
|
+
origin = get_user_id_by_email(
|
|
1388
|
+
email=user_email,
|
|
1389
|
+
config_api_root=config_api_root,
|
|
1390
|
+
bearer_token=access_token,
|
|
1391
|
+
)
|
|
1392
|
+
|
|
1393
|
+
# Create the override
|
|
1394
|
+
endpoint = f"{config_api_root}/scoped_configuration/create"
|
|
1395
|
+
payload: dict[str, Any] = {
|
|
1396
|
+
"config_key": _ScopedConfigKey.CONNECTOR_VERSION.value,
|
|
1397
|
+
"resource_type": _ResourceType.ACTOR_DEFINITION.value,
|
|
1398
|
+
"resource_id": actor_definition_id,
|
|
1399
|
+
"scope_type": _ScopeType.ORGANIZATION.value,
|
|
1400
|
+
"scope_id": organization_id,
|
|
1401
|
+
"value": version_id,
|
|
1402
|
+
"description": override_reason,
|
|
1403
|
+
"origin_type": _OriginType.USER.value,
|
|
1404
|
+
"origin": origin,
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if override_reason_reference_url:
|
|
1408
|
+
payload["reference_url"] = override_reason_reference_url
|
|
1409
|
+
|
|
1410
|
+
response = requests.post(
|
|
1411
|
+
endpoint,
|
|
1412
|
+
json=payload,
|
|
1413
|
+
headers={
|
|
1414
|
+
"Authorization": f"Bearer {access_token}",
|
|
1415
|
+
"User-Agent": ops_constants.USER_AGENT,
|
|
1416
|
+
"Content-Type": "application/json",
|
|
1417
|
+
},
|
|
1418
|
+
timeout=30,
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
if response.status_code not in (200, 201):
|
|
1422
|
+
raise PyAirbyteInputError(
|
|
1423
|
+
message=f"Failed to set organization version override: {response.status_code} {response.text}",
|
|
1424
|
+
context={
|
|
1425
|
+
"organization_id": organization_id,
|
|
1426
|
+
"connector_name": connector_name,
|
|
1427
|
+
"version": version,
|
|
1428
|
+
"endpoint": endpoint,
|
|
1429
|
+
"status_code": response.status_code,
|
|
1430
|
+
"response": response.text,
|
|
1431
|
+
},
|
|
1432
|
+
)
|
|
1433
|
+
|
|
1434
|
+
return True
|