[normalizer] Preserve AnyType for typeless OAS 3.0 schemas in referenced files

JO
7h ago
+180-2

Fixes #25412

Summary

When an OpenAPI 3.0 specification references an external file (such as a PathItem, ApiResponse, Parameter, RequestBody, or a whole-file schema $ref such as sailpoint-oss/api-specs), swagger-parser's ResolverCache.loadRef deserializes the external node via Jackson (ModelDeserializer). Because ModelDeserializer.useArbitrarySchema defaults to false, ModelDeserializer.deserializeArbitraryOrObjectSchema instantiates typeless schemas (description: ... with no type, properties, or additionalProperties) as ObjectSchema (type: "object"), while storing the raw parsed JSON/YAML map in schema.getJsonSchema() (where !schema.getJsonSchema().containsKey("type")).
By contrast, the same typeless schema in the root OpenAPI 3.0 document is parsed by OpenAPIDeserializer via SchemaTypeUtil.createSchemaByType(node) into a base Schema instance (type == null), which OpenAPI Generator maps to AnyType.

Changes

  • OpenAPINormalizer.java:
    • In normalizeSimpleSchema(Schema schema, Set<Schema> visitedSchemas) (reached only after confirming the schema is not an array, not a ComposedSchema, has no properties, and has no Schema additionalProperties), check whether an ObjectSchema has a raw jsonSchema map without "type" or "properties" keys and no object-specific constraints (schema instanceof ObjectSchema && schema.getJsonSchema() != null && !schema.getJsonSchema().containsKey("type") && !schema.getJsonSchema().containsKey("properties") && schema.getProperties() == null && schema.getAdditionalProperties() == null && schema.getRequired() == null && schema.getMinProperties() == null && schema.getMaxProperties() == null && schema.getPatternProperties() == null && schema.getDiscriminator() == null && schema.getEnum() == null) and convert it to a base new Schema<>() (type == null) with metadata and validation fields copied via ModelUtils.copyMetadata(schema, sc) (plus pattern, format, multipleOf, uniqueItems, exclusiveMinimum, exclusiveMaximum, exclusiveMinimumValue, exclusiveMaximumValue, not, xml, externalDocs, and jsonSchema), matching the OAS 3.1 AnyType handling in processNormalize31Spec.
    • In normalizeSchema's array and map branches, assign result.setItems(normalizeSchema(result.getItems(), visitedSchemas)) and result.setAdditionalProperties(normalized) so normalized array items and map additionalProperties schemas are not discarded.
  • OpenAPINormalizerTest.java:
    • Real-world multi-file spec check (sailpoint-oss/api-specs, OAS 3.0): Verified against referenced PathItem schemas that omit type for arbitrary variable values. Resolved schemas and generated Python models now preserve AnyType instead of coercing to type: object.
    • Add testTypelessSchemaInReferencedFileRemainsAnyType verifying that typeless schemas in referenced files (via a PathItem response, a whole-file schema $ref, and an array items $ref) normalize to AnyType (type == null, isObjectSchema == false, isFreeFormObject == false) while explicit type: object and required: [id] schemas in referenced files preserve their object semantics and constraints.
      cc @OpenAPITools/generator-core-team

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.