codegen

package
v0.0.0-...-3cd688d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package codegen loads OpenAPI/GraphQL specs plus a per-operation overlay and emits Aileron connector scaffolding (action.md, manifest.toml, typed clients).

The exported surface is small on purpose: Spec, Operation, Parameter, RequestBody plus the LoadSpec / LoadOverlay / Generate entry points. The OpenAPI and GraphQL parsers live in sibling files and feed into the same Operation shape so the emitter stays format-agnostic.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Generate

func Generate(opts Options) error

Generate is the high-level entry point used by the CLI. It loads the spec and overlay, then dispatches each registered emitter against the parsed inputs.

Types

type ActionEmitter

type ActionEmitter struct{}

ActionEmitter renders one action.md per Spec Operation into actions/<name>/ under outDir. The emitted file is the TOML front-matter only; prose body generation lands in a follow-up PR.

The `0.0.0-dev` and `sha256:bound-at-release` strings in the template are load-bearing placeholders, not bugs. The release workflow substitutes them per ADR-0002 (connector identity + content-addressed hash); the generator emits the placeholders verbatim so that substitution stays idempotent against an unchanged template.

func (ActionEmitter) Emit

func (ActionEmitter) Emit(spec Spec, overlay Overlay, outDir string) error

Emit writes actions/<name>/action.md for every operation in spec, less the ones in overlay.Exclude. Each emission is driven by the kind-based governance defaults (Query / GET / HEAD / PUT / Subscription → idempotent + approval none; Mutation / POST / PATCH / DELETE → non-idempotent + approval required), with overlay.Operations[op.ID] applied as field-by-field overrides when present.

Overlay entries (in Operations or Exclude) whose op id does not appear in the spec are rejected as typos, surfacing renames or removed-but-not-cleaned-up overlays at emission time rather than silently dropping the action like the pre-#7 behavior did.

type ConnectorOverlay

type ConnectorOverlay struct {
	Name      string
	Publisher string
	// Endpoint is the absolute URL the connector POSTs to for every
	// op (the GraphQL endpoint for GraphQL connectors; the base URL
	// for REST). Required for transport-file emission
	// (`connector/graphql.go`); when empty TransportEmitter is a no-op.
	Endpoint   string
	Credential CredentialOverlay
	Network    NetworkOverlay
}

ConnectorOverlay names the connector and declares its credential + network surface.

Credential is connector-scoped because Aileron connectors mediate exactly one credential per WASM binary; per-operation credential would invite surface-area drift.

type CredentialOverlay

type CredentialOverlay struct {
	Kind string

	// api_key kind fields. Defaults applied at emission time:
	// Header  = "Authorization"
	// Format  = "Bearer {key}"
	// These defaults match the aileron runtime's current hard-coded
	// injection (internal/sandbox/host.go), so existing connectors that
	// don't set them keep working once the runtime reads them.
	Header string
	Format string

	// oauth2 kind fields. Render under [capabilities.credential.oauth2]
	// per the Google connector's existing manifest shape.
	AuthorizeURL string
	TokenURL     string
	ClientID     string
	ClientSecret string
	Scopes       []string
}

CredentialOverlay declares the credential kind plus the per-kind config the manifest emitter renders.

Fields are flat by design — yaml.v3 doesn't support tagged unions cleanly. The kind decides which fields render into the emitted manifest.toml: api_key uses Header/Format; oauth2 uses AuthorizeURL/TokenURL/ClientID/ClientSecret/Scopes.

type DispatchEmitter

type DispatchEmitter struct{}

DispatchEmitter renders connector/dispatch.go. Contains the JSON I/O types (input, output, outputError), the Transport function type that HandlerEmitter's handlers reference, the knownOps map derived from the emittable ops, the run() entry point, the per-op dispatch switch, and the writeError / writeSuccess / classify helpers.

No-op when there are no GraphQL operations to dispatch — keeps OpenAPI-only test cases unaffected. Output passes through go/format so it matches `gofmt` byte-for-byte.

func (DispatchEmitter) Emit

func (DispatchEmitter) Emit(spec Spec, overlay Overlay, outDir string) error

type Emitter

type Emitter interface {
	Emit(spec Spec, overlay Overlay, outDir string) error
}

Emitter writes connector scaffolding derived from a Spec and Overlay into an output directory. One implementation per output kind: ActionEmitter + ManifestEmitter + SuiteEmitter today, with typed Go client emission to follow.

Each emitter is responsible for deciding whether to act on a given (Spec, Overlay) pair. ManifestEmitter no-ops when overlay lacks the connector + credential + network fields; SuiteEmitter no-ops when overlay.Suite is nil. That keeps small / partial overlays building just the actions they need without forcing every connector to declare every block.

type HandlerEmitter

type HandlerEmitter struct{}

HandlerEmitter renders connector/handlers.go from the spec + overlay. One per-op function plus the shared arg-extraction helpers. Each handler builds a GraphQL query string (with the selection set derived from Operation.ReturnScalarFields), validates required args, rebuilds any input-object wrapping the loader flattened, and calls the connector's `graphqlCall` transport.

