[SavedObjects] Add zod support for schemas and modelVersions#262683
[SavedObjects] Add zod support for schemas and modelVersions#262683nickofthyme wants to merge 17 commits intoelastic:mainfrom
zod support for schemas and modelVersions#262683Conversation
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
| // We convert `SavedObjectSanitizedDoc` to its validation schema representation | ||
| // to ensure that we don't forget to keep the schema up-to-date. TS will complain | ||
| // if we update `SavedObjectSanitizedDoc` without making changes below. | ||
| type SavedObjectSanitizedDocSchema = { | ||
| [K in keyof Required<SavedObjectSanitizedDoc>]: Type<SavedObjectSanitizedDoc[K]>; | ||
| }; | ||
|
|
||
| const baseSchema = schema.object<SavedObjectSanitizedDocSchema>({ | ||
| id: schema.string({ minLength: 1 }), | ||
| type: schema.string(), | ||
| references: schema.arrayOf( | ||
| schema.object({ | ||
| name: schema.string(), | ||
| type: schema.string(), | ||
| id: schema.string(), | ||
| }), | ||
| { defaultValue: [], maxSize: 1000 } | ||
| ), | ||
| namespace: schema.maybe(schema.string()), | ||
| namespaces: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 100 })), | ||
| migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())), | ||
| coreMigrationVersion: schema.maybe(schema.string()), | ||
| typeMigrationVersion: schema.maybe(schema.string()), | ||
| updated_at: schema.maybe(schema.string()), | ||
| updated_by: schema.maybe(schema.string()), | ||
| created_at: schema.maybe(schema.string()), | ||
| created_by: schema.maybe(schema.string()), | ||
| version: schema.maybe(schema.string()), | ||
| originId: schema.maybe(schema.string()), | ||
| managed: schema.maybe(schema.boolean()), | ||
| accessControl: schema.maybe( | ||
| schema.object({ | ||
| owner: schema.string(), | ||
| accessMode: schema.oneOf([schema.literal('write_restricted'), schema.literal('default')]), | ||
| }) | ||
| ), | ||
| attributes: schema.recordOf(schema.string(), schema.maybe(schema.any())), | ||
| }); |
There was a problem hiding this comment.
Moved without changes to ./base_schema.ts
| export const createSavedObjectSanitizedDocValidator = ( | ||
| attributesSchema?: SavedObjectsValidationSpec | ||
| ): SavedObjectSanitizedDocValidator => { |
There was a problem hiding this comment.
Now returns generic validator function, no longer the bare schema.
- const schema = createSavedObjectSanitizedDocValidator({...})
+ const validate = createSavedObjectSanitizedDocValidator({...})
- schema.validate(doc)
+ validate(doc)| /** | ||
| * Produces a structure list similar to {@link Type.getSchemaStructure} from `@kbn/config-schema`, | ||
| * for Zod v4 schemas (`z` from `@kbn/zod`). Used for saved-object fixture templates and tooling. | ||
| * | ||
| * @remarks | ||
| * This walks Zod's public `def` / `shape` API (Zod v4). Rare wrappers (effects, custom refinements) | ||
| * may fall back to a coarse label from `def.type`. | ||
| */ |
There was a problem hiding this comment.
Logic in this file is meant to match the functionality of schema.getSchemaStructure from @kbn/config-schema.
| // We convert `SavedObjectSanitizedDoc` to its validation schema representation | ||
| // to ensure that we don't forget to keep the schema up-to-date. TS will complain | ||
| // if we update `SavedObjectSanitizedDoc` without making changes below. | ||
| type SavedObjectSanitizedDocSchema = { | ||
| [K in keyof Required<SavedObjectSanitizedDoc>]: Type<SavedObjectSanitizedDoc[K]>; | ||
| }; | ||
|
|
||
| /** | ||
| * Base config-schema schema for a saved object. | ||
| * | ||
| * @internal | ||
| */ | ||
| export const baseConfigSchema = schema.object({ |
There was a problem hiding this comment.
One base for each schema type, both validated against SavedObjectSanitizedDoc.
| if (!result.success) { | ||
| throw new Error(z.prettifyError(result.error)); | ||
| } |
There was a problem hiding this comment.
Return prettified Error instead of ZodError.
| /** | ||
| * Zod equivalent of {@link extractMappingCompatibleSchemaFields}. Walks Zod v4 `def` / `shape` (and | ||
| * common wrappers) so mapping paths align with the Joi-based extractor for the same logical schema. | ||
| * | ||
| * @internal Exported for unit tests | ||
| */ | ||
| export function extractMappingCompatibleZodSchemaFields( |
There was a problem hiding this comment.
Meant to match the functionality of extractMappingCompatibleSchemaFields for @kbn/config-schema type.
janmonschke
left a comment
There was a problem hiding this comment.
kibana-cases changes lgtm (code-review only)
| * @public | ||
| */ | ||
| export type SavedObjectsValidationSpec = ObjectType; | ||
| export type SavedObjectsValidationSpec = ObjectType | z.ZodType<Record<string, unknown>>; |
There was a problem hiding this comment.
SavedObjectsValidationSpec is now a union type, which means consumer tests that call .validate() directly on a validation spec will break at compile time.
The Cases test in this PR shows the migration pattern. Worth calling this out explicitly so consumers know what to expect.
There was a problem hiding this comment.
Added note to description, lmk if you want me to call this out in some other way.
There was a problem hiding this comment.
Since the PR changes the SavedObjectsValidationSpec, we need to run the changes by @gsoldevila. I'm concerned there might be some migrations' related gotcha's that could have been missed.
There was a problem hiding this comment.
This should go in after #263121 (Zod heap fix). Without that patch, every z.object() used in an SO type definition allocates ~12.8 KB of heap. If teams start adopting Zod schemas before the fix is in main, the memory impact adds up quickly.
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Module Count
Public APIs missing comments
Public APIs missing exports
History
|
💔 Build Failed
Failed CI StepsTest Failures
Metrics [docs]Module Count
Public APIs missing comments
Public APIs missing exports
History |
Summary
Add
@kbn/zodsupport to saved objectschemasandmodelVersions.*.schemas.Note
If you use the
SavedObjectsValidationSpecdirectly to test or validate, you should typeguard for the expected schema usingisConfigSchemaorisZodbefore validation.Checklist
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.