airbyte-cdk 6.61.2__py3-none-any.whl → 6.61.3.post2.dev17299502224__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 (32) hide show
  1. airbyte_cdk/manifest_server/Dockerfile +45 -0
  2. airbyte_cdk/manifest_server/README.md +142 -0
  3. airbyte_cdk/manifest_server/__init__.py +3 -0
  4. airbyte_cdk/manifest_server/api_models/__init__.py +49 -0
  5. airbyte_cdk/manifest_server/api_models/capabilities.py +7 -0
  6. airbyte_cdk/manifest_server/api_models/dicts.py +17 -0
  7. airbyte_cdk/manifest_server/api_models/manifest.py +73 -0
  8. airbyte_cdk/manifest_server/api_models/stream.py +76 -0
  9. airbyte_cdk/manifest_server/app.py +17 -0
  10. airbyte_cdk/manifest_server/auth.py +43 -0
  11. airbyte_cdk/manifest_server/cli/__init__.py +5 -0
  12. airbyte_cdk/manifest_server/cli/_common.py +28 -0
  13. airbyte_cdk/manifest_server/cli/_info.py +30 -0
  14. airbyte_cdk/manifest_server/cli/_openapi.py +43 -0
  15. airbyte_cdk/manifest_server/cli/_start.py +38 -0
  16. airbyte_cdk/manifest_server/cli/run.py +59 -0
  17. airbyte_cdk/manifest_server/command_processor/__init__.py +0 -0
  18. airbyte_cdk/manifest_server/command_processor/processor.py +122 -0
  19. airbyte_cdk/manifest_server/command_processor/utils.py +99 -0
  20. airbyte_cdk/manifest_server/main.py +24 -0
  21. airbyte_cdk/manifest_server/openapi.yaml +641 -0
  22. airbyte_cdk/manifest_server/routers/__init__.py +0 -0
  23. airbyte_cdk/manifest_server/routers/capabilities.py +25 -0
  24. airbyte_cdk/manifest_server/routers/health.py +13 -0
  25. airbyte_cdk/manifest_server/routers/manifest.py +155 -0
  26. airbyte_cdk/sources/file_based/file_types/excel_parser.py +3 -3
  27. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/METADATA +4 -1
  28. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/RECORD +32 -7
  29. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/entry_points.txt +1 -0
  30. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/LICENSE.txt +0 -0
  31. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/LICENSE_SHORT +0 -0
  32. {airbyte_cdk-6.61.2.dist-info → airbyte_cdk-6.61.3.post2.dev17299502224.dist-info}/WHEEL +0 -0
