Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ingest pipelines] Upload indexed document to test a pipeline #77939

Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ const appServices = {
api: apiService,
notifications: notificationServiceMock.createSetupContract(),
history,
urlGenerators: {
getUrlGenerator: jest.fn().mockReturnValue({
createUrl: jest.fn(),
}),
},
};

export const setupEnvironment = () => {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ingest_pipelines/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "8.0.0",
"server": true,
"ui": true,
"requiredPlugins": ["licensing", "management", "features"],
"requiredPlugins": ["licensing", "management", "features", "share"],
"optionalPlugins": ["security", "usageCollection"],
"configPath": ["xpack", "ingest_pipelines"],
"requiredBundles": ["esUiShared", "kibanaReact"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
]);
};

const setFetchDocumentsResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('GET', '/api/ingest_pipelines/documents/:index/:id', [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

return {
setSimulatePipelineResponse,
setFetchDocumentsResponse,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ const appServices = {
notifications: notificationServiceMock.createSetupContract(),
history,
uiSettings: {},
urlGenerators: {
getUrlGenerator: jest.fn().mockReturnValue({
createUrl: jest.fn(),
}),
},
};

const testBedSetup = registerTestBed<TestSubject>(
Expand Down Expand Up @@ -180,6 +185,20 @@ const createActions = (testBed: TestBed<TestSubject>) => {
});
component.update();
},

async toggleDocumentsAccordion() {
await act(async () => {
find('importDocumentsAccordion').simulate('click');
});
component.update();
},

async clickImportButton() {
await act(async () => {
find('importDocumentButton').simulate('click');
});
component.update();
},
};
};

Expand Down Expand Up @@ -229,4 +248,7 @@ type TestSubject =
| 'configurationTab'
| 'outputTab'
| 'processorOutputTabContent'
| 'importDocumentsAccordion'
| 'importDocumentButton'
| 'importDocumentError'
| string;
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,9 @@ describe('Test pipeline', () => {
const { actions, find, exists } = testBed;

const error = {
status: 400,
error: 'Bad Request',
message:
'"[parse_exception] [_source] required property is missing, with { property_name="_source" }"',
status: 500,
error: 'Internal server error',
message: 'Internal server error',
};

httpRequestsMockHelpers.setSimulatePipelineResponse(undefined, { body: error });
Expand All @@ -153,13 +152,87 @@ describe('Test pipeline', () => {
actions.clickAddDocumentsButton();

// Add invalid sample documents array and run the pipeline
actions.addDocumentsJson(JSON.stringify([{}]));
actions.addDocumentsJson(
JSON.stringify([
{
_index: 'test',
_id: '1',
_version: 1,
_seq_no: 0,
_primary_term: 1,
_source: {
name: 'John Doe',
},
},
])
);
await actions.clickRunPipelineButton();

// Verify error rendered
expect(exists('pipelineExecutionError')).toBe(true);
expect(find('pipelineExecutionError').text()).toContain(error.message);
});

describe('Import indexed documents', () => {
test('should successfully import an index document', async () => {
const { actions, form } = testBed;

const { _index: index, _id: documentId } = DOCUMENTS[0];

httpRequestsMockHelpers.setFetchDocumentsResponse(DOCUMENTS[0]);

// Open flyout
actions.clickAddDocumentsButton();

// Open documents accordion, click run without required fields, and verify error messages
await actions.toggleDocumentsAccordion();
await actions.clickImportButton();
expect(form.getErrorsMessages()).toEqual([
'An index name is required.',
'A document ID is required.',
]);

// Add required fields, and click run
form.setInputValue('indexField.input', index);
form.setInputValue('idField.input', documentId);
await actions.clickImportButton();

// Verify request
const latestRequest = server.requests[server.requests.length - 1];
expect(latestRequest.status).toEqual(200);
expect(latestRequest.url).toEqual(`/api/ingest_pipelines/documents/${index}/${documentId}`);
});

test('should surface API errors from the request', async () => {
const { actions, form, exists, find } = testBed;

const nonExistentDoc = {
index: 'foo',
id: '1',
};

const error = {
status: 404,
error: 'Not found',
message: '[index_not_found_exception] no such index',
};

httpRequestsMockHelpers.setFetchDocumentsResponse(undefined, { body: error });

// Open flyout
actions.clickAddDocumentsButton();

// Open documents accordion, add required fields, and click run
await actions.toggleDocumentsAccordion();
form.setInputValue('indexField.input', nonExistentDoc.index);
form.setInputValue('idField.input', nonExistentDoc.id);
await actions.clickImportButton();

// Verify error rendered
expect(exists('importDocumentError')).toBe(true);
expect(find('importDocumentError').text()).toContain(error.message);
});
});
});

describe('Processors', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
EuiCallOut,
} from '@elastic/eui';

import { Form, FormHook } from '../../../../../shared_imports';
import { FormHook } from '../../../../../shared_imports';
import { Document } from '../../types';

import { Tabs, TestPipelineFlyoutTab, OutputTab, DocumentsTab } from './test_pipeline_flyout_tabs';
Expand Down Expand Up @@ -71,19 +71,11 @@ export const TestPipelineFlyout: React.FunctionComponent<Props> = ({
} else {
// default to "Documents" tab
tabContent = (
<Form
<DocumentsTab
form={form}
data-test-subj="testPipelineForm"
isInvalid={form.isSubmitted && !form.isValid}
onSubmit={validateAndTestPipeline}
error={form.getErrors()}
>
<DocumentsTab
validateAndTestPipeline={validateAndTestPipeline}
isRunningTest={isRunningTest}
isSubmitButtonDisabled={form.isSubmitted && !form.isValid}
/>
</Form>
validateAndTestPipeline={validateAndTestPipeline}
isRunningTest={isRunningTest}
/>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ export const documentsSchema: FormSchema = {
}
},
},
{
validator: ({ value }: ValidationFuncArg<any, any>) => {
const parsedJSON = JSON.parse(value);

const isMissingSourceField = parsedJSON.find((obj: { _source?: object }) => {
if (!obj._source) {
return true;
}

return false;
});

if (isMissingSourceField) {
return {
message: i18n.translate(
'xpack.ingestPipelines.testPipelineFlyout.documentsForm.sourceFieldRequiredError',
{
defaultMessage: 'Documents require a _source field.',
}
),
};
}
},
},
],
},
};
Loading