The emitted file assumes the connector also has a hand-written (or later codegen-emitted) `dispatch.go` defining `Transport` and a `graphql.go` defining `graphqlCall` in the same package.

Skipped silently when the spec has no GraphQL operations (every op's Method falls outside QUERY / MUTATION / SUBSCRIPTION) — keeps OpenAPI test cases green while OpenAPI handler emission lands later.

func (HandlerEmitter) Emit

func (HandlerEmitter) Emit(spec Spec, overlay Overlay, outDir string) error

Emit writes connector/handlers.go under outDir. No-op when there are no GraphQL operations to emit.

type ManifestEmitter

type ManifestEmitter struct{}

ManifestEmitter renders connector/manifest.toml from the overlay's connector + credential + network blocks. Skipped silently when the overlay doesn't carry enough to render a runnable manifest — see shouldEmitManifest below — so existing test cases that only configure actions stay unchanged.

The `0.0.0-dev` and `sha256:bound-at-release` placeholders the action.md emitter follows apply here too; the release workflow substitutes them per ADR-0002.

func (ManifestEmitter) Emit

func (ManifestEmitter) Emit(_ Spec, overlay Overlay, outDir string) error

Emit writes connector/manifest.toml under outDir. No-op if the overlay doesn't declare credential.kind + network.hosts + connector.name + connector.publisher.

type NetworkOverlay

type NetworkOverlay struct {
	Hosts []string
}

NetworkOverlay enumerates the host:port pairs the connector is allowed to dial. Renders into [capabilities.network] in the emitted manifest.

type Operation

type Operation struct {
	// ID is the canonical operation identifier — drives every downstream
	// Go identifier (handler name, query const) and emitted artifact name
	// (action directory, snake_case op string). For OpenAPI this is the
	// operationId. For GraphQL this is the root-field name, suffixed with
	// "Mutation" when the same field name also appears on type Query (and
	// similarly with "Subscription" for three-way collisions) so that the
	// emitted Go and action names stay unique. The raw GraphQL field name
	// (which the emitted query string must use to call the field) lives in
	// FieldName.
	ID string
	// FieldName is the raw GraphQL root-field name for GraphQL operations.
	// Empty for OpenAPI. The handler emitter uses this when writing the
	// GraphQL document so the field invocation matches the schema even
	// when ID has been suffixed to disambiguate a Query/Mutation collision.
	FieldName string
	// Method is "GET"/"POST"/... for OpenAPI or "QUERY"/"MUTATION" for
	// GraphQL.
	Method string
	// Path is the OpenAPI URL path (empty for GraphQL operations).
	Path string
	// Summary is the human-readable one-line description (OpenAPI summary
	// or GraphQL field description).
	Summary string
	// Parameters are out-of-body inputs (OpenAPI query/path/header params
	// or GraphQL field arguments).
	Parameters []Parameter
	// RequestBody is the JSON body schema for OpenAPI operations; nil when
	// absent or for GraphQL.
	RequestBody *RequestBody
	// ReturnType is the named GraphQL return type (e.g. "Issue") or the
	// OpenAPI response schema name (when populated by the loader). Lists
	// and non-null wrappers are unwrapped to the inner named type;
	// empty when the loader cannot determine one (OpenAPI today).
	ReturnType string
	// ReturnTypeIsScalar is true when ReturnType is a GraphQL scalar /
	// enum / built-in (String, Int, Boolean, ID, custom DateTime, etc).
	// Handler emitters use this to skip building a selection set and
	// just request the value directly.
	ReturnTypeIsScalar bool
	// ReturnFields is the (sorted) selection tree for the return type.
	// Scalar fields appear as leaves (Nested empty); fields whose own
	// type is itself an OBJECT / INTERFACE recurse exactly one level —
	// their scalar fields are surfaced as Nested entries so handler
	// queries get the canonical Linear-style "wrapper { success,
	// lastSyncId, entity { id, ... } }" shape. Empty for scalar
	// returns, unions, and unknown types.
	ReturnFields []ReturnField
	// ArgTypes maps each GraphQL field argument name to its full GraphQL
	// type string (e.g., "String!", "IssueCreateInput!", "[String!]").
	// Used by the handler emitter to build the variables declaration in
	// the emitted GraphQL query string. Empty for OpenAPI ops.
	ArgTypes map[string]string
}

Operation is one spec operation surfaced to the emitter. For OpenAPI this is one HTTP method + path; for GraphQL one Query/Mutation root field.

type OperationOverlay

type OperationOverlay struct {
	// Idempotent (when non-nil) overrides whether retries are safe
	// (ADR-0010). Nil means use the default for the operation's method.
	Idempotent *bool
	// Approval is "required" to gate per-call (ADR-0009), "none" to
	// relax. Empty means use the default for the operation's method.
	Approval string
	// Capabilities is the capability identifier list this operation
	// needs. Empty means default to [snake_case(operationId)].
	Capabilities []string
	// Intent overrides the spec's operation summary when set.
	Intent string
	// ActionName overrides the kebab-cased operationId when set.
	ActionName string
	// ExecuteID overrides the first-word default for the [[execute]] id.
	ExecuteID string
}

OperationOverlay carries per-operation overrides on top of the kind-based governance defaults (Query/Subscription/GET/HEAD/PUT → idempotent, no approval; Mutation/POST/PATCH/DELETE → non-idempotent, approval-required). Every field is optional: an empty field means "use the default."

Booleans use a pointer to distinguish "user explicitly set this" from "user did not set this" — yaml.v3 leaves a *bool nil when the key is absent and points at the parsed value when present.

type Options

type Options struct {
	// SpecPath points at an OpenAPI YAML or GraphQL schema file.
	SpecPath string
	// OverlayPath points at the gen.yaml overlay describing per-operation
	// governance metadata.
	OverlayPath string
	// OutDir is the directory into which scaffolding is written. It is
	// created if it does not exist.
	OutDir string
}

Options configures a Generate call.

type Overlay

type Overlay struct {
	Connector ConnectorOverlay
	// Operations are governance overrides keyed by spec operationId.
	// An entry here is an override on top of the kind-based defaults
	// (Query/Subscription/GET/HEAD/PUT → idempotent, non-write;
	// Mutation/POST/PATCH/DELETE → non-idempotent, approval-required).
	// Operations not declared here still emit, using the defaults. An
	// entry whose operationId does not appear in the spec is a typo
	// and is rejected at emission time.
	Operations map[string]OperationOverlay
	// Exclude drops specific spec operations from emission. Use for
	// introspection helpers, deprecated endpoints, admin/destructive
	// paths the connector deliberately doesn't surface, etc. Listing
	// an operationId here that the spec doesn't define is a typo and
	// is rejected at emission time.
	Exclude []string
	// Suite is optional. When absent, SuiteEmitter is a no-op (some
	// connectors prefer per-action installability without a top-level
	// suite).
	Suite *SuiteOverlay
}

Overlay is the parsed gen.yaml — connector-level metadata plus a per-operation map keyed by OpenAPI operationId (or GraphQL field name in future loaders).

The shape is provisional until two greenfield connectors have shipped without breaking-change releases (acceptance criterion in ALRubinger/aileron#893).

func LoadOverlay

func LoadOverlay(path string) (Overlay, error)

LoadOverlay reads a gen.yaml file at path and parses it into Overlay.

type Parameter

type Parameter struct {
	Name        string
	Type        string
	Description string
	Required    bool
	// WrappedIn names the original GraphQL argument when this Parameter
	// came from flattening a GraphQL input object (e.g. `input` for
	// `issueCreate(input: IssueCreateInput!)`). Empty when the param
	// was a direct scalar arg. The action.md emitter ignores it; the
	// handler emitter uses it to rebuild the wrapping JSON shape the
	// API expects.
	WrappedIn string
}

Parameter is one input surfaced as an [[inputs]] block in the emitted action.md.

type RequestBody

type RequestBody struct {
	Fields []Parameter
}

RequestBody captures the JSON body schema for an OpenAPI operation. Nil for operations without one and for all GraphQL operations.

type ReturnField

type ReturnField struct {
	Name   string
	Nested []ReturnField
}

ReturnField is one entry in the GraphQL selection set for an operation's return type. Leaves (scalars / enums) have Nested empty; object-typed fields have Nested populated with their own scalar leaves. Recursion stops at one level (Nested entries cannot themselves have Nested entries) — deeper recursion blows up query payloads and risks server-side depth limits. Connectors that need deeper selection can declare it per-op via overlay in a future PR.

type Spec

type Spec struct {
	Operations []Operation
}

Spec is the parsed representation of an OpenAPI or GraphQL specification — operation-by-operation, with only the fields consumed by the emitters.

func LoadSpec

func LoadSpec(path string) (Spec, error)

LoadSpec dispatches by file extension: .graphql/.graphqls/.gql route to the GraphQL loader, .yaml/.yml/.json to the OpenAPI loader. Both produce the same Spec shape so the emitter stays format-agnostic.

type SuiteEmitter

type SuiteEmitter struct{}

SuiteEmitter renders suite.toml at the repo root from the overlay's Suite block plus the same operation-filter ActionEmitter uses, so the suite stays in sync by construction. No-op when overlay.Suite is nil.

func (SuiteEmitter) Emit

func (SuiteEmitter) Emit(spec Spec, overlay Overlay, outDir string) error

type SuiteOverlay

type SuiteOverlay struct {
	Name        string
	Description string
}

SuiteOverlay drives the top-level suite.toml. Action paths are computed at emission time from the same filter ActionEmitter applies, so the suite stays in sync with the emitted actions/.

type TransportEmitter

type TransportEmitter struct{}

TransportEmitter renders connector/graphql.go — the GraphQL POST envelope wrapper, response parser, and the *graphqlErrors error type that DispatchEmitter's classify() inspects. Templated against the overlay's connector.endpoint URL and credential.kind.

No-op when overlay.Connector.Endpoint is empty (transport URL is load-bearing; without it the emitted file would compile but the connector would dial the wrong host).

func (TransportEmitter) Emit

func (TransportEmitter) Emit(spec Spec, overlay Overlay, outDir string) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL