Skip to content

Migrate ModelTypeResolver and ObjectModelFactory to IR types#210

Open
MadsBogeskov wants to merge 6 commits intomainfrom
migrate-model-resolver-to-ir
Open

Migrate ModelTypeResolver and ObjectModelFactory to IR types#210
MadsBogeskov wants to merge 6 commits intomainfrom
migrate-model-resolver-to-ir

Conversation

@MadsBogeskov
Copy link
Copy Markdown
Contributor

@MadsBogeskov MadsBogeskov commented Apr 7, 2026

Summary

  • Rewrites ModelTypeResolver, ObjectModelFactory, StringResolver, IntegerResolver, and NumberResolver to accept IRSchema / IR format enums instead of SwaggerSwiftML.Schema / DataFormat
  • Adds Schema.toIR() and Node<Schema>.toIR() extension helpers in SwaggerSwiftML so call sites in SwaggerSwiftCore can convert incrementally without needing a full document context
  • Adds customFields: [String: String] to IRSchema so vendor extensions (x-override-name, x-internal, x-nullable) propagate through the IR layer
  • Updates APIRequestFactory and RequestParameterFactory to convert at the call boundary; preserves the historical behaviour where top-level response/body $refs are not namespace-qualified
  • Updates resolver unit tests to use IR format types

Test plan

  • swift test --parallel — all 41 tests pass (including golden file snapshot test)
  • Golden file output for TestService.swift is byte-for-byte identical to before the migration

🤖 Generated with Claude Code


Note

Medium Risk
Refactors core codegen type-resolution logic and introduces a new IR layer plus OpenAPI 3 parsing, which could subtly change generated types (namespacing, allOf/additionalProperties handling) despite attempts to preserve existing output.

Overview
Adds a new SwaggerSwiftIR target with a spec-agnostic intermediate representation (IRDocument, IRSchema, parameters/operations/responses) and converts both Swagger 2.0 and new OpenAPI 3.x documents into this IR (SwaggerToIRConverter, OpenAPI3ToIRConverter).

Migrates ModelTypeResolver/ObjectModelFactory (and String/Integer/Number resolvers) to operate on IRSchema instead of SwaggerSwiftML schema types, updating APIFactory, APIRequestFactory, and RequestParameterFactory to convert schema nodes via toIR() while preserving the previous non-namespaced behavior for top-level body/response $refs.

Updates the CLI/CI token flag to --github-token, adds OpenAPI3 parsing fixtures/tests, and adjusts resolver unit tests to use IR format enums.

Reviewed by Cursor Bugbot for commit 80867ee. Bugbot is set up for automated code reviews on this repo. Configure here.

MadsBogeskov and others added 4 commits April 7, 2026 10:39
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a new `SwaggerSwiftIR` target containing spec-agnostic
intermediate representation types that both Swagger 2.0 and future
OpenAPI 3.x parsers will convert into. Adds `SwaggerToIRConverter`
in `SwaggerSwiftML` to convert the existing `Swagger` struct into
`IRDocument`, including full schema, parameter, request body, and
response mapping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add OpenAPI3Document and supporting model types (schema, parameter,
  operation, request body, response, components, server, OrRef)
- Add OpenAPI3ToIRConverter converting OAS3 → IRDocument
- Add SwaggerReader.readSpec() with version detection, returning APISpec
  (.swagger2 or .openapi3) — both convert to IRDocument via .toIR()
- Add 33 tests covering parsing, IR conversion, and version detection
- Add BasicOpenAPI3.yaml fixture

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all SwaggerSwiftML-specific DataFormat/Schema references in the
resolver layer with spec-agnostic IRSchema types. Introduces IRStringFormat,
IRIntegerFormat, and IRNumberFormat; adds Schema.toIR() / Node<Schema>.toIR()
helpers for incremental call-site migration in APIRequestFactory and
RequestParameterFactory. All 41 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Test Results (macos-latest)

98 tests  ±0   98 ✅ ±0   5s ⏱️ -1s
 1 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit 80867ee. ± Comparison against base commit b676a08.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Test Results (ubuntu-latest)

98 tests  ±0   98 ✅ ±0   2s ⏱️ ±0s
 1 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit 80867ee. ± Comparison against base commit b676a08.

♻️ This comment has been updated with latest results.

case .int32: return .int32
case .long: return .int64
default: return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Integer format "int64" silently downgrades to Int

High Severity

The DataFormat.irIntegerFormat conversion only maps .int32.int32 and .long.int64. The standard Swagger 2.0 format "int64" is parsed as DataFormat.unsupported("int64"), which falls into default: return nil. The old IntegerResolver explicitly handled .unsupported("int64")Int64 and .unsupported("decimal")Double. Now those formats map to nil, causing IntegerResolver to default to plain Int, silently losing 64-bit precision for a standard format.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6b6662e. Configure here.

allOf: irAllOf
)))
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Items.toIR() drops vendor extension custom fields

Medium Severity

Items.toIR() creates IRSchema instances without propagating Items.customFields. The IRSchema init defaults customFields to [:], so vendor extensions like x-override-name and x-internal on array item objects are silently dropped. The old code explicitly passed node.customFields to objectModelFactory.make when handling Items of type object.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6b6662e. Configure here.

typeName = typeName
.modelNamed
.split(separator: ".").map { String($0).uppercasingFirst }.joined()
let qualifiedName = "\(serviceName).\(typeName)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reference "Type" collision check compares raw name

Medium Severity

In resolveProperty, the condition refName == "Type" checks the raw reference name extracted from the IR, but the old code checked modelReference.typeName == "Type" which was already .modelNamed-transformed. A definition named "type" (lowercase) transforms to "Type" via .modelNamed, triggering collision handling in the old code but not in the new code, potentially producing ServiceName.Type which conflicts with Swift's metatype syntax.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6b6662e. Configure here.

// qualification — matching the historical codegen behaviour where top-level response
// type references are not prefixed with the service name.
if case .reference(let name) = irSchema.type {
return (.object(typeName: name), [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Response $ref ignores actual type of referenced schema

Medium Severity

When a response or body schema contains a bare $ref, the new code unconditionally returns .object(typeName: name). The old code resolved the referenced schema and returned its actual type — e.g., .string(defaultValue: nil) for string typedefs, or .enumeration for enum types. This changes generated function signatures for any spec where a response or body $ref points to a non-object definition.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6b6662e. Configure here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 6 total unresolved issues (including 4 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8ad1c98. Configure here.


mutating func run() async throws {
guard let token = gitHubToken ?? ProcessInfo.processInfo.environment["GITHUB_TOKEN"] else {
guard let token = githubToken ?? ProcessInfo.processInfo.environment["GITHUB_TOKEN"] else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Error message references old CLI flag name

Medium Severity

The property was renamed from gitHubToken to githubToken, which changes the swift-argument-parser derived CLI flag from --git-hub-token to --github-token. The ValidationError message still references the old --git-hub-token flag, so users following the error guidance will use a flag that doesn't exist.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8ad1c98. Configure here.

return (.object(typeName: "[String: AdditionalProperty]"), [])
case .schema(let valueSchema):
if case .reference(let name) = valueSchema.type {
return (.object(typeName: "[String: \(serviceName).\(name)]"), [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dictionary reference values gain unexpected namespace prefix

Medium Severity

When a dictionary schema has a $ref value type, the new code produces [String: ServiceName.TypeName] (with serviceName prefix), whereas the old ModelTypeResolver produced [String: TypeName] (unqualified). This changes the generated Swift type name for dictionary-typed properties with reference values.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8ad1c98. Configure here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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