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.
Files changed (130) hide show
  1. {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/METADATA +2 -1
  2. {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/RECORD +21 -129
  3. airbyte_ops_mcp/cli/cloud.py +31 -2
  4. airbyte_ops_mcp/cloud_admin/api_client.py +506 -33
  5. airbyte_ops_mcp/cloud_admin/models.py +56 -0
  6. airbyte_ops_mcp/constants.py +58 -0
  7. airbyte_ops_mcp/{_legacy/airbyte_ci/metadata_service/docker_hub.py → docker_hub.py} +16 -10
  8. airbyte_ops_mcp/mcp/cloud_connector_versions.py +491 -10
  9. airbyte_ops_mcp/mcp/prerelease.py +5 -44
  10. airbyte_ops_mcp/mcp/prod_db_queries.py +128 -4
  11. airbyte_ops_mcp/mcp/regression_tests.py +10 -5
  12. airbyte_ops_mcp/{_legacy/airbyte_ci/metadata_service/validators/metadata_validator.py → metadata_validator.py} +18 -12
  13. airbyte_ops_mcp/prod_db_access/queries.py +51 -0
  14. airbyte_ops_mcp/prod_db_access/sql.py +76 -0
  15. airbyte_ops_mcp/regression_tests/ci_output.py +8 -4
  16. airbyte_ops_mcp/regression_tests/connection_fetcher.py +16 -5
  17. airbyte_ops_mcp/regression_tests/http_metrics.py +21 -2
  18. airbyte_ops_mcp/regression_tests/models.py +7 -1
  19. airbyte_ops_mcp/telemetry.py +162 -0
  20. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/.gitignore +0 -1
  21. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/README.md +0 -420
  22. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/__init__.py +0 -2
  23. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/__init__.py +0 -1
  24. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/__init__.py +0 -8
  25. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/base_backend.py +0 -16
  26. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/duckdb_backend.py +0 -87
  27. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/backends/file_backend.py +0 -165
  28. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/connection_objects_retrieval.py +0 -377
  29. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/connector_runner.py +0 -247
  30. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/errors.py +0 -7
  31. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/evaluation_modes.py +0 -25
  32. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/hacks.py +0 -23
  33. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/json_schema_helper.py +0 -384
  34. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/mitm_addons.py +0 -37
  35. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/models.py +0 -595
  36. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/proxy.py +0 -207
  37. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/secret_access.py +0 -47
  38. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/segment_tracking.py +0 -45
  39. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/commons/utils.py +0 -214
  40. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/conftest.py.disabled +0 -751
  41. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/consts.py +0 -4
  42. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/poetry.lock +0 -4480
  43. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/pytest.ini +0 -9
  44. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/__init__.py +0 -1
  45. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_check.py +0 -61
  46. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_discover.py +0 -117
  47. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_read.py +0 -627
  48. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/regression_tests/test_spec.py +0 -43
  49. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/report.py +0 -542
  50. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/stash_keys.py +0 -38
  51. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/__init__.py +0 -0
  52. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/private_details.html.j2 +0 -305
  53. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/templates/report.html.j2 +0 -515
  54. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/utils.py +0 -187
  55. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/__init__.py +0 -0
  56. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_check.py +0 -61
  57. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_discover.py +0 -217
  58. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_read.py +0 -177
  59. airbyte_ops_mcp/_legacy/airbyte_ci/connector_live_tests/validation_tests/test_spec.py +0 -631
  60. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/README.md +0 -91
  61. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/bin/bundle-schemas.js +0 -48
  62. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/bin/generate-metadata-models.sh +0 -36
  63. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ActorDefinitionResourceRequirements.py +0 -54
  64. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/AirbyteInternal.py +0 -22
  65. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/AllowedHosts.py +0 -18
  66. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorBreakingChanges.py +0 -65
  67. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorBuildOptions.py +0 -15
  68. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorIPCOptions.py +0 -25
  69. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetadataDefinitionV0.json +0 -897
  70. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetadataDefinitionV0.py +0 -478
  71. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorMetrics.py +0 -24
  72. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorPackageInfo.py +0 -12
  73. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryDestinationDefinition.py +0 -407
  74. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryReleases.py +0 -406
  75. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistrySourceDefinition.py +0 -407
  76. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorRegistryV0.py +0 -413
  77. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorReleases.py +0 -98
  78. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ConnectorTestSuiteOptions.py +0 -58
  79. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/GeneratedFields.py +0 -62
  80. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/GitInfo.py +0 -31
  81. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/JobType.py +0 -23
  82. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/NormalizationDestinationDefinitionConfig.py +0 -24
  83. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RegistryOverrides.py +0 -111
  84. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ReleaseStage.py +0 -15
  85. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RemoteRegistries.py +0 -23
  86. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/ResourceRequirements.py +0 -18
  87. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/RolloutConfiguration.py +0 -29
  88. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/Secret.py +0 -34
  89. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SecretStore.py +0 -22
  90. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SourceFileInfo.py +0 -16
  91. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SuggestedStreams.py +0 -18
  92. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/SupportLevel.py +0 -15
  93. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/TestConnections.py +0 -14
  94. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/__init__.py +0 -31
  95. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/generated/airbyte-connector-metadata-schema.json +0 -0
  96. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ActorDefinitionResourceRequirements.yaml +0 -30
  97. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/AirbyteInternal.yaml +0 -32
  98. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/AllowedHosts.yaml +0 -13
  99. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorBreakingChanges.yaml +0 -65
  100. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorBuildOptions.yaml +0 -10
  101. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorIPCOptions.yaml +0 -29
  102. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorMetadataDefinitionV0.yaml +0 -172
  103. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorMetrics.yaml +0 -30
  104. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorPackageInfo.yaml +0 -9
  105. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryDestinationDefinition.yaml +0 -90
  106. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryReleases.yaml +0 -35
  107. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistrySourceDefinition.yaml +0 -92
  108. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorRegistryV0.yaml +0 -18
  109. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorReleases.yaml +0 -16
  110. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ConnectorTestSuiteOptions.yaml +0 -28
  111. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/GeneratedFields.yaml +0 -16
  112. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/GitInfo.yaml +0 -21
  113. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/JobType.yaml +0 -14
  114. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/NormalizationDestinationDefinitionConfig.yaml +0 -21
  115. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RegistryOverrides.yaml +0 -38
  116. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ReleaseStage.yaml +0 -11
  117. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RemoteRegistries.yaml +0 -25
  118. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/ResourceRequirements.yaml +0 -16
  119. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/RolloutConfiguration.yaml +0 -29
  120. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/Secret.yaml +0 -19
  121. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SecretStore.yaml +0 -16
  122. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SourceFileInfo.yaml +0 -17
  123. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SuggestedStreams.yaml +0 -13
  124. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/SupportLevel.yaml +0 -10
  125. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/models/TestConnections.yaml +0 -17
  126. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/package-lock.json +0 -62
  127. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/package.json +0 -12
  128. airbyte_ops_mcp/_legacy/airbyte_ci/metadata_models/transform.py +0 -71
  129. {airbyte_internal_ops-0.4.2.dist-info → airbyte_internal_ops-0.5.1.dist-info}/WHEEL +0 -0
  130. {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
- api_root: str,
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
- api_root: The API root URL
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"{api_root}/users/list_instance_admin"
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
- api_root: str,
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
- api_root: The API root URL
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"{api_root}/actor_definition_versions/resolve"
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
- api_root: str,
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
- api_root: The API root URL
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"{api_root}/scoped_configuration/get_context"
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
- api_root: str,
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
- api_root: The API root URL
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
- api_root=api_root,
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
- api_root: str,
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
- api_root: The API root URL
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
- # api_root already includes /v1
430
+ # config_api_root already includes /v1
428
431
  if connector_type == "source":
429
- endpoint = f"{api_root}/actor_definition_versions/get_for_source"
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"{api_root}/actor_definition_versions/get_for_destination"
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
- "api_root": api_root,
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"{api_root}/{connector_type}s/get"
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"{api_root}/workspaces/get"
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
- api_root=api_root,
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
- api_root: str,
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
- api_root: The API root URL
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"{api_root}/{connector_type}s/get"
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
- api_root=api_root,
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"{api_root}/scoped_configuration/delete"
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"{api_root}/{connector_type}s/get"
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
- api_root=api_root,
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"{api_root}/workspaces/get"
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
- api_root=api_root,
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"{api_root}/scoped_configuration/delete"
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
- api_root=api_root,
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"{api_root}/scoped_configuration/create"
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