@@ -0,0 +1,122 @@
1
+ import logging
2
+ from typing import Any, List, Mapping, Optional, Tuple
3
+
4
+ from airbyte_protocol_dataclasses.models import (
5
+ AirbyteCatalog,
6
+ Status,
7
+ )
8
+ from fastapi import HTTPException
9
+
10
+ from airbyte_cdk.connector_builder.models import StreamRead
11
+ from airbyte_cdk.connector_builder.test_reader import TestReader
12
+ from airbyte_cdk.entrypoint import AirbyteEntrypoint
13
+ from airbyte_cdk.models import (
14
+ AirbyteStateMessage,
15
+ ConfiguredAirbyteCatalog,
16
+ )
17
+ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
18
+ ConcurrentDeclarativeSource,
19
+ )
20
+ from airbyte_cdk.test.entrypoint_wrapper import AirbyteEntrypointException, EntrypointOutput
21
+
22
+
23
+ class ManifestCommandProcessor:
24
+ _source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]]
25
+ _logger = logging.getLogger("airbyte.manifest-server")
26
+
27
+ def __init__(
28
+ self, source: ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]]
29
+ ) -> None:
30
+ self._source = source
31
+
32
+ def test_read(
33
+ self,
34
+ config: Mapping[str, Any],
35
+ catalog: ConfiguredAirbyteCatalog,
36
+ state: List[AirbyteStateMessage],
37
+ record_limit: int,
38
+ page_limit: int,
39
+ slice_limit: int,
40
+ ) -> StreamRead:
41
+ """
42
+ Test the read method of the source.
43
+ """
44
+
45
+ test_read_handler = TestReader(
46
+ max_pages_per_slice=page_limit,
47
+ max_slices=slice_limit,
48
+ max_record_limit=record_limit,
49
+ )
50
+
51
+ stream_read = test_read_handler.run_test_read(
52
+ source=self._source,
53
+ config=config,
54
+ configured_catalog=catalog,
55
+ state=state,
56
+ stream_name=catalog.streams[0].stream.name,
57
+ record_limit=record_limit,
58
+ )
59
+
60
+ return stream_read
61
+
62
+ def check_connection(
63
+ self,
64
+ config: Mapping[str, Any],
65
+ ) -> Tuple[bool, Optional[str]]:
66
+ """
67
+ Check the connection to the source.
68
+ """
69
+
70
+ spec = self._source.spec(self._logger)
71
+ entrypoint = AirbyteEntrypoint(source=self._source)
72
+ messages = entrypoint.check(spec, config)
73
+ output = EntrypointOutput(
74
+ messages=[AirbyteEntrypoint.airbyte_message_to_string(m) for m in messages],
75
+ command=["check"],
76
+ )
77
+ self._raise_on_trace_message(output)
78
+
79
+ status_messages = output.connection_status_messages
80
+ if not status_messages or status_messages[-1].connectionStatus is None:
81
+ return False, "Connection check did not return a status message"
82
+
83
+ connection_status = status_messages[-1].connectionStatus
84
+ return (
85
+ connection_status.status == Status.SUCCEEDED,
86
+ connection_status.message,
87
+ )
88
+
89
+ def discover(
90
+ self,
91
+ config: Mapping[str, Any],
92
+ ) -> Optional[AirbyteCatalog]:
93
+ """
94
+ Discover the catalog from the source.
95
+ """
96
+ spec = self._source.spec(self._logger)
97
+ entrypoint = AirbyteEntrypoint(source=self._source)
98
+ messages = entrypoint.discover(spec, config)
99
+ output = EntrypointOutput(
100
+ messages=[AirbyteEntrypoint.airbyte_message_to_string(m) for m in messages],
101
+ command=["discover"],
102
+ )
103
+ self._raise_on_trace_message(output)
104
+
105
+ try:
106
+ catalog_message = output.catalog
107
+ return catalog_message.catalog
108
+ except ValueError:
109
+ # No catalog message found
110
+ return None
111
+
112
+ def _raise_on_trace_message(
113
+ self,
114
+ output: EntrypointOutput,
115
+ ) -> None:
116
+ """
117
+ Raise an exception if a trace message is found.
118
+ """
119
+ try:
120
+ output.raise_if_errors()
121
+ except AirbyteEntrypointException as e:
122
+ raise HTTPException(status_code=422, detail=e.message)
@@ -0,0 +1,99 @@
1
+ import copy
2
+ from typing import Any, Dict, List, Mapping, Optional
3
+
4
+ from airbyte_protocol_dataclasses.models import AirbyteStateMessage
5
+
6
+ from airbyte_cdk.models import (
7
+ AirbyteStream,
8
+ ConfiguredAirbyteCatalog,
9
+ ConfiguredAirbyteStream,
10
+ DestinationSyncMode,
11
+ SyncMode,
12
+ )
13
+ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
14
+ ConcurrentDeclarativeSource,
15
+ TestLimits,
16
+ )
17
+
18
+ SHOULD_NORMALIZE_KEY = "__should_normalize"
19
+ SHOULD_MIGRATE_KEY = "__should_migrate"
20
+
21
+
22
+ def build_catalog(stream_name: str) -> ConfiguredAirbyteCatalog:
23
+ return ConfiguredAirbyteCatalog(
24
+ streams=[
25
+ ConfiguredAirbyteStream(
26
+ stream=AirbyteStream(
27
+ name=stream_name,
28
+ json_schema={},
29
+ supported_sync_modes=[SyncMode.full_refresh, SyncMode.incremental],
30
+ ),
31
+ sync_mode=SyncMode.incremental,
32
+ destination_sync_mode=DestinationSyncMode.overwrite,
33
+ )
34
+ ]
35
+ )
36
+
37
+
38
+ def should_migrate_manifest(manifest: Mapping[str, Any]) -> bool:
39
+ """
40
+ Determines whether the manifest should be migrated,
41
+ based on the presence of the "__should_migrate" key.
42
+
43
+ This flag is set by the UI.
44
+ """
45
+ return manifest.get(SHOULD_MIGRATE_KEY, False)
46
+
47
+
48
+ def should_normalize_manifest(manifest: Mapping[str, Any]) -> bool:
49
+ """
50
+ Determines whether the manifest should be normalized,
51
+ based on the presence of the "__should_normalize" key.
52
+
53
+ This flag is set by the UI.
54
+ """
55
+ return manifest.get(SHOULD_NORMALIZE_KEY, False)
56
+
57
+
58
+ def build_source(
59
+ manifest: Dict[str, Any],
60
+ catalog: Optional[ConfiguredAirbyteCatalog],
61
+ config: Mapping[str, Any],
62
+ state: Optional[List[AirbyteStateMessage]],
63
+ record_limit: Optional[int] = None,
64
+ page_limit: Optional[int] = None,
65
+ slice_limit: Optional[int] = None,
66
+ ) -> ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]]:
67
+ # We enforce a concurrency level of 1 so that the stream is processed on a single thread
68
+ # to retain ordering for the grouping of the builder message responses.
69
+ definition = copy.deepcopy(manifest)
70
+ if "concurrency_level" in definition:
71
+ definition["concurrency_level"]["default_concurrency"] = 1
72
+ else:
73
+ definition["concurrency_level"] = {
74
+ "type": "ConcurrencyLevel",
75
+ "default_concurrency": 1,
76
+ }
77
+
78
+ should_normalize = should_normalize_manifest(manifest)
79
+ if should_normalize:
80
+ del definition[SHOULD_NORMALIZE_KEY]
81
+
82
+ should_migrate = should_migrate_manifest(manifest)
83
+ if should_migrate:
84
+ del definition[SHOULD_MIGRATE_KEY]
85
+
86
+ return ConcurrentDeclarativeSource(
87
+ catalog=catalog,
88
+ state=state,
89
+ source_config=definition,
90
+ config=config,
91
+ normalize_manifest=should_normalize,
92
+ migrate_manifest=should_migrate,
93
+ emit_connector_builder_messages=True,
94
+ limits=TestLimits(
95
+ max_pages_per_slice=page_limit or TestLimits.DEFAULT_MAX_PAGES_PER_SLICE,
96
+ max_slices=slice_limit or TestLimits.DEFAULT_MAX_SLICES,
97
+ max_records=record_limit or TestLimits.DEFAULT_MAX_RECORDS,
98
+ ),
99
+ )
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+ """Main entry point for the Airbyte Manifest Server server."""
3
+
4
+ import uvicorn
5
+
6
+
7
+ def run_server(
8
+ host: str = "127.0.0.1", port: int = 8000, reload: bool = False, log_level: str = "info"
9
+ ) -> None:
10
+ """Run the FastAPI server."""
11
+
12
+ print(f"🚀 Starting Airbyte CDK Manifest Server on {host}:{port}")
13
+
14
+ uvicorn.run(
15
+ "airbyte_cdk.manifest_server.app:app",
16
+ host=host,
17
+ port=port,
18
+ reload=reload,
19
+ log_level=log_level,
20
+ )
21
+
22
+
23
+ if __name__ == "__main__":
24
+ run_server()