Skip to content

Commit d4ffa27

Browse files
author
Oleksandr Bazarnov
committed
add migrations to the manifest
1 parent 5c32297 commit d4ffa27

File tree

13 files changed

+1694
-37
lines changed

13 files changed

+1694
-37
lines changed

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,19 +1858,32 @@ definitions:
18581858
type:
18591859
enum: [Bearer]
18601860
HttpRequester:
1861+
migrations:
1862+
- type: replace_field
1863+
description: The `url_base` has been deprecated, in favor of the `url` field.
1864+
original_key: url_base
1865+
replacement_key: url
1866+
- type: handle_url_parts
1867+
description: The `path` has been deprecated, in favor of the `url` field. The value from the `path` field will be joined to the `url` field.
1868+
original_key: path
1869+
replacement_key: url
1870+
- type: remove_field
1871+
description: The `path` has been deprecated, in favor of the `url` field.
1872+
original_key: path
18611873
title: HTTP Requester
18621874
description: Requester submitting HTTP requests and extracting records from the response.
18631875
type: object
18641876
required:
18651877
- type
1866-
- url_base
18671878
properties:
18681879
type:
18691880
type: string
18701881
enum: [HttpRequester]
18711882
url_base:
1883+
deprecated: true
1884+
sharable: true
18721885
title: API Base URL
1873-
description: Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
1886+
description: Deprecated, use the `url` instead. Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
18741887
type: string
18751888
interpolation_context:
18761889
- config
@@ -1886,9 +1899,29 @@ definitions:
18861899
- "{{ config['base_url'] or 'https://app.posthog.com'}}/api"
18871900
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
18881901
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
1902+
url:
1903+
sharable: true
1904+
title: API URL
1905+
description: The URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
1906+
type: string
1907+
interpolation_context:
1908+
- config
1909+
- next_page_token
1910+
- stream_interval
1911+
- stream_partition
1912+
- stream_slice
1913+
- creation_response
1914+
- polling_response
1915+
- download_target
1916+
examples:
1917+
- "https://connect.squareup.com/v2"
1918+
- "{{ config['url'] or 'https://app.posthog.com'}}/api"
1919+
- "https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups"
1920+
- "https://example.com/api/v1/resource/{{ next_page_token['id'] }}"
18891921
path:
1922+
deprecated: true
18901923
title: URL Path
1891-
description: Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
1924+
description: Deprecated, use the `url` instead. Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.
18921925
type: string
18931926
interpolation_context:
18941927
- config

airbyte_cdk/sources/declarative/manifest_declarative_source.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
from airbyte_cdk.sources.declarative.parsers.manifest_component_transformer import (
4040
ManifestComponentTransformer,
4141
)
42+
from airbyte_cdk.sources.declarative.parsers.manifest_migration_handler import (
43+
ManifestMigrationHandler,
44+
)
4245
from airbyte_cdk.sources.declarative.parsers.manifest_reference_resolver import (
4346
ManifestReferenceResolver,
4447
)
@@ -57,6 +60,24 @@
5760
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
5861

5962

63+
def _get_declarative_component_schema() -> Dict[str, Any]:
64+
try:
65+
raw_component_schema = pkgutil.get_data(
66+
"airbyte_cdk", "sources/declarative/declarative_component_schema.yaml"
67+
)
68+
if raw_component_schema is not None:
69+
declarative_component_schema = yaml.load(raw_component_schema, Loader=yaml.SafeLoader)
70+
return declarative_component_schema # type: ignore
71+
else:
72+
raise RuntimeError(
73+
"Failed to read manifest component json schema required for deduplication"
74+
)
75+
except FileNotFoundError as e:
76+
raise FileNotFoundError(
77+
f"Failed to read manifest component json schema required for deduplication: {e}"
78+
)
79+
80+
6081
class ManifestDeclarativeSource(DeclarativeSource):
6182
"""Declarative source defined by a manifest of low-code components that define source connector behavior"""
6283

