Skip to content

Improved Nullable field support and allOf inheritance#80

Merged
RawToast merged 3 commits into
masterfrom
open-api-fixes
Jun 10, 2026
Merged

Improved Nullable field support and allOf inheritance#80
RawToast merged 3 commits into
masterfrom
open-api-fixes

Conversation

@RawToast

@RawToast RawToast commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added a fetch-based Records client for listing, retrieving, and updating record status.
    • Introduced a new OpenAPI spec covering records and error schemas.
    • Schema generator now preserves required fields when merging inherited/allOf schemas.
  • Bug Fixes

    • Improved nullable-type handling so nullable string/number/integer fields are generated and validated correctly.
    • Client now surfaces composed API error messages for clearer failures.
  • Tests

    • Added tests covering nullable handling, allOf error schemas, client behaviors, and runtime validation.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ebd54ce-e012-47e6-a616-a81a3bc48cb6

📥 Commits

Reviewing files that changed from the base of the PR and between f26fc8c and 52c635d.

📒 Files selected for processing (2)
  • packages/examples/src/__tests__/records-client-fetch.test.ts
  • packages/examples/src/records-client-fetch.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/examples/src/records-client-fetch.ts

📝 Walkthrough

Walkthrough

Adds a new OpenAPI 3.1 spec with nullable fields and allOf-composed errors, updates the Zod generator to merge/preserve required fields across allOf, integrates the spec into the example-generation pipeline, adds end-to-end and unit tests, and implements a fetch-based RecordsClientFetch with tests.

Changes

Nullable allOf schema generation and consumption

Layer / File(s) Summary
OpenAPI 3.1 spec with nullable and allOf schemas
packages/specs/resources/nullable-allof-errors.yaml, packages/specs/index.ts
New spec defining listRecords, getRecord, updateRecordStatus, record domain types with nullable fields (type: [string,"null"]), and allOf-composed error schemas inheriting BaseEndpointError.
Zod schema-generator allOf required-field handling
packages/zenko/src/core/schema-generator.ts
Adds getRequiredFromSchema and getMergedRequiredFromAllOfParts, extends getZodTypeFromSchema/buildZodObject to accept ownName and allOfRequired, and propagates merged required properties so required fields are preserved when composing via allOf.
Schema-generator test coverage for nullable and allOf
packages/zenko/src/core/__tests__/schema-generator.test.ts
Unit tests for type: [<primitive>,"null"] → Zod unions, single/empty type arrays, and for ensuring required enum fields remain required after allOf composition (including nested $ref).
End-to-end spec generation and integration
packages/zenko/src/__tests__/nullable-allof-errors.test.ts, packages/examples/generate.js
Generates TypeScript from the new spec, validates snapshots and exports, dynamically imports runtime schemas to assert nullable behavior and allOf error inheritance, and wires the spec into the examples generation script with fail-fast behavior.
RecordsClientFetch implementation and tests
packages/examples/src/records-client-fetch.ts, packages/examples/src/__tests__/records-client-fetch.test.ts
Adds RecordsClientFetch with a generic request helper, response/error schema parsing, and methods listRecords, getRecord, updateRecordStatus; tests cover nullable parsing, pagination URL, PATCH requests, API error handling, and schema validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • RawToast/zenko#60: Both PRs modify packages/zenko/src/core/schema-generator.ts to alter required-property detection and propagation logic.
  • RawToast/zenko#40: Related changes to getZodTypeFromSchema/allOf handling and required-field behavior in the schema generator.
  • RawToast/zenko#73: Similar extension of packages/examples/generate.js to add a new spec generation step and include it in the pipeline.

Suggested labels

enhancement

Poem

🐰 I nibble specs where nulls may hide,
allOf stitched tight so required fields abide,
I fetch and parse with tidy little hops,
tests keep errors fenced in tidy little stops,
Zenko hops forward — schema carrots supplied!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main changes: adding support for nullable fields (via type arrays) and fixing allOf inheritance to preserve required fields in composed schemas.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch open-api-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.87640% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.35%. Comparing base (0d86be4) to head (52c635d).

Files with missing lines Patch % Lines
packages/zenko/src/core/schema-generator.ts 98.83% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #80      +/-   ##
==========================================
+ Coverage   97.31%   97.35%   +0.03%     
==========================================
  Files          20       20              
  Lines        3130     3213      +83     
