n8n/packages/nodes-base/nodes/Elastic/Elasticsearch/Elasticsearch.node.ts
pemontto c6e90d15b5
feat(Elasticsearch Node): Add credential tests, index pipelines and index refresh (#2420)
* 🐛 ES query string not passed to request

* 🔑 Add ES credential test

*  Add ES index pipelines and index refresh

* 🔨 merge fix

*  renamed additional filds as options

*  added ignore ssl to credentials

*  Improvements

*  Improvements

* feat(Redis Node): Add push and pop operations (#3127)

*  Add push and pop operations

*  linter fixes

*  linter fixes

* 🐛 Fix errors and remove overwrite

* 🐛 Remove errant hint

*  Small change

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>

* refactor: Telemetry updates (#3529)

* Init unit tests for telemetry

* Update telemetry tests

* Test Workflow execution errored event

* Add new tracking logic in pulse

* cleanup

* interfaces

* Add event_version for Workflow execution count event

* add version_cli in all events

* add user saved credentials event

* update manual wf exec finished, fixes

* improve typings, lint

* add node_graph_string in User clicked execute workflow button event

* add User set node operation or mode event

* Add instance started event in FE

* Add User clicked retry execution button event

* add expression editor event

* add input node type to add node event

* add User stopped workflow execution wvent

* add error message in saved credential event

* update stop execution event

* add execution preflight event

* Remove instance started even tfrom FE, add session started to FE,BE

* improve typing

* remove node_graph as property from all events

* move back from default export

* move psl npm package to cli package

* cr

* update webhook node domain logic

* fix is_valid for User saved credentials event

* fix Expression Editor variable selector event

* add caused_by_credential in preflight event

* undo webhook_domain

* change node_type to full type

* add webhook_domain property in manual execution event (#3680)

* add webhook_domain property in manual execution event

* lint fix

* feat(SpreadsheetFile Node): Allow skipping headers when writing spreadsheets (#3234)

*  Allow skipping headers when writing spreadsheets

* Fix type on sheet options

* fix(Telegram Node): Fix sending binaryData media (photo, document, video etc.) (#3408)

* fixed send media (photo, document, video etc.) issues on Telegram Node

* fixed send media (photo, document, video etc.) issues on Telegram Node

* file name is optional now

*  lock file and linter fix

*  improvements

*  fixes

*  Improvements

*  Add placeholder to File Name

*  Add error message

* 🔥 Remove requestWithAuthentication

*  Fix typo

* 👕 Fix linting issues

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>

* feat(Freshworks CRM Node): Add Search + Lookup functionality (#3131)

* Add fields and Ops for Lookup Search

* Adds Search (Search + Lookup) operations

* 🔨 credentials update

* 🔨 improvements

*  clean up and linter fixes

*  merged search and query, more hints

*  Improvements

*  Add generic type to authentication method

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>

* feat(Jira Trigger Node): Add optional query auth for security (#3172)

*  Add query auth for Jira Trigger security

*  small fixes:

*  Response with 403 when invalid query authentication

* 👕 Fix linting issues

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>

*  Changed authentication to use the generic type

Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Ahsan Virani <ahsan.virani@gmail.com>
Co-authored-by: Nicholas Penree <nick@penree.com>
Co-authored-by: Taha Sönmez <35905778+tahasonmez@users.noreply.github.com>
Co-authored-by: Jan Thiel <JanThiel@users.noreply.github.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
2022-07-10 12:00:47 +03:00

377 lines
11 KiB
TypeScript

import {
IExecuteFunctions,
} from 'n8n-core';
import {
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject,
INodeCredentialTestResult,
INodeExecutionData,
INodeType,
INodeTypeDescription,
JsonObject,
} from 'n8n-workflow';
import {
elasticsearchApiRequest,
} from './GenericFunctions';
import {
documentFields,
documentOperations,
indexFields,
indexOperations,
} from './descriptions';
import {
DocumentGetAllOptions,
ElasticsearchApiCredentials,
FieldsUiValues,
} from './types';
import {
omit,
} from 'lodash';
export class Elasticsearch implements INodeType {
description: INodeTypeDescription = {
displayName: 'Elasticsearch',
name: 'elasticsearch',
icon: 'file:elasticsearch.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume the Elasticsearch API',
defaults: {
name: 'Elasticsearch',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'elasticsearchApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Document',
value: 'document',
},
{
name: 'Index',
value: 'index',
},
],
default: 'document',
},
...documentOperations,
...documentFields,
...indexOperations,
...indexFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const resource = this.getNodeParameter('resource', 0) as 'document' | 'index';
const operation = this.getNodeParameter('operation', 0) as string;
let responseData;
for (let i = 0; i < items.length; i++) {
if (resource === 'document') {
// **********************************************************************
// document
// **********************************************************************
// https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html
if (operation === 'delete') {
// ----------------------------------------
// document: delete
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html
const indexId = this.getNodeParameter('indexId', i);
const documentId = this.getNodeParameter('documentId', i);
const endpoint = `/${indexId}/_doc/${documentId}`;
responseData = await elasticsearchApiRequest.call(this, 'DELETE', endpoint);
} else if (operation === 'get') {
// ----------------------------------------
// document: get
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html
const indexId = this.getNodeParameter('indexId', i);
const documentId = this.getNodeParameter('documentId', i);
const qs = {} as IDataObject;
const options = this.getNodeParameter('options', i) as IDataObject;
if (Object.keys(options).length) {
Object.assign(qs, options);
qs._source = true;
}
const endpoint = `/${indexId}/_doc/${documentId}`;
responseData = await elasticsearchApiRequest.call(this, 'GET', endpoint, {}, qs);
const simple = this.getNodeParameter('simple', i) as IDataObject;
if (simple) {
responseData = {
_id: responseData._id,
...responseData._source,
};
}
} else if (operation === 'getAll') {
// ----------------------------------------
// document: getAll
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
const indexId = this.getNodeParameter('indexId', i);
const body = {} as IDataObject;
const qs = {} as IDataObject;
const options = this.getNodeParameter('options', i) as DocumentGetAllOptions;
if (Object.keys(options).length) {
const { query, ...rest } = options;
if (query) Object.assign(body, JSON.parse(query));
Object.assign(qs, rest);
qs._source = true;
}
const returnAll = this.getNodeParameter('returnAll', 0);
if (!returnAll) {
qs.size = this.getNodeParameter('limit', 0);
}
responseData = await elasticsearchApiRequest.call(this, 'GET', `/${indexId}/_search`, body, qs);
responseData = responseData.hits.hits;
const simple = this.getNodeParameter('simple', 0) as IDataObject;
if (simple) {
responseData = responseData.map((item: IDataObject) => {
return {
_id: item._id,
...(item._source as {}),
};
});
}
} else if (operation === 'create') {
// ----------------------------------------
// document: create
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html
const body: IDataObject = {};
const dataToSend = this.getNodeParameter('dataToSend', 0) as 'defineBelow' | 'autoMapInputData';
if (dataToSend === 'defineBelow') {
const fields = this.getNodeParameter('fieldsUi.fieldValues', i, []) as FieldsUiValues;
fields.forEach(({ fieldId, fieldValue }) => body[fieldId] = fieldValue);
} else {
const inputData = items[i].json;
const rawInputsToIgnore = this.getNodeParameter('inputsToIgnore', i) as string;
const inputsToIgnore = rawInputsToIgnore.split(',').map(c => c.trim());
for (const key of Object.keys(inputData)) {
if (inputsToIgnore.includes(key)) continue;
body[key] = inputData[key];
}
}
const qs = {} as IDataObject;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const options = this.getNodeParameter('options', i, {}) as IDataObject;
if (Object.keys(additionalFields).length) {
Object.assign(qs, omit(additionalFields, ['documentId']));
}
Object.assign(qs, options);
const indexId = this.getNodeParameter('indexId', i);
const { documentId } = additionalFields;
if (documentId) {
const endpoint = `/${indexId}/_doc/${documentId}`;
responseData = await elasticsearchApiRequest.call(this, 'PUT', endpoint, body, qs);
} else {
const endpoint = `/${indexId}/_doc`;
responseData = await elasticsearchApiRequest.call(this, 'POST', endpoint, body, qs);
}
} else if (operation === 'update') {
// ----------------------------------------
// document: update
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html
const body = { doc: {} } as { doc: { [key: string]: string } };
const dataToSend = this.getNodeParameter('dataToSend', 0) as 'defineBelow' | 'autoMapInputData';
if (dataToSend === 'defineBelow') {
const fields = this.getNodeParameter('fieldsUi.fieldValues', i, []) as FieldsUiValues;
fields.forEach(({ fieldId, fieldValue }) => body.doc[fieldId] = fieldValue);
} else {
const inputData = items[i].json;
const rawInputsToIgnore = this.getNodeParameter('inputsToIgnore', i) as string;
const inputsToIgnore = rawInputsToIgnore.split(',').map(c => c.trim());
for (const key of Object.keys(inputData)) {
if (inputsToIgnore.includes(key)) continue;
body.doc[key] = inputData[key] as string;
}
}
const indexId = this.getNodeParameter('indexId', i);
const documentId = this.getNodeParameter('documentId', i);
const options = this.getNodeParameter('options', i, {}) as IDataObject;
const qs = {
...options,
};
const endpoint = `/${indexId}/_update/${documentId}`;
responseData = await elasticsearchApiRequest.call(this, 'POST', endpoint, body, qs);
}
} else if (resource === 'index') {
// **********************************************************************
// index
// **********************************************************************
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html
if (operation === 'create') {
// ----------------------------------------
// index: create
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
const indexId = this.getNodeParameter('indexId', i);
const body = {} as IDataObject;
const qs = {} as IDataObject;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (Object.keys(additionalFields).length) {
const { aliases, mappings, settings, ...rest } = additionalFields;
Object.assign(body, aliases, mappings, settings);
Object.assign(qs, rest);
}
responseData = await elasticsearchApiRequest.call(this, 'PUT', `/${indexId}`);
responseData = { id: indexId, ...responseData };
delete responseData.index;
} else if (operation === 'delete') {
// ----------------------------------------
// index: delete
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-delete-index.html
const indexId = this.getNodeParameter('indexId', i);
responseData = await elasticsearchApiRequest.call(this, 'DELETE', `/${indexId}`);
responseData = { success: true };
} else if (operation === 'get') {
// ----------------------------------------
// index: get
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-index.html
const indexId = this.getNodeParameter('indexId', i) as string;
const qs = {} as IDataObject;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (Object.keys(additionalFields).length) {
Object.assign(qs, additionalFields);
}
responseData = await elasticsearchApiRequest.call(this, 'GET', `/${indexId}`, {}, qs);
responseData = { id: indexId, ...responseData[indexId] };
} else if (operation === 'getAll') {
// ----------------------------------------
// index: getAll
// ----------------------------------------
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
responseData = await elasticsearchApiRequest.call(this, 'GET', '/_aliases');
responseData = Object.keys(responseData).map(i => ({ indexId: i }));
const returnAll = this.getNodeParameter('returnAll', i);
if (!returnAll) {
const limit = this.getNodeParameter('limit', i) as number;
responseData = responseData.slice(0, limit);
}
}
}
Array.isArray(responseData)
? returnData.push(...responseData)
: returnData.push(responseData);
}
return [this.helpers.returnJsonArray(returnData)];
}
}