[Cases] Replace io-ts with zod#268342
Open
lgestc wants to merge 44 commits into
Open
Conversation
Swap decodeOrThrow / decodeWithExcessOrThrow calls in the configure sub-client to their zod equivalents. Casts are localized to client.ts where zod-inferred literal-union severity types collide with io-ts CaseSeverity at the public interface boundary. Tests updated to match zod's error message format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap decoders in get/find/search/similar/bulk_get/observables to their zod equivalents. Cast at decoder return sites to bridge zod-inferred literal-union enum types and io-ts TypeScript enums at the public interface boundary. Also fixes a stage-1 parity gap in the zod paginationSchema, which was missing the maxPerPage and MAX_DOCS_PER_PAGE refinements present in the io-ts version. Tests updated to match zod's error message format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…R 6) Swap decoders in create/bulk_create/bulk_update/push/delete/replace_custom_field to their zod equivalents. Cast at decoder return sites to bridge zod-inferred literal-union enum types and io-ts TypeScript enums at the public interface boundary. Also fixes two stage-1 parity gaps: - Adds CaseCloseReasonSchema to domain_zod and threads closeReason into CaseBaseOptionalFieldsRequestSchema in api_zod (was present in io-ts). Tests updated to match zod's error message format (path-prefixed error messages, `Excess keys are not allowed`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap decoders in user_actions/{get,find,stats,users,connectors} to their
zod equivalents.
Adds two missing schemas to api_zod/user_action (parity gap from stage 1):
- CaseUserActionDeprecatedResponseSchema
- CaseUserActionsDeprecatedResponseSchema
One test updated to match zod's `Excess keys are not allowed` message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap decoders in attachments/{add,add_file,bulk_create,bulk_delete,
bulk_get,delete,get,update} to their zod equivalents.
Adds three missing schemas to api_zod / domain_zod attachment v2
(parity gap from stage 1):
- AttachmentsSchemaV2 (array)
- AttachmentsFindResponseSchemaV2
- BulkGetAttachmentsResponseSchemaV2
Tests updated to match zod's error message format (path-prefixed
messages, `Excess keys are not allowed`, distinct
`Invalid input: expected …, received …` for missing fields).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap decoders in metrics/{get_case_metrics,get_cases_metrics,
get_status_totals} and workflows/steps/update_case_helpers to their zod
equivalents.
Three test assertions updated to match zod's error message format
(`Excess keys are not allowed`, `Invalid input: expected …`).
Note: metrics/lifespan.ts still uses io-ts `StatusUserActionRt.is()` as
a runtime type guard — that's a different pattern (not a decoder) and
is left for a later cleanup PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swap decoders in routes/api/configure/{post_configure,patch_configure}
to their zod equivalents. These are the only routes that call
decodeOrThrow / decodeWithExcessOrThrow directly — other routes
delegate validation to the cases client.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts in server-side helper type files and their consumers:
- common/types/case.ts — CaseTransformedAttributesSchema,
getPartialCaseTransformedAttributesSchema, OwnerSchema (was *Rt)
- common/types/configure.ts — ConfigurationPartialAttributesSchema,
ConfigurationTransformedAttributesSchema (was *Rt)
- common/types/connector_mappings.ts —
ConnectorMappingsAttributesTransformedSchema,
ConnectorMappingsAttributesPartialSchema (was *Rt)
- common/types/user_actions.ts — UserActionTransformedAttributesSchema,
UserActionPersistedAttributesSchema (was *Rt)
- services/utils.ts — bulkDecodeSOAttributes now takes a ZodType
Consumers updated: services/cases/index.ts, services/configure/index.ts,
services/connector_mappings/index.ts, services/user_actions/index.ts,
services/user_actions/operations/{find,create}.ts. Casts at decoder
return sites bridge zod-inferred types and io-ts types where they
diverge (enum identity, union variant counts).
Test assertions in the affected suites updated to match zod's error
message format (path-prefixed messages, broader `Invalid` substring
where the discriminator-vs-Invalid-input distinction varies).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (PR 12) Replace io-ts in three files: - attachment_framework/attachments/comment.ts — CommentAttachmentDataRt → CommentAttachmentDataSchema (zod). Validator behavior unchanged. - internal_attachments/index.ts — file/lens schema validators swapped to zod (FileAttachmentMetadataSchema, jsonValueSchema, ad-hoc LensAttachmentDataSchema). - saved_object_types/migrations/user_actions/connector_id.ts — io-ts type guards (`CaseConnectorRt.is`, `ExternalServiceRt.is`) replaced with `Schema.safeParse(...).success`. The `actionDetails is X` user- type-guard return signatures preserved by importing the corresponding io-ts-derived `CaseConnector` / `ExternalService` types. These validators are part of the cases plugin's attachment-framework contract used by Security Solution and others. The migration is a type-system refactor (no runtime-behavior change) but the public API surface for plugins registering attachment types changes from io-ts codecs to zod schemas. **Coordinate with stakeholders before merging.** Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `CaseFileMetadataForDeletionRt` with
`CaseFileMetadataForDeletionSchema` (zod) and update the only live
consumer (`server/client/attachments/bulk_delete.ts`), which used the
io-ts `.is()` type-guard. Replaced with `.safeParse(...).success` and
the now-typed `parsed.data` is used directly to narrow the meta access.
The other common helpers in this PR's plan
(`common/api/runtime_types.ts`, `common/api/saved_object.ts`,
`common/schema/{index,types}.ts`) are only consumed by io-ts files
scheduled for deletion in PR 15, so they're left as-is and will go
away with that cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts in five public-side files:
- public/components/all_cases/schema.ts — AllCasesURLQueryParamsRt
→ AllCasesURLQueryParamsSchema (zod). validateSchema rewritten to
use safeParse.
- public/components/all_cases/types.ts — AllCasesURLQueryParams now
inferred from the zod schema.
- public/components/all_cases/utils/parse_url_params.tsx — consumer
updated to the new schema name.
- public/components/attachments/file/types.ts — DownloadableFile
inferred from SingleFileAttachmentMetadataSchema.
- public/components/attachments/{comment,lens}/index.tsx — local
schema validators converted to zod (safeParse(...).success).
Test (schema.test.ts) rewritten against the zod API; all other tests
in the affected directories pass without changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts decoders in public/api/decoders.ts: - CasesFindResponseRt → CasesFindResponseSchema - CasesBulkGetResponseRt → CasesBulkGetResponseSchema - CasesMetricsResponseRt → CasesMetricsResponseSchema - CasesSimilarResponseRt → CasesSimilarResponseSchema Replaces the fp-ts pipe(decode, fold(throwErrors, identity)) chain with a local decodeWithToasterError helper (same pattern as public/containers/utils.ts). Uses safeParse and surfaces validation errors via the existing ToasterError class. The decoder function signatures keep the io-ts-typed Cases*Response parameter and use NonNullable<T> for the return so callers see the narrowed type, matching the contract from PR 1+2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `FileAttachmentMetadataRt.is(...)` with the zod equivalent in public/components/attachments/file/utils.tsx. Restructured isValidFileExternalReferenceMetadata to use the parsed result for the .length check (zod's safeParse(...).success doesn't narrow inline like io-ts's .is() does). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts decoders/types in: - server/common/types/attachments_v1.ts — *Rt → *Schema - server/services/attachments/index.ts — many decodeOrThrow swaps, casts to AttachmentPatchAttributesV2 at decoder return sites - server/services/attachments/operations/get.ts — V2 + legacy decoders - server/services/attachments/operations/utils.ts — UnifiedAttachment* Also fills two stage-1 parity gaps in domain_zod/attachment/v2: - Adds AttachmentPatchAttributesSchemaV2 (mirrors io-ts AttachmentPatchAttributesRtV2) - Adds DocumentAttachmentAttributesSchemaV2 with the UnifiedEventDocumentAttachment* internal pieces Snapshots updated for zod's error format (path-prefixed messages, Excess keys are not allowed). All 79 attachments service tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…R 18) Replace `*Rt.is(...)` with `*Schema.safeParse(...).success` in server/services/user_actions/type_guards.ts: - CaseAssigneesRt → CaseAssigneesSchema - CaseCustomFieldsRt → CaseCustomFieldsSchema - CaseSettingsRt → CaseSettingsSchema - ExtendedFieldsRt → ExtendedFieldsSchema The user-defined type-guard return signatures are preserved by keeping the io-ts-derived parameter types (CaseAssignees, CaseCustomFields, CaseSettings) imported from common/types/domain. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace SuggestUserProfilesRequestRt with SuggestUserProfilesRequestSchema and decodeWithExcessOrThrow with decodeWithExcessOrThrowZod in server/services/user_profiles/index.ts. Test assertion updated to match zod's path-prefixed error format (`size: The size field cannot be more than 10.`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts decoders with zod equivalents in 4 route handlers: - routes/api/cases/push_case.ts — CasePushRequestParamsRt → Schema - routes/api/comments/patch_comment.ts — AttachmentPatchRequestRt with cast to AttachmentPatchRequestV2 to satisfy the io-ts-typed attachments.update payload - routes/api/internal/bulk_get_attachments.ts — BulkGetAttachmentsRequestRt - routes/api/internal/bulk_delete_file_attachments.ts — BulkDeleteFileAttachmentsRequestRt These are the only routes that called decode* directly; other routes delegate validation to the cases client. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts in server/common helper files: - server/common/utils.ts — `ExternalReferenceSOAttachmentPayloadRt.is(...)` and `FileAttachmentMetadataRt.is(...)` swapped to safeParse. The two guards are chained via parsed.data so the second check sees the narrowed metadata field. - server/common/models/case_with_comments.ts — CaseRt decoder swapped to CaseSchema with cast to Case at return site. - server/common/types/id_incrementer.ts — CaseIdIncrementerTransformedAttributesRt → *Schema. No external consumers for this re-export. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace io-ts payload decoders with zod equivalents in server/client/utils.ts. Seven discriminator-payload decoders swapped: ActionsAttachmentPayload, AlertAttachmentPayload, EventAttachmentPayload, ExternalReferenceNoSOAttachmentPayload, ExternalReferenceSOAttachmentPayload, PersistableStateAttachmentPayload, UserCommentAttachmentPayload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `StatusUserActionRt.is(...)` with `StatusUserActionSchema.safeParse(...).success` in server/client/metrics/lifespan.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Additive prep for the io-ts → zod rename. Adds type aliases that the
io-ts files exported but the zod mirror files didn't, so consumers
will keep working when the api/domain dirs become zod-only:
api_zod:
- case/v1.ts — AllCategoriesFindRequest, AllReportersFindRequest,
AllTagsFindRequest, CaseRequestCustomField, CaseRequestCustomFields,
CasesFindRequestSortFields, CasesFindRequestWithCustomFields,
CasesSearchRequest, CasesSimilarResponse,
FindCasesContainingAllAlertsResponse,
FindCasesContainingAllDocumentsRequest,
GetRelatedCasesByAlertResponse, SimilarCasesSearchRequest
- configure/v1.ts — GetConfigureResponse, CreateConfigureResponse,
UpdateConfigureResponse
- connector/v1.ts — GetCaseConnectorsPushDetails
- metrics/v1.ts — AlertHostsMetrics, AlertUsersMetrics, StatusInfo,
SingleCaseMetricsFeatureField, CasesMetricsFeatureField
- user_action/v1.ts — UserActionFindRequestTypes, UserActionWithResponse<T>
domain_zod:
- connector/v1.ts — inlines ConnectorTypes/SwimlaneConnectorType enums
and ActionConnector/ActionTypeConnector aliases (previously
re-exported from io-ts); adds Connector{CasesWebhook,Jira,Resilient,
ServiceNowITSM,ServiceNowSIR,Swimlane,TheHive}TypeFields aliases
- user_action/action/v1.ts — inlines UserActionTypes/UserActionActions
enums and UserActionType/UserActionAction aliases (previously
re-exported from io-ts)
- user_action/v1.ts — imports missing TemplateUserActionSchema,
ExtendedFieldsUserActionSchema, CommentUserActionPayloadWithoutIdsSchema,
SettingsUserActionPayloadSchema; adds the *UserAction discriminated-
union type aliases plus UserAction<T>, UserActionWithAttributes<T>,
UserActionWithDeprecatedResponse<T> generics
- attachment/v2.ts — AttachmentMode
Also fills a stage-1 parity gap: the zod BasicUserActionsSchema was
missing ExtendedFieldsUserActionSchema and TemplateUserActionSchema,
so the zod UserActionPayload union was narrower than the io-ts one.
Both added to the union.
This PR is additive only. Type-check is clean and 112 schema parity
tests still pass. PRs 24b–d still required for the actual rename +
delete + tsconfig cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six zod-native files lived inside `common/types/{api,domain}/template/`
(the io-ts dirs). The zod mirrors at `common/types/{api_zod,domain_zod}/
template/v1.ts` did `export * from '../../{api,domain}/template/v1'` to
re-export them.
When PR 24d deletes the io-ts dirs, those re-exports become broken
self-loops after the rename. To prevent that, copy the zod-native
sources into the zod dirs so the content survives the deletion:
- domain_zod/template/v1.ts (replaces re-export)
- domain_zod/template/fields.ts
- domain_zod/template/translations.ts
- domain_zod/template/evaluate_conditions.ts
- domain_zod/template/validate_extended_fields.ts
- api_zod/template/v1.ts (replaces re-export; updates one import to
use ../../domain_zod/template/v1 instead of ../../domain/template/v1)
The originals remain in place (consumers still resolve them through
the io-ts side) and will be deleted with the rest of the io-ts dirs in
PR 24d. 103 template-related tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…PR 24c)
Pre-rename consumer-side prep. Two changes:
1) Inline enums + replace io-ts cross-imports with local definitions
in the four zod files that still cross the io-ts/zod boundary.
After PR 24d's rename these would otherwise become self-loops:
- domain_zod/custom_field/v1.ts — inlines CustomFieldTypes enum
- domain_zod/attachment/v1.ts — inlines AttachmentType and
ExternalReferenceStorageType enums
- domain_zod/case/v1.ts — inlines CaseSeverity enum, drops two
unused bundled-types imports
- api_zod/user_action/v1.ts — switches UserActionTypes import to
../../domain_zod/user_action/action/v1 (already inlined in PR 24a)
2) Switch CaseStatusSchema and CaseSeveritySchema to z.nativeEnum so
the inferred types are the TS enums (CaseStatuses, CaseSeverity)
instead of literal unions. Previously the bundled schemas inferred
as `'closed' | 'in-progress' | 'open'`, forcing ~80 cast errors at
consumer call sites that operate on the enum types. nativeEnum
collapses that gap in one place.
Pre-rename dry-run after these changes: 660 errors (initial attempt)
→ 250 (after PR 24a) → 117 (after enum inlining) → ~30 (after the
nativeEnum switch). Remaining errors are concrete shape mismatches
PR 24d will address case-by-case during the actual rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final stage of the io-ts → zod migration. After this commit the cases
plugin no longer references io-ts, fp-ts, or @kbn/securitysolution-io-ts-utils.
Mechanical changes:
- Deleted the io-ts directories (common/types/{api,domain}, common/schema)
and helpers (common/api/{runtime_types,saved_object}.ts,
server/common/runtime_types{,.test}.ts).
- Renamed `*_zod` directories and files to their canonical names:
common/types/api_zod → common/types/api
common/types/domain_zod → common/types/domain
common/schema_zod → common/schema
server/common/runtime_types_zod{,.test}.ts → runtime_types{,.test}.ts
- Bulk-rewrote `_zod` import paths across consumer files.
- Dropped `@kbn/securitysolution-io-ts-utils` from
x-pack/platform/plugins/shared/cases/tsconfig.json kbn_references.
- Removed `throwErrors` (no longer exists) from common/index.ts and the
io-ts re-exports from common/api/index.ts.
Schema parity fixes uncovered during the rename dry-run (the remaining
~30 errors PR 24c didn't address):
- domain/user/v1: User fields use `z.union([string, null, undefined])`
(required keys with possibly-undefined values) so the inferred type
matches the persisted-attributes `User` interface; avatar fields are
optional to match the original io-ts `rt.partial`.
- domain/case/v1: add the `extended_fields_labels` record (was present
in io-ts but missing from the bundled zod schema).
- domain/attachment/v1: re-export the `IsolateHostActionType` enum.
- domain/attachment/v2: `attachmentId` on UnifiedReferenceAttachment*
schemas accepts `string | string[]` to match the original io-ts.
- domain/user_action/action/v1: cast UserActionActionsSchema enum
values to the literal-union type (not `[string, ...string[]]`) so
consumers see the proper `UserActionAction` literal.
- domain/user_action/status/v1: add optional `closeReason` and
`syncedAlertCount` to the StatusUserAction payload.
- api/case/v1: add `extendedFieldFilters` to CasesSearchRequestSchema.
- api/user_action/v1: re-export `UserActionInternalFindResponse` and
type the find request types as the proper literal union.
- api/attachment/v2: re-export `BulkGetAttachmentsRequestV2`.
Targeted consumer fixes:
- server/services/user_actions/transform.ts: cast
`userAction.attributes.payload` (Record<string, unknown>) to
`UserActionAttributes['payload']` at the function boundary.
- server/client/attachments/bulk_get.ts: cast the legacy-mode decode
result via `unknown` to BulkGetAttachmentsResponseV2 (V1 ⊂ V2).
Validation: type_check on the cases project passes; eslint --fix on
the plugin clears all duplicate-import errors introduced by the bulk
rename; check_changes.ts is green; representative jest suites
(runtime_types, user_actions/transform) pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix three inline-snapshot mismatches surfaced by the full Jest run after PR 24c/24d. The "and N more" counts in zod's error messages went up because the schemas legitimately gained fields restored from the io-ts source-of-truth: - StatusUserActionPayloadSchema gained closeReason + syncedAlertCount - CaseAttributesSchema gained extended_fields_labels - UnifiedReferenceAttachmentPayloadSchema.attachmentId became string | string[] Each test still asserts the same behavior (invalid input throws); only the count of reported issues increased: - services/attachments/index.test.ts:108 "and 25 more" → "and 26 more" - services/attachments/index.test.ts:208 "and 25 more" → "and 26 more" - services/user_actions/index.test.ts:1868 "and 24 more" → "and 28 more" Snapshots updated by hand (no `jest -u`) so the diff stays auditable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # x-pack/platform/plugins/shared/cases/server/client/cases/bulk_create.ts # x-pack/platform/plugins/shared/cases/server/client/cases/bulk_update.ts # x-pack/platform/plugins/shared/cases/server/client/cases/create.ts
Three behavioral regressions surfaced by porting the deleted
common/schema/index.test.ts to zod:
- NonEmptyString accepted whitespace-only strings (" ", "\t\n");
io-ts rejected them via s.trim() !== ''. Replace z.string().min(1)
with a refinement on trimmed length so " " is rejected and the
original (untrimmed) value is preserved on success.
- paginationSchema silently coerced non-numeric page/perPage strings
to NaN (z.string().transform((s) => Number(s))), bypassing the
downstream > maxPerPage / MAX_DOCS_PER_PAGE guards. Move the parse
onto the union and emit a "cannot parse to a number" issue when
Number(s) is not finite, matching io-ts NumberFromString.
- limitedStringSchema rejected empty / whitespace-only strings even
with min === 0; io-ts only rejected when min > 0. Gate the empty
check on min > 0 so the "min: 0, max: N" cases accept "" / " "
as before.
Restore the deleted unit-test coverage: a zod-native port of
common/schema/index.test.ts (38 cases locking the three regressions
above and the rest of the helpers), and the four template
business-logic test files that were swept up in the codec cleanup
but cover pure functions independent of io-ts:
- evaluate_conditions.test.ts (compound condition evaluator)
- fields.test.ts (field schema helpers)
- validate_extended_fields.test.ts (extended-field validation)
- v1.test.ts (Template / CreateTemplateInput / PatchTemplateInput
zod schemas)
Add three DeepStrict regression tests in runtime_types.test.ts
covering excess keys inside arrays of objects, union variants, and
discriminated-union variants — the cases most likely to drift from
io-ts exactCheck parity at depth.
Tighten the decodeOrThrowZod docstring to clarify that strip-on-
unknown matches rt.exact (rt.strict rejected; for that behavior use
decodeWithExcessOrThrowZod).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Many field-level jsdoc comments and section headers were dropped when the io-ts schemas were rewritten as zod. They captured non-obvious domain detail (e.g. that `closeReason` is the value synced to attached alerts; that the `extended_fields` enum key intentionally uses snake_case to match the SO attribute name returned by `Object.keys(updatedAttributes)` in `getUserActionItemByDifference`) that's not derivable from the schema shape alone. Restored across 12 files (~100 jsdocs total): - common/types/api/case/v1.ts (33 + section headers): all field descriptions on CaseBaseOptionalFieldsRequestSchema, CaseRequestFieldsSchema, CasePostRequestSchema, CasesFindRequestBaseFieldsSchema, CasesSearchRequestSchema and related Schemas; restored the "search cases" section header. - common/types/api/metrics/v1.ts (24): every field on StatusInfo, AlertHostsMetrics, AlertUsersMetrics, SingleCaseMetrics, CasesMetricsRequest/Response. - common/types/api/configure/v1.ts (10): connector / closure_type / owner / customFields / templates and CustomField / Template inner fields on the Request schema. - common/types/api/attachment/v1.ts (2): the AttachmentPatchRequestSchema partial-update warning, and the sortOrder pagination field. - common/types/api/stats/v1.ts (3): from / to / owner on CasesStatusRequestSchema. - common/types/api/user_action/v1.ts (2 section headers): "User actions stats API" and "Find User Actions API". - common/types/domain/case/v1.ts (12): every field on CaseBaseFields plus status / owner on CaseBasicSchema, and the enrichCasesWithFieldLabels comment on CASE_EXTENDED_FIELDS_LABELS. - common/types/domain/configure/v1.ts (12): all CustomField / Template / Configuration field descriptions. - common/types/domain/attachment/v1.ts (1): the AttachmentPatchAttributesSchema partial-update warning. - common/types/domain/attachment/v2.ts (6): the Unified Reference / Value / Attributes / Schema / Partial / AttachmentMode block-level docs. - common/types/domain/user_action/v1.ts (3): the CaseUserActionInjectedDeprecatedIdsSchema @deprecated pointer, UserActionAttributesSchema scope, and "User actions" section. - common/types/domain/user_action/action/v1.ts (3): the UserActionTypes "do not remove" preamble, the extended_fields snake_case explanation, and the UserActionType / UserActionAction type docstrings. Pure comment changes — type check passes (cases tsconfig). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restores per-schema validation coverage for the most-used request shapes — the gap flagged in the Cases reviewer pass on this PR. Route-handler and cases_api_integration tests still cover the end-to-end paths, but these unit suites lock the schema contract itself so any future drift between an exported TS type and the zod schema's inferred shape is caught at the schema layer instead of on a route trip. Adds: - common/types/api/case/v1.test.ts (43 cases) covering CasePostRequestSchema (length / array / customField / connector excess-key behavior, unknown-field stripping, NonEmpty / trim parity), CasePatchRequestSchema (required id/version, partial- update guards, whitespace title rejection), CasesPatchRequestSchema (cases array bounds), CasesFindRequestSchema (page/perPage NumberFromString parity, perPage cap, array-vs-string filter forms, MAX_*_FILTER_LENGTH array bounds, sortField/searchField enum dispatch, decodeWithExcessOrThrowZod), and CasesSearchRequestSchema (searchFields enum, extendedFieldFilters shape). - common/types/api/attachment/v1.test.ts (37 cases) covering AttachmentRequestSchema across all six variants (user, alert, event, actions, externalReference NoSO + SO, persistableState) with happy paths and per-variant required-field rejection, AttachmentPatchRequestSchema (the partial-update guard — patch body must carry the full type payload + id + version), BulkCreateAttachmentsRequestSchema bounds, BulkDeleteFileAttachmentsRequestSchema (NonEmptyString id parity including the whitespace case), and BulkGetAttachmentsRequestSchema bounds. All 80 cases pass; type check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The schema unit tests added in 9905dfc imported decodeWithExcessOrThrowZod from server/common/runtime_types — a common→server import that violates the layering rule (common code must not depend on server code). Replace those references with a direct DeepStrict(schema) call from @kbn/zod-helpers (already a declared dep), which is what decodeWithExcessOrThrowZod wraps. The tests assert the same behavior — a DeepStrict-wrapped schema rejects unknown top-level fields — and now stay within common. All 80 schema tests still pass; type check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Pinging @elastic/kibana-cases (Team:Cases) |
GetCaseUsersResponseRt was renamed to GetCaseUsersResponseSchema in the io-ts → zod migration. The cases_api_integration test still imported the old name, breaking the x-pack/platform/test type-check on CI (missed in the original audit because the multi-line scan only covered x-pack/solutions, x-pack/plugins, and src — not x-pack/platform/test). The .encode() call is replaced with .parse() — both validate the shape and throw on mismatch, which is what the surrounding try/catch expects. Verified by running: node scripts/type_check --project x-pack/platform/test/tsconfig.json
Replaces the inline `any`-typed safeParse helper with a properly typed `(schema: ZodType<unknown>, value: unknown) => string[]` and narrows the `(result.data.connector as any).foo` cast to `Record<string, unknown>`. Auto-fixes prettier wrapping reported by check_changes (eslint --fix). No behavioral changes — all 80 schema tests still pass; type-check and lint clean.
The io-ts→zod migration aliased CommentUserActionWithoutIdsSchema to CommentUserActionSchema, losing the distinction between the with-refs and without-refs payloads. The persisted user-action payload has externalReferenceId (and persistable-state SO refs) extracted into the SO's references array, so validating against the with-refs schema rejected file/persistable-state comment user actions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The get_case_metrics integration test asserted the io-ts error message "Invalid value \"bananas\" supplied to \"features\"". After the zod migration, decodeWithExcessOrThrowZod runs the request through stringifyZodError, which formats union-literal failures as 'Invalid input: expected "<literal>"' per branch. Update the assertion to match the new format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Resolve merge conflicts in 8 files (comment/file/lens attachments, server services, client utils, internal attachments) — keep zod functions but adopt structural changes from main (new inject steps, simplified paths) - Fix domain_zod/attachment/file/v2.ts: update import of SingleFileAttachmentMetadataSchema from non-existent ../v1 to the canonical ../../../domain/attachment/v1 path (our branch moved domain_zod → domain) - Fix FTR test assertion in get_case_metrics.ts: the zod union-literal error for 6 branches hits MAX_ERRORS=5 so "lifespan" (6th) becomes "and 1 more"; update assertion to check "features.0: Invalid input: expected "alerts.count"" which reliably appears first Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- transform.ts: add missing UserActionAttributes['payload'] cast to the early-return path in addReferenceIdToPayload (matches the existing cast at the bottom of the function) - transform.test.ts: route the payload override through unknown to avoid an unsafe direct cast that TypeScript now rejects - services/attachments/index.ts: replace stale io-ts names decodeOrThrow / AttachmentAttributesRtV2 with zod equivalents decodeOrThrowZod / AttachmentAttributesSchemaV2 in the unified find path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Zod v4's stringifyZodError flattens invalid_union inner errors which carry empty paths (not the parent array index path). The assertion was checking for the io-ts-style path prefix 'features.0:' which is absent in the actual zod error output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores 37 deleted test files covering api/ and domain/ schema types, rewritten to verify the Zod schemas (safeParse) rather than the removed io-ts codecs. All previously covered schemas are tested including full attachment types, CasesSchema, and nested-stripping behaviour for metrics, configure, connector, and case attribute schemas. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
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.
Summary
Replaces the io-ts-based runtime validation layer in the Cases plugin with zod, consolidating the multi-PR migration tracked through #258427 and the subsequent staged PRs (
PR 23,PR 24a–PR 24d). Every*Rtcodec undercommon/types/api,common/types/domain, andcommon/schemais rewritten in zod (*Schema);decodeOrThrow/decodeWithExcessOrThrowroute call sites becomedecodeOrThrowZod/decodeWithExcessOrThrowZod; io-ts is removed from the plugin's runtime imports entirely, and the@kbn/securitysolution-io-ts-utils/io-ts/fp-tsdeps drop out oftsconfig.json. All publicly exported TypeScript types (Case,Cases,CasePostRequest,CaseConnector,AttachmentType,CaseSeverity, …) are preserved throughz.infer<…>so no external consumer is affected.Motivation
io-ts is in maintenance mode upstream and the Cases plugin was the last large io-ts consumer in
x-pack/platform/plugins/shared/. Moving to zod aligns Cases with the rest of Kibana (route validation,@kbn/zod,@kbn/zod-helpers, OpenAPI-generated bundled types), unblocks future schema work that depends on zod-only features (e.g.DeepStrict, the OpenAPI generator pipeline already used incommon/bundled-types.gen.ts), and removes a transitivefp-tsruntime dependency from the plugin.Changes
*Rt) undercommon/types/{api,domain}andcommon/schemais rewritten as a zod schema (*Schema). The parallel scaffolding undercommon/types/api_zod/common/types/domain_zodfrom the earlier staging PRs is folded back into the canonical paths and removed.server/common/runtime_types.tsnow exposesdecodeOrThrowZod(strip-on-unknown, matchingrt.exact) anddecodeWithExcessOrThrowZod(usesDeepStrictfrom@kbn/zod-helpersto reject unknown keys at any depth, matching io-tsexactCheck). All route handlers, client methods, and SO migrations are switched over.NonEmptyStringrejects whitespace-only strings (s.trim().length >= 1),paginationSchemarejects non-numericpage/perPagestrings (NumberFromStringparity, replaces silentNaN), andlimitedStringSchemaaccepts empty / whitespace-only input only whenmin === 0.common/schema/index.test.tsto a zod-native suite; restores the fourcommon/types/domain/template/*business-logic tests (evaluate_conditions,fields,validate_extended_fields,v1) that were swept up in the codec cleanup but cover pure functions independent of io-ts; adds new unit suites for the top-level request schemas (CasePostRequestSchema,CasePatchRequestSchema,CasesPatchRequestSchema,CasesFindRequestSchema,CasesSearchRequestSchema,AttachmentRequestSchema,AttachmentPatchRequestSchema, bulk attachment requests); addsDeepStrictregression tests covering excess keys inside arrays of objects, union variants, and discriminated-union variants.extended_fieldssnake_case key justification anchored togetUserActionItemByDifference, theAttachmentPatchRequestSchemapartial-update warning anchored toinjectAttachmentSOAttributesFromRefsForPatch, the@deprecatedpointer onCaseUserActionInjectedDeprecatedIdsSchema, and theUserActionTypes"do not remove" preamble).Risk & Migration
Score: 3 — Wholesale validation-layer rewrite isolated to the Cases plugin. No public TypeScript types change shape (preserved via
z.infer), no SO schema or RBAC surface is touched, and there are no external consumers of the removed io-ts codec exports (verified by monorepo grep acrossx-pack/solutions/,x-pack/plugins/,src/). Backward compatible at the API layer: every error message that downstream tooling/integrations might rely on ("The X field cannot be an empty string.","cannot parse to a number","The provided perPage value is too high. …","The length of the field X is too long. Array must be of length <= N.") is preserved verbatim under zod viasuperRefine.Testing
Manual:
yarn start --no-base-pathor your usual flow) and log in.curl -u elastic:<pw> -H "kbn-xsrf: true" -H "Content-Type: application/json" -X POST http://localhost:5601/api/cases -d '{"title":" ","description":"d","tags":[],"connector":{"id":"none","name":"none","type":".none","fields":null},"settings":{"syncAlerts":false},"owner":"cases"}'→ expect400 "title: The title field cannot be an empty string."curl -u elastic:<pw> "http://localhost:5601/api/cases/_find?page=abc"→ expect400 "page: cannot parse to a number"curl -u elastic:<pw> "http://localhost:5601/api/cases/_find?perPage=999"→ expect400 "The provided perPage value is too high. …"Automated:
x-pack/platform/plugins/shared/cases/common/schema/index.test.ts(zod-native port, 38 cases)x-pack/platform/plugins/shared/cases/common/types/api/case/v1.test.ts(43 cases)x-pack/platform/plugins/shared/cases/common/types/api/attachment/v1.test.ts(37 cases)x-pack/platform/plugins/shared/cases/server/common/runtime_types.test.ts(DeepStrict parity at depth)x-pack/platform/plugins/shared/cases/common/types/domain/template/{evaluate_conditions,fields,validate_extended_fields,v1}.test.ts(103 cases, restored)server/routes/api/**,server/client/cases/**,server/services/**,server/saved_object_types/migrations/**updated for the zod helper switchx-pack/platform/test/cases_api_integration/suite covers route-level wiring; no new tests were needed there since the route contract didn't change.Checklist
Check the PR satisfies following conditions.
Reviewers should verify this PR satisfies this list as well.
release_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.