diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/fingerprint.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/fingerprint.test.tsx
new file mode 100644
index 0000000000000..7c2ca012a0460
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/fingerprint.test.tsx
@@ -0,0 +1,128 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { act } from 'react-dom/test-utils';
+import { setup, SetupResult, getProcessorValue } from './processor.helpers';
+
+// Default parameter values automatically added to the registered domain processor when saved
+const defaultFingerprintParameters = {
+ if: undefined,
+ tag: undefined,
+ method: undefined,
+ salt: undefined,
+ description: undefined,
+ ignore_missing: undefined,
+ ignore_failure: undefined,
+ target_field: undefined,
+};
+
+const FINGERPRINT_TYPE = 'fingerprint';
+
+describe('Processor: Fingerprint', () => {
+ let onUpdate: jest.Mock;
+ let testBed: SetupResult;
+
+ beforeAll(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ beforeEach(async () => {
+ onUpdate = jest.fn();
+
+ await act(async () => {
+ testBed = await setup({
+ value: {
+ processors: [],
+ },
+ onFlyoutOpen: jest.fn(),
+ onUpdate,
+ });
+ });
+
+ testBed.component.update();
+
+ // Open flyout to add new processor
+ testBed.actions.addProcessor();
+ // Add type (the other fields are not visible until a type is selected)
+ await testBed.actions.addProcessorType(FINGERPRINT_TYPE);
+ });
+
+ test('prevents form submission if required fields are not provided', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ } = testBed;
+
+ // Click submit button with only the type defined
+ await saveNewProcessor();
+
+ // Expect form error as "field" is required parameter
+ expect(form.getErrorsMessages()).toEqual(['A field value is required.']);
+ });
+
+ test('saves with default parameter values', async () => {
+ const {
+ actions: { saveNewProcessor },
+ find,
+ component,
+ } = testBed;
+
+ // Add "fields" value (required)
+ await act(async () => {
+ find('fieldsValueField.input').simulate('change', [{ label: 'user' }]);
+ });
+ component.update();
+ // Save the field
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, FINGERPRINT_TYPE);
+ expect(processors[0][FINGERPRINT_TYPE]).toEqual({
+ ...defaultFingerprintParameters,
+ fields: ['user'],
+ });
+ });
+
+ test('allows optional parameters to be set', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ find,
+ component,
+ } = testBed;
+
+ // Add "fields" value (required)
+ await act(async () => {
+ find('fieldsValueField.input').simulate('change', [{ label: 'user' }]);
+ });
+ component.update();
+
+ // Set optional parameteres
+ form.setInputValue('targetField.input', 'target_field');
+ form.setSelectValue('methodsValueField', 'SHA-256');
+ form.setInputValue('saltValueField.input', 'salt');
+ form.toggleEuiSwitch('ignoreMissingSwitch.input');
+ form.toggleEuiSwitch('ignoreFailureSwitch.input');
+
+ // Save the field with new changes
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, FINGERPRINT_TYPE);
+ expect(processors[0][FINGERPRINT_TYPE]).toEqual({
+ ...defaultFingerprintParameters,
+ fields: ['user'],
+ target_field: 'target_field',
+ method: 'SHA-256',
+ salt: 'salt',
+ ignore_missing: true,
+ ignore_failure: true,
+ });
+ });
+});
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
index d69ceb385ddd7..9dd0d6cc72de1 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
@@ -154,4 +154,7 @@ type TestSubject =
| 'separatorValueField.input'
| 'quoteValueField.input'
| 'emptyValueField.input'
+ | 'fieldsValueField.input'
+ | 'saltValueField.input'
+ | 'methodsValueField'
| 'trimSwitch.input';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/fingerprint.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/fingerprint.tsx
new file mode 100644
index 0000000000000..5e52d560020c0
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/fingerprint.tsx
@@ -0,0 +1,152 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiCode } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+import { FieldsConfig, from, to } from './shared';
+import { TargetField } from './common_fields/target_field';
+import { IgnoreMissingField } from './common_fields/ignore_missing_field';
+import {
+ FIELD_TYPES,
+ Field,
+ UseField,
+ SelectField,
+ ComboBoxField,
+ fieldValidators,
+} from '../../../../../../shared_imports';
+
+const fieldsConfig: FieldsConfig = {
+ fields: {
+ type: FIELD_TYPES.COMBO_BOX,
+ deserializer: to.arrayOfStrings,
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.fingerprint.fieldNameField', {
+ defaultMessage: 'Fields',
+ }),
+ helpText: i18n.translate('xpack.ingestPipelines.pipelineEditor.fingerprint.fieldNameHelpText', {
+ defaultMessage: 'Fields to include in the fingerprint.',
+ }),
+ validations: [
+ {
+ validator: fieldValidators.emptyField(
+ i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.fingerprint.fieldNameRequiredError',
+ {
+ defaultMessage: 'A field value is required.',
+ }
+ )
+ ),
+ },
+ ],
+ },
+ salt: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.fingerprint.saltFieldLabel', {
+ defaultMessage: 'Salt (optional)',
+ }),
+ helpText: (
+
+ ),
+ },
+ method: {
+ type: FIELD_TYPES.SELECT,
+ defaultValue: 'SHA-1',
+ serializer: (v) => (v === 'SHA-1' || v === '' ? undefined : v),
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.fingerprint.methodFieldLabel', {
+ defaultMessage: 'Method',
+ }),
+ helpText: (
+
+ ),
+ },
+};
+
+export const Fingerprint: FunctionComponent = () => {
+ return (
+ <>
+
+
+ {'fingerprint'},
+ }}
+ />
+ }
+ />
+
+
+
+
+
+ {'fields'},
+ }}
+ />
+ }
+ />
+ >
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
index 4fb4365c477b5..5e3e5f82478bd 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
@@ -17,6 +17,7 @@ export { DotExpander } from './dot_expander';
export { Drop } from './drop';
export { Enrich } from './enrich';
export { Fail } from './fail';
+export { Fingerprint } from './fingerprint';
export { Foreach } from './foreach';
export { GeoIP } from './geoip';
export { Grok } from './grok';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
index b5e42ea56bdf8..983fb0ea67bb0 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
@@ -23,6 +23,7 @@ import {
Drop,
Enrich,
Fail,
+ Fingerprint,
Foreach,
GeoIP,
Grok,
@@ -308,6 +309,20 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = {
defaultMessage: 'Raises an exception that halts execution',
}),
},
+ fingerprint: {
+ FieldsComponent: Fingerprint,
+ docLinkPath: '/fingerprint-processor.html',
+ label: i18n.translate('xpack.ingestPipelines.processors.label.fingerprint', {
+ defaultMessage: 'Fingerprint',
+ }),
+ typeDescription: i18n.translate('xpack.ingestPipelines.processors.description.fingerprint', {
+ defaultMessage: 'Computes a hash of the document’s content.',
+ }),
+ getDefaultDescription: () =>
+ i18n.translate('xpack.ingestPipelines.processors.defaultDescription.fingerprint', {
+ defaultMessage: 'Computes a hash of the document’s content.',
+ }),
+ },
foreach: {
FieldsComponent: Foreach,
docLinkPath: '/foreach-processor.html',