ADR 0039: Generated connector catalog with full form fidelity¶
Status: Accepted
Date: 2026-06-04
Supersedes (in part): ADR 0036 ยง"Form widgets" โ the decision to return extra_fields: {} and accept a generic connection form.
Companions: ADR 0038 (connectors: sugar), ADR 0024 (parser structural shim), ADR 0014 (admin panel is Go-only)
Context¶
The admin connection form is driven entirely by GET /ui/connections/hook_meta.
Until now Leoflow served a hand-written registry of ~15 connector types
(internal/connectors) that carried only the standard-field hidden flags and an
empty extra_fields: {} (ADR 0036 named this tradeoff: generic form, runtime
unaffected). Two problems surfaced when we looked closely:
- Coverage. 15 curated types out of Airflow's ~90 providers. The long tail
worked only via the
dependencies:escape hatch, with no dropdown entry. - Field fidelity. With
extra_fields: {}, the form rendered only host/login/password/port/schema + a raw "Extra JSON" textarea. For cloud / config connectors (Snowflakeaccount/warehouse, AWS keys, GCP keyfile, Kafka config) the actual credential fields are provider-specific, so the user had to hand-type JSON. We also dropped the standard-field relabels (Postgres "Schema" โ "Database") and placeholders.
We verified (bundle archaeology + Airflow docs + issues #60370/#21188) that the
embedded Airflow 3.2 SPA does render extra_fields via its FlexibleForm
component. So the empty {} was leaving real UX on the table.
Decision¶
Generate the connector catalog from a real Airflow install instead of hand-
maintaining it, and serve full form fidelity (standard_fields + extra_fields).
scripts/gen_connectors.pyruns offline in an env withapache-airflow+ the target providers (scripts/connectors-providers.txt). It calls Airflow's own serializer โHookMetaService.hook_meta_data(), the exact code path behind/ui/connections/hook_metaโ plusProvidersManagerfor the pip package per conn_type, and writesinternal/connectors/catalog.json.- The Go
connectorspackage embeds that JSON (//go:embed) and is the single source of truth for all three consumers: the admin form, theconnectors:sugar, and compile-time validation. A small hand-curated overlay adds sugar aliases (gcp/google) that Airflow's vocabulary lacks. - The form handler serves each entry's
standard_fields+extra_fieldsverbatim โ the precise FlexibleForm shape โ so the SPA renders the labeled, provider-specific fields.
Why generated, not hand-written¶
Hand-maintaining ~90 providers ร N fields each is exactly the whack-a-mole
maintenance debt that got the runtime shim deferred (ADR 0036). Generating from
Airflow's own serializer means we never translate WTForms widgets by hand and
never drift: bumping a provider version + re-running the script regenerates the
truth. catalog.json is a committed, reviewable artifact (~100 KB).
Consequences¶
- ~86 connector types now ship with a dropdown entry, the correct
standard-field behavior (hide/relabel/placeholder), and provider-specific
extra_fields. The long tail still also works viadependencies:. - Regeneration is a documented, reproducible step, not editing.
catalog.jsonmust never be edited by hand; the provider list is pinned inscripts/connectors-providers.txt. - Known upstream caveat: Airflow 3.2 FlexibleForm marks some sensitive fields as required incorrectly (#60370). We inherit Airflow's serialized schema as-is; if it bites, the fix belongs upstream (we do not fork the metadata).
- Core conn types (
generic,email) carry no pip package: they appear in the form but are not resolvable by theconnectors:sugar (nothing to install). - ADR 0036's runtime-shim deferral is unchanged; this ADR only supersedes its
form-widget
{}decision.