diff --git a/packages/nodes-base/nodes/RenameKeys/RenameKeys.node.ts b/packages/nodes-base/nodes/RenameKeys/RenameKeys.node.ts index e92b9141cf..e646edf572 100644 --- a/packages/nodes-base/nodes/RenameKeys/RenameKeys.node.ts +++ b/packages/nodes-base/nodes/RenameKeys/RenameKeys.node.ts @@ -1,5 +1,6 @@ import { IExecuteFunctions } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, @@ -10,6 +11,7 @@ import { set, unset, } from 'lodash'; +import { options } from 'rhea'; interface IRenameKey { currentKey: string; @@ -67,6 +69,82 @@ export class RenameKeys implements INodeType { }, ], }, + { + displayName: 'Additional Options', + name: 'additionalOptions', + type: 'collection', + default: {}, + placeholder: 'Add Option', + options: [ + { + displayName: 'Regex', + name: 'regexReplacement', + placeholder: 'Add new regular expression', + description: 'Adds a regular expressiond', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + sortable: true, + }, + default: {}, + options: [ + { + displayName: 'Replacement', + name: 'replacements', + values: [ + { + displayName: 'Be aware that by using regular expression previously renamed keys can be affected', + name: 'regExNotice', + type: 'notice', + default: '', + }, + { + displayName: 'Regular Expression', + name: 'searchRegex', + type: 'string', + default: '', + placeholder: 'e.g. [N-n]ame', + description: 'Regex to match the key name', + hint: 'Learn more and test RegEx here', + }, + { + displayName: 'Replace With', + name: 'replaceRegex', + type: 'string', + default: '', + placeholder: 'replacedName', + description: 'The name the key/s should be renamed to. It\'s possible to use regex captures e.g. $1, $2, ...', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + default: {}, + placeholder: 'Add Regex Option', + options: [ + { + displayName: 'Case Insensitive', + name: 'caseInsensitive', + type: 'boolean', + description: 'Whether to use case insensitive match', + default: false, + }, + { + displayName: 'Max Depth', + name: 'depth', + type: 'number', + default: -1, + description: 'Maximum depth to replace keys', + hint: 'Specify number for depth level (-1 for unlimited, 0 for top level only)', + }, + ], + }, + ], + }, + ], + }, + ], + }, ], }; @@ -81,8 +159,11 @@ export class RenameKeys implements INodeType { let newItem: INodeExecutionData; let renameKeys: IRenameKey[]; let value: any; // tslint:disable-line:no-any + for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { renameKeys = this.getNodeParameter('keys.key', itemIndex, []) as IRenameKey[]; + const regexReplacements = this.getNodeParameter('additionalOptions.regexReplacement.replacements', itemIndex, []) as IDataObject[]; + item = items[itemIndex]; // Copy the whole JSON data as data on any level can be renamed @@ -113,6 +194,39 @@ export class RenameKeys implements INodeType { unset(newItem.json, renameKey.currentKey as string); }); + regexReplacements.forEach(replacement => { + const { searchRegex, replaceRegex, options } = replacement; + const {depth, caseInsensitive} = options as IDataObject; + + const flags = (caseInsensitive as boolean) ? 'i' : undefined; + + const regex = new RegExp(searchRegex as string, flags); + + const renameObjectKeys = (obj: IDataObject, depth: number) => { + for (const key in obj) { + if (Array.isArray(obj)) { + // Don't rename array object references + if (depth !== 0) { + renameObjectKeys(obj[key] as IDataObject, depth - 1); + } + } else if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object' && depth !== 0) { + renameObjectKeys(obj[key] as IDataObject, depth - 1); + } + if (key.match(regex)) { + const newKey = key.replace(regex, replaceRegex as string); + if (newKey !== key) { + obj[newKey] = obj[key]; + delete obj[key]; + } + } + } + } + return obj; + }; + newItem.json = renameObjectKeys(newItem.json, depth as number); + }); + returnData.push(newItem); }