2019-06-23 03:35:23 -07:00
import { IExecuteFunctions } from 'n8n-core' ;
import {
2022-07-10 02:43:13 -07:00
IDataObject ,
2019-06-23 03:35:23 -07:00
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
import {
get ,
set ,
unset ,
} from 'lodash' ;
2022-07-10 02:43:13 -07:00
import { options } from 'rhea' ;
2019-06-23 03:35:23 -07:00
interface IRenameKey {
currentKey : string ;
newKey : string ;
}
export class RenameKeys implements INodeType {
description : INodeTypeDescription = {
displayName : 'Rename Keys' ,
name : 'renameKeys' ,
2019-07-26 02:27:46 -07:00
icon : 'fa:edit' ,
2019-06-23 03:35:23 -07:00
group : [ 'transform' ] ,
version : 1 ,
2021-07-03 05:40:16 -07:00
description : 'Renames keys' ,
2019-06-23 03:35:23 -07:00
defaults : {
name : 'Rename Keys' ,
color : '#772244' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
properties : [
{
displayName : 'Keys' ,
name : 'keys' ,
placeholder : 'Add new key' ,
2022-05-06 14:01:25 -07:00
description : 'Adds a key which should be renamed' ,
2019-06-23 03:35:23 -07:00
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
2021-02-15 00:53:19 -08:00
sortable : true ,
2019-06-23 03:35:23 -07:00
} ,
default : { } ,
options : [
{
displayName : 'Key' ,
name : 'key' ,
values : [
{
displayName : 'Current Key Name' ,
name : 'currentKey' ,
type : 'string' ,
default : '' ,
placeholder : 'currentKey' ,
2022-04-22 09:29:51 -07:00
description : 'The current name of the key. It is also possible to define deep keys by using dot-notation like for example: "level1.level2.currentKey".' ,
2019-06-23 03:35:23 -07:00
} ,
{
displayName : 'New Key Name' ,
name : 'newKey' ,
type : 'string' ,
default : '' ,
placeholder : 'newKey' ,
2022-06-03 10:23:49 -07:00
description : 'The name the key should be renamed to. It is also possible to define deep keys by using dot-notation like for example: "level1.level2.newKey".' ,
2019-06-23 03:35:23 -07:00
} ,
2020-10-22 06:46:03 -07:00
] ,
2019-06-23 03:35:23 -07:00
} ,
] ,
2020-10-22 06:46:03 -07:00
} ,
2022-07-10 02:43:13 -07:00
{
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 <a href="https://regex101.com/">here</a>' ,
} ,
{
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)' ,
} ,
] ,
} ,
] ,
} ,
] ,
} ,
] ,
} ,
2020-10-22 06:46:03 -07:00
] ,
2019-06-23 03:35:23 -07:00
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
2019-08-01 13:55:33 -07:00
const returnData : INodeExecutionData [ ] = [ ] ;
2019-06-23 03:35:23 -07:00
let item : INodeExecutionData ;
2019-08-01 13:55:33 -07:00
let newItem : INodeExecutionData ;
2019-06-23 03:35:23 -07:00
let renameKeys : IRenameKey [ ] ;
let value : any ; // tslint:disable-line:no-any
2022-07-10 02:43:13 -07:00
2019-06-23 03:35:23 -07:00
for ( let itemIndex = 0 ; itemIndex < items . length ; itemIndex ++ ) {
renameKeys = this . getNodeParameter ( 'keys.key' , itemIndex , [ ] ) as IRenameKey [ ] ;
2022-07-10 02:43:13 -07:00
const regexReplacements = this . getNodeParameter ( 'additionalOptions.regexReplacement.replacements' , itemIndex , [ ] ) as IDataObject [ ] ;
2019-06-23 03:35:23 -07:00
item = items [ itemIndex ] ;
2019-08-01 13:55:33 -07:00
// Copy the whole JSON data as data on any level can be renamed
newItem = {
json : JSON.parse ( JSON . stringify ( item . json ) ) ,
2022-06-03 08:25:07 -07:00
pairedItem : {
item : itemIndex ,
} ,
2019-08-01 13:55:33 -07:00
} ;
if ( item . binary !== undefined ) {
// Reference binary data if any exists. We can reference it
// as this nodes does not change it
newItem . binary = item . binary ;
}
2019-06-23 03:35:23 -07:00
renameKeys . forEach ( ( renameKey ) = > {
2021-01-07 04:42:05 -08:00
if ( renameKey . currentKey === '' || renameKey . newKey === '' || renameKey . currentKey === renameKey . newKey ) {
// Ignore all which do not have all the values set or if the new key is equal to the current key
2019-06-23 03:35:23 -07:00
return ;
}
value = get ( item . json , renameKey . currentKey as string ) ;
if ( value === undefined ) {
return ;
}
2019-08-01 13:55:33 -07:00
set ( newItem . json , renameKey . newKey , value ) ;
2019-06-23 03:35:23 -07:00
2019-08-01 13:55:33 -07:00
unset ( newItem . json , renameKey . currentKey as string ) ;
2019-06-23 03:35:23 -07:00
} ) ;
2019-08-01 13:55:33 -07:00
2022-07-10 02:43:13 -07:00
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 ) ;
} ) ;
2019-08-01 13:55:33 -07:00
returnData . push ( newItem ) ;
2019-06-23 03:35:23 -07:00
}
2019-08-01 13:55:33 -07:00
return [ returnData ] ;
2019-06-23 03:35:23 -07:00
}
}