airbyte-cdk 6.61.1__py3-none-any.whl → 6.61.3__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 (48) hide show
  1. airbyte_cdk/__init__.py +0 -2
  2. airbyte_cdk/connector_builder/connector_builder_handler.py +7 -5
  3. airbyte_cdk/connector_builder/main.py +4 -2
  4. airbyte_cdk/connector_builder/test_reader/reader.py +10 -8
  5. airbyte_cdk/legacy/__init__.py +1 -0
  6. airbyte_cdk/legacy/sources/__init__.py +1 -0
  7. airbyte_cdk/legacy/sources/declarative/__init__.py +1 -0
  8. airbyte_cdk/legacy/sources/declarative/incremental/__init__.py +1 -0
  9. airbyte_cdk/{sources → legacy/sources}/declarative/manifest_declarative_source.py +1 -1
  10. airbyte_cdk/manifest_server/Dockerfile +45 -0
  11. airbyte_cdk/manifest_server/README.md +142 -0
  12. airbyte_cdk/manifest_server/__init__.py +3 -0
  13. airbyte_cdk/manifest_server/api_models/__init__.py +49 -0
  14. airbyte_cdk/manifest_server/api_models/capabilities.py +7 -0
  15. airbyte_cdk/manifest_server/api_models/dicts.py +17 -0
  16. airbyte_cdk/manifest_server/api_models/manifest.py +73 -0
  17. airbyte_cdk/manifest_server/api_models/stream.py +76 -0
  18. airbyte_cdk/manifest_server/app.py +17 -0
  19. airbyte_cdk/manifest_server/auth.py +43 -0
  20. airbyte_cdk/manifest_server/cli/__init__.py +5 -0
  21. airbyte_cdk/manifest_server/cli/_common.py +28 -0
  22. airbyte_cdk/manifest_server/cli/_info.py +30 -0
  23. airbyte_cdk/manifest_server/cli/_openapi.py +43 -0
  24. airbyte_cdk/manifest_server/cli/_start.py +38 -0
  25. airbyte_cdk/manifest_server/cli/run.py +59 -0
  26. airbyte_cdk/manifest_server/command_processor/__init__.py +0 -0
  27. airbyte_cdk/manifest_server/command_processor/processor.py +122 -0
  28. airbyte_cdk/manifest_server/command_processor/utils.py +99 -0
  29. airbyte_cdk/manifest_server/main.py +24 -0
  30. airbyte_cdk/manifest_server/openapi.yaml +641 -0
  31. airbyte_cdk/manifest_server/routers/__init__.py +0 -0
  32. airbyte_cdk/manifest_server/routers/capabilities.py +25 -0
  33. airbyte_cdk/manifest_server/routers/health.py +13 -0
  34. airbyte_cdk/manifest_server/routers/manifest.py +155 -0
  35. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +507 -24
  36. airbyte_cdk/sources/declarative/incremental/__init__.py +4 -4
  37. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +4 -4
  38. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +12 -0
  39. airbyte_cdk/sources/declarative/retrievers/retriever.py +1 -2
  40. airbyte_cdk/sources/streams/http/http_client.py +21 -0
  41. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/METADATA +4 -1
  42. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/RECORD +48 -19
  43. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/entry_points.txt +1 -0
  44. /airbyte_cdk/{sources → legacy/sources}/declarative/declarative_source.py +0 -0
  45. /airbyte_cdk/{sources → legacy/sources}/declarative/incremental/per_partition_cursor.py +0 -0
  46. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/LICENSE.txt +0 -0
  47. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/LICENSE_SHORT +0 -0
  48. {airbyte_cdk-6.61.1.dist-info → airbyte_cdk-6.61.3.dist-info}/WHEEL +0 -0
