mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Streams 🌊] Schema editor bug fixes (#218225)](https://github.com/elastic/kibana/pull/218225) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Marco Antonio Ghiani","email":"marcoantonio.ghiani01@gmail.com"},"sourceCommit":{"committedDate":"2025-04-15T13:36:09Z","message":"[Streams 🌊] Schema editor bug fixes (#218225)\n\n## 📓 Summary\n\nThese changes address various minor issues reported on the Schema\nEditor.\n\nCloses #217888 \nCloses #217889\nCloses #217891\nCloses #217892\nCloses #217893 \n\n### [Streams 🌊] \"System managed\" appears in the list of field types in\nthe schema editor\n\n<img width=\"659\" alt=\"Screenshot 2025-04-15 at 10 50 37\"\nsrc=\"https://github.com/user-attachments/assets/5f9e832a-e7ea-4e19-9507-2bd3669c7043\"\n/>\n\n### [Streams 🌊] Clicking the link in the schema editor to edit a field\nmapping in the parent stream loads a new page\n\n\nhttps://github.com/user-attachments/assets/de1a1b09-5eca-4143-a822-2de6814333b6\n\n### [Streams 🌊] Saving changes in the schema editor for an inherited\nfield results in error\n\n\nhttps://github.com/user-attachments/assets/603c8a89-6df3-474a-91bc-ee7bbee0f250\n\n### [Streams 🌊] Disable submit button when there is invalid input for\nmapping in the schema editor\n\n\nhttps://github.com/user-attachments/assets/22dfb91a-fa37-4b68-a8c5-c5f3a89a98e5\n\n### Advanced fields mapping simulation and client-side validation\n\n\nhttps://github.com/user-attachments/assets/faf99f86-5074-4587-9cc6-65f39f3595e9\n\n### [Streams 🌊] Increase width in the type filter popup in schema editor\n\n\nhttps://github.com/user-attachments/assets/b6eab484-308b-42dd-887b-560fb91986da","sha":"d36b269e60308a6b0a5db19b2eac047d2fd51f9d","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:obs-ux-logs","backport:version","Feature:Streams","v9.1.0","v8.19.0"],"title":"[Streams 🌊] Schema editor bug fixes","number":218225,"url":"https://github.com/elastic/kibana/pull/218225","mergeCommit":{"message":"[Streams 🌊] Schema editor bug fixes (#218225)\n\n## 📓 Summary\n\nThese changes address various minor issues reported on the Schema\nEditor.\n\nCloses #217888 \nCloses #217889\nCloses #217891\nCloses #217892\nCloses #217893 \n\n### [Streams 🌊] \"System managed\" appears in the list of field types in\nthe schema editor\n\n<img width=\"659\" alt=\"Screenshot 2025-04-15 at 10 50 37\"\nsrc=\"https://github.com/user-attachments/assets/5f9e832a-e7ea-4e19-9507-2bd3669c7043\"\n/>\n\n### [Streams 🌊] Clicking the link in the schema editor to edit a field\nmapping in the parent stream loads a new page\n\n\nhttps://github.com/user-attachments/assets/de1a1b09-5eca-4143-a822-2de6814333b6\n\n### [Streams 🌊] Saving changes in the schema editor for an inherited\nfield results in error\n\n\nhttps://github.com/user-attachments/assets/603c8a89-6df3-474a-91bc-ee7bbee0f250\n\n### [Streams 🌊] Disable submit button when there is invalid input for\nmapping in the schema editor\n\n\nhttps://github.com/user-attachments/assets/22dfb91a-fa37-4b68-a8c5-c5f3a89a98e5\n\n### Advanced fields mapping simulation and client-side validation\n\n\nhttps://github.com/user-attachments/assets/faf99f86-5074-4587-9cc6-65f39f3595e9\n\n### [Streams 🌊] Increase width in the type filter popup in schema editor\n\n\nhttps://github.com/user-attachments/assets/b6eab484-308b-42dd-887b-560fb91986da","sha":"d36b269e60308a6b0a5db19b2eac047d2fd51f9d"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/218225","number":218225,"mergeCommit":{"message":"[Streams 🌊] Schema editor bug fixes (#218225)\n\n## 📓 Summary\n\nThese changes address various minor issues reported on the Schema\nEditor.\n\nCloses #217888 \nCloses #217889\nCloses #217891\nCloses #217892\nCloses #217893 \n\n### [Streams 🌊] \"System managed\" appears in the list of field types in\nthe schema editor\n\n<img width=\"659\" alt=\"Screenshot 2025-04-15 at 10 50 37\"\nsrc=\"https://github.com/user-attachments/assets/5f9e832a-e7ea-4e19-9507-2bd3669c7043\"\n/>\n\n### [Streams 🌊] Clicking the link in the schema editor to edit a field\nmapping in the parent stream loads a new page\n\n\nhttps://github.com/user-attachments/assets/de1a1b09-5eca-4143-a822-2de6814333b6\n\n### [Streams 🌊] Saving changes in the schema editor for an inherited\nfield results in error\n\n\nhttps://github.com/user-attachments/assets/603c8a89-6df3-474a-91bc-ee7bbee0f250\n\n### [Streams 🌊] Disable submit button when there is invalid input for\nmapping in the schema editor\n\n\nhttps://github.com/user-attachments/assets/22dfb91a-fa37-4b68-a8c5-c5f3a89a98e5\n\n### Advanced fields mapping simulation and client-side validation\n\n\nhttps://github.com/user-attachments/assets/faf99f86-5074-4587-9cc6-65f39f3595e9\n\n### [Streams 🌊] Increase width in the type filter popup in schema editor\n\n\nhttps://github.com/user-attachments/assets/b6eab484-308b-42dd-887b-560fb91986da","sha":"d36b269e60308a6b0a5db19b2eac047d2fd51f9d"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani01@gmail.com>
This commit is contained in:
parent
cdd4855188
commit
0e10b2ea53
10 changed files with 226 additions and 108 deletions
|
@ -171,13 +171,7 @@ export const schemaFieldsSimulationRoute = createServerRoute({
|
|||
}
|
||||
|
||||
const propertiesForSimulation = Object.fromEntries(
|
||||
userFieldDefinitions.map((field) => [
|
||||
field.name,
|
||||
{
|
||||
type: field.type,
|
||||
...(field.format ? { format: field.format } : {}),
|
||||
},
|
||||
])
|
||||
userFieldDefinitions.map(({ name, ...field }) => [name, field])
|
||||
);
|
||||
|
||||
const fieldDefinitionKeys = Object.keys(propertiesForSimulation);
|
||||
|
|
|
@ -14,41 +14,49 @@ export const FIELD_TYPE_MAP = {
|
|||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableBooleanType', {
|
||||
defaultMessage: 'Boolean',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
date: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableDateType', {
|
||||
defaultMessage: 'Date',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
keyword: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableKeywordType', {
|
||||
defaultMessage: 'Keyword',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
match_only_text: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableTextType', {
|
||||
defaultMessage: 'Text (match_only_text)',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
long: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableNumberType', {
|
||||
defaultMessage: 'Number (long)',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
double: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableNumberType', {
|
||||
defaultMessage: 'Number (double)',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
ip: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableIpType', {
|
||||
defaultMessage: 'IP',
|
||||
}),
|
||||
readonly: false,
|
||||
},
|
||||
system: {
|
||||
label: i18n.translate('xpack.streams.streamDetailSchemaEditorFieldsTableSystemType', {
|
||||
defaultMessage: 'System managed',
|
||||
}),
|
||||
readonly: true,
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { useBoolean } from '@kbn/react-hooks';
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
export const FilterGroup = ({
|
||||
filterGroupButtonLabel,
|
||||
|
@ -57,9 +58,9 @@ export const FilterGroup = ({
|
|||
<EuiSelectable aria-label={filterGroupButtonLabel} options={items} onChange={onChange}>
|
||||
{(list) => (
|
||||
<div
|
||||
css={{
|
||||
minWidth: '200px',
|
||||
}}
|
||||
css={css`
|
||||
min-width: 220px;
|
||||
`}
|
||||
>
|
||||
{list}
|
||||
</div>
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { uniq } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { EuiSelectableOption, EuiSelectableProps } from '@elastic/eui';
|
||||
import { FilterGroup } from './filter_group';
|
||||
import { FIELD_STATUS_MAP } from '../constants';
|
||||
import { TControlsChangeHandler } from '../hooks/use_controls';
|
||||
import { SchemaFieldStatus } from '../types';
|
||||
import { useSchemaEditorContext } from '../schema_editor_context';
|
||||
|
||||
const BUTTON_LABEL = i18n.translate(
|
||||
'xpack.streams.streamDetailSchemaEditor.fieldStatusFilterGroupButtonLabel',
|
||||
|
@ -20,14 +21,28 @@ const BUTTON_LABEL = i18n.translate(
|
|||
);
|
||||
|
||||
export const FieldStatusFilterGroup = ({ onChange }: { onChange: TControlsChangeHandler }) => {
|
||||
const [items, setItems] = useState<EuiSelectableOption[]>(() =>
|
||||
Object.entries(FIELD_STATUS_MAP).map(([key, value]) => {
|
||||
return {
|
||||
label: value.label,
|
||||
key,
|
||||
};
|
||||
})
|
||||
);
|
||||
const { fields } = useSchemaEditorContext();
|
||||
|
||||
const fieldStatus = useMemo(() => uniq(fields.map((field) => field.status)), [fields]);
|
||||
|
||||
const [items, setItems] = useState<EuiSelectableOption[]>(() => getStatusOptions(fieldStatus));
|
||||
|
||||
// This side effect is due to the fact that the available field status can be updated once the unmapped fields are fetched.
|
||||
useEffect(() => {
|
||||
setItems((prevItems) => {
|
||||
const prevSelection = new Map(prevItems.map((item) => [item.key, item.checked]));
|
||||
|
||||
const nextItems = getStatusOptions(fieldStatus);
|
||||
|
||||
nextItems.forEach((item) => {
|
||||
if (prevSelection.has(item.key)) {
|
||||
item.checked = prevSelection.get(item.key);
|
||||
}
|
||||
});
|
||||
|
||||
return nextItems;
|
||||
});
|
||||
}, [fieldStatus]);
|
||||
|
||||
const onChangeItems = useCallback<Required<EuiSelectableProps>['onChange']>(
|
||||
(nextItems) => {
|
||||
|
@ -45,3 +60,10 @@ export const FieldStatusFilterGroup = ({ onChange }: { onChange: TControlsChange
|
|||
<FilterGroup items={items} filterGroupButtonLabel={BUTTON_LABEL} onChange={onChangeItems} />
|
||||
);
|
||||
};
|
||||
|
||||
const getStatusOptions = (fieldStatus: SchemaFieldStatus[]): EuiSelectableOption[] => {
|
||||
return fieldStatus.map((key) => ({
|
||||
label: FIELD_STATUS_MAP[key].label,
|
||||
key,
|
||||
}));
|
||||
};
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { uniq } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { EuiSelectableOption, EuiSelectableProps } from '@elastic/eui';
|
||||
import { FilterGroup } from './filter_group';
|
||||
import { FIELD_TYPE_MAP } from '../constants';
|
||||
import { FIELD_TYPE_MAP, FieldTypeOption } from '../constants';
|
||||
import { TControlsChangeHandler } from '../hooks/use_controls';
|
||||
import { SchemaFieldType } from '../types';
|
||||
import { useSchemaEditorContext } from '../schema_editor_context';
|
||||
|
||||
const BUTTON_LABEL = i18n.translate(
|
||||
'xpack.streams.streamDetailSchemaEditor.fieldTypeFilterGroupButtonLabel',
|
||||
|
@ -20,22 +20,44 @@ const BUTTON_LABEL = i18n.translate(
|
|||
);
|
||||
|
||||
export const FieldTypeFilterGroup = ({ onChange }: { onChange: TControlsChangeHandler }) => {
|
||||
const [items, setItems] = useState<EuiSelectableOption[]>(() =>
|
||||
Object.entries(FIELD_TYPE_MAP).map(([key, value]) => {
|
||||
return {
|
||||
label: value.label,
|
||||
key,
|
||||
};
|
||||
})
|
||||
const { fields } = useSchemaEditorContext();
|
||||
|
||||
const fieldTypes = useMemo(
|
||||
() =>
|
||||
uniq(
|
||||
fields
|
||||
.map((field) => field.type)
|
||||
.filter((type): type is FieldTypeOption => type !== undefined)
|
||||
),
|
||||
[fields]
|
||||
);
|
||||
|
||||
const [items, setItems] = useState<EuiSelectableOption[]>(() => getTypeOptions(fieldTypes));
|
||||
|
||||
// This side effect is due to the fact that the available field status can be updated once the unmapped fields are fetched.
|
||||
useEffect(() => {
|
||||
setItems((prevItems) => {
|
||||
const prevSelection = new Map(prevItems.map((item) => [item.key, item.checked]));
|
||||
|
||||
const nextItems = getTypeOptions(fieldTypes);
|
||||
|
||||
nextItems.forEach((item) => {
|
||||
if (prevSelection.has(item.key)) {
|
||||
item.checked = prevSelection.get(item.key);
|
||||
}
|
||||
});
|
||||
|
||||
return nextItems;
|
||||
});
|
||||
}, [fieldTypes]);
|
||||
|
||||
const onChangeItems = useCallback<Required<EuiSelectableProps>['onChange']>(
|
||||
(nextItems) => {
|
||||
setItems(nextItems);
|
||||
onChange({
|
||||
type: nextItems
|
||||
.filter((nextItem) => nextItem.checked === 'on')
|
||||
.map((item) => item.key as SchemaFieldType),
|
||||
.map((item) => item.key as FieldTypeOption),
|
||||
});
|
||||
},
|
||||
[onChange]
|
||||
|
@ -45,3 +67,10 @@ export const FieldTypeFilterGroup = ({ onChange }: { onChange: TControlsChangeHa
|
|||
<FilterGroup items={items} filterGroupButtonLabel={BUTTON_LABEL} onChange={onChangeItems} />
|
||||
);
|
||||
};
|
||||
|
||||
const getTypeOptions = (fieldStatus: FieldTypeOption[]): EuiSelectableOption[] => {
|
||||
return fieldStatus.map((key) => ({
|
||||
label: FIELD_TYPE_MAP[key].label,
|
||||
key,
|
||||
}));
|
||||
};
|
||||
|
|
|
@ -7,17 +7,21 @@
|
|||
import {
|
||||
EuiAccordion,
|
||||
EuiCodeBlock,
|
||||
EuiFormRow,
|
||||
EuiLink,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
import { CodeEditor } from '@kbn/code-editor';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { getAdvancedParameters } from '@kbn/streams-schema';
|
||||
import {
|
||||
FieldDefinitionConfigAdvancedParameters,
|
||||
isSchema,
|
||||
recursiveRecord,
|
||||
} from '@kbn/streams-schema';
|
||||
import { useBoolean } from '@kbn/react-hooks';
|
||||
import { SchemaField } from '../types';
|
||||
import { useKibana } from '../../../../hooks/use_kibana';
|
||||
|
||||
|
@ -28,16 +32,23 @@ const label = i18n.translate('xpack.streams.advancedFieldMappingOptions.label',
|
|||
export const AdvancedFieldMappingOptions = ({
|
||||
field,
|
||||
onChange,
|
||||
onValidate,
|
||||
isEditing,
|
||||
}: {
|
||||
field: SchemaField;
|
||||
onChange: (field: Partial<SchemaField>) => void;
|
||||
onValidate?: (isValid: boolean) => void;
|
||||
isEditing: boolean;
|
||||
}) => {
|
||||
const { core } = useKibana();
|
||||
|
||||
const accordionId = useGeneratedHtmlId({ prefix: 'accordionID' });
|
||||
|
||||
const [hasParsingError, { on: markAsParsingError, off: resetParsingErrorFlag }] =
|
||||
useBoolean(false);
|
||||
|
||||
const isInvalid = hasParsingError || !getValidFlag(field.additionalParameters);
|
||||
|
||||
const jsonOptions = useMemo(
|
||||
() => (field.additionalParameters ? JSON.stringify(field.additionalParameters, null, 2) : ''),
|
||||
[field.additionalParameters]
|
||||
|
@ -46,50 +57,73 @@ export const AdvancedFieldMappingOptions = ({
|
|||
return (
|
||||
<EuiAccordion id={accordionId} buttonContent={label}>
|
||||
<EuiPanel color="subdued">
|
||||
<EuiText size="xs">
|
||||
<FormattedMessage
|
||||
id="xpack.streams.advancedFieldMappingOptions.docs.label"
|
||||
defaultMessage="Parameters can be defined with JSON. {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
data-test-subj="streamsAppAdvancedFieldMappingOptionsViewDocumentationLink"
|
||||
href={core.docLinks.links.elasticsearch.docsBase.concat('mapping-params.html')}
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.streams.indexPattern.randomSampling.learnMore"
|
||||
defaultMessage="View documentation."
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{isEditing ? (
|
||||
<CodeEditor
|
||||
height={120}
|
||||
languageId="json"
|
||||
value={jsonOptions}
|
||||
onChange={(value) => {
|
||||
try {
|
||||
onChange({
|
||||
additionalParameters:
|
||||
value === '' ? undefined : getAdvancedParameters(field.name, JSON.parse(value)),
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
// do nothing
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<EuiCodeBlock language="json" isCopyable>
|
||||
{jsonOptions ?? ''}
|
||||
</EuiCodeBlock>
|
||||
)}
|
||||
<EuiFormRow
|
||||
isInvalid={isInvalid}
|
||||
error={
|
||||
isInvalid
|
||||
? i18n.translate('xpack.streams.advancedFieldMappingOptions.error', {
|
||||
defaultMessage:
|
||||
'Invalid advanced field mapping parameters. It should be defined as a JSON object.',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.streams.advancedFieldMappingOptions.docs.label"
|
||||
defaultMessage="Parameters can be defined with JSON. {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink
|
||||
data-test-subj="streamsAppAdvancedFieldMappingOptionsViewDocumentationLink"
|
||||
href={core.docLinks.links.elasticsearch.docsBase.concat('mapping-params.html')}
|
||||
target="_blank"
|
||||
external
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.streams.indexPattern.randomSampling.learnMore"
|
||||
defaultMessage="View documentation."
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{isEditing ? (
|
||||
<CodeEditor
|
||||
height={120}
|
||||
languageId="json"
|
||||
value={jsonOptions}
|
||||
onChange={(value) => {
|
||||
try {
|
||||
const additionalParameters =
|
||||
value === ''
|
||||
? undefined
|
||||
: (JSON.parse(value) as FieldDefinitionConfigAdvancedParameters);
|
||||
onChange({ additionalParameters });
|
||||
if (onValidate) onValidate(getValidFlag(additionalParameters));
|
||||
resetParsingErrorFlag();
|
||||
} catch (error: unknown) {
|
||||
markAsParsingError();
|
||||
if (onValidate) onValidate(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<EuiCodeBlock language="json" isCopyable>
|
||||
{jsonOptions}
|
||||
</EuiCodeBlock>
|
||||
)}
|
||||
</EuiFormRow>
|
||||
</EuiPanel>
|
||||
</EuiAccordion>
|
||||
);
|
||||
};
|
||||
|
||||
const getValidFlag = (additionalParameters?: FieldDefinitionConfigAdvancedParameters) => {
|
||||
return Boolean(
|
||||
!additionalParameters ||
|
||||
additionalParameters === '' ||
|
||||
isSchema(recursiveRecord, additionalParameters)
|
||||
);
|
||||
};
|
||||
|
|
|
@ -67,6 +67,13 @@ interface FieldTypeSelectorProps {
|
|||
value?: FieldTypeOption;
|
||||
}
|
||||
|
||||
const typeSelectorOptions = Object.entries(FIELD_TYPE_MAP)
|
||||
.filter(([_, { readonly }]) => !readonly)
|
||||
.map(([optionKey, { label }]) => ({
|
||||
text: label,
|
||||
value: optionKey,
|
||||
}));
|
||||
|
||||
const FieldTypeSelector = ({ value, onChange, isLoading = false }: FieldTypeSelectorProps) => {
|
||||
return (
|
||||
<EuiSelect
|
||||
|
@ -77,10 +84,7 @@ const FieldTypeSelector = ({ value, onChange, isLoading = false }: FieldTypeSele
|
|||
onChange(event.target.value as FieldTypeOption);
|
||||
}}
|
||||
value={value}
|
||||
options={Object.entries(FIELD_TYPE_MAP).map(([optionKey, optionConfig]) => ({
|
||||
text: optionConfig.label,
|
||||
value: optionKey,
|
||||
}))}
|
||||
options={typeSelectorOptions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -108,6 +108,7 @@ export const FieldSummary = (props: FieldSummaryProps) => {
|
|||
tab: 'schemaEditor',
|
||||
},
|
||||
})}
|
||||
target="_blank"
|
||||
>
|
||||
{i18n.translate('xpack.streams.fieldSummary.editInParentButtonLabel', {
|
||||
defaultMessage: 'Edit in parent stream',
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
EuiTitle,
|
||||
EuiButton,
|
||||
} from '@elastic/eui';
|
||||
import React, { useReducer } from 'react';
|
||||
import React, { useReducer, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IngestStreamDefinition } from '@kbn/streams-schema';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
|
@ -43,6 +43,8 @@ export const SchemaEditorFlyout = ({
|
|||
withFieldSimulation = false,
|
||||
}: SchemaEditorFlyoutProps) => {
|
||||
const [isEditing, toggleEditMode] = useToggle(isEditingByDefault);
|
||||
const [isValidAdvancedFieldMappings, setValidAdvancedFieldMappings] = useState(true);
|
||||
const [isValidSimulation, setValidSimulation] = useState(true);
|
||||
|
||||
const [nextField, setNextField] = useReducer(
|
||||
(prev: SchemaField, updated: Partial<SchemaField>) =>
|
||||
|
@ -53,6 +55,8 @@ export const SchemaEditorFlyout = ({
|
|||
field
|
||||
);
|
||||
|
||||
const hasValidFieldType = nextField.type !== undefined;
|
||||
|
||||
const [{ loading: isSaving }, saveChanges] = useAsyncFn(async () => {
|
||||
await onSave(nextField);
|
||||
if (onClose) onClose();
|
||||
|
@ -78,39 +82,52 @@ export const SchemaEditorFlyout = ({
|
|||
<AdvancedFieldMappingOptions
|
||||
field={nextField}
|
||||
onChange={setNextField}
|
||||
onValidate={setValidAdvancedFieldMappings}
|
||||
isEditing={isEditing}
|
||||
/>
|
||||
{withFieldSimulation && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<SamplePreviewTable stream={stream} nextField={nextField} />
|
||||
<SamplePreviewTable
|
||||
stream={stream}
|
||||
nextField={nextField}
|
||||
onValidate={setValidSimulation}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutBody>
|
||||
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="streamsAppSchemaEditorFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={onClose}
|
||||
flush="left"
|
||||
>
|
||||
{i18n.translate('xpack.streams.schemaEditorFlyout.closeButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
<EuiButton
|
||||
data-test-subj="streamsAppSchemaEditorFieldSaveButton"
|
||||
isLoading={isSaving}
|
||||
onClick={saveChanges}
|
||||
>
|
||||
{i18n.translate('xpack.streams.fieldForm.saveButtonLabel', {
|
||||
defaultMessage: 'Save changes',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
{isEditing && (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="streamsAppSchemaEditorFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={onClose}
|
||||
flush="left"
|
||||
>
|
||||
{i18n.translate('xpack.streams.schemaEditorFlyout.closeButtonLabel', {
|
||||
defaultMessage: 'Cancel',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
<EuiButton
|
||||
data-test-subj="streamsAppSchemaEditorFieldSaveButton"
|
||||
disabled={
|
||||
isSaving ||
|
||||
!hasValidFieldType ||
|
||||
!isValidAdvancedFieldMappings ||
|
||||
!isValidSimulation
|
||||
}
|
||||
isLoading={isSaving}
|
||||
onClick={saveChanges}
|
||||
>
|
||||
{i18n.translate('xpack.streams.fieldForm.saveButtonLabel', {
|
||||
defaultMessage: 'Save changes',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
|
@ -21,6 +21,7 @@ import { convertToFieldDefinitionConfig } from '../utils';
|
|||
interface SamplePreviewTableProps {
|
||||
stream: IngestStreamDefinition;
|
||||
nextField: SchemaField;
|
||||
onValidate?: (isValid: boolean) => void;
|
||||
}
|
||||
|
||||
export const SamplePreviewTable = (props: SamplePreviewTableProps) => {
|
||||
|
@ -37,6 +38,7 @@ const SAMPLE_DOCUMENTS_TO_SHOW = 20;
|
|||
const SamplePreviewTableContent = ({
|
||||
stream,
|
||||
nextField,
|
||||
onValidate,
|
||||
}: SamplePreviewTableProps & { nextField: MappedSchemaField }) => {
|
||||
const { streamsRepositoryClient } = useKibana().dependencies.start.streams;
|
||||
|
||||
|
@ -63,6 +65,12 @@ const SamplePreviewTableContent = ({
|
|||
{ disableToastOnError: true }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (onValidate) {
|
||||
onValidate(value?.status === 'failure' || error ? false : true);
|
||||
}
|
||||
}, [value, error, onValidate]);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [nextField.name];
|
||||
}, [nextField.name]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue