feat(codegen/cloudflare): add lookup and adopt methods to all models (#1142) #111

Merged
stack72 merged 2 commits from feat/cloudflare-lookup-adopt-1142 into main 2026-07-18 00:49:22 +00:00
Owner

Summary

  • Adds lookup and adopt methods to every generated Cloudflare extension model via the codegen pipeline (extensionModelGenerator.ts)
  • lookup lists resources via the Cloudflare API (using the resource's detected pagination style), filters by matching set GlobalArgs scalar fields (string/number/boolean/enum) against each record, requires exactly 1 match, and writes it to state — enabling users to discover and import existing infrastructure by name/type
  • adopt fetches a resource by its Cloudflare ID and writes it to state with no validation — for importing when the ID is already known (from a dashboard, list call, or another workflow)
  • Updates the Cloudflare README template, design doc (new Section 13), module doc comments, and snapshot tests

Codegen changes

File Change
codegen/cloudflare/extensionModelGenerator.ts Added lookup + adopt method generation, listAll import, collectFilterableFields helper
codegen/cloudflare/extensionModelGenerator_test.ts 4 new assertion tests for filter logic, synthetic name exclusion, adopt naming, pagination style
codegen/shared/readmeGenerator.ts Added lookup + adopt bullets to Cloudflare README template
codegen/designs/cloudflare.md New Section 13 documenting lookup/adopt semantics, filter logic, instance naming

Generated output

186 model files across 76 services regenerated with:

  • New lookup and adopt methods
  • CalVer version bumps + upgrade entries
  • Updated manifest versions
  • Updated README method lists

Verification

  • Snapshot tests pass (11/11)
  • deno check, deno lint, deno fmt clean
  • Full regeneration idempotent (second run = 0 changed)
  • Upgrade path verified across 10 published extensions (dns, d1, r2, workers-kv, tunnel, tokens, email, custom-hostnames, load-balancing, vectorize) — all upgrade cleanly from published version to new version

Test plan

  • Snapshot tests for lookup and adopt method generation
  • Assertion tests: scalar filter inclusion, object/array/auth exclusion, synthetic name skip, cursor pagination
  • Full Cloudflare regeneration — no collateral damage
  • Idempotency — second generation produces zero diff
  • Upgrade path — 10 extensions verified via swamp extension source add + method run

Closes swamp-club/lab#1142

🤖 Generated with Claude Code

## Summary - Adds **lookup** and **adopt** methods to every generated Cloudflare extension model via the codegen pipeline (`extensionModelGenerator.ts`) - **lookup** lists resources via the Cloudflare API (using the resource's detected pagination style), filters by matching set GlobalArgs scalar fields (string/number/boolean/enum) against each record, requires exactly 1 match, and writes it to state — enabling users to discover and import existing infrastructure by name/type - **adopt** fetches a resource by its Cloudflare ID and writes it to state with no validation — for importing when the ID is already known (from a dashboard, list call, or another workflow) - Updates the Cloudflare README template, design doc (new Section 13), module doc comments, and snapshot tests ### Codegen changes | File | Change | |------|--------| | `codegen/cloudflare/extensionModelGenerator.ts` | Added lookup + adopt method generation, `listAll` import, `collectFilterableFields` helper | | `codegen/cloudflare/extensionModelGenerator_test.ts` | 4 new assertion tests for filter logic, synthetic name exclusion, adopt naming, pagination style | | `codegen/shared/readmeGenerator.ts` | Added lookup + adopt bullets to Cloudflare README template | | `codegen/designs/cloudflare.md` | New Section 13 documenting lookup/adopt semantics, filter logic, instance naming | ### Generated output 186 model files across 76 services regenerated with: - New `lookup` and `adopt` methods - CalVer version bumps + upgrade entries - Updated manifest versions - Updated README method lists ### Verification - Snapshot tests pass (11/11) - `deno check`, `deno lint`, `deno fmt` clean - Full regeneration idempotent (second run = 0 changed) - Upgrade path verified across 10 published extensions (dns, d1, r2, workers-kv, tunnel, tokens, email, custom-hostnames, load-balancing, vectorize) — all upgrade cleanly from published version to new version ## Test plan - [x] Snapshot tests for lookup and adopt method generation - [x] Assertion tests: scalar filter inclusion, object/array/auth exclusion, synthetic name skip, cursor pagination - [x] Full Cloudflare regeneration — no collateral damage - [x] Idempotency — second generation produces zero diff - [x] Upgrade path — 10 extensions verified via `swamp extension source add` + method run Closes swamp-club/lab#1142 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(codegen/cloudflare): add lookup and adopt methods to all models (#1142)
Some checks failed
CI / workflows/s3-bootstrap - test (pull_request) Has been skipped
CI / workflows/gcs-bootstrap - lockfile up to date (pull_request) Has been skipped
CI / workflows/s3-bootstrap - lockfile up to date (pull_request) Has been skipped
CI / cve/dirtyfrag - check (pull_request) Has been skipped
CI / cve/dirtyfrag - fmt (pull_request) Has been skipped
CI / cve/dirtyfrag - lint (pull_request) Has been skipped
CI / cve/dirtyfrag - test (pull_request) Has been skipped
CI / cve/mini-shai-hulud - check (pull_request) Has been skipped
CI / cve/mini-shai-hulud - fmt (pull_request) Has been skipped
CI / cve/mini-shai-hulud - lint (pull_request) Has been skipped
CI / cve/mini-shai-hulud - test (pull_request) Has been skipped
CI / cve/researcher - check (pull_request) Has been skipped
CI / cve/researcher - fmt (pull_request) Has been skipped
CI / cve/researcher - lint (pull_request) Has been skipped
CI / cve/researcher - test (pull_request) Has been skipped
CI / cve/dirtyfrag - lockfile up to date (pull_request) Has been skipped
CI / cve/mini-shai-hulud - lockfile up to date (pull_request) Has been skipped
CI / cve/researcher - lockfile up to date (pull_request) Has been skipped
CI / software-factory - check (pull_request) Has been skipped
CI / software-factory - fmt (pull_request) Has been skipped
CI / software-factory - lint (pull_request) Has been skipped
CI / software-factory - test (pull_request) Has been skipped
CI / software-factory - lockfile up to date (pull_request) Has been skipped
CI / model/digitalocean - check (pull_request) Successful in 1m5s
CI / model/digitalocean - lockfile up to date (pull_request) Successful in 58s
CI / model/hetzner-cloud - check (pull_request) Successful in 1m10s
CI / CI Security Review (pull_request) Has been skipped
CI / Adversarial Code Review (pull_request) Has been skipped
CI / Claude Code Review (pull_request) Has been skipped
CI / Merge Gate (pull_request) Has been cancelled
de6b0af9a4
Add two new methods to every generated Cloudflare extension model for
importing existing infrastructure into swamp state:

- **lookup** — lists resources via the API, filters by matching set
  GlobalArgs scalar fields, requires exactly 1 match, writes to state.
  For discovering existing resources by name/type.
- **adopt** — fetches a resource by Cloudflare ID, writes to state with
  no validation. For importing when the ID is already known.

The codegen change in extensionModelGenerator.ts propagates to all 186
Cloudflare models across 76 services on regeneration.

Closes swamp-club/lab#1142

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: format cloudflare design doc
All checks were successful
CI / workflows/gcs-bootstrap - test (pull_request) Has been skipped
CI / workflows/s3-bootstrap - test (pull_request) Has been skipped
CI / workflows/gcs-bootstrap - lockfile up to date (pull_request) Has been skipped
CI / cve/dirtyfrag - check (pull_request) Has been skipped
CI / workflows/s3-bootstrap - lockfile up to date (pull_request) Has been skipped
CI / cve/dirtyfrag - fmt (pull_request) Has been skipped
CI / cve/dirtyfrag - lint (pull_request) Has been skipped
CI / cve/dirtyfrag - test (pull_request) Has been skipped
CI / cve/mini-shai-hulud - check (pull_request) Has been skipped
CI / cve/mini-shai-hulud - fmt (pull_request) Has been skipped
CI / cve/mini-shai-hulud - lint (pull_request) Has been skipped
CI / cve/mini-shai-hulud - test (pull_request) Has been skipped
CI / cve/researcher - test (pull_request) Has been skipped
CI / cve/researcher - check (pull_request) Has been skipped
CI / cve/researcher - fmt (pull_request) Has been skipped
CI / cve/researcher - lint (pull_request) Has been skipped
CI / cve/dirtyfrag - lockfile up to date (pull_request) Has been skipped
CI / cve/mini-shai-hulud - lockfile up to date (pull_request) Has been skipped
CI / cve/researcher - lockfile up to date (pull_request) Has been skipped
CI / software-factory - check (pull_request) Has been skipped
CI / software-factory - fmt (pull_request) Has been skipped
CI / software-factory - lint (pull_request) Has been skipped
CI / software-factory - test (pull_request) Has been skipped
CI / software-factory - lockfile up to date (pull_request) Has been skipped
CI / CI Security Review (pull_request) Has been skipped
CI / model/digitalocean - check (pull_request) Successful in 1m9s
CI / model/hetzner-cloud - check (pull_request) Successful in 59s
CI / Claude Code Review (pull_request) Successful in 2m32s
CI / Adversarial Code Review (pull_request) Successful in 3m58s
CI / Merge Gate (pull_request) Successful in 28s
ca805f5130
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

Code Review

Blocking Issues

None.

Suggestions

  1. Misleading test name (codegen/cloudflare/extensionModelGenerator_test.ts, line 577): The test is named "adopt - uses cursor pagination style in lookup for cursor-paginated resources" but it exercises the lookup method (by asserting on listAll), not adopt. The adopt method calls read directly and never touches listAll. Renaming to "lookup - uses cursor pagination style for cursor-paginated resources" would match the assertion.

  2. Lookup can be vacuously unfilterable (codegen/cloudflare/extensionModelGenerator.ts, collectFilterableFields): If a resource has only non-scalar properties (arrays, objects), filterFields is empty and the generated lookup method unconditionally throws "At least one global argument must be set to filter by" — even with all GlobalArgs populated. The design doc acknowledges this is intentional for now, but it may surprise users of such resources. Consider a generator-time warning when a resource produces an unfilterable lookup.

  3. collectFilterableFields merge order (extensionModelGenerator.ts, line 697): The function merges updateProperties first, then createProperties (so create wins on collision). This is consistent with buildGlobalArgsProperties, but the opposite of the natural expectation (update fields refine create fields). A comment explaining the intentional ordering would help future maintainers.

## Code Review ### Blocking Issues None. ### Suggestions 1. **Misleading test name** (`codegen/cloudflare/extensionModelGenerator_test.ts`, line 577): The test is named `"adopt - uses cursor pagination style in lookup for cursor-paginated resources"` but it exercises the `lookup` method (by asserting on `listAll`), not `adopt`. The `adopt` method calls `read` directly and never touches `listAll`. Renaming to `"lookup - uses cursor pagination style for cursor-paginated resources"` would match the assertion. 2. **Lookup can be vacuously unfilterable** (`codegen/cloudflare/extensionModelGenerator.ts`, `collectFilterableFields`): If a resource has only non-scalar properties (arrays, objects), `filterFields` is empty and the generated `lookup` method unconditionally throws "At least one global argument must be set to filter by" — even with all GlobalArgs populated. The design doc acknowledges this is intentional for now, but it may surprise users of such resources. Consider a generator-time warning when a resource produces an unfilterable `lookup`. 3. **`collectFilterableFields` merge order** (`extensionModelGenerator.ts`, line 697): The function merges `updateProperties` first, then `createProperties` (so create wins on collision). This is consistent with `buildGlobalArgsProperties`, but the opposite of the natural expectation (update fields refine create fields). A comment explaining the intentional ordering would help future maintainers.
Author
Owner

Adversarial Review

Reviewed files (skipping all model/ auto-generated files per CLAUDE.md):

  • codegen/cloudflare/extensionModelGenerator.ts
  • codegen/cloudflare/extensionModelGenerator_test.ts
  • codegen/cloudflare/__snapshots__/extensionModelGenerator_test.ts.snap
  • codegen/designs/cloudflare.md
  • codegen/shared/readmeGenerator.ts

Medium

  1. Update method lacks identifyingField guard — extensionModelGenerator.ts:381-431

    The sync method generates a guard before using the stored identifying field:

    if (!existing.${resource.identifyingField}) throw new Error("Stored state has no ${resource.identifyingField} - cannot sync");
    

    The update method does NOT generate this guard. If stored state is corrupted (e.g., the create response lacked the field, or state was manually edited), the update method will pass undefined as the resource ID to the update() function, constructing a request to endpoint/undefined. Depending on the Cloudflare API, this could:

    • Return a confusing 404 (best case)
    • Match a different endpoint (worst case for path-based routing)

    Breaking example: User creates a resource, then manually edits the stored JSON and removes the id field. Running update would call update(endpoint, undefined, body, "PATCH", auth), producing a PATCH to https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/undefined.

    Suggested fix: Add the same guard as sync before line 424:

    lines.push(`        if (!existing.${resource.identifyingField}) throw new Error("Stored state has no ${resource.identifyingField} - run create first");`);
    
  2. Design doc listAll signature doesn't match generated code — codegen/designs/cloudflare.md section 10 (line ~715)

    The design doc shows listAll with 3 parameters:

    export async function listAll(
      endpoint: string,
      style: "page" | "cursor",
      queryParams?: Record<string, string>,
    ): Promise<Record<string, unknown>[]>;
    

    But the generated code passes 4 arguments (e.g., snapshot line 120):

    await listAll(endpoint, "page", undefined, { apiToken: g.apiToken, apiKey: g.apiKey, email: g.email })
    

    The 4th argument (auth overrides) is missing from the documented signature. Anyone reading the design doc to understand the shared lib contract will be misled.

    Suggested fix: Update the listAll and tryFindByField signatures in section 10 to include the auth parameter.

Low

  1. Grammar: "a" instead of "an" before vowel-starting display names — extensionModelGenerator.ts:207,239,283,350,357

    The generated description uses a hardcoded article: "Create a ${singular}". For display names starting with a vowel sound (e.g., "Address Map", "OAuth Client"), this produces "Create a Address Map" instead of "Create an Address Map". This appears in the create, get, lookup, adopt, delete, and sync method descriptions. Visible in the snapshot at line 747: "Create a Address Map".

    Suggested fix: Add a helper like:

    function article(word: string): string {
      return /^[aeiou]/i.test(word) ? "an" : "a";
    }
    
  2. paginationStyle: "none" could produce invalid generated code — extensionModelGenerator.ts:307

    The CloudflareResource.paginationStyle type (defined in pipeline.ts) allows "page" | "cursor" | "none". The lookup method generator interpolates it directly: listAll(endpoint, "${resource.paginationStyle}", ...). If a resource with paginationStyle: "none" reaches the generator, the generated code would call listAll(endpoint, "none", ...) — a value the shared lib doesn't handle. The pipeline likely prevents this, but the generator itself doesn't validate.

Verdict

PASS — The code is well-structured, follows existing patterns from other providers, has good test coverage (5 snapshot tests + 5 behavioral tests), and the design doc is thorough. The update-method guard gap (Medium #1) is a real inconsistency with the sync method but has low probability of triggering in practice since state is always written by methods that include the identifying field. The design doc signature mismatch (Medium #2) is a documentation accuracy issue. Neither is blocking.

## Adversarial Review Reviewed files (skipping all `model/` auto-generated files per CLAUDE.md): - `codegen/cloudflare/extensionModelGenerator.ts` - `codegen/cloudflare/extensionModelGenerator_test.ts` - `codegen/cloudflare/__snapshots__/extensionModelGenerator_test.ts.snap` - `codegen/designs/cloudflare.md` - `codegen/shared/readmeGenerator.ts` ### Medium 1. **Update method lacks `identifyingField` guard — `extensionModelGenerator.ts:381-431`** The `sync` method generates a guard before using the stored identifying field: ```typescript if (!existing.${resource.identifyingField}) throw new Error("Stored state has no ${resource.identifyingField} - cannot sync"); ``` The `update` method does NOT generate this guard. If stored state is corrupted (e.g., the create response lacked the field, or state was manually edited), the update method will pass `undefined` as the resource ID to the `update()` function, constructing a request to `endpoint/undefined`. Depending on the Cloudflare API, this could: - Return a confusing 404 (best case) - Match a different endpoint (worst case for path-based routing) **Breaking example:** User creates a resource, then manually edits the stored JSON and removes the `id` field. Running `update` would call `update(endpoint, undefined, body, "PATCH", auth)`, producing a PATCH to `https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/undefined`. **Suggested fix:** Add the same guard as sync before line 424: ```typescript lines.push(` if (!existing.${resource.identifyingField}) throw new Error("Stored state has no ${resource.identifyingField} - run create first");`); ``` 2. **Design doc `listAll` signature doesn't match generated code — `codegen/designs/cloudflare.md` section 10 (line ~715)** The design doc shows `listAll` with 3 parameters: ```typescript export async function listAll( endpoint: string, style: "page" | "cursor", queryParams?: Record<string, string>, ): Promise<Record<string, unknown>[]>; ``` But the generated code passes 4 arguments (e.g., snapshot line 120): ```typescript await listAll(endpoint, "page", undefined, { apiToken: g.apiToken, apiKey: g.apiKey, email: g.email }) ``` The 4th argument (auth overrides) is missing from the documented signature. Anyone reading the design doc to understand the shared lib contract will be misled. **Suggested fix:** Update the `listAll` and `tryFindByField` signatures in section 10 to include the auth parameter. ### Low 1. **Grammar: "a" instead of "an" before vowel-starting display names — `extensionModelGenerator.ts:207,239,283,350,357`** The generated description uses a hardcoded article: `"Create a ${singular}"`. For display names starting with a vowel sound (e.g., "Address Map", "OAuth Client"), this produces "Create a Address Map" instead of "Create an Address Map". This appears in the `create`, `get`, `lookup`, `adopt`, `delete`, and `sync` method descriptions. Visible in the snapshot at line 747: `"Create a Address Map"`. **Suggested fix:** Add a helper like: ```typescript function article(word: string): string { return /^[aeiou]/i.test(word) ? "an" : "a"; } ``` 2. **`paginationStyle: "none"` could produce invalid generated code — `extensionModelGenerator.ts:307`** The `CloudflareResource.paginationStyle` type (defined in `pipeline.ts`) allows `"page" | "cursor" | "none"`. The `lookup` method generator interpolates it directly: `listAll(endpoint, "${resource.paginationStyle}", ...)`. If a resource with `paginationStyle: "none"` reaches the generator, the generated code would call `listAll(endpoint, "none", ...)` — a value the shared lib doesn't handle. The pipeline likely prevents this, but the generator itself doesn't validate. ### Verdict **PASS** — The code is well-structured, follows existing patterns from other providers, has good test coverage (5 snapshot tests + 5 behavioral tests), and the design doc is thorough. The update-method guard gap (Medium #1) is a real inconsistency with the sync method but has low probability of triggering in practice since state is always written by methods that include the identifying field. The design doc signature mismatch (Medium #2) is a documentation accuracy issue. Neither is blocking.
stack72 deleted branch feat/cloudflare-lookup-adopt-1142 2026-07-18 00:49:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!111
No description provided.