22from dataclasses import asdict
33from typing import Any , Dict , List
44
5- from fastapi import APIRouter , Depends
5+ import jsonschema
6+ from fastapi import APIRouter , Depends , HTTPException
67
78from airbyte_cdk .models import AirbyteStateMessageSerializer
89from airbyte_cdk .sources .declarative .parsers .custom_code_compiler import (
2122from ..manifest_runner .runner import ManifestRunner
2223from ..manifest_runner .utils import build_catalog , build_source
2324
25+
26+ def safe_build_source (manifest_dict , config_dict ):
27+ """Wrapper around build_source that converts ValidationError to HTTPException."""
28+ try :
29+ return build_source (manifest_dict , config_dict )
30+ except jsonschema .exceptions .ValidationError as e :
31+ raise HTTPException (status_code = 400 , detail = f"Invalid manifest: { e .message } " )
32+
33+
2434router = APIRouter (
2535 prefix = "/manifest" ,
2636 tags = ["manifest" ],
@@ -34,7 +44,8 @@ def test_read(request: StreamTestReadRequest) -> StreamRead:
3444 Test reading from a specific stream in the manifest.
3545 """
3646 config_dict = request .config .model_dump ()
37- source = build_source (request .manifest .model_dump (), config_dict )
47+
48+ source = safe_build_source (request .manifest .model_dump (), config_dict )
3849 catalog = build_catalog (request .stream_name )
3950 state = [AirbyteStateMessageSerializer .load (state ) for state in request .state ]
4051
@@ -59,7 +70,7 @@ def test_read(request: StreamTestReadRequest) -> StreamRead:
5970@router .post ("/resolve" , operation_id = "resolve" )
6071def resolve (request : ResolveRequest ) -> ManifestResponse :
6172 """Resolve a manifest to its final configuration."""
62- source = build_source (request .manifest .model_dump (), {})
73+ source = safe_build_source (request .manifest .model_dump (), {})
6374 return ManifestResponse (manifest = source .resolved_manifest )
6475
6576
@@ -71,7 +82,7 @@ def full_resolve(request: FullResolveRequest) -> ManifestResponse:
7182 Generates dynamic streams up to the specified limit and includes
7283 them in the resolved manifest.
7384 """
74- source = build_source (request .manifest .model_dump (), request .config .model_dump ())
85+ source = safe_build_source (request .manifest .model_dump (), request .config .model_dump ())
7586 manifest = {** source .resolved_manifest }
7687 streams = manifest .get ("streams" , [])
7788 for stream in streams :
0 commit comments