fix(resource locator): add back extract value logic (#4178)

add back extract value logic
This commit is contained in:
Mutasem Aldmour 2022-09-23 11:56:57 +02:00 committed by GitHub
parent 87e6877f1d
commit 46f95622e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 342 additions and 40 deletions

View file

@ -0,0 +1,183 @@
import {
INode,
INodeParameters,
INodeProperties,
INodePropertyCollection,
INodePropertyOptions,
INodeType,
NodeOperationError,
NodeParameterValueType,
NodeHelpers,
LoggerProxy,
} from 'n8n-workflow';
function findPropertyFromParameterName(
parameterName: string,
nodeType: INodeType,
node: INode,
nodeParameters: INodeParameters,
): INodePropertyOptions | INodeProperties | INodePropertyCollection {
let property: INodePropertyOptions | INodeProperties | INodePropertyCollection | undefined;
const paramParts = parameterName.split('.');
let currentParamPath = '';
const findProp = (
name: string,
options: Array<INodePropertyOptions | INodeProperties | INodePropertyCollection>,
): INodePropertyOptions | INodeProperties | INodePropertyCollection | undefined => {
return options.find(
(i) =>
i.name === name &&
NodeHelpers.displayParameterPath(nodeParameters, i, currentParamPath, node),
);
};
// eslint-disable-next-line no-restricted-syntax
for (const p of paramParts) {
const param = p.split('[')[0];
if (!property) {
property = findProp(param, nodeType.description.properties);
} else if ('options' in property && property.options) {
property = findProp(param, property.options);
currentParamPath += `.${param}`;
} else if ('values' in property) {
property = findProp(param, property.values);
currentParamPath += `.${param}`;
} else {
throw new Error(`Couldn't not find property "${parameterName}"`);
}
if (!property) {
throw new Error(`Couldn't not find property "${parameterName}"`);
}
}
if (!property) {
throw new Error(`Couldn't not find property "${parameterName}"`);
}
return property;
}
function executeRegexExtractValue(
value: string,
regex: RegExp,
parameterName: string,
parameterDisplayName: string,
): NodeParameterValueType | object {
const extracted = regex.exec(value);
if (!extracted) {
throw new Error(
`ERROR: ${parameterDisplayName} parameter's value is invalid. This is likely because the URL entered is incorrect`,
);
}
if (extracted.length < 2 || extracted.length > 2) {
throw new Error(
`Property "${parameterName}" has an invalid extractValue regex "${regex.source}". extractValue expects exactly one group to be returned.`,
);
}
return extracted[1];
}
function extractValueRLC(
value: NodeParameterValueType | object,
property: INodeProperties,
parameterName: string,
): NodeParameterValueType | object {
// Not an RLC value
if (typeof value !== 'object' || !value || !('mode' in value) || !('value' in value)) {
return value;
}
const modeProp = (property.modes ?? []).find((i) => i.name === value.mode);
if (!modeProp) {
return value.value;
}
if (!('extractValue' in modeProp) || !modeProp.extractValue) {
return value.value;
}
if (typeof value.value !== 'string') {
let typeName: string | undefined = value.value?.constructor.name;
if (value.value === null) {
typeName = 'null';
} else if (typeName === undefined) {
typeName = 'undefined';
}
LoggerProxy.error(
`Only strings can be passed to extractValue. Parameter "${parameterName}" passed "${typeName}"`,
);
throw new Error(
`ERROR: ${property.displayName} parameter's value is invalid. Please enter a valid ${modeProp.displayName}.`,
);
}
if (modeProp.extractValue.type !== 'regex') {
throw new Error(
`Property "${parameterName}" has an unknown extractValue type "${
modeProp.extractValue.type as string
}"`,
);
}
const regex = new RegExp(modeProp.extractValue.regex);
return executeRegexExtractValue(value.value, regex, parameterName, property.displayName);
}
function extractValueOther(
value: NodeParameterValueType | object,
property: INodeProperties | INodePropertyCollection,
parameterName: string,
): NodeParameterValueType | object {
if (!('extractValue' in property) || !property.extractValue) {
return value;
}
if (typeof value !== 'string') {
let typeName: string | undefined = value?.constructor.name;
if (value === null) {
typeName = 'null';
} else if (typeName === undefined) {
typeName = 'undefined';
}
LoggerProxy.error(
`Only strings can be passed to extractValue. Parameter "${parameterName}" passed "${typeName}"`,
);
throw new Error(
`ERROR: ${property.displayName} parameter's value is invalid. Please enter a valid value.`,
);
}
if (property.extractValue.type !== 'regex') {
throw new Error(
`Property "${parameterName}" has an unknown extractValue type "${
property.extractValue.type as string
}"`,
);
}
const regex = new RegExp(property.extractValue.regex);
return executeRegexExtractValue(value, regex, parameterName, property.displayName);
}
export function extractValue(
value: NodeParameterValueType | object,
parameterName: string,
node: INode,
nodeType: INodeType,
): NodeParameterValueType | object {
let property: INodePropertyOptions | INodeProperties | INodePropertyCollection;
try {
property = findPropertyFromParameterName(parameterName, nodeType, node, node.parameters);
// Definitely doesn't have value extractor
if (!('type' in property)) {
return value;
}
if (property.type === 'resourceLocator') {
return extractValueRLC(value, property, parameterName);
}
return extractValueOther(value, property, parameterName);
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
throw new NodeOperationError(node, error);
}
}

View file

@ -59,6 +59,7 @@ import {
LoggerProxy as Logger,
IExecuteData,
OAuth2GrantType,
IGetNodeParameterOptions,
NodeParameterValueType,
NodeExecutionWithMetadata,
IPairedItemData,
@ -99,6 +100,7 @@ import {
IWorkflowSettings,
PLACEHOLDER_EMPTY_EXECUTION_ID,
} from '.';
import { extractValue } from './ExtractValue';
axios.defaults.timeout = 300000;
// Prevent axios from adding x-form-www-urlencoded headers by default
@ -1725,6 +1727,7 @@ export function getNodeParameter(
additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object {
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
if (nodeType === undefined) {
@ -1759,6 +1762,11 @@ export function getNodeParameter(
throw e;
}
// This is outside the try/catch because it throws errors with proper messages
if (options?.extractValue) {
returnData = extractValue(returnData, parameterName, node, nodeType);
}
return returnData;
}
@ -1931,6 +1939,7 @@ export function getExecutePollFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
@ -1950,6 +1959,7 @@ export function getExecutePollFunctions(
getAdditionalKeys(additionalData),
undefined,
fallbackValue,
options,
);
},
getRestApiUrl: (): string => {
@ -2084,6 +2094,7 @@ export function getExecuteTriggerFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
@ -2103,6 +2114,7 @@ export function getExecuteTriggerFunctions(
getAdditionalKeys(additionalData),
undefined,
fallbackValue,
options,
);
},
getRestApiUrl: (): string => {
@ -2302,6 +2314,7 @@ export function getExecuteFunctions(
parameterName: string,
itemIndex: number,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
return getNodeParameter(
workflow,
@ -2316,6 +2329,7 @@ export function getExecuteFunctions(
getAdditionalKeys(additionalData),
executeData,
fallbackValue,
options,
);
},
getMode: (): WorkflowExecuteMode => {
@ -2575,6 +2589,7 @@ export function getExecuteSingleFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
return getNodeParameter(
workflow,
@ -2589,6 +2604,7 @@ export function getExecuteSingleFunctions(
getAdditionalKeys(additionalData),
executeData,
fallbackValue,
options,
);
},
getWorkflow: () => {
@ -2742,6 +2758,7 @@ export function getLoadOptionsFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
@ -2761,6 +2778,7 @@ export function getLoadOptionsFunctions(
getAdditionalKeys(additionalData),
undefined,
fallbackValue,
options,
);
},
getTimezone: (): string => {
@ -2868,6 +2886,7 @@ export function getExecuteHookFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
@ -2887,6 +2906,7 @@ export function getExecuteHookFunctions(
getAdditionalKeys(additionalData),
undefined,
fallbackValue,
options,
);
},
getNodeWebhookUrl: (name: string): string | undefined => {
@ -3026,6 +3046,7 @@ export function getExecuteWebhookFunctions(
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object => {
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
@ -3045,6 +3066,7 @@ export function getExecuteWebhookFunctions(
getAdditionalKeys(additionalData),
undefined,
fallbackValue,
options,
);
},
getParamsData(): object {

View file

@ -508,8 +508,15 @@ export class Airtable implements INodeType {
const operation = this.getNodeParameter('operation', 0) as string;
const application = this.getNodeParameter('application', 0) as string;
const table = encodeURI(this.getNodeParameter('table', 0) as string);
const application = this.getNodeParameter('application', 0, undefined, {
extractValue: true,
}) as string;
const table = encodeURI(
this.getNodeParameter('table', 0, undefined, {
extractValue: true,
}) as string,
);
let returnAll = false;
let endpoint = '';

View file

@ -2153,7 +2153,9 @@ export class GoogleDrive implements INodeType {
// delete
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
const driveId = this.getNodeParameter('driveId', i, undefined, {
extractValue: true,
}) as string;
await googleApiRequest.call(this, 'DELETE', `/drive/v3/drives/${driveId}`);
@ -2169,7 +2171,9 @@ export class GoogleDrive implements INodeType {
// get
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
const driveId = this.getNodeParameter('driveId', i, undefined, {
extractValue: true,
}) as string;
const qs: IDataObject = {};
@ -2229,7 +2233,9 @@ export class GoogleDrive implements INodeType {
// update
// ----------------------------------
const driveId = this.getNodeParameter('driveId', i) as string;
const driveId = this.getNodeParameter('driveId', i, undefined, {
extractValue: true,
}) as string;
const body: IDataObject = {};
@ -2256,7 +2262,9 @@ export class GoogleDrive implements INodeType {
// copy
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const fileId = this.getNodeParameter('fileId', i, undefined, {
extractValue: true,
}) as string;
const body: IDataObject = {
fields: queryFields,
@ -2292,7 +2300,9 @@ export class GoogleDrive implements INodeType {
// download
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const fileId = this.getNodeParameter('fileId', i, undefined, {
extractValue: true,
}) as string;
const options = this.getNodeParameter('options', i) as IDataObject;
const requestOptions = {
@ -2635,7 +2645,9 @@ export class GoogleDrive implements INodeType {
// file:update
// ----------------------------------
const id = this.getNodeParameter('fileId', i) as string;
const id = this.getNodeParameter('fileId', i, undefined, {
extractValue: true,
}) as string;
const updateFields = this.getNodeParameter('updateFields', i, {}) as IDataObject;
const qs: IDataObject = {
@ -2709,7 +2721,9 @@ export class GoogleDrive implements INodeType {
// delete
// ----------------------------------
const fileId = this.getNodeParameter('fileId', i) as string;
const fileId = this.getNodeParameter('fileId', i, undefined, {
extractValue: true,
}) as string;
await googleApiRequest.call(
this,
@ -2731,7 +2745,9 @@ export class GoogleDrive implements INodeType {
returnData.push(...executionData);
}
if (operation === 'share') {
const fileId = this.getNodeParameter('fileId', i) as string;
const fileId = this.getNodeParameter('fileId', i, undefined, {
extractValue: true,
}) as string;
const permissions = this.getNodeParameter('permissionsUi', i) as IDataObject;

View file

@ -239,7 +239,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const id = this.getNodeParameter('id', i) as string;
const id = this.getNodeParameter('id', i, undefined, {
extractValue: true,
}) as string;
endpoint = `boards/${id}`;
} else if (operation === 'get') {
@ -249,7 +251,7 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const id = this.getNodeParameter('id', i);
const id = this.getNodeParameter('id', i, undefined, { extractValue: true });
endpoint = `boards/${id}`;
@ -262,7 +264,7 @@ export class Trello implements INodeType {
requestMethod = 'PUT';
const id = this.getNodeParameter('id', i);
const id = this.getNodeParameter('id', i, undefined, { extractValue: true });
endpoint = `boards/${id}`;
@ -365,7 +367,7 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const id = this.getNodeParameter('id', i);
const id = this.getNodeParameter('id', i, undefined, { extractValue: true });
endpoint = `cards/${id}`;
} else if (operation === 'get') {
@ -375,7 +377,7 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const id = this.getNodeParameter('id', i);
const id = this.getNodeParameter('id', i, undefined, { extractValue: true });
endpoint = `cards/${id}`;
@ -388,7 +390,7 @@ export class Trello implements INodeType {
requestMethod = 'PUT';
const id = this.getNodeParameter('id', i);
const id = this.getNodeParameter('id', i, undefined, { extractValue: true });
endpoint = `cards/${id}`;
@ -407,7 +409,9 @@ export class Trello implements INodeType {
// create
// ----------------------------------
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
qs.text = this.getNodeParameter('text', i) as string;
@ -421,7 +425,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const commentId = this.getNodeParameter('commentId', i) as string;
@ -433,7 +439,9 @@ export class Trello implements INodeType {
requestMethod = 'PUT';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const commentId = this.getNodeParameter('commentId', i) as string;
@ -552,7 +560,9 @@ export class Trello implements INodeType {
requestMethod = 'POST';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const url = this.getNodeParameter('url', i) as string;
@ -571,7 +581,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const id = this.getNodeParameter('id', i) as string;
@ -583,7 +595,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const id = this.getNodeParameter('id', i) as string;
@ -598,7 +612,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
endpoint = `cards/${cardId}/attachments`;
@ -619,7 +635,9 @@ export class Trello implements INodeType {
requestMethod = 'POST';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const name = this.getNodeParameter('name', i) as string;
@ -636,7 +654,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const id = this.getNodeParameter('id', i) as string;
@ -661,7 +681,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
endpoint = `cards/${cardId}/checklists`;
@ -674,7 +696,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const checkItemId = this.getNodeParameter('checkItemId', i) as string;
@ -703,7 +727,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const checkItemId = this.getNodeParameter('checkItemId', i) as string;
@ -715,7 +741,9 @@ export class Trello implements INodeType {
requestMethod = 'PUT';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const checkItemId = this.getNodeParameter('checkItemId', i) as string;
@ -730,7 +758,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
endpoint = `cards/${cardId}/checkItemStates`;
@ -751,7 +781,9 @@ export class Trello implements INodeType {
requestMethod = 'POST';
const idBoard = this.getNodeParameter('boardId', i) as string;
const idBoard = this.getNodeParameter('boardId', i, undefined, {
extractValue: true,
}) as string;
const name = this.getNodeParameter('name', i) as string;
const color = this.getNodeParameter('color', i) as string;
@ -793,7 +825,9 @@ export class Trello implements INodeType {
requestMethod = 'GET';
const idBoard = this.getNodeParameter('boardId', i) as string;
const idBoard = this.getNodeParameter('boardId', i, undefined, {
extractValue: true,
}) as string;
endpoint = `board/${idBoard}/labels`;
@ -820,7 +854,9 @@ export class Trello implements INodeType {
requestMethod = 'POST';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const id = this.getNodeParameter('id', i) as string;
@ -834,7 +870,9 @@ export class Trello implements INodeType {
requestMethod = 'DELETE';
const cardId = this.getNodeParameter('cardId', i) as string;
const cardId = this.getNodeParameter('cardId', i, undefined, {
extractValue: true,
}) as string;
const id = this.getNodeParameter('id', i) as string;

View file

@ -543,6 +543,10 @@ export interface IN8nRequestOperationPaginationOffset extends IN8nRequestOperati
};
}
export interface IGetNodeParameterOptions {
extractValue?: boolean;
}
export interface IExecuteFunctions {
continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
@ -564,6 +568,7 @@ export interface IExecuteFunctions {
parameterName: string,
itemIndex: number,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getWorkflowStaticData(type: string): IDataObject;
@ -601,7 +606,11 @@ export interface IExecuteSingleFunctions {
getItemIndex(): number;
getMode(): WorkflowExecuteMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getRestApiUrl(): string;
getTimezone(): string;
getExecuteData(): IExecuteData;
@ -647,7 +656,11 @@ export interface ICredentialTestFunctions {
export interface ILoadOptionsFunctions {
getCredentials(type: string): Promise<ICredentialDataDecryptedObject>;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getCurrentNodeParameter(parameterName: string): NodeParameterValueType | object | undefined;
getCurrentNodeParameters(): INodeParameters | undefined;
getTimezone(): string;
@ -680,7 +693,11 @@ export interface IHookFunctions {
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeWebhookUrl: (name: string) => string | undefined;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getTimezone(): string;
getWebhookDescription(name: string): IWebhookDescription | undefined;
getWebhookName(): string;
@ -706,7 +723,11 @@ export interface IPollFunctions {
getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
@ -736,7 +757,11 @@ export interface ITriggerFunctions {
getMode(): WorkflowExecuteMode;
getActivationMode(): WorkflowActivateMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getRestApiUrl(): string;
getTimezone(): string;
getWorkflow(): IWorkflowMetadata;
@ -761,7 +786,11 @@ export interface IWebhookFunctions {
getHeaderData(): object;
getMode(): WorkflowExecuteMode;
getNode(): INode;
getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValueType | object;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getNodeWebhookUrl: (name: string) => string | undefined;
getParamsData(): object;
getQueryData(): object;

View file

@ -643,8 +643,15 @@ export class RoutingNode {
if (nodeProperties.routing) {
let parameterValue: string | undefined;
if (basePath + nodeProperties.name && 'type' in nodeProperties) {
// Extract value if it has extractValue defined or if it's a
// resourceLocator component. Resource locators are likely to have extractors
// and we can't know if the mode has one unless we dig all the way in.
const shouldExtractValue =
nodeProperties.extractValue !== undefined || nodeProperties.type === 'resourceLocator';
parameterValue = executeSingleFunctions.getNodeParameter(
basePath + nodeProperties.name,
undefined,
{ extractValue: shouldExtractValue },
) as string;
}