==========================================
+ Hits         3046     3128      +82     
- Misses         84       85       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/zenko/src/core/schema-generator.ts (1)

700-737: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve nullable object unions by evaluating type arrays first.

At Line 700, schemas with properties short-circuit to buildZodObject(...) before the Array.isArray(schema.type) path runs.
That makes type: ["object", "null"] generate a non-nullable object schema and drops null.

Suggested fix
-  // Check for object with properties (including those without explicit type)
-  if (schema.type === "object" || schema.properties || schema.allOf) {
-    return buildZodObject(
-      schema,
-      options,
-      nameMap,
-      schemaRegistry,
-      currentSchemaName,
-      allOfRequired
-    )
-  }
-
   if (Array.isArray(schema.type)) {
     if (schema.type.length === 0) return "z.unknown()"
     if (schema.type.length === 1) {
       return getZodTypeFromSchema(
         { ...schema, type: schema.type[0] },
         options,
         nameMap,
         schemaRegistry,
         currentSchemaName,
         ownName,
         allOfRequired
       )
     }
     const unionParts = schema.type.map((memberType: string) =>
       getZodTypeFromSchema(
         { ...schema, type: memberType },
         options,
         nameMap,
         schemaRegistry,
         currentSchemaName,
         ownName,
         allOfRequired
       )
     )
     return `z.union([${unionParts.join(", ")}])`
   }
