feat(declarative): support XML inside GzipDecoder and ZipfileDecoder - #1131
feat(declarative): support XML inside GzipDecoder and ZipfileDecoder#1131devin-ai-integration[bot] wants to merge 1 commit into
Conversation
Co-Authored-By: bot_apk <apk@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksTesting This CDK VersionYou can test this version of the CDK using the following: # Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@devin/1787677605-gzip-xml-decoder#egg=airbyte-python-cdk[dev]' --help
# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch devin/1787677605-gzip-xml-decoderPR Slash CommandsAirbyte Maintainers can execute the following slash commands on your PR:
|
There was a problem hiding this comment.
Pull request overview
This PR extends the CDK’s declarative “composite raw decoder” framework to support parsing XML after decompression, enabling manifests like GzipDecoder(XmlDecoder) and ZipfileDecoder(XmlDecoder) to work (including cases where gzip must be detected by payload magic bytes rather than HTTP headers).
Changes:
- Adds a new
XmlParser(byte-oriented) to the composite parser framework and wires it intoModelToComponentFactory._get_parser. - Expands declarative schema unions so
XmlDecoderis a valid inner decoder forGzipDecoderandZipfileDecoder. - Adds unit tests covering
XmlParserbehavior,GzipParser(XmlParser())composition, and end-to-end factory creation for gzip+xml.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py |
Adds XmlParser that parses XML from raw bytes to enable composition with gzip/zip parsers. |
airbyte_cdk/sources/declarative/decoders/__init__.py |
Exposes XmlParser from the declarative decoders package. |
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py |
Adds XmlDecoderModel -> XmlParser mapping in _get_parser so composite decoders can build XML parsing pipelines. |
airbyte_cdk/sources/declarative/models/declarative_component_schema.py |
Updates pydantic model unions to allow XmlDecoder inside GzipDecoder and ZipfileDecoder. |
airbyte_cdk/sources/declarative/declarative_component_schema.yaml |
Mirrors schema union updates for declarative component YAML schema. |
unit_tests/sources/declarative/decoders/test_composite_decoder.py |
Adds direct XmlParser tests + gzip composition + mocked-request decode test. |
unit_tests/sources/declarative/parsers/test_model_to_component_factory.py |
Adds factory-level test ensuring create_gzip_decoder can accept an XML inner decoder across header variations and builder/sync modes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
Resolves https://github.com/airbytehq/product-request-backlog/issues/527:
Manifest sources that download gzip-compressed XML (e.g. presigned S3 objects served with
Content-Type: application/gzipand noContent-Encoding) could not be configured at all:GzipDecoder.decoderdid not acceptXmlDecoderin the schema, and even if it had,ModelToComponentFactory._get_parserraisedValueError: Decoder type ... does not have parser associated to itforXmlDecoderModelbecause the composite raw decoder framework had no XML parser.This adds
XmlParserto the composite framework and allows XML as an inner decoder of bothGzipDecoderandZipfileDecoder:XmlParserreads raw bytes rather thanresponse.text, so it composes withGzipParser, which already detects gzip payloads by magic bytes (#1124) rather than trusting headers. That means all three response shapes work throughcreate_gzip_decoder:Content-Encoding: gzip,Content-Type: application/gzip, and a gzip body with neither header.Manifest that now works:
Note this is CDK/manifest-level support; the Connector Builder UI may need a separate change to surface XML as a gzip inner decoder in the dropdown.
Declarative-First Evaluation
No custom Python component was added to any connector. The gap was in the CDK's built-in decoder framework itself — no declarative combination of existing decoders can decompress gzip and then parse XML, since
XmlDecoderoperates onresponse.text. The fix extends the built-in composite parser framework (following the existingCsvParser/JsonParserpattern) so the behavior remains fully declarative for connector authors.Test Coverage
unit_tests/sources/declarative/decoders/test_composite_decoder.py:XmlParseroutput (attributes as@id, repeated elements, malformed XML yielding{}),GzipParser(XmlParser())composition, and arequests_mockend-to-end decode.unit_tests/sources/declarative/parsers/test_model_to_component_factory.py:test_create_gzip_decoder_handles_xml_inner_decoderbuilds the model throughcreate_gzip_decoderand parametrizes overContent-Type: application/gzip,application/x-gzip,application/xml(no compression header) andContent-Encoding: gzip, withemit_connector_builder_messagesboth on and off.These tests fail on
main— constructingGzipDecoderModel(decoder=XmlDecoderModel(...))raises a pydanticValidationErrorthere, andXmlParserdoes not exist.Local verification:
Not a breaking change: no field is removed, renamed, or retyped, no stream/state/cursor behavior changes, and every previously valid decoder configuration remains valid — the schema unions only gain an additional allowed member.
Requested by Devin Bot via the
/ai-fixworkflow.Link to Devin session: https://app.devin.ai/sessions/37af92dfe8b948098f0304c7977821f8