Skip to content

fix(spec): resolve UserType name collision with inline User.type schema - #575

Open
BinoyOza-okta wants to merge 2 commits into
masterfrom
OKTA-1241970
Open

fix(spec): resolve UserType name collision with inline User.type schema#575
BinoyOza-okta wants to merge 2 commits into
masterfrom
OKTA-1241970

Conversation

@BinoyOza-okta

Copy link
Copy Markdown
Contributor

Summary

Fixes a long-standing bug where okta.models.UserType was generated with only an id field, missing every other property (name, displayName, description, created, createdBy, default, lastUpdated, lastUpdatedBy, _links) that the UserType component schema defines in openapi/api.yaml.

Customer-reported: any deserialization of a /api/v1/meta/types/user/* response into UserType silently dropped every field except id.

Root cause

Two schemas in api.yaml compete for the same generated class name:

  1. components/schemas/UserType (line 82944) — the full model.
  2. User.type (line 80775) — an inline anonymous object with a single id property.

OpenAPI Generator 7.x auto-promotes inline object schemas into standalone models and names them <ParentSchema <PropertyName> in TitleCase. Parent = User, property = type → generated name = UserType, which collides with the real component. The inline {id}-only shape wins, shadowing the full schema.

Evidence (pre-fix okta/models/user_type.py):

  • Class docstring is copied verbatim from the inline User.type description ("The user type that determines the schema for the user's profile..."), not the component schema.
  • Only field: id: Optional[StrictStr] with description "The ID of the user type" — the inline schema's exact wording.

This is why no other model is affected — User.type is the only spot in the spec where an inline object's auto-derived name matches an existing component name.

Fix

Spec-side change in openapi/api.yaml:

  • Add a new named component UserTypeRef that captures the {id}-only shape used by User.type.
  • Replace the inline object on User.type with $ref: '#/components/schemas/serTypeRef'.
UserTypeRef:
  type: object
  description: The user type that determines the schema for the user's profile...
  properties:
    id:
      type: string
      description: The ID of the user type

# User schema
type:
  $ref: '#/components/schemas/UserTypeRef'

The on-wire JSON shape of User.type is unchanged (still { "id": "..." }), so this is backward-compatible for API consumers.

Regenerated artifacts

Ran openapi/generate.sh (OpenAPI Generator 7.7.0). Resulting changes:

File Change
okta/models/user_type.py Now carries the full component schema (all fields, _links)
okta/models/user_type_ref.py New{id}-only model used by User.type
okta/models/user.py type field re-typed from UserTypeUserTypeRef
okta/models/user_get_singleton.py Same as above
okta/__init__.py, okta/models/__init__.py Export UserTypeRef
docs/UserType.md Regenerated with full field list
docs/UserTypeRef.md New
docs/User.md, docs/UserGetSingleton.md Reference UserTypeRef

Tests

Added tests/test_user_type.py (12 cases, all passing) to lock in the fix and guard against regression:

  • Regression guard: asserts UserType declares the full component field set and UserTypeRef stays {id}-only.
  • from_dict: hydrates all response fields (incl. datetime parsing on created/lastUpdated), nested _linksUserTypeLinks, None input, and missing-optional handling.
  • Construction: enforces name/displayName as required; accepts both alias and Python-field-name kwargs.
  • to_dict: verifies the generator's read-only exclusion behavior (id, created, createdBy, default, lastUpdated, lastUpdatedBy are omitted on serialization) and that _links serializes via nested to_dict().
  • JSON roundtrip: from_jsonto_json preserves writable fields.

Run:

pytest tests/test_user_type.py -v
# 12 passed

Breaking-change note

The Python-level type of User.type changes from UserType to UserTypeRef. Any user code that explicitly annotates or isinstance-checks User.type against UserType will need to switch to UserTypeRef. Runtime/JSON behavior is unchanged.

Checklist

  • Spec fix in openapi/api.yaml
  • SDK regenerated via openapi/generate.sh
  • New regression test added
  • All new/regenerated files reviewed for correctness
  • Docs regenerated

Fixes

The top-level UserType component schema (api.yaml) was being generated with only an `id` field, dropping all other properties (name, displayName, description, created, createdBy, default, lastUpdated, lastUpdatedBy, _links).

Root cause: OpenAPI Generator 7.x auto-promotes inline object schemas to standalone models, naming them <ParentSchema><PropertyName> in TitleCase. The `User` schema's inline `type` property (an anonymous object with a single `id` field) was therefore promoted to a model named `UserType`, colliding with the real component of the same name. The inline `{id}`-only shape won, shadowing the full schema.

Fix: extract the inline `User.type` object into a new named component `UserTypeRef` and `$ref` it from `User.type`. This frees the `UserType` name for the real component and preserves the on-wire JSON shape (User.type payload is unchanged — still `{ "id": "..." }`).

Regenerated SDK reflects the fix:
- okta/models/user_type.py now carries the full component schema
- okta/models/user_type_ref.py (new) holds the {id}-only shape
- okta/models/user.py, user_get_singleton.py: User.type now typed as
  UserTypeRef instead of UserType
- okta/__init__.py, okta/models/__init__.py: export UserTypeRef
- docs/UserType.md, docs/UserTypeRef.md, docs/User.md,
  docs/UserGetSingleton.md updated by generator

Added tests/test_user_type.py to guard against regressions of the collision — asserts UserType declares the full component field set, UserTypeRef stays id-only, and covers from_dict/to_dict/JSON roundtrips including the readOnly-field exclusion behavior on serialization.
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.

UserType response model discards all fields except id

1 participant