@@ -0,0 +1,155 @@
1
+ import hashlib
2
+ from dataclasses import asdict
3
+ from typing import Any, Dict, List, Mapping, Optional
4
+
5
+ import jsonschema
6
+ from airbyte_protocol_dataclasses.models import AirbyteStateMessage, ConfiguredAirbyteCatalog
7
+ from fastapi import APIRouter, Depends, HTTPException
8
+
9
+ from airbyte_cdk.models import AirbyteStateMessageSerializer
10
+ from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
11
+ ConcurrentDeclarativeSource,
12
+ )
13
+ from airbyte_cdk.sources.declarative.parsers.custom_code_compiler import (
14
+ INJECTED_COMPONENTS_PY,
15
+ INJECTED_COMPONENTS_PY_CHECKSUMS,
16
+ )
17
+
18
+ from ..api_models import (
19
+ CheckRequest,
20
+ CheckResponse,
21
+ DiscoverRequest,
22
+ DiscoverResponse,
23
+ FullResolveRequest,
24
+ Manifest,
25
+ ManifestResponse,
26
+ ResolveRequest,
27
+ StreamReadResponse,
28
+ StreamTestReadRequest,
29
+ )
30
+ from ..auth import verify_jwt_token
31
+ from ..command_processor.processor import ManifestCommandProcessor
32
+ from ..command_processor.utils import build_catalog, build_source
33
+
34
+
35
+ def safe_build_source(
36
+ manifest_dict: Dict[str, Any],
37
+ config_dict: Mapping[str, Any],
38
+ catalog: Optional[ConfiguredAirbyteCatalog] = None,
39
+ state: Optional[List[AirbyteStateMessage]] = None,
40
+ page_limit: Optional[int] = None,
41
+ slice_limit: Optional[int] = None,
42
+ record_limit: Optional[int] = None,
43
+ ) -> ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]]:
44
+ """Wrapper around build_source that converts ValidationError to HTTPException."""
45
+ try:
46
+ return build_source(
47
+ manifest_dict,
48
+ catalog,
49
+ config_dict,
50
+ state,
51
+ record_limit,
52
+ page_limit,
53
+ slice_limit,
54
+ )
55
+ except jsonschema.exceptions.ValidationError as e:
56
+ raise HTTPException(status_code=400, detail=f"Invalid manifest: {e.message}")
57
+
58
+
59
+ router = APIRouter(
60
+ prefix="/manifest",
61
+ tags=["manifest"],
62
+ dependencies=[Depends(verify_jwt_token)],
63
+ )
64
+
65
+
66
+ @router.post("/test_read", operation_id="testRead")
67
+ def test_read(request: StreamTestReadRequest) -> StreamReadResponse:
68
+ """
69
+ Test reading from a specific stream in the manifest.
70
+ """
71
+ config_dict = request.config.model_dump()
72
+
73
+ catalog = build_catalog(request.stream_name)
74
+ converted_state = [AirbyteStateMessageSerializer.load(state) for state in request.state]
75
+
76
+ if request.custom_components_code:
77
+ config_dict[INJECTED_COMPONENTS_PY] = request.custom_components_code
78
+ config_dict[INJECTED_COMPONENTS_PY_CHECKSUMS] = {
79
+ "md5": hashlib.md5(request.custom_components_code.encode()).hexdigest()
80
+ }
81
+
82
+ source = safe_build_source(
83
+ request.manifest.model_dump(),
84
+ config_dict,
85
+ catalog,
86
+ converted_state,
87
+ request.page_limit,
88
+ request.slice_limit,
89
+ request.record_limit,
90
+ )
91
+
92
+ runner = ManifestCommandProcessor(source)
93
+ cdk_result = runner.test_read(
94
+ config_dict,
95
+ catalog,
96
+ converted_state,
97
+ request.record_limit,
98
+ request.page_limit,
99
+ request.slice_limit,
100
+ )
101
+ return StreamReadResponse.model_validate(asdict(cdk_result))
102
+
103
+
104
+ @router.post("/check", operation_id="check")
105
+ def check(request: CheckRequest) -> CheckResponse:
106
+ """Check configuration against a manifest"""
107
+ source = safe_build_source(request.manifest.model_dump(), request.config.model_dump())
108
+ runner = ManifestCommandProcessor(source)
109
+ success, message = runner.check_connection(request.config.model_dump())
110
+ return CheckResponse(success=success, message=message)
111
+
112
+
113
+ @router.post("/discover", operation_id="discover")
114
+ def discover(request: DiscoverRequest) -> DiscoverResponse:
115
+ """Discover streams from a manifest"""
116
+ source = safe_build_source(request.manifest.model_dump(), request.config.model_dump())
117
+ runner = ManifestCommandProcessor(source)
118
+ catalog = runner.discover(request.config.model_dump())
119
+ if catalog is None:
120
+ raise HTTPException(status_code=422, detail="Connector did not return a discovered catalog")
121
+ return DiscoverResponse(catalog=catalog)
122
+
123
+
124
+ @router.post("/resolve", operation_id="resolve")
125
+ def resolve(request: ResolveRequest) -> ManifestResponse:
126
+ """Resolve a manifest to its final configuration."""
127
+ source = safe_build_source(request.manifest.model_dump(), {})
128
+ return ManifestResponse(manifest=Manifest(**source.resolved_manifest))
129
+
130
+
131
+ @router.post("/full_resolve", operation_id="fullResolve")
132
+ def full_resolve(request: FullResolveRequest) -> ManifestResponse:
133
+ """
134
+ Fully resolve a manifest, including dynamic streams.
135
+
136
+ This is a similar operation to resolve, but has an extra step which generates streams from dynamic stream templates if the manifest contains any. This is used when a user clicks the generate streams button on a stream template in the Builder UI
137
+ """
138
+ source = safe_build_source(request.manifest.model_dump(), request.config.model_dump())
139
+ manifest = {**source.resolved_manifest}
140
+ streams = manifest.get("streams", [])
141
+ for stream in streams:
142
+ stream["dynamic_stream_name"] = None
143
+
144
+ mapped_streams: Dict[str, List[Dict[str, Any]]] = {}
145
+ for stream in source.dynamic_streams:
146
+ generated_streams = mapped_streams.setdefault(stream["dynamic_stream_name"], [])
147
+
148
+ if len(generated_streams) < request.stream_limit:
149
+ generated_streams += [stream]
150
+
151
+ for generated_streams_list in mapped_streams.values():
152
+ streams.extend(generated_streams_list)
153
+
154
+ manifest["streams"] = streams
155
+ return ManifestResponse(manifest=Manifest(**manifest))