Fix: Primitive Fallback for oneOf Deserialization in Device Assurance Models#523
Merged
BinoyOza-okta merged 1 commit intomasterfrom Apr 12, 2026
Merged
Fix: Primitive Fallback for oneOf Deserialization in Device Assurance Models#523BinoyOza-okta merged 1 commit intomasterfrom
BinoyOza-okta merged 1 commit intomasterfrom
Conversation
- `list_device_assurance_policies` and `get_device_assurance_policy` failed with a ValidationError when deserializing the `GracePeriodExpiry` field.
- The `expiry` field is defined as `oneOf: [ByDateTimeExpiry, ByDurationExpiry]`, but the API returns a raw string like `"2025-12-31T00:00:00Z"` instead of `{"value": "2025-12-31T00:00:00Z"}`.
- Additionally, the ISO 8601 duration regex in `ByDurationExpiry` was malformed (`(?:$)` and `(?:\d)` instead of `(?!$)` and `(?=\d)`), causing valid duration strings to be rejected.
Root Cause:
1. The oneOf deserializer expects a JSON object matching one of the schemas, but the API returns a bare scalar value for fields with single-property oneOf schemas.
2. The regex used incorrect non-capturing groups instead of lookahead assertions, so it could never match valid ISO 8601 durations.
Solution:
1. Added `x-okta-primitive-fallback` vendor extension to the `expiry` field in the OpenAPI spec (`openapi/api.yaml`).
2. Updated the `model_oneof.mustache` template to emit retry logic: when no oneOf schema matches and the input is a primitive, wrap it into each single-property schema and retry validation.
3. Regenerated `okta/models/grace_period_expiry.py` with the new fallback logic.
4. Fixed the ISO 8601 duration regex in both `openapi/api.yaml` and `okta/models/by_duration_expiry.py` (changed `(?:$)` → `(?!$)` and `(?:\d)` → `(?=\d)`).
aniket-okta
approved these changes
Apr 12, 2026
aniket-okta
left a comment
There was a problem hiding this comment.
Security audit, static analysis, and live API verification passed. The primitive-fallback retry logic in the mustache template is correct and backward-compatible. Regex fix for ByDurationExpiry is accurate. Verified against okta-mcp-server.oktapreview.com — policies with BY_DURATION grace period (e.g. PT168H) now deserialize correctly. All 15 CI checks green. Minor: consider adding a CHANGELOG entry for this specific fix and a unit test for the fallback path in a follow-up.
Contributor
Author
|
CHANGELOG will be added in the release changes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix: Primitive Fallback for oneOf Deserialization in Device Assurance Models
Summary
Adds a primitive-fallback retry mechanism to oneOf deserialization so that bare scalar API responses (e.g.
"2025-12-31T00:00:00Z") are automatically wrapped into the corresponding single-property schema object. Also fixes a malformed ISO 8601 duration regex that rejected all valid duration strings.Ticket: OKTA-1154600
Problem
The Okta API returns a raw primitive string for the
expiryfield inside Device Assurance policy responses, but the SDK'sGracePeriodExpirymodel expects a JSON object matching one of itsoneOfschemas (ByDateTimeExpiryorByDurationExpiry). This caused every call to the Device Assurance endpoints to fail:Affected endpoints:
list_device_assurance_policies()get_device_assurance_policy(policy_id)Additionally, the ISO 8601 duration regex contained incorrect constructs (
(?:$)instead of the negative lookahead(?!$), and(?:\d)instead of the positive lookahead(?=\d)), meaning it could never match valid duration strings likeP30DorPT1H.Root Cause
"2025-12-31T00:00:00Z"(a bare string), but the generatedfrom_jsonmethod tries to parse it directly asByDateTimeExpiry(which expects{"value": "..."}) and fails.(?:$)is a non-capturing group containing end-of-string, not a negative lookahead. It causes the regex to silently fail on every non-empty input.Solution
Files Changed
openapi/api.yamlx-okta-primitive-fallback: truevendor extension on theexpiryfield of theGracePeriodschema.ByDurationExpiry.value:openapi/templates/model_oneof.mustachex-okta-primitive-fallbackthat:{field_name: value}and retries validation.okta/models/grace_period_expiry.py(regenerated)ByDateTimeExpiryandByDurationExpiry, generated from the updated mustache template.okta/models/by_duration_expiry.py(regenerated)(?:$)→(?!$)and(?:\d)→(?=\d).Testing
✅ Verified
list_device_assurance_policies()returns fully deserialized policy objects✅ Verified
get_device_assurance_policy(policy_id)returns a correctly deserialized policy✅ ISO 8601 duration strings (
P30D,PT1H,P1Y2M3DT4H5M6S) now pass regex validation✅ No breaking changes — the fallback only activates when the standard deserialization finds zero matches
✅ The mustache template change is backward-compatible; models without
x-okta-primitive-fallbackare unaffectedHow It Works
Fixes