diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.styles.scss new file mode 100644 index 000000000000..546b6d95eab6 --- /dev/null +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.styles.scss @@ -0,0 +1,6 @@ +.json-flattening-form { + margin-top: 16px; + &__item { + margin-bottom: 12px; + } +} diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.tsx b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.tsx new file mode 100644 index 000000000000..cfdc16fe4362 --- /dev/null +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/JsonFlattening.tsx @@ -0,0 +1,110 @@ +import './JsonFlattening.styles.scss'; + +import { InfoCircleOutlined } from '@ant-design/icons'; +import { Form, Input, Space, Switch, Tooltip } from 'antd'; +import { useEffect, useState } from 'react'; +import { ProcessorData } from 'types/api/pipeline/def'; + +import { PREDEFINED_MAPPING } from '../config'; +import KeyValueList from './KeyValueList'; + +interface JsonFlatteningProps { + selectedProcessorData?: ProcessorData; + isAdd: boolean; +} + +function JsonFlattening({ + selectedProcessorData, + isAdd, +}: JsonFlatteningProps): JSX.Element | null { + const form = Form.useFormInstance(); + const mappingValue = selectedProcessorData?.mapping || {}; + const enableFlattening = Form.useWatch('enable_flattening', form); + const enablePaths = Form.useWatch('enable_paths', form); + + const [enableMapping, setEnableMapping] = useState( + !!mappingValue && Object.keys(mappingValue).length > 0, + ); + + const selectedMapping = selectedProcessorData?.mapping; + useEffect(() => { + if (!enableMapping) { + form.setFieldsValue({ mapping: undefined }); + } else if (form.getFieldValue('mapping') === undefined) { + form.setFieldsValue({ + mapping: selectedMapping || PREDEFINED_MAPPING, + }); + } + }, [enableMapping, form, selectedMapping]); + + const handleEnableMappingChange = (checked: boolean): void => { + setEnableMapping(checked); + }; + + const handleEnablePathsChange = (checked: boolean): void => { + form.setFieldValue('enable_paths', checked); + }; + + if (!enableFlattening) { + return null; + } + + return ( +
+ + + + Enable Paths + + + + {enablePaths && ( + + + + )} + + + + + Enable Mapping + + + + + + + {enableMapping && ( + + + + )} +
+ ); +} + +JsonFlattening.defaultProps = { + selectedProcessorData: undefined, +}; + +export default JsonFlattening; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/KeyValueList.tsx b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/KeyValueList.tsx new file mode 100644 index 000000000000..b8a4c952ed1c --- /dev/null +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/FormFields/KeyValueList.tsx @@ -0,0 +1,47 @@ +import { Form, Select } from 'antd'; + +import { PREDEFINED_MAPPING } from '../config'; + +interface KeyValueListProps { + value?: Record; + onChange?: (value: Record) => void; +} + +function KeyValueList({ + value = PREDEFINED_MAPPING, + onChange, +}: KeyValueListProps): JSX.Element { + const handleValueChange = (key: string, newValue: string[]): void => { + const newMapping = { + ...value, + [key]: newValue, + }; + if (onChange) { + onChange(newMapping); + } + }; + + return ( +
+ {Object.keys(value).map((key) => ( + + ; } @@ -68,40 +80,82 @@ function ProcessorFieldInput({ )} - {fieldData.fieldName}} - name={fieldData.name} - initialValue={fieldData.initialValue} - rules={fieldData.rules ? fieldData.rules : formValidationRules} - dependencies={fieldData.dependencies || []} - > - {inputField} - + {fieldData.name === 'enable_flattening' ? ( + + + { + form.setFieldValue('enable_flattening', checked); + }} + /> + {fieldData.fieldName} + + + ) : ( + {fieldData.fieldName}} + name={fieldData.name} + initialValue={fieldData.initialValue} + rules={fieldData.rules ? fieldData.rules : formValidationRules} + dependencies={fieldData.dependencies || []} + > + {inputField} + + )} + {fieldData.name === 'enable_flattening' && inputField}
); } +ProcessorFieldInput.defaultProps = { + selectedProcessorData: undefined, +}; + interface ProcessorFieldInputProps { fieldData: ProcessorFormField; + selectedProcessorData?: ProcessorData; + isAdd: boolean; } -function ProcessorForm({ processorType }: ProcessorFormProps): JSX.Element { +function ProcessorForm({ + processorType, + selectedProcessorData, + isAdd, +}: ProcessorFormProps): JSX.Element { return (
{processorFields[processorType]?.map((fieldData: ProcessorFormField) => ( ))}
); } +ProcessorForm.defaultProps = { + selectedProcessorData: undefined, +}; + interface ProcessorFormProps { processorType: string; + selectedProcessorData?: ProcessorData; + isAdd: boolean; } export default ProcessorForm; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/config.ts b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/config.ts index 2c9a67689849..4205de722e4f 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/config.ts +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/config.ts @@ -136,6 +136,13 @@ export const processorFields: { [key: string]: Array } = { name: 'parse_to', initialValue: 'attributes', }, + { + id: 4, + fieldName: 'Enable Flattening', + placeholder: '', + name: 'enable_flattening', + initialValue: false, + }, ], regex_parser: [ { @@ -458,3 +465,14 @@ export const processorFields: { [key: string]: Array } = { }, ], }; + +export const PREDEFINED_MAPPING = { + environment: ['service.env', 'environment', 'env'], + host: ['host', 'hostname', 'host.name'], + message: ['message', 'msg', 'log'], + service: ['service', 'appname'], + severity: ['status', 'severity', 'level'], + span_id: ['span_id', 'span.id'], + trace_flags: ['flags'], + trace_id: ['trace_id', 'trace.id'], +}; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx index 661fc4043ae9..7d9edbc48bec 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/index.tsx @@ -160,6 +160,7 @@ function AddNewProcessor({ width={800} footer={null} onCancel={onCancelModal} + destroyOnClose >
- + diff --git a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.scss b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.scss index ef6acfe83813..20af7b763a0d 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.scss +++ b/frontend/src/container/PipelinePage/PipelineListsView/AddNewProcessor/styles.scss @@ -24,3 +24,7 @@ flex-grow: 1; margin-left: 2.5rem; } + +.enable-flattening-switch .ant-form-item-control-input { + min-height: unset !important; +} diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx index 17761ad99f9f..d00f3fad830a 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx @@ -219,21 +219,6 @@ function PipelineExpandView({ moveRow: moveProcessorRow, } as React.HTMLAttributes); - const processorData = useMemo( - () => - expandedPipelineData?.config && - expandedPipelineData?.config.map( - (item: ProcessorData): ProcessorData => ({ - id: item.id, - orderId: item.orderId, - type: item.type, - name: item.name, - enabled: item.enabled, - }), - ), - [expandedPipelineData], - ); - const getLocales = (): TableLocale => ({ emptyText: , }); @@ -248,7 +233,7 @@ function PipelineExpandView({ rowKey="id" size="small" components={tableComponents} - dataSource={processorData} + dataSource={expandedPipelineData?.config} pagination={false} onRow={onRowHandler} footer={footer} diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx index 71b353defec6..d3d8739a874e 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx @@ -6,7 +6,7 @@ import { ExpandableConfig } from 'antd/es/table/interface'; import logEvent from 'api/common/logEvent'; import savePipeline from 'api/pipeline/post'; import { useNotifications } from 'hooks/useNotifications'; -import { isUndefined } from 'lodash-es'; +import { isEqual, isUndefined } from 'lodash-es'; import cloneDeep from 'lodash-es/cloneDeep'; import React, { useCallback, @@ -75,7 +75,7 @@ function PipelinesListEmptyState(): JSX.Element { here @@ -407,15 +407,46 @@ function PipelineListsView({ return undefined; }, [isEditingActionMode, addNewPipelineHandler, t]); + const getModifiedJsonFlatteningConfigs = useCallback( + () => + currPipelineData.flatMap((pipeline) => { + const prevPipeline = prevPipelineData.find((p) => p.name === pipeline.name); + + return (pipeline.config || []) + .filter((processor) => { + const prevProcessor = prevPipeline?.config?.find( + (p) => p.name === processor.name, + ); + return ( + processor.type === 'json_parser' && + (!prevProcessor || + prevProcessor.enable_flattening !== processor.enable_flattening || + prevProcessor.enable_paths !== processor.enable_paths || + prevProcessor.path_prefix !== processor.path_prefix || + !isEqual(prevProcessor.mapping, processor.mapping)) + ); + }) + .map((processor) => ({ + enableFlattening: !!processor.enable_flattening, + enablePaths: !!processor.enable_paths, + pathPrefix: processor.path_prefix || '', + mapping: processor.mapping || {}, + })); + }), + [currPipelineData, prevPipelineData], + ); + const onSaveConfigurationHandler = useCallback(async () => { const modifiedPipelineData = currPipelineData.map((item: PipelineData) => { const pipelineData = { ...item }; delete pipelineData?.id; return pipelineData; }); + const response = await savePipeline({ data: { pipelines: modifiedPipelineData }, }); + if (response.statusCode === 200) { refetchPipelineLists(); setActionMode(ActionMode.Viewing); @@ -425,6 +456,15 @@ function PipelineListsView({ setCurrPipelineData(pipelinesInDB); setPrevPipelineData(pipelinesInDB); + // Log modified JSON flattening configurations + const modifiedConfigs = getModifiedJsonFlatteningConfigs(); + if (modifiedConfigs.length > 0) { + logEvent('Logs pipeline: Saved JSON Flattening Configuration', { + count: modifiedConfigs.length, + configurations: modifiedConfigs, + }); + } + logEvent('Logs: Pipelines: Saved Pipelines', { count: pipelinesInDB.length, enabled: pipelinesInDB.filter((p) => p.enabled).length, @@ -446,7 +486,14 @@ function PipelineListsView({ setPrevPipelineData(modifiedPipelineData); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currPipelineData, notifications, refetchPipelineLists, setActionMode, t]); + }, [ + currPipelineData, + notifications, + refetchPipelineLists, + setActionMode, + t, + getModifiedJsonFlatteningConfigs, + ]); const onCancelConfigurationHandler = useCallback((): void => { setActionMode(ActionMode.Viewing); diff --git a/frontend/src/container/PipelinePage/mocks/pipeline.ts b/frontend/src/container/PipelinePage/mocks/pipeline.ts index db309b0e50f4..5dd7b37a8489 100644 --- a/frontend/src/container/PipelinePage/mocks/pipeline.ts +++ b/frontend/src/container/PipelinePage/mocks/pipeline.ts @@ -58,6 +58,15 @@ export const pipelineMockData: Array = [ from: 'attributes.auth', to: 'attributes.username', }, + { + orderId: 3, + enabled: true, + type: 'json_parser', + id: 'jsonparser', + name: 'json parser', + from: 'attributes.auth', + to: 'attributes.username', + }, ], createdBy: 'nityananda@signoz.io', createdAt: '2023-03-07T16:56:53.36071141Z', diff --git a/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx b/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx index 8a226861bf83..e1e961e125aa 100644 --- a/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx +++ b/frontend/src/container/PipelinePage/tests/AddNewProcessor.test.tsx @@ -1,8 +1,16 @@ -import { render } from 'tests/test-utils'; +import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { render as customRender } from 'tests/test-utils'; +import { ProcessorData } from 'types/api/pipeline/def'; import { pipelineMockData } from '../mocks/pipeline'; import AddNewProcessor from '../PipelineListsView/AddNewProcessor'; +// Mock the config module to set JSON parser as default +jest.mock('../PipelineListsView/AddNewProcessor/config', () => ({ + ...jest.requireActual('../PipelineListsView/AddNewProcessor/config'), + DEFAULT_PROCESSOR_TYPE: 'json_parser', +})); + jest.mock('uplot', () => { const paths = { spline: jest.fn(), @@ -17,44 +25,233 @@ jest.mock('uplot', () => { }; }); -beforeAll(() => { - Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), - removeListener: jest.fn(), - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), - }); -}); - const selectedProcessorData = { id: '1', orderId: 1, - type: 'grok_parser', - name: 'grok use common', - output: 'grokusecommon', + type: 'json_parser', + name: 'json parser', + output: 'jsonparser', }; -describe('PipelinePage container test', () => { - it('should render AddNewProcessor section', () => { - const setActionType = jest.fn(); - const isActionType = 'add-processor'; - const { asFragment } = render( - , - ); - expect(asFragment()).toMatchSnapshot(); +// Constants for repeated text +const ENABLE_PATHS_TEXT = 'Enable Paths'; +const ENABLE_MAPPING_TEXT = 'Enable Mapping'; +const PATH_PREFIX_LABEL = 'Path Prefix'; + +// Helper function to render AddNewProcessor with JSON parser type +const renderJsonProcessor = ({ + selectedProcessorData: processorData = selectedProcessorData, + isActionType = 'add-processor', +}: { + selectedProcessorData?: ProcessorData; + isActionType?: 'add-processor' | 'edit-processor'; +}): ReturnType => { + const defaultProps = { + isActionType, + setActionType: jest.fn(), + selectedProcessorData: processorData, + setShowSaveButton: jest.fn(), + expandedPipelineData: pipelineMockData[2], + setExpandedPipelineData: jest.fn(), + }; + + // eslint-disable-next-line react/jsx-props-no-spreading + return customRender(); +}; + +describe('JSON Flattening Processor Tests', () => { + describe('Enable/Disable Flattening', () => { + it('should display the form when enable flattening is turned on', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: true, + }, + }); + + // Verify the JSON flattening form is displayed + expect(screen.queryByText(ENABLE_PATHS_TEXT)).toBeInTheDocument(); + expect(screen.queryByText(ENABLE_MAPPING_TEXT)).toBeInTheDocument(); + }); + it('should not display the form when enable flattening is turned off', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: false, + }, + }); + + // Verify the JSON flattening form is not displayed + expect(screen.queryByText(ENABLE_PATHS_TEXT)).not.toBeInTheDocument(); + expect(screen.queryByText(ENABLE_MAPPING_TEXT)).not.toBeInTheDocument(); + }); + it('should display the form when enable flattening switch is toggled on', async () => { + renderJsonProcessor({}); + + // Wait for the component to render and find the enable flattening switch + await waitFor(() => { + expect(screen.getByRole('switch')).toBeInTheDocument(); + }); + + // Find the enable flattening switch + const enableFlatteningSwitch = screen.getByRole('switch'); + // Turn on the switch + fireEvent.click(enableFlatteningSwitch); + + // Verify the JSON flattening form is displayed + await waitFor(() => { + expect(screen.getByText(ENABLE_PATHS_TEXT)).toBeInTheDocument(); + expect(screen.getByText(ENABLE_MAPPING_TEXT)).toBeInTheDocument(); + }); + }); + it('should hide the form when enable flattening switch is toggled off', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: true, + }, + }); + + // Wait for the component to render and find the switches + await waitFor(() => { + expect(screen.getAllByRole('switch')[0]).toBeInTheDocument(); + }); + + // Find the enable flattening switch + const enableFlatteningSwitch = screen.getAllByRole('switch')[0]; + // Turn off the switch + fireEvent.click(enableFlatteningSwitch); + await waitFor(() => { + expect(screen.queryByText(ENABLE_PATHS_TEXT)).not.toBeInTheDocument(); + expect(screen.queryByText(ENABLE_MAPPING_TEXT)).not.toBeInTheDocument(); + }); + }); + }); + + describe('Enable/Disable Paths', () => { + it('should toggle path prefix visibility when enable paths switch is toggled', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: true, + enable_paths: false, + }, + }); + + // Wait for the component to render and find the switches + await waitFor(() => { + expect(screen.getAllByRole('switch')[1]).toBeInTheDocument(); + }); + + // In add mode, enable_paths is always true initially, so the path prefix should be visible + await waitFor(() => { + expect(screen.getByLabelText(PATH_PREFIX_LABEL)).toBeInTheDocument(); + }); + + // Find the enable paths switch (second switch in the form) and turn it off + const enablePathsSwitch = screen.getAllByRole('switch')[1]; + fireEvent.click(enablePathsSwitch); + + // Verify the path prefix field is now hidden + await waitFor(() => { + expect(screen.queryByLabelText(PATH_PREFIX_LABEL)).not.toBeInTheDocument(); + }); + + // Turn the paths switch back on + fireEvent.click(enablePathsSwitch); + + // Verify the path prefix field is displayed again + await waitFor(() => { + expect(screen.getByLabelText(PATH_PREFIX_LABEL)).toBeInTheDocument(); + }); + }); + it('should hide path prefix when enable paths switch is turned off', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: true, + enable_paths: true, + }, + }); + + // Wait for the component to render and find the switches + await waitFor(() => { + expect(screen.getAllByRole('switch')[1]).toBeInTheDocument(); + }); + + // Verify the path prefix is initially visible + await waitFor(() => { + expect(screen.getByLabelText(PATH_PREFIX_LABEL)).toBeInTheDocument(); + }); + + // Find the enable paths switch and turn it off + const enablePathsSwitch = screen.getAllByRole('switch')[1]; + fireEvent.click(enablePathsSwitch); + + // Verify the path prefix field is now hidden + await waitFor(() => { + expect(screen.queryByLabelText(PATH_PREFIX_LABEL)).not.toBeInTheDocument(); + }); + }); + }); + + describe('Enable/Disable Mapping', () => { + it('should display the mapping fields when enable mapping is turned on', async () => { + renderJsonProcessor({ + selectedProcessorData: { + ...selectedProcessorData, + enable_flattening: true, + enable_paths: true, + mapping: { + environment: ['existing.env'], + host: ['existing.host'], + }, + }, + }); + + // Verify the mapping fields are displayed + await waitFor(() => { + expect(screen.getByText('environment')).toBeInTheDocument(); + expect(screen.getByText('host')).toBeInTheDocument(); + }); + }); + }); + + describe('Edit Processor Flow', () => { + it('should load existing processor data correctly when editing', async () => { + const existingProcessorData = { + id: '1', + orderId: 1, + type: 'json_parser', + name: 'test json parser', + output: 'testoutput', + enable_flattening: true, + enable_paths: true, + path_prefix: 'existing.prefix', + enable_mapping: true, + mapping: { + environment: ['existing.env'], + host: ['existing.host'], + }, + }; + + renderJsonProcessor({ + selectedProcessorData: existingProcessorData, + isActionType: 'edit-processor', + }); + + // Verify the form is displayed with existing data + await waitFor(() => { + expect(screen.getByDisplayValue('existing.prefix')).toBeInTheDocument(); + }); + + // Verify flattening is enabled + const enableFlatteningSwitch = screen.getAllByRole('switch')[0]; + expect(enableFlatteningSwitch).toBeChecked(); + + // Verify paths is enabled + const enablePathsSwitch = screen.getAllByRole('switch')[1]; + expect(enablePathsSwitch).toBeChecked(); + }); }); }); diff --git a/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx b/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx index 003e0ac63e69..3542e0b3165c 100644 --- a/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx +++ b/frontend/src/container/PipelinePage/tests/PipelineListsView.test.tsx @@ -192,7 +192,7 @@ describe('PipelinePage container test', () => { '.ant-table-expanded-row [data-icon="delete"]', ); - expect(deleteBtns.length).toBe(2); + expect(deleteBtns.length).toBe(3); // delete pipeline await fireEvent.click(deleteBtns[0] as HTMLElement); @@ -213,7 +213,7 @@ describe('PipelinePage container test', () => { expect( document.querySelectorAll('.ant-table-expanded-row [data-icon="delete"]') .length, - ).toBe(1); + ).toBe(2); }); it('should be able to toggle and delete pipeline', async () => { diff --git a/frontend/src/container/PipelinePage/tests/__snapshots__/AddNewProcessor.test.tsx.snap b/frontend/src/container/PipelinePage/tests/__snapshots__/AddNewProcessor.test.tsx.snap deleted file mode 100644 index 1a91dcb17c9e..000000000000 --- a/frontend/src/container/PipelinePage/tests/__snapshots__/AddNewProcessor.test.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PipelinePage container test should render AddNewProcessor section 1`] = ``; diff --git a/frontend/src/container/PipelinePage/tests/__snapshots__/PipelineExpandView.test.tsx.snap b/frontend/src/container/PipelinePage/tests/__snapshots__/PipelineExpandView.test.tsx.snap index 6e27fbea9759..13b89bec4355 100644 --- a/frontend/src/container/PipelinePage/tests/__snapshots__/PipelineExpandView.test.tsx.snap +++ b/frontend/src/container/PipelinePage/tests/__snapshots__/PipelineExpandView.test.tsx.snap @@ -124,6 +124,37 @@ exports[`PipelinePage should render PipelineExpandView section 1`] = ` + + + + + 3 + + + + +
+ json parser +
+ + diff --git a/frontend/src/container/PipelinePage/tests/__snapshots__/PipelinePageLayout.test.tsx.snap b/frontend/src/container/PipelinePage/tests/__snapshots__/PipelinePageLayout.test.tsx.snap index 1261f7282c83..de2cf63c5f4a 100644 --- a/frontend/src/container/PipelinePage/tests/__snapshots__/PipelinePageLayout.test.tsx.snap +++ b/frontend/src/container/PipelinePage/tests/__snapshots__/PipelinePageLayout.test.tsx.snap @@ -118,7 +118,7 @@ exports[`PipelinePage container test should render PipelinePageLayout section 1` learn_moreĀ  here diff --git a/frontend/src/types/api/pipeline/def.ts b/frontend/src/types/api/pipeline/def.ts index 3aef70f1fdbc..956954cf3212 100644 --- a/frontend/src/types/api/pipeline/def.ts +++ b/frontend/src/types/api/pipeline/def.ts @@ -31,6 +31,13 @@ export interface ProcessorData { // time parser fields layout_type?: string; layout?: string; + + // json flattening fields + enable_flattening?: boolean; + enable_paths?: boolean; + path_prefix?: string; + enable_mapping?: boolean; + mapping?: Record; } export interface PipelineData {