From 47e1cc252ca47c022dbcf36741466e466240dced Mon Sep 17 00:00:00 2001 From: ChristoGrab Date: Thu, 25 Sep 2025 13:51:09 -0700 Subject: [PATCH 1/6] fix: add basic error handling to manifest-server calls --- .../manifest_server/routers/manifest.py | 113 ++++++++++------ .../manifest_server/routers/test_manifest.py | 123 ++++++++++++++++++ 2 files changed, 197 insertions(+), 39 deletions(-) diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index 4fefb2129..55f04228e 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -5,8 +5,10 @@ import jsonschema from airbyte_protocol_dataclasses.models import AirbyteStateMessage, ConfiguredAirbyteCatalog from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import JSONResponse from airbyte_cdk.models import AirbyteStateMessageSerializer +from airbyte_cdk.utils.traced_exception import AirbyteTracedException from airbyte_cdk.sources.declarative.concurrent_declarative_source import ( ConcurrentDeclarativeSource, ) @@ -98,15 +100,21 @@ def test_read(request: StreamTestReadRequest) -> StreamReadResponse: ) runner = ManifestCommandProcessor(source) - cdk_result = runner.test_read( - config_dict, - catalog, - converted_state, - request.record_limit, - request.page_limit, - request.slice_limit, - ) - return StreamReadResponse.model_validate(asdict(cdk_result)) + try: + cdk_result = runner.test_read( + config_dict, + catalog, + converted_state, + request.record_limit, + request.page_limit, + request.slice_limit, + ) + return StreamReadResponse.model_validate(asdict(cdk_result)) + except Exception as exc: + error = AirbyteTracedException.from_exception( + exc, message=f"Error reading stream: {str(exc)}" + ) + raise HTTPException(status_code=400, detail=error.message) @router.post("/check", operation_id="check") @@ -119,10 +127,16 @@ def check(request: CheckRequest) -> CheckResponse: project_id=request.context.project_id, ) - source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) - runner = ManifestCommandProcessor(source) - success, message = runner.check_connection(request.config.model_dump()) - return CheckResponse(success=success, message=message) + try: + source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) + runner = ManifestCommandProcessor(source) + success, message = runner.check_connection(request.config.model_dump()) + return CheckResponse(success=success, message=message) + except Exception as exc: + error = AirbyteTracedException.from_exception( + exc, message=f"Error checking connection: {str(exc)}" + ) + raise HTTPException(status_code=400, detail=error.message) @router.post("/discover", operation_id="discover") @@ -135,12 +149,21 @@ def discover(request: DiscoverRequest) -> DiscoverResponse: project_id=request.context.project_id, ) - source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) - runner = ManifestCommandProcessor(source) - catalog = runner.discover(request.config.model_dump()) - if catalog is None: - raise HTTPException(status_code=422, detail="Connector did not return a discovered catalog") - return DiscoverResponse(catalog=catalog) + try: + source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) + runner = ManifestCommandProcessor(source) + catalog = runner.discover(request.config.model_dump()) + if catalog is None: + raise HTTPException(status_code=422, detail="Connector did not return a discovered catalog") + return DiscoverResponse(catalog=catalog) + except HTTPException: + # Re-raise HTTPExceptions as-is (like the catalog None check above) + raise + except Exception as exc: + error = AirbyteTracedException.from_exception( + exc, message=f"Error discovering streams: {str(exc)}" + ) + raise HTTPException(status_code=400, detail=error.message) @router.post("/resolve", operation_id="resolve") @@ -153,8 +176,14 @@ def resolve(request: ResolveRequest) -> ManifestResponse: project_id=request.context.project_id, ) - source = safe_build_source(request.manifest.model_dump(), {}) - return ManifestResponse(manifest=Manifest(**source.resolved_manifest)) + try: + source = safe_build_source(request.manifest.model_dump(), {}) + return ManifestResponse(manifest=Manifest(**source.resolved_manifest)) + except Exception as exc: + error = AirbyteTracedException.from_exception( + exc, message=f"Error resolving manifest: {str(exc)}" + ) + raise HTTPException(status_code=400, detail=error.message) @router.post("/full_resolve", operation_id="fullResolve") @@ -171,21 +200,27 @@ def full_resolve(request: FullResolveRequest) -> ManifestResponse: project_id=request.context.project_id, ) - source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) - manifest = {**source.resolved_manifest} - streams = manifest.get("streams", []) - for stream in streams: - stream["dynamic_stream_name"] = None - - mapped_streams: Dict[str, List[Dict[str, Any]]] = {} - for stream in source.dynamic_streams: - generated_streams = mapped_streams.setdefault(stream["dynamic_stream_name"], []) - - if len(generated_streams) < request.stream_limit: - generated_streams += [stream] - - for generated_streams_list in mapped_streams.values(): - streams.extend(generated_streams_list) - - manifest["streams"] = streams - return ManifestResponse(manifest=Manifest(**manifest)) + try: + source = safe_build_source(request.manifest.model_dump(), request.config.model_dump()) + manifest = {**source.resolved_manifest} + streams = manifest.get("streams", []) + for stream in streams: + stream["dynamic_stream_name"] = None + + mapped_streams: Dict[str, List[Dict[str, Any]]] = {} + for stream in source.dynamic_streams: + generated_streams = mapped_streams.setdefault(stream["dynamic_stream_name"], []) + + if len(generated_streams) < request.stream_limit: + generated_streams += [stream] + + for generated_streams_list in mapped_streams.values(): + streams.extend(generated_streams_list) + + manifest["streams"] = streams + return ManifestResponse(manifest=Manifest(**manifest)) + except Exception as exc: + error = AirbyteTracedException.from_exception( + exc, message=f"Error full resolving manifest: {str(exc)}" + ) + raise HTTPException(status_code=400, detail=error.message) diff --git a/unit_tests/manifest_server/routers/test_manifest.py b/unit_tests/manifest_server/routers/test_manifest.py index 4f8a90b93..8e3f8ce2b 100644 --- a/unit_tests/manifest_server/routers/test_manifest.py +++ b/unit_tests/manifest_server/routers/test_manifest.py @@ -527,3 +527,126 @@ def test_discover_endpoint_missing_catalog( assert response.status_code == 422 data = response.json() assert "Connector did not return a discovered catalog" in data["detail"] + + # Test cases for error handling improvements + @patch("airbyte_cdk.manifest_server.routers.manifest.ManifestCommandProcessor") + @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") + def test_test_read_cdk_error_handling( + self, mock_build_source, mock_runner_class, sample_manifest, sample_config, mock_source + ): + """Test that CDK errors in test_read are properly caught and converted to HTTP 400.""" + request_data = { + "manifest": sample_manifest, + "config": sample_config, + "stream_name": "products" + } + + mock_build_source.return_value = mock_source + + mock_runner = Mock() + # Simulate a CDK error (like datetime parsing error) + mock_runner.test_read.side_effect = ValueError("time data '' does not match format '%Y-%m-%dT%H:%M:%SZ'") + mock_runner_class.return_value = mock_runner + + response = client.post("/v1/manifest/test_read", json=request_data) + + assert response.status_code == 400 + data = response.json() + assert "detail" in data + assert "Error reading stream:" in data["detail"] + assert "time data" in data["detail"] + assert "does not match format" in data["detail"] + + @patch("airbyte_cdk.manifest_server.routers.manifest.ManifestCommandProcessor") + @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") + def test_check_cdk_error_handling( + self, mock_build_source, mock_runner_class, sample_manifest, sample_config, mock_source + ): + """Test that CDK errors in check are properly caught and converted to HTTP 400.""" + request_data = { + "manifest": sample_manifest, + "config": sample_config, + } + + mock_build_source.return_value = mock_source + + mock_runner = Mock() + # Simulate a CDK error (like connection error) + mock_runner.check_connection.side_effect = ConnectionError("Failed to connect to API") + mock_runner_class.return_value = mock_runner + + response = client.post("/v1/manifest/check", json=request_data) + + assert response.status_code == 400 + data = response.json() + assert "detail" in data + assert "Error checking connection:" in data["detail"] + assert "Failed to connect to API" in data["detail"] + + @patch("airbyte_cdk.manifest_server.routers.manifest.ManifestCommandProcessor") + @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") + def test_discover_cdk_error_handling( + self, mock_build_source, mock_runner_class, sample_manifest, sample_config, mock_source + ): + """Test that CDK errors in discover are properly caught and converted to HTTP 400.""" + request_data = { + "manifest": sample_manifest, + "config": sample_config, + } + + mock_build_source.return_value = mock_source + + mock_runner = Mock() + # Simulate a CDK error + mock_runner.discover.side_effect = RuntimeError("Schema validation failed") + mock_runner_class.return_value = mock_runner + + response = client.post("/v1/manifest/discover", json=request_data) + + assert response.status_code == 400 + data = response.json() + assert "detail" in data + assert "Error discovering streams:" in data["detail"] + assert "Schema validation failed" in data["detail"] + + @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") + def test_resolve_cdk_error_handling( + self, mock_build_source, sample_manifest + ): + """Test that CDK errors in resolve are properly caught and converted to HTTP 400.""" + request_data = { + "manifest": sample_manifest, + } + + # Simulate a CDK error during source building + mock_build_source.side_effect = AttributeError("'NoneType' object has no attribute 'get'") + + response = client.post("/v1/manifest/resolve", json=request_data) + + assert response.status_code == 400 + data = response.json() + assert "detail" in data + assert "Error resolving manifest:" in data["detail"] + assert "'NoneType' object has no attribute 'get'" in data["detail"] + + @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") + def test_full_resolve_cdk_error_handling( + self, mock_build_source, sample_manifest, sample_config + ): + """Test that CDK errors in full_resolve are properly caught and converted to HTTP 400.""" + request_data = { + "manifest": sample_manifest, + "config": sample_config, + "stream_limit": 10 + } + + # Simulate a CDK error during source building + mock_build_source.side_effect = KeyError("Missing required field 'streams'") + + response = client.post("/v1/manifest/full_resolve", json=request_data) + + assert response.status_code == 400 + data = response.json() + assert "detail" in data + assert "Error full resolving manifest:" in data["detail"] + assert "Missing required field 'streams'" in data["detail"] From 4f7ac0f97886b91b33a642486ee29978464cc072 Mon Sep 17 00:00:00 2001 From: octavia-squidington-iii Date: Thu, 25 Sep 2025 21:11:33 +0000 Subject: [PATCH 2/6] Auto-fix lint and format issues --- airbyte_cdk/manifest_server/routers/manifest.py | 6 ++++-- .../manifest_server/routers/test_manifest.py | 16 ++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index 55f04228e..895cf5b3b 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -8,7 +8,6 @@ from fastapi.responses import JSONResponse from airbyte_cdk.models import AirbyteStateMessageSerializer -from airbyte_cdk.utils.traced_exception import AirbyteTracedException from airbyte_cdk.sources.declarative.concurrent_declarative_source import ( ConcurrentDeclarativeSource, ) @@ -16,6 +15,7 @@ INJECTED_COMPONENTS_PY, INJECTED_COMPONENTS_PY_CHECKSUMS, ) +from airbyte_cdk.utils.traced_exception import AirbyteTracedException from ..api_models import ( CheckRequest, @@ -154,7 +154,9 @@ def discover(request: DiscoverRequest) -> DiscoverResponse: runner = ManifestCommandProcessor(source) catalog = runner.discover(request.config.model_dump()) if catalog is None: - raise HTTPException(status_code=422, detail="Connector did not return a discovered catalog") + raise HTTPException( + status_code=422, detail="Connector did not return a discovered catalog" + ) return DiscoverResponse(catalog=catalog) except HTTPException: # Re-raise HTTPExceptions as-is (like the catalog None check above) diff --git a/unit_tests/manifest_server/routers/test_manifest.py b/unit_tests/manifest_server/routers/test_manifest.py index 8e3f8ce2b..c53bb5aa2 100644 --- a/unit_tests/manifest_server/routers/test_manifest.py +++ b/unit_tests/manifest_server/routers/test_manifest.py @@ -538,14 +538,16 @@ def test_test_read_cdk_error_handling( request_data = { "manifest": sample_manifest, "config": sample_config, - "stream_name": "products" + "stream_name": "products", } mock_build_source.return_value = mock_source mock_runner = Mock() # Simulate a CDK error (like datetime parsing error) - mock_runner.test_read.side_effect = ValueError("time data '' does not match format '%Y-%m-%dT%H:%M:%SZ'") + mock_runner.test_read.side_effect = ValueError( + "time data '' does not match format '%Y-%m-%dT%H:%M:%SZ'" + ) mock_runner_class.return_value = mock_runner response = client.post("/v1/manifest/test_read", json=request_data) @@ -610,9 +612,7 @@ def test_discover_cdk_error_handling( assert "Schema validation failed" in data["detail"] @patch("airbyte_cdk.manifest_server.routers.manifest.build_source") - def test_resolve_cdk_error_handling( - self, mock_build_source, sample_manifest - ): + def test_resolve_cdk_error_handling(self, mock_build_source, sample_manifest): """Test that CDK errors in resolve are properly caught and converted to HTTP 400.""" request_data = { "manifest": sample_manifest, @@ -634,11 +634,7 @@ def test_full_resolve_cdk_error_handling( self, mock_build_source, sample_manifest, sample_config ): """Test that CDK errors in full_resolve are properly caught and converted to HTTP 400.""" - request_data = { - "manifest": sample_manifest, - "config": sample_config, - "stream_limit": 10 - } + request_data = {"manifest": sample_manifest, "config": sample_config, "stream_limit": 10} # Simulate a CDK error during source building mock_build_source.side_effect = KeyError("Missing required field 'streams'") From b7bf3c8af04233b71fc132c9eea064a92330f833 Mon Sep 17 00:00:00 2001 From: ChristoGrab Date: Thu, 25 Sep 2025 14:22:18 -0700 Subject: [PATCH 3/6] chore: openapi spec generation: : --- .../manifest_server/api_models/__init__.py | 2 + .../manifest_server/api_models/manifest.py | 6 +++ airbyte_cdk/manifest_server/app.py | 2 +- airbyte_cdk/manifest_server/openapi.yaml | 42 ++++++++++++++++++- .../manifest_server/routers/manifest.py | 21 +++++++--- 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/airbyte_cdk/manifest_server/api_models/__init__.py b/airbyte_cdk/manifest_server/api_models/__init__.py index 3469fd7e3..1f8dcdce5 100644 --- a/airbyte_cdk/manifest_server/api_models/__init__.py +++ b/airbyte_cdk/manifest_server/api_models/__init__.py @@ -10,6 +10,7 @@ CheckResponse, DiscoverRequest, DiscoverResponse, + ErrorResponse, FullResolveRequest, ManifestResponse, RequestContext, @@ -40,6 +41,7 @@ "CheckResponse", "DiscoverRequest", "DiscoverResponse", + "ErrorResponse", # Stream models "AuxiliaryRequest", "HttpRequest", diff --git a/airbyte_cdk/manifest_server/api_models/manifest.py b/airbyte_cdk/manifest_server/api_models/manifest.py index a17ac9c63..fc969385f 100644 --- a/airbyte_cdk/manifest_server/api_models/manifest.py +++ b/airbyte_cdk/manifest_server/api_models/manifest.py @@ -83,3 +83,9 @@ class FullResolveRequest(BaseModel): config: ConnectorConfig stream_limit: int = Field(default=100, ge=1, le=100) context: Optional[RequestContext] = None + + +class ErrorResponse(BaseModel): + """Error response for API requests.""" + + detail: str diff --git a/airbyte_cdk/manifest_server/app.py b/airbyte_cdk/manifest_server/app.py index 89e98045e..11812b0a8 100644 --- a/airbyte_cdk/manifest_server/app.py +++ b/airbyte_cdk/manifest_server/app.py @@ -11,7 +11,7 @@ app = FastAPI( title="Manifest Server", description="A service for running low-code Airbyte connectors", - version="0.1.0", + version="0.2.0", contact={ "name": "Airbyte", "url": "https://airbyte.com", diff --git a/airbyte_cdk/manifest_server/openapi.yaml b/airbyte_cdk/manifest_server/openapi.yaml index b807953e6..66d69af59 100644 --- a/airbyte_cdk/manifest_server/openapi.yaml +++ b/airbyte_cdk/manifest_server/openapi.yaml @@ -8,7 +8,7 @@ info: contact: name: Airbyte url: https://airbyte.com/ - version: 0.1.0 + version: 0.2.0 paths: /health/: get: @@ -62,6 +62,12 @@ paths: application/json: schema: $ref: '#/components/schemas/StreamReadResponse' + '400': + description: Bad Request - Error processing request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '422': description: Validation Error content: @@ -90,6 +96,12 @@ paths: application/json: schema: $ref: '#/components/schemas/CheckResponse' + '400': + description: Bad Request - Error processing request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '422': description: Validation Error content: @@ -118,6 +130,12 @@ paths: application/json: schema: $ref: '#/components/schemas/DiscoverResponse' + '400': + description: Bad Request - Error processing request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '422': description: Validation Error content: @@ -146,6 +164,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ManifestResponse' + '400': + description: Bad Request - Error processing request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '422': description: Validation Error content: @@ -180,6 +204,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ManifestResponse' + '400': + description: Bad Request - Error processing request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '422': description: Validation Error content: @@ -353,6 +383,16 @@ components: - catalog title: DiscoverResponse description: Response to discover a manifest. + ErrorResponse: + properties: + detail: + type: string + title: Detail + type: object + required: + - detail + title: ErrorResponse + description: Error response for API requests. FullResolveRequest: properties: manifest: diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index 55f04228e..53f3de8bc 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -22,6 +22,7 @@ CheckResponse, DiscoverRequest, DiscoverResponse, + ErrorResponse, FullResolveRequest, Manifest, ManifestResponse, @@ -66,7 +67,9 @@ def safe_build_source( ) -@router.post("/test_read", operation_id="testRead") +@router.post("/test_read", operation_id="testRead", responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} +}) def test_read(request: StreamTestReadRequest) -> StreamReadResponse: """ Test reading from a specific stream in the manifest. @@ -117,7 +120,9 @@ def test_read(request: StreamTestReadRequest) -> StreamReadResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/check", operation_id="check") +@router.post("/check", operation_id="check", responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} +}) def check(request: CheckRequest) -> CheckResponse: """Check configuration against a manifest""" # Apply trace tags from context if provided @@ -139,7 +144,9 @@ def check(request: CheckRequest) -> CheckResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/discover", operation_id="discover") +@router.post("/discover", operation_id="discover", responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} +}) def discover(request: DiscoverRequest) -> DiscoverResponse: """Discover streams from a manifest""" # Apply trace tags from context if provided @@ -166,7 +173,9 @@ def discover(request: DiscoverRequest) -> DiscoverResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/resolve", operation_id="resolve") +@router.post("/resolve", operation_id="resolve", responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} +}) def resolve(request: ResolveRequest) -> ManifestResponse: """Resolve a manifest to its final configuration.""" # Apply trace tags from context if provided @@ -186,7 +195,9 @@ def resolve(request: ResolveRequest) -> ManifestResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/full_resolve", operation_id="fullResolve") +@router.post("/full_resolve", operation_id="fullResolve", responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} +}) def full_resolve(request: FullResolveRequest) -> ManifestResponse: """ Fully resolve a manifest, including dynamic streams. From 17ee2e6a6ef5d6396092a8d01b5171909da43f12 Mon Sep 17 00:00:00 2001 From: octavia-squidington-iii Date: Thu, 25 Sep 2025 21:30:43 +0000 Subject: [PATCH 4/6] Auto-fix lint and format issues --- .../manifest_server/routers/manifest.py | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index aad6ab7a7..94633a699 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -67,9 +67,13 @@ def safe_build_source( ) -@router.post("/test_read", operation_id="testRead", responses={ - 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} -}) +@router.post( + "/test_read", + operation_id="testRead", + responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} + }, +) def test_read(request: StreamTestReadRequest) -> StreamReadResponse: """ Test reading from a specific stream in the manifest. @@ -120,9 +124,13 @@ def test_read(request: StreamTestReadRequest) -> StreamReadResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/check", operation_id="check", responses={ - 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} -}) +@router.post( + "/check", + operation_id="check", + responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} + }, +) def check(request: CheckRequest) -> CheckResponse: """Check configuration against a manifest""" # Apply trace tags from context if provided @@ -144,9 +152,13 @@ def check(request: CheckRequest) -> CheckResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/discover", operation_id="discover", responses={ - 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} -}) +@router.post( + "/discover", + operation_id="discover", + responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} + }, +) def discover(request: DiscoverRequest) -> DiscoverResponse: """Discover streams from a manifest""" # Apply trace tags from context if provided @@ -175,9 +187,13 @@ def discover(request: DiscoverRequest) -> DiscoverResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/resolve", operation_id="resolve", responses={ - 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} -}) +@router.post( + "/resolve", + operation_id="resolve", + responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} + }, +) def resolve(request: ResolveRequest) -> ManifestResponse: """Resolve a manifest to its final configuration.""" # Apply trace tags from context if provided @@ -197,9 +213,13 @@ def resolve(request: ResolveRequest) -> ManifestResponse: raise HTTPException(status_code=400, detail=error.message) -@router.post("/full_resolve", operation_id="fullResolve", responses={ - 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} -}) +@router.post( + "/full_resolve", + operation_id="fullResolve", + responses={ + 400: {"description": "Bad Request - Error processing request", "model": ErrorResponse} + }, +) def full_resolve(request: FullResolveRequest) -> ManifestResponse: """ Fully resolve a manifest, including dynamic streams. From ca28b404d9fa639899b44b44ab77f2e9914ff968 Mon Sep 17 00:00:00 2001 From: Christo Grabowski <108154848+ChristoGrab@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:35:50 -0400 Subject: [PATCH 5/6] Update airbyte_cdk/manifest_server/routers/manifest.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- airbyte_cdk/manifest_server/routers/manifest.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index 94633a699..edee06102 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -5,8 +5,6 @@ import jsonschema from airbyte_protocol_dataclasses.models import AirbyteStateMessage, ConfiguredAirbyteCatalog from fastapi import APIRouter, Depends, HTTPException -from fastapi.responses import JSONResponse - from airbyte_cdk.models import AirbyteStateMessageSerializer from airbyte_cdk.sources.declarative.concurrent_declarative_source import ( ConcurrentDeclarativeSource, From 9df9c675307524f3032ebc381181a715915accd1 Mon Sep 17 00:00:00 2001 From: ChristoGrab Date: Thu, 25 Sep 2025 14:38:37 -0700 Subject: [PATCH 6/6] chore: format --- airbyte_cdk/manifest_server/routers/manifest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/airbyte_cdk/manifest_server/routers/manifest.py b/airbyte_cdk/manifest_server/routers/manifest.py index edee06102..5a3106ea3 100644 --- a/airbyte_cdk/manifest_server/routers/manifest.py +++ b/airbyte_cdk/manifest_server/routers/manifest.py @@ -5,6 +5,7 @@ import jsonschema from airbyte_protocol_dataclasses.models import AirbyteStateMessage, ConfiguredAirbyteCatalog from fastapi import APIRouter, Depends, HTTPException + from airbyte_cdk.models import AirbyteStateMessageSerializer from airbyte_cdk.sources.declarative.concurrent_declarative_source import ( ConcurrentDeclarativeSource,