+
+  // Check for object with properties (including those without explicit type)
+  if (schema.type === "object" || schema.properties || schema.allOf) {
+    return buildZodObject(
+      schema,
+      options,
+      nameMap,
+      schemaRegistry,
+      currentSchemaName,
+      allOfRequired
+    )
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/zenko/src/core/schema-generator.ts` around lines 700 - 737, The
current early return that calls buildZodObject when schema.type === "object" or
schema.properties causes schemas with type arrays (e.g., type:
["object","null"]) to lose the nullable union; move the
Array.isArray(schema.type) handling ahead of the object/properties check or add
a guard so that if schema.type is an array you first resolve the union via
getZodTypeFromSchema (the Array.isArray branch that builds unionParts) before
falling back to buildZodObject; adjust logic in getZodTypeFromSchema to prefer
the type-array branch so buildZodObject is only used for definitive object types
(e.g., when schema.type === "object" or no type array present).
🧹 Nitpick comments (2)
packages/zenko/src/__tests__/nullable-allof-errors.test.ts (1)

1-7: ⚡ Quick win

Reorder imports to match the TypeScript import-order rule.

Built-in modules should come before third-party imports in this file.

Proposed fix
-import { describe, test, expect } from "bun:test"
 import * as fs from "fs"
 import * as path from "path"
 import { pathToFileURL } from "url"
+import { describe, test, expect } from "bun:test"
 import { nullableAllOfErrorsYamlPath } from "`@zenko/specs`"
 import { parseYaml } from "../utils/yaml"
 import { generate, type OpenAPISpec } from "../zenko"

As per coding guidelines, TypeScript imports must be ordered built-in
→ third-party → workspace → local modules.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/zenko/src/__tests__/nullable-allof-errors.test.ts` around lines 1 -
7, Reorder the import statements to satisfy the TypeScript import-order rule:
place built-in modules (fs, path, url's pathToFileURL) first, then third-party
imports (bun:test), then workspace packages (e.g., `@zenko/specs` ->
nullableAllOfErrorsYamlPath), and lastly local project imports (../utils/yaml ->
parseYaml and ../zenko -> generate, OpenAPISpec); update the import block so
symbols like describe, test, expect remain associated with bun:test and all
other named imports remain unchanged while only changing their order.

Source: Coding guidelines

packages/zenko/src/core/__tests__/schema-generator.test.ts (1)

584-612: ⚡ Quick win

Use named snapshots for these generated-output assertions.

These new cases are validating rendered generator output; converting the assertion blocks to toMatchSnapshot("<descriptive-name>") would align with the test guideline and reduce brittle string-fragment checks.

As per coding guidelines, packages/zenko/src/**/*.test.{ts,tsx} should “Use snapshot testing with expect(result).toMatchSnapshot() for output verification.”

Also applies to: 714-806

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/zenko/src/core/__tests__/schema-generator.test.ts` around lines 584
- 612, Replace the brittle string equality assertions in the tests that call
getZodTypeFromSchema(...) with named snapshot assertions: for each test case
(e.g., OpenAPI 3.1 nullable string/number/integer, single-element type array,
empty type array) replace expect(...).toBe("...") with
expect(getZodTypeFromSchema(...)).toMatchSnapshot("<descriptive-name>") using a
clear descriptive snapshot name (e.g., "nullable-string-union",
"nullable-integer-union", "nullable-number-union", "single-element-string-type",
"empty-type-array"); ensure each test uses a distinct snapshot name so the
snapshots are stored and compared instead of hard-coded string fragments and
update any similar blocks later in the file (around the other cases referenced)
to the same pattern.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/examples/src/records-client-fetch.ts`:
- Around line 35-41: The code currently always parses non-OK responses with
ListRecordsError; change each handler to validate against the endpoint-specific
error schema instead (e.g., in the listRecords flow keep ListRecordsError, in
getRecord use GetRecordError, in updateRecordStatus use
UpdateRecordStatusError), by replacing the generic parse call
(ListRecordsError.safeParse) with the appropriate schema.safeParse for the
function handling the response, and fall back to the generic HTTP Error
(`response.status`/`response.statusText`) only if the endpoint-specific parse
fails; apply the same fix to the other non-OK response blocks in this file (the
block around lines 48-67).

---

Outside diff comments:
In `@packages/zenko/src/core/schema-generator.ts`:
- Around line 700-737: The current early return that calls buildZodObject when
schema.type === "object" or schema.properties causes schemas with type arrays
(e.g., type: ["object","null"]) to lose the nullable union; move the
Array.isArray(schema.type) handling ahead of the object/properties check or add
a guard so that if schema.type is an array you first resolve the union via
getZodTypeFromSchema (the Array.isArray branch that builds unionParts) before
falling back to buildZodObject; adjust logic in getZodTypeFromSchema to prefer
the type-array branch so buildZodObject is only used for definitive object types
(e.g., when schema.type === "object" or no type array present).

---

Nitpick comments:
In `@packages/zenko/src/__tests__/nullable-allof-errors.test.ts`:
- Around line 1-7: Reorder the import statements to satisfy the TypeScript
import-order rule: place built-in modules (fs, path, url's pathToFileURL) first,
then third-party imports (bun:test), then workspace packages (e.g., `@zenko/specs`
-> nullableAllOfErrorsYamlPath), and lastly local project imports (../utils/yaml
-> parseYaml and ../zenko -> generate, OpenAPISpec); update the import block so
symbols like describe, test, expect remain associated with bun:test and all
other named imports remain unchanged while only changing their order.

In `@packages/zenko/src/core/__tests__/schema-generator.test.ts`:
- Around line 584-612: Replace the brittle string equality assertions in the
tests that call getZodTypeFromSchema(...) with named snapshot assertions: for
each test case (e.g., OpenAPI 3.1 nullable string/number/integer, single-element
type array, empty type array) replace expect(...).toBe("...") with
expect(getZodTypeFromSchema(...)).toMatchSnapshot("<descriptive-name>") using a
clear descriptive snapshot name (e.g., "nullable-string-union",
"nullable-integer-union", "nullable-number-union", "single-element-string-type",
"empty-type-array"); ensure each test uses a distinct snapshot name so the
snapshots are stored and compared instead of hard-coded string fragments and
update any similar blocks later in the file (around the other cases referenced)
to the same pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3f2acdf-6482-46a2-85d1-e63f154b65a0

📥 Commits

Reviewing files that changed from the base of the PR and between 0d86be4 and f26fc8c.

⛔ Files ignored due to path filters (1)
  • packages/zenko/src/__tests__/__snapshots__/nullable-allof-errors.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • packages/examples/generate.js
  • packages/examples/src/__tests__/records-client-fetch.test.ts
  • packages/examples/src/records-client-fetch.ts
  • packages/specs/index.ts
  • packages/specs/resources/nullable-allof-errors.yaml
  • packages/zenko/src/__tests__/nullable-allof-errors.test.ts
  • packages/zenko/src/core/__tests__/schema-generator.test.ts
  • packages/zenko/src/core/schema-generator.ts

Comment thread packages/examples/src/records-client-fetch.ts
@RawToast RawToast merged commit 881cbd2 into master Jun 10, 2026
6 checks passed
@RawToast RawToast deleted the open-api-fixes branch June 10, 2026 01:52
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