Skip to content

feat(scripts): scaffold new resources from OpenAPI + drift-check existing ones#3

Open
timgl wants to merge 1 commit into
mainfrom
posthog-code/openapi-codegen-scaffolder
Open

feat(scripts): scaffold new resources from OpenAPI + drift-check existing ones#3
timgl wants to merge 1 commit into
mainfrom
posthog-code/openapi-codegen-scaffolder

Conversation

@timgl
Copy link
Copy Markdown
Contributor

@timgl timgl commented May 13, 2026

Summary

Two devtools, both driven by the PostHog OpenAPI spec (../posthog/frontend/tmp/openapi.json):

  • pnpm scaffold-resource --name <foo> --path <list-endpoint> --key-field <field> generates a new resource end-to-end: client.ts (deep Zod schema with all transitive component refs inlined via openapi-zod-client, plus CRUD wrappers), sdk.ts, pipeline.ts (skeleton with TODO(human) markers over the per-resource judgment calls — hash projection, identity mechanism, payload, validation, display), pipeline.test.ts, index.ts. Wires the resource into src/resources/index.ts, src/index.ts, scripts/lib/registry.ts, and flips the matching row in docs/resources.md.
  • pnpm check-resources walks the resource registry, compares each ServerXSchema (or type ServerX = {…} fallback for the older resources) against the current OpenAPI component, and reports added / removed / type-mismatched fields. readOnly server-set fields hidden by default; --all to include. Exits non-zero on drift so it can gate CI.

Skill updates (add-resource, add-singleton-resource) document the new entry point and the TODO(human) markers that still require human judgment.

Why the openapi-zod-client path

A homegrown OpenAPI-to-Zod converter would punt on $ref resolution, allOf merging, and recursive types — the cohort schema alone has 5 levels of $ref cycles. openapi-zod-client handles all of that. The output is 1.6MB for the full spec, so we don't commit it: the extractor (scripts/lib/extract-zod.ts) runs the CLI to a tmp file (cached by openapi mtime), parses the prettier-formatted output into top-level declarations, transitively closes from a single component, topo-sorts, and inlines only what each resource needs.

Two rewrites make the output satisfy strict Zod 4:

  • const X: z.ZodType<X> = z.lazy(...)const X = (z.lazy(...)) as z.ZodType<X>
  • z.discriminatedUnion("type", [...])z.union([...])

Test plan

  • pnpm typecheck passes (pre-existing acceptance.ts error is unrelated to this PR).
  • pnpm test — all 59 unit tests pass.
  • pnpm check-resources against the 4 shipped resources surfaces 27 added user fields and 1 removed (data_freshness_seconds on endpoint).
  • Smoke test: scaffold cohort end-to-end. ServerCohortSchema.filters is CohortFilters.nullish() with the full recursive CohortFilterGroup; created_by is UserBasic; cohort_type is z.union([CohortTypeEnum, BlankEnum, NullEnum]). Typechecks under strict Zod 4. Reverted before commit.
  • Reviewer: spot-check the generated client.ts for one resource by running pnpm scaffold-resource --name cohort --path /api/projects/{project_id}/cohorts/ --key-field name against your local ../posthog/frontend/tmp/openapi.json.

Created with PostHog Code

…ting ones

Adds two devtools driven by the PostHog OpenAPI spec:

- `pnpm scaffold-resource` generates the 5 files for a new resource
  (client/sdk/pipeline/pipeline.test/index), wires it into the resource
  registry, SDK index, and docs matrix, and inlines a deep Zod schema for
  the response component via `openapi-zod-client`. Recursive types and
  discriminated unions are rewritten to satisfy strict Zod 4.

- `pnpm check-resources` compares each shipped resource's `ServerXSchema`
  (or `ServerX` TS type fallback) against the current OpenAPI component
  and reports added / removed / type-mismatched fields. Exits non-zero on
  drift so it can gate CI. ReadOnly server-set fields hidden by default;
  `--all` includes them.

Skill updates: `add-resource` now opens with a "Start with the scaffolder"
section listing what's auto-generated and the `TODO(human)` markers that
require judgment, and gains a "Checking for API drift" section. The
singleton skill clarifies the scaffolder is collection-only.

Generated-By: PostHog Code
Task-Id: 667f64df-ab33-400b-a081-8eb131ecccf7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant