[Streams] Add field descriptions and documentation-only field overrides#255136
[Streams] Add field descriptions and documentation-only field overrides#255136flash1293 merged 22 commits intoelastic:mainfrom
Conversation
This PR adds support for field descriptions in Streams, allowing users to document fields without creating Elasticsearch mappings (doc-only overrides). ## Features ### Field Descriptions - Optional `description` property on field definitions for documenting fields - Description column in schema table with tooltip support - "Edit field" flyout allows setting/editing descriptions for all field types - "Clear description" context menu action to remove descriptions - Descriptions are stored as metadata and not sent to Elasticsearch ### Documentation-Only Field Overrides - Fields can have descriptions without ES mappings (typeless definitions) - Child streams can add description overrides to inherited fields - Doc-only overrides don't trigger ES mapping updates or simulation - Confirm changes modal filters out doc-only changes ### UI Improvements - Renamed "Map field" to "Edit field" since users can now add descriptions without mapping - "Edit field" action available for all field statuses (mapped, unmapped, inherited) - Status filter shows "Dynamic" (not "Unmapped") for Classic streams - Field simulation excludes description-only changes from API calls ### Validation - Server-side validation prevents invalid type overrides on inherited fields - UI hides "Unmap field" for fields that would conflict with parent mappings - Child streams can map fields when parent has doc-only field (no type) ### Classic Streams Support - Description-only overrides work via `ingest.classic.field_overrides` - Mapping dry-run/update filters out doc-only overrides - Skips mapping actions when only doc-only overrides present ## Test Coverage - Unit tests for schema validation and field definition parsing - Integration tests for doc-only field inheritance - FTR API tests for description-only overrides - Scout UI tests for schema editor interactions - Comprehensive test coverage for all field action scenarios Made-with: Cursor
|
This is a clean PR created from #250785, which had accumulated many iterative changes during review. The changes have been squashed into a single commit with a comprehensive summary describing all features:
All tests from the original PR are included. Written by cursor, wait for @flash1293 to chime in |
|
/ralph Please simplify this: Classic streams shouldn't be able to have description-only fields. The UI (the field flyout) already enforces this, please check all changes on this PR and remove the support for this (in the API and the backend handling). Make sure that description-only still works for wired streams |
…c streams Classic streams should not support description-only field overrides (fields with description but no type). The UI already enforces this, so this removes the redundant defensive handling from the API/backend: - Add classicFieldDefinitionSchema that requires a type - Update classic stream schema to use the stricter schema - Simplify getClassicFieldOverrideMappings to expect type always present - Remove validate_ingest_upsert.ts validation (now handled by schema) - Remove API-level validation for typeless descriptions - Add tests for the new classic field definition schema Made-with: Cursor
|
Addressed the simplification request. Changes made: Schema Changes:
Backend Simplification:
API Cleanup:
Testing:
Description-only field overrides still work for wired streams via the general |
| "xpack.stackConnectors.xmatters.unexpectedNullResponseErrorMessage": "unerwartete Nullreaktion von xmatters", | ||
| "xpack.stackConnectors.xmatters.unexpectedStatusErrorMessage": "Fehler beim Auslösen des xMatters-Flows: unerwarteter Status {status}", | ||
| "xpack.streams.actions.editFieldLabel": "Feld bearbeiten", | ||
| "xpack.streams.actions.mapFieldLabel": "Kartenfeld", |
There was a problem hiding this comment.
Side note - this translation was very very wrong anyway
|
Pinging @elastic/obs-onboarding-team (Team:obs-onboarding) |
|
/ci-ralph |
|
/ralph check the test failures of build 402162 and fix them Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/402162 Use |
- Remove test case for description-only classic stream fields (no longer supported) - Prevent description-only fields from being added for classic streams in buildSchemaSavePayload - Add proper type casting for ClassicFieldDefinition in stream utils - Export ClassicFieldDefinition and ClassicFieldDefinitionConfig from @kbn/streams-schema Made-with: Cursor
|
Fixed the type errors from build 402162. Changes made:
All type checks and Jest tests pass locally. |
|
/ralph check the test failures of build 402264 and fix them Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/402264 Use |
|
/ralph check the test failures of build 405062 and fix them Build URL: https://buildkite.com/elastic/kibana-pull-request/builds/405062 Use |
|
Investigated the test failure from build 405062. Finding: The failure is a known flaky test unrelated to this PR. Failed test:
Evidence:
Recommendation: Retrigger CI — the flaky test should eventually pass on retry, or will be automatically skipped. |
|
Thanks @diogolima-elastic , I fixed those issues:
|
diogolima-elastic
left a comment
There was a problem hiding this comment.
Thank you for the quick changes, LGTM! 🚀
📝 WalkthroughWalkthroughAdds support for documentation-only (description-only) field overrides across Streams: schema changes, validation, storage, simulation, UI (schema editor flyout/table/actions), state machines, tests, and API/integration tests to treat typeless overrides as non-mapping-only metadata. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant UI as Schema Editor UI
participant Server as Streams Server
participant Sim as Simulation Handler
participant ES as Elasticsearch
User->>UI: Open/Edit field (add description-only override)
UI->>Server: stageDocOnlyOverride / save request (doc-only override)
Server->>Server: merge docOnlyOverrides into detected/upsert payload
Server->>Sim: request simulation with simulatable fields (typed only)
Sim->>ES: run mapping simulation (exclude doc-only, strip descriptions)
ES-->>Sim: simulation result
Sim-->>Server: simulation outcome
Server-->>UI: updated preview / confirmation (may skip review modal if no mapping-affecting changes)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts (1)
88-107:⚠️ Potential issue | 🟠 MajorRequire strict validation in the documentation-only field definition branch.
The current schema intersects
recursiveRecordwith the union of field variants. This means the documentation-only branch (Lines 96–101) inheritsrecursiveRecord's permissive key handling, allowing payloads like{ description: 'x', index: false }to validate even though documentation-only overrides should carry only a description. Theas z.ZodType<FieldDefinitionConfig>cast masks this runtime/type mismatch.Split the union outside the intersection and apply
.strict()to the documentation-only variant:Suggested schema split
- export const fieldDefinitionConfigSchema = z.intersection( - recursiveRecord, - z.union([ - z.object({ - type: z.enum(FIELD_DEFINITION_TYPES), - format: z.optional(NonEmptyString), - description: z.optional(z.string()), - }), - z.object({ - // Documentation-only override: require description and forbid type entirely - description: z.string(), - type: z.never().optional(), - format: z.never().optional(), - }), - z.object({ - type: z.literal('system'), - description: z.optional(z.string()), - }), - ]) - ) as z.ZodType<FieldDefinitionConfig>; + const mappingFieldDefinitionConfigSchema = z.intersection( + recursiveRecord, + z.object({ + type: z.enum(FIELD_DEFINITION_TYPES), + format: z.optional(NonEmptyString), + description: z.optional(z.string()), + }) + ); + + const documentationOnlyFieldDefinitionConfigSchema = z + .object({ + description: z.string(), + }) + .strict(); + + export const fieldDefinitionConfigSchema = z.union([ + mappingFieldDefinitionConfigSchema, + documentationOnlyFieldDefinitionConfigSchema, + z.object({ + type: z.literal('system'), + description: z.optional(z.string()), + }), + ]) as z.ZodType<FieldDefinitionConfig>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts` around lines 88 - 107, The documentation-only union branch is inheriting permissive keys from recursiveRecord via the intersection in fieldDefinitionConfigSchema, allowing extra keys like index to pass validation; change the schema so the union of variants is created first and then intersected with recursiveRecord only for the variants that should allow those keys, and make the documentation-only variant a strict schema (use .strict()) so it permits only description and forbids all other keys; update the construction around fieldDefinitionConfigSchema and remove the unsafe cast-as z.ZodType<FieldDefinitionConfig> if necessary to ensure runtime validation matches the TypeScript type.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)
122-141:⚠️ Potential issue | 🟠 MajorKeep descriptions when unmapping wired fields.
Unmap fieldnow removes the mapping by staging only{ name, parent, status: 'unmapped' }. On wired streams that also drops any existing description, even though the new doc-only model keeps metadata independent from ES mappings. Please preservefield.descriptionin this path so unmapping converts to a doc-only override instead of silently deleting the docs.💡 Suggested change
onClick: () => { onFieldUpdate({ name: field.name, parent: field.parent, status: 'unmapped', + ...(Streams.WiredStream.Definition.is(stream) && field.description + ? { description: field.description } + : {}), } as SchemaField); },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx` around lines 122 - 141, The unmap action currently calls onFieldUpdate with only { name, parent, status: 'unmapped' } which drops field.description for wired streams; update the unmap action (the object passed to onFieldUpdate in the Unmap field branch where viewFieldAction, editFieldAction and clearDescriptionAction are assembled) to include field.description so the staged SchemaField preserves the existing description and becomes a doc-only override; keep the rest of the properties (name, parent, status: 'unmapped') intact and ensure the object is typed as SchemaField when passed to onFieldUpdate.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx (1)
21-41:⚠️ Potential issue | 🟠 MajorDon't auto-apply metadata recommendations in the wired doc-only flow.
This new filtering makes
unmappeda wired-only choice, butFieldFormTypestill callsonTypeChange(recommendation)wheneverfield.typeis empty. Opening an inherited ECS/OTel field just to add a description will therefore silently stage a real mapping override as soon as metadata resolves. Please gate that autofill path so a wired field can stay typeless.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx` around lines 21 - 41, The wired "doc-only" flow is still auto-applying metadata recommendations: in FieldFormType where onTypeChange(recommendation) is invoked when field.type is empty, add a guard so recommendations are not auto-applied for wired doc-only/inherited fields; only call onTypeChange(recommendation) when either streamType !== 'wired' or the field is not a doc-only/inherited field (e.g., field.inherited or field.isDocOnly is false) and recommendation is present. This keeps getFieldTypeOptions unchanged but prevents FieldFormType from silently staging a real mapping override for wired typeless fields.
🧹 Nitpick comments (10)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts (1)
38-43: Use inlinetypemodifier for type-only imports.The import mixes value imports (
Streams,isSchema) with type-only imports (ClassicFieldDefinition,FieldDefinition). Per coding guidelines, type-only imports should useimport type.♻️ Suggested fix using inline type imports
-import { - Streams, - isSchema, - type ClassicFieldDefinition, - type FieldDefinition, -} from '@kbn/streams-schema'; +import { + Streams, + isSchema, +} from '@kbn/streams-schema'; +import type { ClassicFieldDefinition, FieldDefinition } from '@kbn/streams-schema';Alternatively, if the codebase prefers inline type modifiers (TypeScript 4.5+):
import { Streams, isSchema, type ClassicFieldDefinition, type FieldDefinition, } from '@kbn/streams-schema';As per coding guidelines: "Use
import typefor type-only imports in TypeScript."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts` around lines 38 - 43, The import currently mixes runtime values (Streams, isSchema) with types (ClassicFieldDefinition, FieldDefinition); update the import to mark the type-only imports with the inline type modifier so the line imports Streams and isSchema as normal values and imports ClassicFieldDefinition and FieldDefinition as types (use "type ClassicFieldDefinition" and "type FieldDefinition" in the same import) to satisfy the coding guideline requiring type-only imports be declared with import type.x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts (1)
821-829: Add explicitPromise<void>return types to the new page-object methods.These helpers are part of the exported
StreamsAppAPI, so the new methods should declare their return types explicitly for consistency with the repo rule. As per coding guidelines "Prefer explicit return types for public APIs and exported functions in TypeScript."Also applies to: 931-939
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts` around lines 821 - 829, The new page-object method confirmChangesInReviewModalIfPresent should declare an explicit Promise<void> return type (i.e., change its signature to confirmChangesInReviewModalIfPresent(): Promise<void>) to comply with the exported StreamsApp API typing rule; update this method and the other new helper method(s) introduced in the same file around the other diff (the methods shown in the 931-939 range) to similarly declare explicit Promise<void> return types so the public/page-object API has consistent explicit return types.x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts (1)
11-60: Add a typeless description-only case here.These tests cover typed fields, but the new feature is a typeless override like
{ description, from }. SincewithoutInheritedFieldMetadata()now sits on the export path for doc-only overrides, it would be worth locking in that it preservesdescriptionwhile stripping inherited metadata for a field with notype.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts` around lines 11 - 60, Add a new unit test in the describe('withoutInheritedFieldMetadata') suite that verifies a typeless, description-only field (e.g. { description: 'desc', from: 'logs.parent' }) is returned with description preserved and inherited metadata stripped; call withoutInheritedFieldMetadata with an input like { 'doc.only': { description: 'desc', from: 'logs.parent' } } and assert the result equals { 'doc.only': { description: 'desc' } } so the function (withoutInheritedFieldMetadata) handles fields that lack a type.x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts (1)
66-72: NarrowdocOnlyOverridesto the doc-only shape.Lines 66-72 document that this bag must only hold typeless documentation overrides, but
FieldDefinitionalso admits mapped field configs. That makes it easy for a future reducer to stash a typed field here and have it flow through the doc-only path accidentally. Prefer a dedicated shape such asRecord<string, { description?: string }>or the narrowest exported typeless field-definition type.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts` around lines 66 - 72, The docOnlyOverrides property is currently typed as FieldDefinition which permits full typed/mapped field configs; narrow it to a dedicated typeless shape to prevent typed field configs flowing into the doc-only path—replace the type of docOnlyOverrides (in types.ts) with a specific shape such as Record<string, { description?: string }> or use the project’s exported “typeless” field-definition type if one exists; update any related exports/consumers (references to docOnlyOverrides) to the new type so reducers and helpers enforce only documentation-only entries.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts (1)
23-24: Consider renamingPersistableSchemaFieldTypeto avoid confusion.The JSDoc above notes that
'system'and'unmapped'are pseudo-types that "must never be persisted as real mapping types." However,PersistableSchemaFieldTypeis defined as an alias ofSchemaFieldType, which includes these pseudo-types. This creates a naming contradiction—the type name suggests these values can be persisted, but they shouldn't be.Consider either:
- Renaming to something like
UISchemaFieldTypeto clarify it's for UI purposes only- Or narrowing
PersistableSchemaFieldTypeto exclude the pseudo-types:export type PersistableSchemaFieldType = NonNullable<FieldDefinitionConfig['type']>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts` around lines 23 - 24, PersistableSchemaFieldType currently aliases SchemaFieldType which includes pseudo-types 'system' and 'unmapped' that must not be persisted; change PersistableSchemaFieldType to only include real mapping types by narrowing it to NonNullable<FieldDefinitionConfig['type']> (i.e. exclude 'system' and 'unmapped'), and keep SchemaFieldType as the UI union that includes the pseudo-types so callers that need persistable-only types use PersistableSchemaFieldType while the UI continues to use SchemaFieldType.x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx (1)
356-359: Consider memoizing thegetChangesresult to avoid duplicate computation.
getChanges(schemaEditorFields, definitionFields)is computed twice - once inopenConfirmationModal(Line 261) and again in theonConfirmprop (Line 357). While the performance impact is likely negligible for typical field counts, memoizing this result would be cleaner.♻️ Suggested refactor
+ const mappingChanges = React.useMemo( + () => getChanges(schemaEditorFields, definitionFields), + [schemaEditorFields, definitionFields] + ); + const openConfirmationModal = () => { - const changes = getChanges(schemaEditorFields, definitionFields); - if (changes.length === 0) { + if (mappingChanges.length === 0) { // If there are no mapping-affecting changes, skip the review modal entirely. void saveChanges(); return; } // ... rest of function }; // Later in ManagementBottomBar: onConfirm={ - schemaEditorFields.length > 0 && - getChanges(schemaEditorFields, definitionFields).length > 0 + mappingChanges.length > 0 ? openConfirmationModal : saveChanges }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx` around lines 356 - 359, Compute the diff once and reuse it: create a memoized value (e.g. pendingChanges = useMemo(() => getChanges(schemaEditorFields, definitionFields), [schemaEditorFields, definitionFields])) and replace the two direct calls to getChanges(...) (the one used inside openConfirmationModal and the one in the onConfirm prop selection) with this pendingChanges; ensure you import/use React.useMemo and update the conditional that currently chooses between openConfirmationModal and saveChanges to reference pendingChanges.length > 0.x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts (1)
38-93: Add regression coverage for the descendant-side relaxation.These new cases cover
validateAncestorFields(), but the matchingvalidateDescendantFields()change invalidate_fields.tsstill has no test for doc-only fields. A small pair of cases there would keep both directions aligned.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts` around lines 38 - 93, Add tests mirroring the ancestor-side cases for the descendant-side validator: in validate_fields.test.ts add two it() cases that call validateDescendantFields() (same style as the existing validateAncestorFields tests) — one where an ancestor defines a doc-only field (no type, only description) and the descendant supplies a real type, and another where the descendant supplies a real type plus a description. Use the same helpers (createWiredStreamDefinition) and streamName ('logs.otel') and assert that validateDescendantFields({... ancestors: [ancestor], fields, streamName }) does not throw; name the tests to indicate they cover "doc-only" ancestor fields and the descendant relaxation.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx (1)
27-28: Prefer@emotion/reactfor the new description-cell styles.This change pulls in
@emotion/cssjust for one cell renderer. Keeping the styles on thecssprop /@emotion/reactpath avoids mixing Emotion APIs in the same React file and stays aligned with the rest of the EUI styling stack.As per coding guidelines, "Use
@elastic/euicomponents with Emotion (@emotion/react) for styling in React."Also applies to: 204-216
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx` around lines 27 - 28, The file schema_editor_table.tsx imports and uses `@emotion/css` for the description-cell styles; replace that by using `@emotion/react`'s css prop to stay consistent with EUI/Emotion usage: remove the import from '@emotion/css', import the css (and/or jsx pragma if needed) from '@emotion/react', and update the description cell renderer (the description-cell code around lines ~204-216 / any function like renderDescriptionCell or the JSX element that currently uses css(...)) to apply styles via the JSX css prop (e.g. <div css={css`...`}> or pass the css object to the css prop) instead of calling the `@emotion/css` helper; ensure any className generated by css(...) is converted to the css prop usage and that getStreamTypeFromDefinition and other imports remain unchanged.x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts (1)
52-63: Good coverage, consider adding a test for fields with both type and description.The test suite covers the key filtering scenarios well. Consider adding a test case verifying that fields with both a valid type and a description are preserved (not accidentally filtered):
it('preserves fields with both type and description', () => { const fields: NamedFieldDefinitionConfig[] = [ { name: 'attributes.a', type: 'keyword', description: 'has both' }, ]; const result = __test__.getSimulatableFieldDefinitions(fields); expect(result).toEqual([{ name: 'attributes.a', type: 'keyword', description: 'has both' }]); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts` around lines 52 - 63, Add a unit test to ensure getSimulatableFieldDefinitions preserves fields that include both a valid type and a description: in route.test.ts add a case (similar to existing ones) that passes a NamedFieldDefinitionConfig like { name: 'attributes.a', type: 'keyword', description: 'has both' } to __test__.getSimulatableFieldDefinitions and assert the returned array contains the same object (including description) to verify the function does not drop the description field.x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts (1)
191-213: Minor: Tests at Lines 191-196 and 207-213 overlap.Both tests verify that
isMappingPropertiesreturnsfalsewhen fields contain description-only entries (no type). Consider consolidating these into a single test or differentiating them more clearly (e.g., one for explicitly missing type, one forundefinedtype).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts` around lines 191 - 213, The two tests using isMappingProperties that both assert false for description-only fields are redundant; consolidate them into one clearer test (e.g., "should return false for doc-only/undefined type fields") that covers both cases: a field with only description (no type key) and a field with type explicitly set to undefined, using the same FieldDefinition objects and a single expect(isMappingProperties(...)).toBe(false) assertion, and remove the duplicate test block (the ones with descriptions 'should return false for description-only overrides (no type)' and 'should return false when containing doc-only fields (description without type)').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx`:
- Around line 95-102: When staging the field in onAddField from
add_field_flyout.tsx, drop any mapping-only keys when rawType === 'unmapped' so
you don't forward stale mapping metadata; specifically, after const { type:
rawType, ...rest } = data and const effectiveType = rawType === 'unmapped' ?
undefined : rawType, create a sanitized payload that, if effectiveType is
undefined, removes mapping-only keys (e.g. format and any advanced mapping
params such as analyzer/search_analyzer/fields or other mapping-specific
properties) from rest before calling onAddField; otherwise include rest as-is
and set type/status as you already do (use the existing onAddField and
SchemaField symbols to locate where to change the object construction).
In `@x-pack/platform/plugins/shared/streams/server/routes/content/route.ts`:
- Around line 86-92: The merge is dropping user overrides because
withoutBaseFields is applied after inheritedFields and local fields are merged;
move the base-field removal to operate on inheritedFields before merging so
explicit local overrides are preserved. Specifically, call withoutBaseFields on
inheritedFields (reference: inheritedFields, withoutBaseFields) first, then
merge that result with stream.ingest.wired.fields, and finally call
withoutInheritedFieldMetadata on the merged result (reference:
stream.ingest.wired.fields, withoutInheritedFieldMetadata) to strip metadata.
---
Outside diff comments:
In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.ts`:
- Around line 88-107: The documentation-only union branch is inheriting
permissive keys from recursiveRecord via the intersection in
fieldDefinitionConfigSchema, allowing extra keys like index to pass validation;
change the schema so the union of variants is created first and then intersected
with recursiveRecord only for the variants that should allow those keys, and
make the documentation-only variant a strict schema (use .strict()) so it
permits only description and forbids all other keys; update the construction
around fieldDefinitionConfigSchema and remove the unsafe cast-as
z.ZodType<FieldDefinitionConfig> if necessary to ensure runtime validation
matches the TypeScript type.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 122-141: The unmap action currently calls onFieldUpdate with only
{ name, parent, status: 'unmapped' } which drops field.description for wired
streams; update the unmap action (the object passed to onFieldUpdate in the
Unmap field branch where viewFieldAction, editFieldAction and
clearDescriptionAction are assembled) to include field.description so the staged
SchemaField preserves the existing description and becomes a doc-only override;
keep the rest of the properties (name, parent, status: 'unmapped') intact and
ensure the object is typed as SchemaField when passed to onFieldUpdate.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsx`:
- Around line 21-41: The wired "doc-only" flow is still auto-applying metadata
recommendations: in FieldFormType where onTypeChange(recommendation) is invoked
when field.type is empty, add a guard so recommendations are not auto-applied
for wired doc-only/inherited fields; only call onTypeChange(recommendation) when
either streamType !== 'wired' or the field is not a doc-only/inherited field
(e.g., field.inherited or field.isDocOnly is false) and recommendation is
present. This keeps getFieldTypeOptions unchanged but prevents FieldFormType
from silently staging a real mapping override for wired typeless fields.
---
Nitpick comments:
In `@x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts`:
- Around line 191-213: The two tests using isMappingProperties that both assert
false for description-only fields are redundant; consolidate them into one
clearer test (e.g., "should return false for doc-only/undefined type fields")
that covers both cases: a field with only description (no type key) and a field
with type explicitly set to undefined, using the same FieldDefinition objects
and a single expect(isMappingProperties(...)).toBe(false) assertion, and remove
the duplicate test block (the ones with descriptions 'should return false for
description-only overrides (no type)' and 'should return false when containing
doc-only fields (description without type)').
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsx`:
- Around line 27-28: The file schema_editor_table.tsx imports and uses
`@emotion/css` for the description-cell styles; replace that by using
`@emotion/react`'s css prop to stay consistent with EUI/Emotion usage: remove the
import from '@emotion/css', import the css (and/or jsx pragma if needed) from
'@emotion/react', and update the description cell renderer (the description-cell
code around lines ~204-216 / any function like renderDescriptionCell or the JSX
element that currently uses css(...)) to apply styles via the JSX css prop (e.g.
<div css={css`...`}> or pass the css object to the css prop) instead of calling
the `@emotion/css` helper; ensure any className generated by css(...) is converted
to the css prop usage and that getStreamTypeFromDefinition and other imports
remain unchanged.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`:
- Around line 23-24: PersistableSchemaFieldType currently aliases
SchemaFieldType which includes pseudo-types 'system' and 'unmapped' that must
not be persisted; change PersistableSchemaFieldType to only include real mapping
types by narrowing it to NonNullable<FieldDefinitionConfig['type']> (i.e.
exclude 'system' and 'unmapped'), and keep SchemaFieldType as the UI union that
includes the pseudo-types so callers that need persistable-only types use
PersistableSchemaFieldType while the UI continues to use SchemaFieldType.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`:
- Around line 356-359: Compute the diff once and reuse it: create a memoized
value (e.g. pendingChanges = useMemo(() => getChanges(schemaEditorFields,
definitionFields), [schemaEditorFields, definitionFields])) and replace the two
direct calls to getChanges(...) (the one used inside openConfirmationModal and
the one in the onConfirm prop selection) with this pendingChanges; ensure you
import/use React.useMemo and update the conditional that currently chooses
between openConfirmationModal and saveChanges to reference pendingChanges.length
> 0.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.ts`:
- Around line 66-72: The docOnlyOverrides property is currently typed as
FieldDefinition which permits full typed/mapped field configs; narrow it to a
dedicated typeless shape to prevent typed field configs flowing into the
doc-only path—replace the type of docOnlyOverrides (in types.ts) with a specific
shape such as Record<string, { description?: string }> or use the project’s
exported “typeless” field-definition type if one exists; update any related
exports/consumers (references to docOnlyOverrides) to the new type so reducers
and helpers enforce only documentation-only entries.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.ts`:
- Around line 38-43: The import currently mixes runtime values (Streams,
isSchema) with types (ClassicFieldDefinition, FieldDefinition); update the
import to mark the type-only imports with the inline type modifier so the line
imports Streams and isSchema as normal values and imports ClassicFieldDefinition
and FieldDefinition as types (use "type ClassicFieldDefinition" and "type
FieldDefinition" in the same import) to satisfy the coding guideline requiring
type-only imports be declared with import type.
In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-829: The new page-object method
confirmChangesInReviewModalIfPresent should declare an explicit Promise<void>
return type (i.e., change its signature to
confirmChangesInReviewModalIfPresent(): Promise<void>) to comply with the
exported StreamsApp API typing rule; update this method and the other new helper
method(s) introduced in the same file around the other diff (the methods shown
in the 931-939 range) to similarly declare explicit Promise<void> return types
so the public/page-object API has consistent explicit return types.
In
`@x-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.ts`:
- Around line 11-60: Add a new unit test in the
describe('withoutInheritedFieldMetadata') suite that verifies a typeless,
description-only field (e.g. { description: 'desc', from: 'logs.parent' }) is
returned with description preserved and inherited metadata stripped; call
withoutInheritedFieldMetadata with an input like { 'doc.only': { description:
'desc', from: 'logs.parent' } } and assert the result equals { 'doc.only': {
description: 'desc' } } so the function (withoutInheritedFieldMetadata) handles
fields that lack a type.
In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`:
- Around line 38-93: Add tests mirroring the ancestor-side cases for the
descendant-side validator: in validate_fields.test.ts add two it() cases that
call validateDescendantFields() (same style as the existing
validateAncestorFields tests) — one where an ancestor defines a doc-only field
(no type, only description) and the descendant supplies a real type, and another
where the descendant supplies a real type plus a description. Use the same
helpers (createWiredStreamDefinition) and streamName ('logs.otel') and assert
that validateDescendantFields({... ancestors: [ancestor], fields, streamName })
does not throw; name the tests to indicate they cover "doc-only" ancestor fields
and the descendant relaxation.
In
`@x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts`:
- Around line 52-63: Add a unit test to ensure getSimulatableFieldDefinitions
preserves fields that include both a valid type and a description: in
route.test.ts add a case (similar to existing ones) that passes a
NamedFieldDefinitionConfig like { name: 'attributes.a', type: 'keyword',
description: 'has both' } to __test__.getSimulatableFieldDefinitions and assert
the returned array contains the same object (including description) to verify
the function does not drop the description field.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 9045abad-8632-472a-adeb-5c7c5b12a569
📒 Files selected for processing (69)
oas_docs/output/kibana.serverless.yamloas_docs/output/kibana.yamlx-pack/platform/packages/shared/kbn-streams-schema/index.tsx-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.tsx-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.tsx-pack/platform/packages/shared/kbn-streams-schema/src/helpers/get_advanced_parameters.tsx-pack/platform/packages/shared/kbn-streams-schema/src/models/ingest/classic.tsx-pack/platform/plugins/private/translations/translations/de-DE.jsonx-pack/platform/plugins/private/translations/translations/fr-FR.jsonx-pack/platform/plugins/private/translations/translations/ja-JP.jsonx-pack/platform/plugins/private/translations/translations/zh-CN.jsonx-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.tsx-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_layer.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_otel_layer.tsx-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.tsx-pack/platform/plugins/shared/streams/server/lib/streams/state_management/streams/classic_stream.tsx-pack/platform/plugins/shared/streams/server/routes/content/route.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/processing/simulation_handler.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.tsx-pack/platform/plugins/shared/streams/server/routes/streams/ingest/route.tsx-pack/platform/plugins/shared/streams_app/moon.ymlx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/constants.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/filters/status_filter_group.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_format.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/sample_preview_table.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/schema_editor_flyout.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/simulation_state_machine.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/use_stream_enrichment.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.tsxx-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_mapping/data_mapping.spec.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.tsx-pack/platform/plugins/shared/streams_app/tsconfig.jsonx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/basic.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/description_only_inheritance.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/index.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/schema.ts
💤 Files with no reviewable changes (5)
- x-pack/platform/plugins/private/translations/translations/ja-JP.json
- x-pack/platform/plugins/private/translations/translations/zh-CN.json
- x-pack/platform/plugins/private/translations/translations/fr-FR.json
- x-pack/platform/plugins/private/translations/translations/de-DE.json
- x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx
| const { type: rawType, ...rest } = data; | ||
| const effectiveType = rawType === 'unmapped' ? undefined : rawType; | ||
|
|
||
| onAddField({ | ||
| ...rest, | ||
| ...(effectiveType !== undefined ? { type: effectiveType } : {}), | ||
| status: effectiveType !== undefined ? 'mapped' : 'unmapped', | ||
| } as SchemaField); |
There was a problem hiding this comment.
Drop mapping-only state when staging a typeless field.
If the user picks a concrete type, edits format or advanced params, and then switches back to unmapped, this branch still forwards those hidden fields via rest. That can stage an invalid typeless definition with stale mapping metadata. Please strip mapping-only keys when effectiveType is undefined.
💡 Suggested change
- const { type: rawType, ...rest } = data;
+ const { type: rawType, format, additionalParameters, ...rest } = data;
const effectiveType = rawType === 'unmapped' ? undefined : rawType;
onAddField({
...rest,
- ...(effectiveType !== undefined ? { type: effectiveType } : {}),
+ ...(effectiveType !== undefined
+ ? {
+ type: effectiveType,
+ ...(format ? { format } : {}),
+ ...(additionalParameters ? { additionalParameters } : {}),
+ }
+ : {}),
status: effectiveType !== undefined ? 'mapped' : 'unmapped',
} as SchemaField);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { type: rawType, ...rest } = data; | |
| const effectiveType = rawType === 'unmapped' ? undefined : rawType; | |
| onAddField({ | |
| ...rest, | |
| ...(effectiveType !== undefined ? { type: effectiveType } : {}), | |
| status: effectiveType !== undefined ? 'mapped' : 'unmapped', | |
| } as SchemaField); | |
| const { type: rawType, format, additionalParameters, ...rest } = data; | |
| const effectiveType = rawType === 'unmapped' ? undefined : rawType; | |
| onAddField({ | |
| ...rest, | |
| ...(effectiveType !== undefined | |
| ? { | |
| type: effectiveType, | |
| ...(format ? { format } : {}), | |
| ...(additionalParameters ? { additionalParameters } : {}), | |
| } | |
| : {}), | |
| status: effectiveType !== undefined ? 'mapped' : 'unmapped', | |
| } as SchemaField); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsx`
around lines 95 - 102, When staging the field in onAddField from
add_field_flyout.tsx, drop any mapping-only keys when rawType === 'unmapped' so
you don't forward stale mapping metadata; specifically, after const { type:
rawType, ...rest } = data and const effectiveType = rawType === 'unmapped' ?
undefined : rawType, create a sanitized payload that, if effectiveType is
undefined, removes mapping-only keys (e.g. format and any advanced mapping
params such as analyzer/search_analyzer/fields or other mapping-specific
properties) from rest before calling onAddField; otherwise include rest as-is
and set type/status as you already do (use the existing onAddField and
SchemaField symbols to locate where to change the object construction).
| // strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition | ||
| stream.ingest.wired.fields = withoutInheritedFieldMetadata( | ||
| withoutBaseFields({ | ||
| ...inheritedFields, | ||
| ...stream.ingest.wired.fields, | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Preserve explicit overrides on base fields during export.
withoutBaseFields() is running after inherited and local fields are merged, so any user-authored override on a built-in base field gets dropped too. That means a description-only override on something like a base field will disappear from the exported pack and won’t round-trip on import.
💡 Proposed fix
- stream.ingest.wired.fields = withoutInheritedFieldMetadata(
- withoutBaseFields({
- ...inheritedFields,
- ...stream.ingest.wired.fields,
- })
- );
+ stream.ingest.wired.fields = withoutInheritedFieldMetadata({
+ ...withoutBaseFields(inheritedFields),
+ ...stream.ingest.wired.fields,
+ });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition | |
| stream.ingest.wired.fields = withoutInheritedFieldMetadata( | |
| withoutBaseFields({ | |
| ...inheritedFields, | |
| ...stream.ingest.wired.fields, | |
| }) | |
| ); | |
| // strip inherited field metadata (from, alias_for) so exports contain clean FieldDefinition | |
| stream.ingest.wired.fields = withoutInheritedFieldMetadata({ | |
| ...withoutBaseFields(inheritedFields), | |
| ...stream.ingest.wired.fields, | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@x-pack/platform/plugins/shared/streams/server/routes/content/route.ts` around
lines 86 - 92, The merge is dropping user overrides because withoutBaseFields is
applied after inheritedFields and local fields are merged; move the base-field
removal to operate on inheritedFields before merging so explicit local overrides
are preserved. Specifically, call withoutBaseFields on inheritedFields
(reference: inheritedFields, withoutBaseFields) first, then merge that result
with stream.ingest.wired.fields, and finally call withoutInheritedFieldMetadata
on the merged result (reference: stream.ingest.wired.fields,
withoutInheritedFieldMetadata) to strip metadata.
📝 WalkthroughWalkthroughThis change introduces support for description-only field overrides in Elasticsearch Streams, allowing fields to store documentation without type mappings. It includes schema updates, new UI components for editing descriptions separately from types, state management enhancements, and test coverage for wired and classic stream handling. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx (1)
395-434:⚠️ Potential issue | 🟡 MinorAssert that the confirm path does not submit immediately.
This still passes if the click both opens the modal and calls
submitChangesbefore the user confirms. Add a negative assertion so the test actually guards the modal gate.Suggested assertion
const submitButton = screen.getByTestId('streamsAppSchemaEditorReviewStagedChangesButton'); await user.click(submitButton); expect(mockOpenModal).toHaveBeenCalled(); + expect(mockSubmitChanges).not.toHaveBeenCalled();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx` around lines 395 - 434, The test currently only checks that clicking the submit button opens the modal but doesn't assert the submission is blocked; after the user.click(submitButton) and before any simulated modal confirmation, add a negative assertion that the mockSubmitChanges spy was not called (e.g., expect(mockSubmitChanges).not.toHaveBeenCalled()) to ensure StreamDetailSchemaEditor does not call submitChanges until the modal confirm path runs; you can also assert mockCloseModal wasn't called yet to further guard the modal gate.
🧹 Nitpick comments (7)
x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts (1)
140-149: Use direct descendant lookup here too.Lines 146-149 still rescan
descendant.ingest.wired.fieldsfor every field. Matching the ancestor path with a keyed lookup keeps the two validators symmetric and avoids repeatedObject.entries()allocations.♻️ Proposed refactor
if (!Object.hasOwn(fields, fieldName)) { continue; } const fieldType = fields[fieldName].type; + const descendantField = descendant.ingest.wired.fields[fieldName]; if ( fieldType !== undefined && - Object.entries(descendant.ingest.wired.fields).some( - ([descendantFieldName, attr]) => - descendantFieldName === fieldName && attr.type !== undefined && attr.type !== fieldType - ) + descendantField?.type !== undefined && + descendantField.type !== fieldType ) { throw new MalformedFieldsError( `Field ${fieldName} is already defined with incompatible type in the child stream ${descendant.name}` ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts` around lines 140 - 149, The loop currently rescans descendant.ingest.wired.fields with Object.entries(...) for each fieldName; replace that repeated allocation with a direct keyed lookup: get const descendantField = descendant.ingest.wired.fields[fieldName] (or check Object.hasOwn to be safe) and then compare descendantField.type !== undefined && descendantField.type !== fieldType, keeping the existing guards (the surrounding Object.hasOwn(fields, fieldName) and fieldType checks) and using the same fieldName, fields, descendant, and descendant.ingest.wired.fields symbols so the validator becomes symmetric and avoids repeated Object.entries allocations.x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts (1)
38-93: Add the inverse doc-only inheritance regression too.These cases cover
parent doc-only -> child typed, but the primary feature path is alsoparent typed -> child description-only override. A matchingvalidateDescendantFieldsno-conflict case would also lock down the newfieldType === undefinedbranch.🧪 Suggested test additions
describe('type validation', () => { + it('should allow description-only override when parent already maps the field', () => { + const ancestor = createWiredStreamDefinition('logs', { + 'attributes.field1': { type: 'keyword' }, + }); + + const fields = { + 'attributes.field1': { description: 'Child documentation' }, + }; + + expect(() => + validateAncestorFields({ + ancestors: [ancestor], + fields, + streamName: 'logs.otel', + }) + ).not.toThrow(); + }); + it('should allow same type with description override', () => { const ancestor = createWiredStreamDefinition('logs', { 'attributes.field1': { type: 'keyword' }, @@ describe('validateDescendantFields', () => { + it('should allow description-only updates when a descendant already maps the field', () => { + const descendants = [ + createWiredStreamDefinition('logs.child', { + 'attributes.level': { type: 'keyword' }, + }), + ]; + const fields: FieldDefinition = { + 'attributes.level': { description: 'Updated docs' }, + }; + + expect(() => validateDescendantFields({ descendants, fields })).not.toThrow(); + }); + it('should throw when field type conflicts with descendant', () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts` around lines 38 - 93, Add the complementary regression test for the inverse path (parent has a real type, child supplies only a description) by creating a case that uses validateDescendantFields (mirroring the existing ancestor tests) to assert no-throw when a parent stream defines 'attributes.field1' with type 'keyword' and the descendant/child defines only description (no type). Use the same helpers (createWiredStreamDefinition) and test structure as the existing type validation tests, target the branch that handles fieldType === undefined, and ensure the test calls validateDescendantFields with ancestors/fields/streamName analogous to the current validateAncestorFields tests.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)
144-161: Consider consolidating duplicate "Edit field" action definitions.Lines 149-154 and the fallback at line 157 both add an "Edit field" action with identical behavior (
openFlyout({ isEditingByDefault: true })). Consider reusing the pre-definededitFieldActionfor both branches to reduce duplication.Proposed simplification
case 'unmapped': actions = [viewFieldAction]; - // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped') - // If the parent has a real mapping, the child can't map it differently - if (!hasRealMappingInParent) { - actions.push({ - name: i18n.translate('xpack.streams.actions.editFieldLabel', { - defaultMessage: 'Edit field', - }), - onClick: () => openFlyout({ isEditingByDefault: true }), - }); - } else { - // If parent has real mapping, only allow editing description - actions.push(editFieldAction); - } + // Edit field is always available - the flyout handles restrictions based on parent mappings + actions.push(editFieldAction); if (field.description) { actions.push(clearDescriptionAction); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx` around lines 144 - 161, The branch handling 'unmapped' currently pushes a duplicate inline "Edit field" action that calls openFlyout({ isEditingByDefault: true }) and also pushes editFieldAction in the else branch; replace the inline action with the existing editFieldAction to eliminate duplication. Locate the 'unmapped' case where viewFieldAction, hasRealMappingInParent, openFlyout and clearDescriptionAction are used and change the push of the inline Edit field action to push(editFieldAction) so both branches reuse the same editFieldAction definition.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx (1)
14-14: Consider adding explicit return type for exported component.Per coding guidelines, exported functions should have explicit return types for better maintainability and API clarity.
Proposed change
-export const FieldType = ({ type, aliasFor }: { type: FieldTypeOption; aliasFor?: string }) => { +export const FieldType = ({ type, aliasFor }: { type: FieldTypeOption; aliasFor?: string }): React.ReactElement => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx` at line 14, The exported component FieldType lacks an explicit return type; update its signature to include one (e.g., annotate as : JSX.Element or use React.FC<{ type: FieldTypeOption; aliasFor?: string }>) so the export has a clear, explicit return type; modify the declaration of FieldType (which accepts props type: FieldTypeOption and optional aliasFor) to include the chosen return type.x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx (1)
111-131: Consider extracting complex boolean derivations to named functions for readability.The derived state logic is correct but dense. The inline comments help, but extracting these to small helper functions could improve readability:
const isDescriptionOnlyEditing = computeIsDescriptionOnlyEditing(isEditing, streamType, field.status, isDescriptionOnlyMode);This is a minor readability suggestion; the current implementation is functionally correct.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx` around lines 111 - 131, Extract the dense boolean derivations into small named helper functions to improve readability: create functions such as computeIsDescriptionOnlyEditing(isEditing, streamType, fieldStatus, isDescriptionOnlyMode), computeHasDescriptionChanged(field, nextField), computeIsInheritedDescriptionOnlyEditing(isDescriptionOnlyEditing, fieldStatus) and computeIsUnmappedDescriptionOnlyChange(isEditing, streamType, fieldStatus, fieldType, nextFieldType, hasDescriptionChanged) and replace the inline expressions for isDescriptionOnlyEditing, hasDescriptionChanged, isInheritedDescriptionOnlyEditing, isDocOnlyField and isUnmappedDescriptionOnlyChange with calls to these helpers (use the existing symbols: isDescriptionOnlyEditing, hasDescriptionChanged, isInheritedDescriptionOnlyEditing, isDocOnlyField, isUnmappedDescriptionOnlyChange and field/status/type/nextField variables to wire inputs).x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts (1)
24-24: Consider whetherPersistableSchemaFieldTypealias adds value.The alias
PersistableSchemaFieldType = SchemaFieldTypedoesn't narrow the type—it's identical toSchemaFieldType. If the intent is to represent types safe for persistence, it should exclude pseudo-types:export type PersistableSchemaFieldType = Exclude<SchemaFieldType, 'system' | 'unmapped'>;If this is intentional (e.g., for future disambiguation), consider adding a JSDoc comment explaining the purpose.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts` at line 24, PersistableSchemaFieldType is currently a direct alias of SchemaFieldType and doesn't add value; update its definition to explicitly exclude non-persistable pseudo-types or document intent. Change the type alias PersistableSchemaFieldType to exclude 'system' and 'unmapped' (using Exclude<SchemaFieldType, 'system' | 'unmapped'>) so it represents only persistable types, or if you want to keep the alias as-is for future use, add a JSDoc above PersistableSchemaFieldType explaining why it mirrors SchemaFieldType and when it will diverge.x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx (1)
355-359: Consider extracting the changes check to avoid duplication.The
getChanges(schemaEditorFields, definitionFields).length > 0check appears both here (line 357) and inopenConfirmationModal(line 262). While currently consistent, this could be memoized to avoid redundant computation and reduce the risk of divergence.♻️ Optional: Memoize the changes check
+ const mappingChanges = React.useMemo( + () => getChanges(schemaEditorFields, definitionFields), + [schemaEditorFields, definitionFields] + ); + const openConfirmationModal = () => { - const changes = getChanges(schemaEditorFields, definitionFields); - if (changes.length === 0) { + if (mappingChanges.length === 0) { // If there are no mapping-affecting changes, skip the review modal entirely. void saveChanges(); return; } // ... rest of function }; // ... later in JSX onConfirm={ - schemaEditorFields.length > 0 && - getChanges(schemaEditorFields, definitionFields).length > 0 + schemaEditorFields.length > 0 && mappingChanges.length > 0 ? openConfirmationModal : saveChanges }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx` around lines 355 - 359, Extract the repeated changes check into a memoized boolean (e.g., hasChanges) computed with useMemo(() => getChanges(schemaEditorFields, definitionFields).length > 0, [schemaEditorFields, definitionFields]) and replace both occurrences (the onConfirm ternary and the check inside openConfirmationModal) to use hasChanges; this avoids duplicate getChanges calls and prevents divergence while keeping existing behavior when invoking openConfirmationModal or saveChanges.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`:
- Around line 245-246: The read-only description branch in the FieldSummary
component currently renders `${field.description ?? EMPTY_CONTENT}` as a plain
string which collapses newlines; update the rendering of the read-only
description (the branch that uses EMPTY_CONTENT and field.description) to output
the value inside an element styled with `white-space: pre-wrap` (e.g., apply a
className or inline style) so multiline input preserves line breaks while
keeping EMPTY_CONTENT fallback behavior intact.
- Around line 234-244: The Description textarea (EuiTextArea in the isEditing
branch) lacks an accessible label; update the render for EuiTextArea used in the
field summary editor to be programmatically labeled—either wrap it in an
EuiFormRow with a visible label "Description" or add aria-labelledby/aria-label
directly to the EuiTextArea (e.g., aria-label="Description" or aria-labelledby
referencing the Description heading) so screen readers can associate the visible
"Description" heading with the textarea; adjust the component around
field.description and the onChange handler accordingly.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx`:
- Around line 91-97: When handling field.status === 'unmapped', only call
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription
}) for wired streams; add a guard that checks the stream type (e.g.,
isWiredStream or stream.type === 'wired') before staging a typeless doc-only
override and fall back to unmapField(field.name) for Classic/non-wired streams
so Classic overrides still require a type. Ensure you reference the existing
symbols: field.status, normalizedDescription, stageDocOnlyOverride, and
unmapField when adding the conditional.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts`:
- Around line 390-430: The helper stageDocOnlyOverride currently treats any call
with an empty or undefined params.description as an overwrite to
status:'unmapped' and type:undefined, which causes cleared doc-only overrides to
leave the UI showing an unmapped field; update stageDocOnlyOverride to check
params.description and if it is empty/undefined, restore the original field
snapshot instead of mutating it: look up the original SchemaField from
context.detectedSchemaFieldsCache or from the existing variable (found via
context.detectedSchemaFields.find by params.fieldName) and put that original
snapshot back into both detectedSchemaFields and detectedSchemaFieldsCache,
otherwise (when description is present) continue to set status:'unmapped', clear
type and set description as before. Ensure you reference the function
stageDocOnlyOverride, the params.fieldName and detectedSchemaFields /
detectedSchemaFieldsCache when making the change.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`:
- Around line 14-52: Test fixture uses a Classic stream but expects a typeless
doc-only override for "foo", which contradicts the new behavior requiring
Classic field overrides to include a type; update the test for getUpsertFields
to either (a) convert the stream definition to a wired stream definition in the
context (so typeless doc-only overrides are valid) or (b) change the expected
result to assert that the Classic path filters out the doc-only override for
"foo" (i.e., only keep "keep" with its type), and ensure the context still uses
StreamEnrichmentContextType and Streams.ClassicStream.GetResponse symbols to
locate the setup to modify.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`:
- Around line 440-475: The test's mock for mockUseSchemaFields uses
storedFields: [] which makes the hook treat the field as new instead of an
existing mapped field with a description change; update the mockUseSchemaFields
return to include a storedFields entry for 'attributes.test_field' (matching
name and type) so the test represents a description-only change, and ensure the
createMockClassicStreamDefinition fixture still contains the original
description in field_overrides so submitChanges/mocking behavior asserts the
description diff correctly.
In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-828: The check in confirmChangesInReviewModalIfPresent uses
Locator.isVisible with a timeout which is ignored; replace that with awaiting
submitButton.waitFor({ state: 'visible', timeout: 3_000' }) (or equivalent) and
catch the timeout to return false so the modal wait is reliable; if waitFor
succeeds then proceed to await expect(submitButton).toBeEnabled(...) and
submitButton.click() as before, keeping the same timeouts and error handling
around submitButton.
In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts`:
- Around line 52-54: The test still calls saveStepsListChanges() in the
update/retry branches without handling the optional review modal, causing
flakes; either update saveStepsListChanges() (the page object helper) to
internally call confirmChangesInReviewModalIfPresent() after the save so all
save flows are guarded, or in the spec replace the later update/retry save calls
to follow each saveStepsListChanges() with await
pageObjects.streams.confirmChangesInReviewModalIfPresent(); ensuring every save
path (create, update, retry) handles the optional review modal.
---
Outside diff comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`:
- Around line 395-434: The test currently only checks that clicking the submit
button opens the modal but doesn't assert the submission is blocked; after the
user.click(submitButton) and before any simulated modal confirmation, add a
negative assertion that the mockSubmitChanges spy was not called (e.g.,
expect(mockSubmitChanges).not.toHaveBeenCalled()) to ensure
StreamDetailSchemaEditor does not call submitChanges until the modal confirm
path runs; you can also assert mockCloseModal wasn't called yet to further guard
the modal gate.
---
Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 144-161: The branch handling 'unmapped' currently pushes a
duplicate inline "Edit field" action that calls openFlyout({ isEditingByDefault:
true }) and also pushes editFieldAction in the else branch; replace the inline
action with the existing editFieldAction to eliminate duplication. Locate the
'unmapped' case where viewFieldAction, hasRealMappingInParent, openFlyout and
clearDescriptionAction are used and change the push of the inline Edit field
action to push(editFieldAction) so both branches reuse the same editFieldAction
definition.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsx`:
- Line 14: The exported component FieldType lacks an explicit return type;
update its signature to include one (e.g., annotate as : JSX.Element or use
React.FC<{ type: FieldTypeOption; aliasFor?: string }>) so the export has a
clear, explicit return type; modify the declaration of FieldType (which accepts
props type: FieldTypeOption and optional aliasFor) to include the chosen return
type.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsx`:
- Around line 111-131: Extract the dense boolean derivations into small named
helper functions to improve readability: create functions such as
computeIsDescriptionOnlyEditing(isEditing, streamType, fieldStatus,
isDescriptionOnlyMode), computeHasDescriptionChanged(field, nextField),
computeIsInheritedDescriptionOnlyEditing(isDescriptionOnlyEditing, fieldStatus)
and computeIsUnmappedDescriptionOnlyChange(isEditing, streamType, fieldStatus,
fieldType, nextFieldType, hasDescriptionChanged) and replace the inline
expressions for isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField and
isUnmappedDescriptionOnlyChange with calls to these helpers (use the existing
symbols: isDescriptionOnlyEditing, hasDescriptionChanged,
isInheritedDescriptionOnlyEditing, isDocOnlyField,
isUnmappedDescriptionOnlyChange and field/status/type/nextField variables to
wire inputs).
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.ts`:
- Line 24: PersistableSchemaFieldType is currently a direct alias of
SchemaFieldType and doesn't add value; update its definition to explicitly
exclude non-persistable pseudo-types or document intent. Change the type alias
PersistableSchemaFieldType to exclude 'system' and 'unmapped' (using
Exclude<SchemaFieldType, 'system' | 'unmapped'>) so it represents only
persistable types, or if you want to keep the alias as-is for future use, add a
JSDoc above PersistableSchemaFieldType explaining why it mirrors SchemaFieldType
and when it will diverge.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsx`:
- Around line 355-359: Extract the repeated changes check into a memoized
boolean (e.g., hasChanges) computed with useMemo(() =>
getChanges(schemaEditorFields, definitionFields).length > 0,
[schemaEditorFields, definitionFields]) and replace both occurrences (the
onConfirm ternary and the check inside openConfirmationModal) to use hasChanges;
this avoids duplicate getChanges calls and prevents divergence while keeping
existing behavior when invoking openConfirmationModal or saveChanges.
In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.ts`:
- Around line 38-93: Add the complementary regression test for the inverse path
(parent has a real type, child supplies only a description) by creating a case
that uses validateDescendantFields (mirroring the existing ancestor tests) to
assert no-throw when a parent stream defines 'attributes.field1' with type
'keyword' and the descendant/child defines only description (no type). Use the
same helpers (createWiredStreamDefinition) and test structure as the existing
type validation tests, target the branch that handles fieldType === undefined,
and ensure the test calls validateDescendantFields with
ancestors/fields/streamName analogous to the current validateAncestorFields
tests.
In
`@x-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.ts`:
- Around line 140-149: The loop currently rescans descendant.ingest.wired.fields
with Object.entries(...) for each fieldName; replace that repeated allocation
with a direct keyed lookup: get const descendantField =
descendant.ingest.wired.fields[fieldName] (or check Object.hasOwn to be safe)
and then compare descendantField.type !== undefined && descendantField.type !==
fieldType, keeping the existing guards (the surrounding Object.hasOwn(fields,
fieldName) and fieldType checks) and using the same fieldName, fields,
descendant, and descendant.ingest.wired.fields symbols so the validator becomes
symmetric and avoids repeated Object.entries allocations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 460be384-8d02-4ccf-a7f8-73f120e19b32
📒 Files selected for processing (69)
oas_docs/output/kibana.serverless.yamloas_docs/output/kibana.yamlx-pack/platform/packages/shared/kbn-streams-schema/index.tsx-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.tsx-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.tsx-pack/platform/packages/shared/kbn-streams-schema/src/helpers/get_advanced_parameters.tsx-pack/platform/packages/shared/kbn-streams-schema/src/models/ingest/classic.tsx-pack/platform/plugins/private/translations/translations/de-DE.jsonx-pack/platform/plugins/private/translations/translations/fr-FR.jsonx-pack/platform/plugins/private/translations/translations/ja-JP.jsonx-pack/platform/plugins/private/translations/translations/zh-CN.jsonx-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.test.tsx-pack/platform/plugins/shared/streams/server/lib/content/stream/helpers.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/generate_layer.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_layer.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/component_templates/logs_otel_layer.tsx-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.test.tsx-pack/platform/plugins/shared/streams/server/lib/streams/helpers/validate_fields.tsx-pack/platform/plugins/shared/streams/server/lib/streams/state_management/streams/classic_stream.tsx-pack/platform/plugins/shared/streams/server/routes/content/route.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/processing/simulation_handler.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.tsx-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.tsx-pack/platform/plugins/shared/streams/server/routes/streams/ingest/route.tsx-pack/platform/plugins/shared/streams_app/moon.ymlx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/constants.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_type.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/filters/status_filter_group.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/add_field_flyout.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_format.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_form_type.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/index.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/sample_preview_table.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/schema_editor_flyout.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/hooks/use_schema_fields.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_editor_table.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/page_content.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/simulation_state_machine.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/types.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/use_stream_enrichment.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/utils.tsx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.tsxx-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_mapping/data_mapping.spec.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.tsx-pack/platform/plugins/shared/streams_app/tsconfig.jsonx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/basic.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/description_only_inheritance.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/index.tsx-pack/platform/test/api_integration_deployment_agnostic/apis/streams/schema.ts
💤 Files with no reviewable changes (5)
- x-pack/platform/plugins/private/translations/translations/fr-FR.json
- x-pack/platform/plugins/private/translations/translations/de-DE.json
- x-pack/platform/plugins/private/translations/translations/zh-CN.json
- x-pack/platform/plugins/private/translations/translations/ja-JP.json
- x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/schema_changes_review_modal.test.tsx
.../shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
Show resolved
Hide resolved
| ) : ( | ||
| `${field.description ?? EMPTY_CONTENT}` |
There was a problem hiding this comment.
Preserve line breaks in read-only descriptions.
The editor accepts multiline input, but this branch renders a plain string, so saved newlines collapse into spaces. Render the read-only value with white-space: pre-wrap so the display matches what was entered.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`
around lines 245 - 246, The read-only description branch in the FieldSummary
component currently renders `${field.description ?? EMPTY_CONTENT}` as a plain
string which collapses newlines; update the rendering of the read-only
description (the branch that uses EMPTY_CONTENT and field.description) to output
the value inside an element styled with `white-space: pre-wrap` (e.g., apply a
className or inline style) so multiline input preserves line breaks while
keeping EMPTY_CONTENT fallback behavior intact.
| } else if (field.status === 'unmapped') { | ||
| unmapField(field.name); | ||
| // Keep "Unmapped" selectable for documentation-only overrides. | ||
| if (normalizedDescription) { | ||
| stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription }); | ||
| } else { | ||
| unmapField(field.name); | ||
| } |
There was a problem hiding this comment.
Guard doc-only staging to wired streams only.
This branch now stages a typeless override for any unmapped field with a description, including Classic streams. That conflicts with the PR contract that Classic overrides must include a type. In practice this can leave the UI showing a staged description that the Classic save path later drops or rejects.
Suggested fix
if (field.status === 'mapped') {
mapField(field);
} else if (field.status === 'unmapped') {
// Keep "Unmapped" selectable for documentation-only overrides.
- if (normalizedDescription) {
+ if (isWiredStream && normalizedDescription) {
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription });
} else {
unmapField(field.name);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (field.status === 'unmapped') { | |
| unmapField(field.name); | |
| // Keep "Unmapped" selectable for documentation-only overrides. | |
| if (normalizedDescription) { | |
| stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription }); | |
| } else { | |
| unmapField(field.name); | |
| } | |
| } else if (field.status === 'unmapped') { | |
| // Keep "Unmapped" selectable for documentation-only overrides. | |
| if (isWiredStream && normalizedDescription) { | |
| stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription }); | |
| } else { | |
| unmapField(field.name); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/detected_fields_editor.tsx`
around lines 91 - 97, When handling field.status === 'unmapped', only call
stageDocOnlyOverride({ fieldName: field.name, description: normalizedDescription
}) for wired streams; add a guard that checks the stream type (e.g.,
isWiredStream or stream.type === 'wired') before staging a typeless doc-only
override and fall back to unmapField(field.name) for Classic/non-wired streams
so Classic overrides still require a type. Ensure you reference the existing
symbols: field.status, normalizedDescription, stageDocOnlyOverride, and
unmapField when adding the conditional.
| export function stageDocOnlyOverride( | ||
| context: SimulationContext, | ||
| params: { fieldName: string; description?: string } | ||
| ): { | ||
| detectedSchemaFields: SchemaField[]; | ||
| detectedSchemaFieldsCache: Map<string, SchemaField>; | ||
| } { | ||
| const updatedCache = new Map(context.detectedSchemaFieldsCache); | ||
|
|
||
| const updatedFields = (() => { | ||
| const existing = context.detectedSchemaFields.find((f) => f.name === params.fieldName); | ||
| if (!existing) { | ||
| const schemaField: SchemaField = { | ||
| name: params.fieldName, | ||
| parent: context.streamName, | ||
| status: 'unmapped', | ||
| description: params.description, | ||
| }; | ||
| updatedCache.set(schemaField.name, schemaField); | ||
| return [...context.detectedSchemaFields, schemaField]; | ||
| } | ||
|
|
||
| return context.detectedSchemaFields.map((field) => { | ||
| if (field.name !== params.fieldName) return field; | ||
|
|
||
| const schemaField: SchemaField = { | ||
| ...field, | ||
| status: 'unmapped', | ||
| type: undefined, | ||
| description: params.description, | ||
| }; | ||
| updatedCache.set(schemaField.name, schemaField); | ||
| return schemaField; | ||
| }); | ||
| })(); | ||
|
|
||
| return { | ||
| detectedSchemaFields: updatedFields, | ||
| detectedSchemaFieldsCache: updatedCache, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Clearing a doc-only override leaves the field in the wrong state.
description is optional here, but the helper still forces the row to status: 'unmapped' and drops type when params.description is empty. The state machine deletes docOnlyOverrides in that case, so “clear description” can leave the UI showing an unmapped field even though there is no override left to persist.
Please handle the empty-description path separately and restore the original field snapshot instead of mutating it into an unmapped placeholder.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts`
around lines 390 - 430, The helper stageDocOnlyOverride currently treats any
call with an empty or undefined params.description as an overwrite to
status:'unmapped' and type:undefined, which causes cleared doc-only overrides to
leave the UI showing an unmapped field; update stageDocOnlyOverride to check
params.description and if it is empty/undefined, restore the original field
snapshot instead of mutating it: look up the original SchemaField from
context.detectedSchemaFieldsCache or from the existing variable (found via
context.detectedSchemaFields.find by params.fieldName) and put that original
snapshot back into both detectedSchemaFields and detectedSchemaFieldsCache,
otherwise (when description is present) continue to set status:'unmapped', clear
type and set description as before. Ensure you reference the function
stageDocOnlyOverride, the params.fieldName and detectedSchemaFields /
detectedSchemaFieldsCache when making the change.
...ment/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
Show resolved
Hide resolved
| mockUseSchemaFields.mockReturnValue({ | ||
| fields: [ | ||
| { | ||
| name: 'attributes.test_field', | ||
| type: 'keyword', | ||
| parent: 'logs.classic-test', | ||
| status: 'mapped', | ||
| description: 'Updated description', | ||
| }, | ||
| ], | ||
| storedFields: [], | ||
| isLoadingFields: false, | ||
| refreshFields: jest.fn(), | ||
| addField: jest.fn(), | ||
| updateField: jest.fn(), | ||
| pendingChangesCount: 1, | ||
| discardChanges: jest.fn(), | ||
| submitChanges: mockSubmitChanges, | ||
| }); | ||
|
|
||
| const definition = createMockClassicStreamDefinition({ | ||
| stream: { | ||
| ...createMockClassicStreamDefinition().stream, | ||
| ingest: { | ||
| ...createMockClassicStreamDefinition().stream.ingest, | ||
| classic: { | ||
| field_overrides: { | ||
| 'attributes.test_field': { | ||
| type: 'keyword', | ||
| description: 'Original description', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Seed storedFields for the description-only scenario.
With storedFields: [], this mock represents a new field, not an existing mapped field whose description changed. That makes the test depend on implementation details outside the hook contract and can miss regressions if the submit-flow starts diffing against storedFields.
Suggested fixture shape
+ const baseDefinition = createMockClassicStreamDefinition();
+
const mockSubmitChanges = jest.fn().mockResolvedValue(undefined);
mockUseSchemaFields.mockReturnValue({
fields: [
{
name: 'attributes.test_field',
type: 'keyword',
- parent: 'logs.classic-test',
+ parent: baseDefinition.stream.name,
status: 'mapped',
description: 'Updated description',
},
],
- storedFields: [],
+ storedFields: [
+ {
+ name: 'attributes.test_field',
+ type: 'keyword',
+ parent: baseDefinition.stream.name,
+ status: 'mapped',
+ description: 'Original description',
+ },
+ ],
isLoadingFields: false,
refreshFields: jest.fn(),
addField: jest.fn(),
updateField: jest.fn(),
pendingChangesCount: 1,
discardChanges: jest.fn(),
submitChanges: mockSubmitChanges,
});
const definition = createMockClassicStreamDefinition({
stream: {
- ...createMockClassicStreamDefinition().stream,
+ ...baseDefinition.stream,
ingest: {
- ...createMockClassicStreamDefinition().stream.ingest,
+ ...baseDefinition.stream.ingest,
classic: {
field_overrides: {
'attributes.test_field': {
type: 'keyword',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_schema_editor/index.test.tsx`
around lines 440 - 475, The test's mock for mockUseSchemaFields uses
storedFields: [] which makes the hook treat the field as new instead of an
existing mapped field with a description change; update the mockUseSchemaFields
return to include a storedFields entry for 'attributes.test_field' (matching
name and type) so the test represents a description-only change, and ensure the
createMockClassicStreamDefinition fixture still contains the original
description in field_overrides so submitChanges/mocking behavior asserts the
description diff correctly.
x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
Show resolved
Hide resolved
...hared/streams_app/test/scout/ui/tests/data_management/data_processing/error_handling.spec.ts
Show resolved
Hide resolved
|
Good catch by coderabbit on the "unmap field" action, fixed that |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx (1)
246-247:⚠️ Potential issue | 🟡 MinorPreserve multiline descriptions in read-only mode.
Line 247 still renders the saved description as a plain string, so any newline entered in the textarea collapses after leaving edit mode. Render the read-only value inside an element with
white-space: pre-wrapso the summary matches what the user entered.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx` around lines 246 - 247, The read-only description branch currently renders `${field.description ?? EMPTY_CONTENT}` as a plain string, which collapses newlines; update the read-only rendering in the FieldSummary component to wrap the description (or EMPTY_CONTENT fallback) in a container element that applies CSS white-space: pre-wrap so multiline text preserves line breaks—target the JSX branch that references field.description and EMPTY_CONTENT and replace the plain string with that wrapped element.
🧹 Nitpick comments (3)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)
147-164: Remove duplicate inline action definition.Lines 152-157 define an inline "Edit field" action that's identical to the
editFieldActionconstant defined at lines 107-112. Both branches (if (!hasRealMappingInParent)andelse) push functionally equivalent actions.♻️ Simplify by using `editFieldAction` in both branches
case 'unmapped': actions = [viewFieldAction]; - // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped') - // If the parent has a real mapping, the child can't map it differently - if (!hasRealMappingInParent) { - actions.push({ - name: i18n.translate('xpack.streams.actions.editFieldLabel', { - defaultMessage: 'Edit field', - }), - onClick: () => openFlyout({ isEditingByDefault: true }), - }); - } else { - // If parent has real mapping, only allow editing description - actions.push(editFieldAction); - } + // Edit field is always available - if parent has real mapping, + // the flyout restricts editing to description only + actions.push(editFieldAction); if (field.description) { actions.push(clearDescriptionAction); }If the behavior should differ between the two branches, that distinction should be documented or handled via props passed to the flyout.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx` around lines 147 - 164, The inline "Edit field" action defined inside the 'unmapped' case duplicates the existing editFieldAction constant; replace the inline action pushed in the if (!hasRealMappingInParent) branch with the existing editFieldAction so both branches push editFieldAction (and remove the duplicate inline object), and if you need different behavior pass a flag into openFlyout or adjust editFieldAction to call openFlyout({ isEditingByDefault: true, ... }) based on hasRealMappingInParent; locate edits around editFieldAction, openFlyout, hasRealMappingInParent, viewFieldAction and clearDescriptionAction in the 'unmapped' case.x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts (2)
22-37: Keep these fixtures type-checked instead of double-casting throughunknown.
as unknown as StreamEnrichmentContextTypedisables the compiler right where this test should catch shape drift inStreamEnrichmentContextTypeandsimulatorRef.getSnapshot(). A typed helper orsatisfieson the minimal surface used bygetUpsertFields()keeps the fixture honest without widening everything tounknown.Suggested tightening
- const context = { + const context = { definition: wiredDefinition, simulatorRef: { getSnapshot: () => ({ context: { detectedSchemaFields: [ { name: 'foo', parent: 'wired-stream', status: 'unmapped' }, { name: 'keep', parent: 'wired-stream', status: 'mapped', type: 'date' }, ], docOnlyOverrides: { foo: { description: 'Foo description' }, }, }, }), }, - } as unknown as StreamEnrichmentContextType; + } satisfies Pick<StreamEnrichmentContextType, 'definition' | 'simulatorRef'>; - const result = getUpsertFields(context); + const result = getUpsertFields(context as StreamEnrichmentContextType);As per coding guidelines, "Use TypeScript for all new code; avoid
anyandunknown."Also applies to: 54-66
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts` around lines 22 - 37, The fixture is being double-cast through unknown which disables type-checking; instead make the test fixture conform to StreamEnrichmentContextType (or the minimal shape used by getUpsertFields) so the compiler validates it—replace the "as unknown as StreamEnrichmentContextType" with either a properly typed helper factory or use TypeScript's "satisfies" on the object, ensuring simulatorRef.getSnapshot() returns the correctly typed snapshot shape and the context variable is typed as StreamEnrichmentContextType so getUpsertFields() will be type-checked by the compiler.
73-73: Drop the JSON serialization assertion here.
expect(result).toEqual({ foo: { description: 'Foo description' } })already fails iffoo.typeis present, so theJSON.stringify(...)check is redundant and less precise. If you want the explicit guard, assert the property structurally.More direct assertion
- expect(JSON.stringify(result)).not.toContain('"type":"unmapped"'); + expect(result.foo).not.toHaveProperty('type');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts` at line 73, Remove the redundant JSON serialization assertion in the test: delete the line using expect(JSON.stringify(result)).not.toContain('"type":"unmapped"') and rely on the existing structural assertion expect(result).toEqual({ foo: { description: 'Foo description' } }); if you want an explicit guard instead, add a structural check such as expect(result.foo).not.toHaveProperty('type') referencing the result variable in the utils.test.ts test case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx`:
- Around line 246-247: The read-only description branch currently renders
`${field.description ?? EMPTY_CONTENT}` as a plain string, which collapses
newlines; update the read-only rendering in the FieldSummary component to wrap
the description (or EMPTY_CONTENT fallback) in a container element that applies
CSS white-space: pre-wrap so multiline text preserves line breaks—target the JSX
branch that references field.description and EMPTY_CONTENT and replace the plain
string with that wrapped element.
---
Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 147-164: The inline "Edit field" action defined inside the
'unmapped' case duplicates the existing editFieldAction constant; replace the
inline action pushed in the if (!hasRealMappingInParent) branch with the
existing editFieldAction so both branches push editFieldAction (and remove the
duplicate inline object), and if you need different behavior pass a flag into
openFlyout or adjust editFieldAction to call openFlyout({ isEditingByDefault:
true, ... }) based on hasRealMappingInParent; locate edits around
editFieldAction, openFlyout, hasRealMappingInParent, viewFieldAction and
clearDescriptionAction in the 'unmapped' case.
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts`:
- Around line 22-37: The fixture is being double-cast through unknown which
disables type-checking; instead make the test fixture conform to
StreamEnrichmentContextType (or the minimal shape used by getUpsertFields) so
the compiler validates it—replace the "as unknown as
StreamEnrichmentContextType" with either a properly typed helper factory or use
TypeScript's "satisfies" on the object, ensuring simulatorRef.getSnapshot()
returns the correctly typed snapshot shape and the context variable is typed as
StreamEnrichmentContextType so getUpsertFields() will be type-checked by the
compiler.
- Line 73: Remove the redundant JSON serialization assertion in the test: delete
the line using expect(JSON.stringify(result)).not.toContain('"type":"unmapped"')
and rely on the existing structural assertion expect(result).toEqual({ foo: {
description: 'Foo description' } }); if you want an explicit guard instead, add
a structural check such as expect(result.foo).not.toHaveProperty('type')
referencing the result variable in the utils.test.ts test case.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: a298d04b-612b-4106-b321-d4f4aab31f29
📒 Files selected for processing (4)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
There was a problem hiding this comment.
🧹 Nitpick comments (2)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx (1)
148-164: Redundant conditional: both branches add the same action.The if-else at lines 151-161 performs identical operations in both branches — both add an "Edit field" action with the same i18n key and the same
openFlyout({ isEditingByDefault: true })call. The inline action in theifbranch is functionally identical to the predefinededitFieldAction.This can be simplified to unconditionally push
editFieldAction.♻️ Suggested simplification
case 'unmapped': actions = [viewFieldAction]; - // Only show "Edit field" if the parent doesn't have a real ES mapping (type !== 'unmapped') - // If the parent has a real mapping, the child can't map it differently - if (!hasRealMappingInParent) { - actions.push({ - name: i18n.translate('xpack.streams.actions.editFieldLabel', { - defaultMessage: 'Edit field', - }), - onClick: () => openFlyout({ isEditingByDefault: true }), - }); - } else { - // If parent has real mapping, only allow editing description - actions.push(editFieldAction); - } + // Edit action allows mapping (if no parent ES mapping) or editing description (if parent has mapping) + actions.push(editFieldAction); if (field.description) { actions.push(clearDescriptionAction); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx` around lines 148 - 164, The if-else that checks hasRealMappingInParent is redundant because both branches push an identical "Edit field" action; replace the entire conditional with a single unconditional actions.push(editFieldAction) to simplify the logic—locate where actions is initialized (actions = [viewFieldAction]) and remove the block that creates an inline action calling openFlyout({ isEditingByDefault: true }), relying instead on the existing editFieldAction (and still conditionally push clearDescriptionAction if field.description is present).x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts (1)
821-831: Add explicit return types to these new page-object APIs.These helpers are part of the public
StreamsAppsurface, so it would be better to declarePromise<void>explicitly instead of relying on inference.♻️ Proposed change
- async confirmChangesInReviewModalIfPresent() { + async confirmChangesInReviewModalIfPresent(): Promise<void> { - async fillFieldDescription(description: string) { + async fillFieldDescription(description: string): Promise<void> { - async clickEditFieldButton() { + async clickEditFieldButton(): Promise<void> {As per coding guidelines, "Prefer explicit return types for public APIs and exported functions in TypeScript."
Also applies to: 933-941
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts` around lines 821 - 831, The new page-object APIs (e.g., the async method confirmChangesInReviewModalIfPresent in streams_app.ts and the other similar helpers mentioned around lines 933-941) should declare explicit return types; update each public async method signature to include Promise<void> (for example: async confirmChangesInReviewModalIfPresent(): Promise<void>) so the StreamsApp public surface uses explicit return types instead of relying on inference.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsx`:
- Around line 148-164: The if-else that checks hasRealMappingInParent is
redundant because both branches push an identical "Edit field" action; replace
the entire conditional with a single unconditional actions.push(editFieldAction)
to simplify the logic—locate where actions is initialized (actions =
[viewFieldAction]) and remove the block that creates an inline action calling
openFlyout({ isEditingByDefault: true }), relying instead on the existing
editFieldAction (and still conditionally push clearDescriptionAction if
field.description is present).
In
`@x-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts`:
- Around line 821-831: The new page-object APIs (e.g., the async method
confirmChangesInReviewModalIfPresent in streams_app.ts and the other similar
helpers mentioned around lines 933-941) should declare explicit return types;
update each public async method signature to include Promise<void> (for example:
async confirmChangesInReviewModalIfPresent(): Promise<void>) so the StreamsApp
public surface uses explicit return types instead of relying on inference.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 99a68ac8-4aec-43d8-b65f-760e61ee4ee9
📒 Files selected for processing (4)
x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/field_actions.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsxx-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.tsx-pack/platform/plugins/shared/streams_app/test/scout/ui/fixtures/page_objects/streams_app.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.test.ts
- x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/flyout/field_summary.tsx
💔 Build Failed
Failed CI Steps
Test Failures
Metrics [docs]Public APIs missing comments
Async chunks
History
|
…e_fix * commit '565f7545c422192218b803874fbdf93e8d8f08ee': (27 commits) [Lens API] ESQL schema for XY separately for Agent and some small token optimizations (elastic#256885) Fix "Accessing resource attributes before async attributes settled" telemetry error (elastic#256880) [Security Solution][Attacks/Alerts][Attacks page][Table section] Preserver "Sort by" state on Attacks page (elastic#256717) (elastic#256795) [APM] Improve redirect with default date range guard (elastic#256887) [Security Solution][Attacks/Alerts][Attacks page][Table section] Add assignees avatars to the group component (elastic#250126) (elastic#256901) [Docs] add xpack.alerting.rules.maxScheduledPerMinute setting description (elastic#257041) [SO] Fix non-deterministic ordering in nested find API integration tests (elastic#256447) [Write-restricted dashboards] Update user profile retrieval for getShouldAddAccessControl (elastic#255065) [One Workflow] Add Scout API test scaffold and execution tests (elastic#256300) [Fleet] add use_apm if dynamic_signal_types are enabled (elastic#256429) [Fleet] ignore data streams starting with `.` in Fleet API (elastic#256625) [ES|QL] METRICS_INFO support: columns_after & summary (elastic#256758) [Agent Builder] Agent plugins: initial installation support (elastic#256478) [Streams] Add field descriptions and documentation-only field overrides (elastic#255136) [api-docs] 2026-03-11 Daily api_docs build (elastic#257023) [Security Solution] fix alerts page infinite loading state due to data view error (elastic#256983) [Logging] Add `service.*` global fields (elastic#256878) [Canvas] Apply embeddable transforms to embeddable elements (elastic#252191) [table_list_view_table] stabilize jest test (elastic#254991) [Obs AI] get_index_info: add unit tests (elastic#256802) ...
…es (#255136) ## Summary This PR adds support for field descriptions in Streams, allowing users to document fields without creating Elasticsearch mappings (documentation-only field overrides). Closes https://github.com/elastic/streams-program/issues/765 **This is a clean version of #250785**, which accumulated many iterative changes during review. ## Features ### Field Descriptions - Optional `description` property on field definitions for documenting fields - Description column in schema table with tooltip support - "Edit field" flyout allows setting/editing descriptions for all field types - "Clear description" context menu action to remove descriptions - Descriptions are stored as metadata and not sent to Elasticsearch ### Documentation-Only Field Overrides - Fields can have descriptions without ES mappings (typeless definitions) - Child streams can add description overrides to inherited fields - Doc-only overrides don't trigger ES mapping updates or simulation - Confirm changes modal filters out doc-only changes ### UI Improvements - Renamed "Map field" to "Edit field" since users can now add descriptions without mapping - "Edit field" action available for all field statuses (mapped, unmapped, inherited) - Status filter shows "Dynamic" (not "Unmapped") for Classic streams - Field simulation excludes description-only changes from API calls ### Validation - Server-side validation prevents invalid type overrides on inherited fields - UI hides "Unmap field" for fields that would conflict with parent mappings - Child streams can map fields when parent has doc-only field (no type) ### Classic Streams Support - Classic streams can have descriptions on their fields, but it's _not_ possible to have description-only fields. It's necessary to map the field explicitly if you want to provide a description. ## Test Plan ### Unit Tests - `yarn test:jest x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.ts` - `yarn test:jest x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.ts` - `yarn test:jest x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/validate_ingest_upsert.test.ts` - `yarn test:jest x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/` ### Integration Tests - `yarn test:ftr --config x-pack/platform/test/api_integration_deployment_agnostic/configs/stateful/platform.serverless.config.ts` (for streams inheritance tests) ### Type Checking - `yarn test:type_check --project x-pack/platform/plugins/shared/streams/tsconfig.json` - `yarn test:type_check --project x-pack/platform/plugins/shared/streams_app/tsconfig.json` Made with [Cursor](https://cursor.com) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Field descriptions supported across the schema editor and save flows * Documentation-only overrides (description without a mapping) and an "Unmapped" field type for wired streams * **Improvements** * Description-aware inheritance so child streams can override descriptions without forcing mappings * UI updates: description column, editor, and flyout workflows; review flow skips modal for description-only changes * **Bug Fixes** * Validation and mapping generation now ignore doc-only fields to prevent incorrect mappings <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Summary
This PR adds support for field descriptions in Streams, allowing users to document fields without creating Elasticsearch mappings (documentation-only field overrides).
Closes https://github.com/elastic/streams-program/issues/765
This is a clean version of #250785, which accumulated many iterative changes during review.
Features
Field Descriptions
descriptionproperty on field definitions for documenting fieldsDocumentation-Only Field Overrides
UI Improvements
Validation
Classic Streams Support
Test Plan
Unit Tests
yarn test:jest x-pack/platform/packages/shared/kbn-streams-schema/src/fields/index.test.tsyarn test:jest x-pack/platform/plugins/shared/streams/server/routes/internal/streams/schema/route.test.tsyarn test:jest x-pack/platform/plugins/shared/streams/server/routes/streams/ingest/validate_ingest_upsert.test.tsyarn test:jest x-pack/platform/plugins/shared/streams_app/public/components/data_management/schema_editor/Integration Tests
yarn test:ftr --config x-pack/platform/test/api_integration_deployment_agnostic/configs/stateful/platform.serverless.config.ts(for streams inheritance tests)Type Checking
yarn test:type_check --project x-pack/platform/plugins/shared/streams/tsconfig.jsonyarn test:type_check --project x-pack/platform/plugins/shared/streams_app/tsconfig.jsonMade with Cursor
Summary by CodeRabbit