@@ -68,7 +89,7 @@ def __init__(
6889
debug: bool = False,
6990
emit_connector_builder_messages: bool = False,
7091
component_factory: Optional[ModelToComponentFactory] = None,
71-
):
92+
) -> None:
7293
"""
7394
Args:
7495
config: The provided config dict.
@@ -78,6 +99,8 @@ def __init__(
7899
component_factory: optional factory if ModelToComponentFactory's default behavior needs to be tweaked.
79100
"""
80101
self.logger = logging.getLogger(f"airbyte.{self.name}")
102+
103+
self._declarative_component_schema = _get_declarative_component_schema()
81104
# For ease of use we don't require the type to be specified at the top level manifest, but it should be included during processing
82105
manifest = dict(source_config)
83106
if "type" not in manifest:
@@ -90,7 +113,13 @@ def __init__(
90113
propagated_source_config = ManifestComponentTransformer().propagate_types_and_parameters(
91114
"", resolved_source_config, {}
92115
)
93-
self._source_config = propagated_source_config
116+
117+
# migrate definitions to the new format, if any are present
118+
migrated_source_config = ManifestMigrationHandler(
119+
propagated_source_config, self._declarative_component_schema
120+
).migrate()
121+
122+
self._source_config = migrated_source_config
94123
self._debug = debug
95124
self._emit_connector_builder_messages = emit_connector_builder_messages
96125
self._constructor = (

airbyte_cdk/sources/declarative/models/declarative_component_schema.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2-
31
# generated by datamodel-codegen:
42
# filename: declarative_component_schema.yaml
53

@@ -2149,9 +2147,10 @@ class SessionTokenAuthenticator(BaseModel):
21492147

21502148
class HttpRequester(BaseModel):
21512149
type: Literal["HttpRequester"]
2152-
url_base: str = Field(
2153-
...,
2154-
description="Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2150+
url_base: Optional[str] = Field(
2151+
None,
2152+
deprecated=True,
2153+
description="Deprecated, use the `url` instead. Base URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
21552154
examples=[
21562155
"https://connect.squareup.com/v2",
21572156
"{{ config['base_url'] or 'https://app.posthog.com'}}/api",
@@ -2160,9 +2159,21 @@ class HttpRequester(BaseModel):
21602159
],
21612160
title="API Base URL",
21622161
)
2162+
url: Optional[str] = Field(
2163+
None,
2164+
description="The URL of the API source. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2165+
examples=[
2166+
"https://connect.squareup.com/v2",
2167+
"{{ config['url'] or 'https://app.posthog.com'}}/api",
2168+
"https://connect.squareup.com/v2/quotes/{{ stream_partition['id'] }}/quote_line_groups",
2169+
"https://example.com/api/v1/resource/{{ next_page_token['id'] }}",
2170+
],
2171+
title="API URL",
2172+
)
21632173
path: Optional[str] = Field(
21642174
None,
2165-
description="Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
2175+
deprecated=True,
2176+
description="Deprecated, use the `url` instead. Path the specific API endpoint that this stream represents. Do not put sensitive information (e.g. API tokens) into this field - Use the Authentication component for this.",
21662177
examples=[
21672178
"/products",
21682179
"/quotes/{{ stream_partition['id'] }}/quote_line_groups",

airbyte_cdk/sources/declarative/parsers/custom_exceptions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,21 @@ class UndefinedReferenceException(Exception):
1919

2020
def __init__(self, path: str, reference: str) -> None:
2121
super().__init__(f"Undefined reference {reference} from {path}")
22+
23+
24+
class ManifestNormalizationException(Exception):
25+
"""
26+
Raised when a circular reference is detected in a manifest.
27+
"""
28+
29+
def __init__(self, message: str) -> None:
30+
super().__init__(f"Failed to deduplicate manifest: {message}")
31+
32+
33+
class ManifestMigrationException(Exception):
34+
"""
35+
Raised when a migration error occurs in the manifest.
36+
"""
37+
38+
def __init__(self, message: str) -> None:
39+
super().__init__(f"Failed to migrate the manifest: {message}")

airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import copy
66
import typing
7-
from typing import Any, Mapping, Optional
7+
from typing import Any, Dict, Mapping, Optional
88

99
PARAMETERS_STR = "$parameters"
1010

@@ -95,7 +95,7 @@ def propagate_types_and_parameters(
9595
declarative_component: Mapping[str, Any],
9696
parent_parameters: Mapping[str, Any],
9797
use_parent_parameters: Optional[bool] = None,
98-
) -> Mapping[str, Any]:
98+
) -> Dict[str, Any]:
9999
"""
100100
Recursively transforms the specified declarative component and subcomponents to propagate parameters and insert the
101101
default component type if it was not already present. The resulting transformed components are a deep copy of the input

0 commit comments

Comments
 (0)