mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Fix mapping with special characters (#5837)
* fix: Fix mapping with special characters * refactor: rename var * test: update more unit tests * test: update mapping test * test: update mapping test
This commit is contained in:
parent
ddc8f30e6d
commit
f8f584c136
|
@ -56,7 +56,7 @@ describe('Data mapping', () => {
|
||||||
ndv.actions.mapDataFromHeader(2, 'value');
|
ndv.actions.mapDataFromHeader(2, 'value');
|
||||||
ndv.getters
|
ndv.getters
|
||||||
.inlineExpressionEditorInput()
|
.inlineExpressionEditorInput()
|
||||||
.should('have.text', '{{ $json.timestamp }} {{ $json["Readable date"] }}');
|
.should('have.text', "{{ $json.timestamp }} {{ $json['Readable date'] }}");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maps expressions from table json, and resolves value based on hover', () => {
|
it('maps expressions from table json, and resolves value based on hover', () => {
|
||||||
|
@ -193,7 +193,7 @@ describe('Data mapping', () => {
|
||||||
ndv.actions.mapToParameter('value');
|
ndv.actions.mapToParameter('value');
|
||||||
ndv.getters
|
ndv.getters
|
||||||
.inlineExpressionEditorInput()
|
.inlineExpressionEditorInput()
|
||||||
.should('have.text', `{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }}`);
|
.should('have.text', `{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }}`);
|
||||||
ndv.getters.parameterExpressionPreview('value').should('not.exist');
|
ndv.getters.parameterExpressionPreview('value').should('not.exist');
|
||||||
|
|
||||||
ndv.actions.switchInputMode('Table');
|
ndv.actions.switchInputMode('Table');
|
||||||
|
@ -202,7 +202,7 @@ describe('Data mapping', () => {
|
||||||
.inlineExpressionEditorInput()
|
.inlineExpressionEditorInput()
|
||||||
.should(
|
.should(
|
||||||
'have.text',
|
'have.text',
|
||||||
`{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }} {{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input }}`,
|
`{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }} {{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input }}`,
|
||||||
);
|
);
|
||||||
ndv.getters.parameterExpressionPreview('value').should('not.exist');
|
ndv.getters.parameterExpressionPreview('value').should('not.exist');
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ describe('Data mapping', () => {
|
||||||
ndv.getters
|
ndv.getters
|
||||||
.parameterInput('fieldName')
|
.parameterInput('fieldName')
|
||||||
.find('input')
|
.find('input')
|
||||||
.should('have.value', 'input[0]["hello.world"]["my count"]');
|
.should('have.value', "input[0]['hello.world']['my count']");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maps expressions to updated fields correctly', () => {
|
it('maps expressions to updated fields correctly', () => {
|
||||||
|
|
|
@ -77,7 +77,9 @@ export default Vue.extend({
|
||||||
segment.kind === 'plaintext'
|
segment.kind === 'plaintext'
|
||||||
? segment.plaintext.length
|
? segment.plaintext.length
|
||||||
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(segment.resolved as any).toString().length;
|
segment.resolved
|
||||||
|
? (segment.resolved as any).toString().length
|
||||||
|
: 0;
|
||||||
|
|
||||||
segment.to = cursor;
|
segment.to = cursor;
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,9 @@ export default Vue.extend({
|
||||||
segment.kind === 'plaintext'
|
segment.kind === 'plaintext'
|
||||||
? segment.plaintext.length
|
? segment.plaintext.length
|
||||||
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(segment.resolved as any).toString().length;
|
segment.resolved
|
||||||
|
? (segment.resolved as any).toString().length
|
||||||
|
: 0;
|
||||||
segment.to = cursor;
|
segment.to = cursor;
|
||||||
return segment;
|
return segment;
|
||||||
})
|
})
|
||||||
|
|
|
@ -301,9 +301,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = `
|
||||||
class="label"
|
class="label"
|
||||||
data-depth="1"
|
data-depth="1"
|
||||||
data-name="hello world"
|
data-name="hello world"
|
||||||
data-path="[\\"hello world\\"]"
|
data-path="['hello world']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
data-value="{{ $json[\\"hello world\\"] }}"
|
data-value="{{ $json['hello world'] }}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon-stub
|
<font-awesome-icon-stub
|
||||||
icon="list"
|
icon="list"
|
||||||
|
@ -347,9 +347,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = `
|
||||||
class="label"
|
class="label"
|
||||||
data-depth="2"
|
data-depth="2"
|
||||||
data-name="object[0]"
|
data-name="object[0]"
|
||||||
data-path="[\\"hello world\\"][0]"
|
data-path="['hello world'][0]"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
data-value="{{ $json[\\"hello world\\"][0] }}"
|
data-value="{{ $json['hello world'][0] }}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon-stub
|
<font-awesome-icon-stub
|
||||||
icon="cube"
|
icon="cube"
|
||||||
|
@ -395,9 +395,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = `
|
||||||
class="label"
|
class="label"
|
||||||
data-depth="3"
|
data-depth="3"
|
||||||
data-name="test"
|
data-name="test"
|
||||||
data-path="[\\"hello world\\"][0].test"
|
data-path="['hello world'][0].test"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
data-value="{{ $json[\\"hello world\\"][0].test }}"
|
data-value="{{ $json['hello world'][0].test }}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon-stub
|
<font-awesome-icon-stub
|
||||||
icon="cube"
|
icon="cube"
|
||||||
|
@ -441,9 +441,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = `
|
||||||
class="label"
|
class="label"
|
||||||
data-depth="4"
|
data-depth="4"
|
||||||
data-name="more to think about"
|
data-name="more to think about"
|
||||||
data-path="[\\"hello world\\"][0].test[\\"more to think about\\"]"
|
data-path="['hello world'][0].test['more to think about']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
data-value="{{ $json[\\"hello world\\"][0].test[\\"more to think about\\"] }}"
|
data-value="{{ $json['hello world'][0].test['more to think about'] }}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon-stub
|
<font-awesome-icon-stub
|
||||||
icon="hashtag"
|
icon="hashtag"
|
||||||
|
@ -481,9 +481,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = `
|
||||||
class="label"
|
class="label"
|
||||||
data-depth="3"
|
data-depth="3"
|
||||||
data-name="test.how"
|
data-name="test.how"
|
||||||
data-path="[\\"hello world\\"][0][\\"test.how\\"]"
|
data-path="['hello world'][0]['test.how']"
|
||||||
data-target="mappable"
|
data-target="mappable"
|
||||||
data-value="{{ $json[\\"hello world\\"][0][\\"test.how\\"] }}"
|
data-value="{{ $json['hello world'][0]['test.how'] }}"
|
||||||
>
|
>
|
||||||
<font-awesome-icon-stub
|
<font-awesome-icon-stub
|
||||||
icon="font"
|
icon="font"
|
||||||
|
|
|
@ -228,7 +228,30 @@ describe('Mapping Utils', () => {
|
||||||
};
|
};
|
||||||
const result = getMappedExpression(input);
|
const result = getMappedExpression(input);
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'{{ $node.nodeName.json.sample["path with-space"]["path-with-hyphen"] }}',
|
"{{ $node.nodeName.json.sample['path with-space']['path-with-hyphen'] }}",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle paths with special characters', () => {
|
||||||
|
const input = {
|
||||||
|
nodeName: 'nodeName',
|
||||||
|
distanceFromActive: 2,
|
||||||
|
path: [
|
||||||
|
'sample',
|
||||||
|
'"Execute"',
|
||||||
|
'`Execute`',
|
||||||
|
"'Execute'",
|
||||||
|
'[Execute]',
|
||||||
|
'{Execute}',
|
||||||
|
'execute?',
|
||||||
|
'test,',
|
||||||
|
'test:',
|
||||||
|
'path.',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const result = getMappedExpression(input);
|
||||||
|
expect(result).toBe(
|
||||||
|
"{{ $node.nodeName.json.sample['\"Execute\"']['`Execute`']['\\'Execute\\'']['[Execute]']['{Execute}']['execute?']['test,']['test:']['path.'] }}",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -239,7 +262,7 @@ describe('Mapping Utils', () => {
|
||||||
path: ['propertyName', 'capitalizedName', 'hyphen-prop'],
|
path: ['propertyName', 'capitalizedName', 'hyphen-prop'],
|
||||||
};
|
};
|
||||||
const result = getMappedExpression(input);
|
const result = getMappedExpression(input);
|
||||||
expect(result).toBe('{{ $json.propertyName.capitalizedName["hyphen-prop"] }}');
|
expect(result).toBe("{{ $json.propertyName.capitalizedName['hyphen-prop'] }}");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a mapped expression with a complex path', () => {
|
it('should generate a mapped expression with a complex path', () => {
|
||||||
|
@ -250,7 +273,7 @@ describe('Mapping Utils', () => {
|
||||||
};
|
};
|
||||||
const result = getMappedExpression(input);
|
const result = getMappedExpression(input);
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'{{ $json.propertyName.capitalizedName.stringVal["some-value"].capitalizedProp }}',
|
"{{ $json.propertyName.capitalizedName.stringVal['some-value'].capitalizedProp }}",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -275,13 +275,13 @@ describe('Types Utils', () => {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
key: 'with space',
|
key: 'with space',
|
||||||
value: [],
|
value: [],
|
||||||
path: '["with space"]',
|
path: "['with space']",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
key: 'with.dot',
|
key: 'with.dot',
|
||||||
value: 'test',
|
value: 'test',
|
||||||
path: '["with.dot"]',
|
path: "['with.dot']",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
path: '',
|
path: '',
|
||||||
|
|
|
@ -6,8 +6,11 @@ export function generatePath(root: string, path: Array<string | number>): string
|
||||||
return `${accu}[${part}]`;
|
return `${accu}[${part}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.includes('-') || part.includes(' ') || part.includes('.')) {
|
const special = ['-', ' ', '.', "'", '"', '`', '[', ']', '{', '}', '(', ')', ':', ',', '?'];
|
||||||
return `${accu}["${part}"]`;
|
const hasSpecial = !!special.find((s) => part.includes(s));
|
||||||
|
if (hasSpecial) {
|
||||||
|
const escaped = part.replaceAll("'", "\\'");
|
||||||
|
return `${accu}['${escaped}']`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${accu}.${part}`;
|
return `${accu}.${part}`;
|
||||||
|
|
Loading…
Reference in a new issue