mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
extract convert/select workflow inputs to generic function
This commit is contained in:
parent
c83bbcf50a
commit
f5b7ecdcda
|
@ -1,15 +1,34 @@
|
|||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||
import type {
|
||||
ExecuteWorkflowData,
|
||||
FieldValueOption,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
ResourceMapperField,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { getWorkflowInfo } from './GenericFunctions';
|
||||
import { getWorkflowInputs } from './methods/resourceMapping';
|
||||
import { loadWorkflowInputMappings } from './methods/resourceMapping';
|
||||
import { generatePairedItemData } from '../../../utils/utilities';
|
||||
import { getWorkflowInputData } from '../GenericFunctions';
|
||||
|
||||
function getCurrentWorkflowInputData(this: IExecuteFunctions) {
|
||||
const inputData = this.getInputData();
|
||||
|
||||
if (this.getNode().typeVersion < 1.2) {
|
||||
return inputData;
|
||||
} else {
|
||||
const schema = this.getNodeParameter('workflowInputs.schema', 0, []) as ResourceMapperField[];
|
||||
const newParams = schema
|
||||
.filter((x) => !x.removed)
|
||||
.map((x) => ({ name: x.displayName, type: x.type ?? 'any' })) as FieldValueOption[];
|
||||
|
||||
// TODO: map every row to field values so we can set the static value or expression later on
|
||||
return getWorkflowInputData.call(this, inputData, newParams);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExecuteWorkflow implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -201,7 +220,7 @@ export class ExecuteWorkflow implements INodeType {
|
|||
typeOptions: {
|
||||
loadOptionsDependsOn: ['workflowId.value'],
|
||||
resourceMapper: {
|
||||
localResourceMapperMethod: 'getWorkflowInputs',
|
||||
localResourceMapperMethod: 'loadWorkflowInputMappings',
|
||||
valuesLabel: 'Workflow Inputs',
|
||||
mode: 'add',
|
||||
fieldWords: {
|
||||
|
@ -266,14 +285,14 @@ export class ExecuteWorkflow implements INodeType {
|
|||
|
||||
methods = {
|
||||
localResourceMapping: {
|
||||
getWorkflowInputs,
|
||||
loadWorkflowInputMappings,
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const source = this.getNodeParameter('source', 0) as string;
|
||||
const mode = this.getNodeParameter('mode', 0, false) as string;
|
||||
const items = this.getInputData();
|
||||
const items = getCurrentWorkflowInputData.call(this);
|
||||
|
||||
const workflowProxy = this.getWorkflowDataProxy(0);
|
||||
const currentWorkflowId = workflowProxy.$workflow.id as string;
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
|
||||
import { getFieldEntries } from '../../GenericFunctions';
|
||||
|
||||
export async function getWorkflowInputs(
|
||||
export async function loadWorkflowInputMappings(
|
||||
this: ILocalLoadOptionsFunctions,
|
||||
): Promise<ResourceMapperFields> {
|
||||
const nodeLoadContext = await this.getWorkflowNodeContext(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE);
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import {
|
||||
type INodeExecutionData,
|
||||
NodeConnectionType,
|
||||
NodeOperationError,
|
||||
type IExecuteFunctions,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
validateFieldType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
|
@ -14,10 +11,9 @@ import {
|
|||
JSON_EXAMPLE,
|
||||
VALUES,
|
||||
INPUT_OPTIONS,
|
||||
DEFAULT_PLACEHOLDER,
|
||||
TYPE_OPTIONS,
|
||||
} from '../constants';
|
||||
import { getFieldEntries } from '../GenericFunctions';
|
||||
import { getFieldEntries, getWorkflowInputData } from '../GenericFunctions';
|
||||
|
||||
export class ExecuteWorkflowTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -202,83 +198,9 @@ export class ExecuteWorkflowTrigger implements INodeType {
|
|||
if (this.getNode().typeVersion < 1.1) {
|
||||
return [inputData];
|
||||
} else {
|
||||
const items: INodeExecutionData[] = [];
|
||||
const newParams = getFieldEntries(this);
|
||||
|
||||
for (const [itemIndex, item] of inputData.entries()) {
|
||||
const attemptToConvertTypes = this.getNodeParameter(
|
||||
`${INPUT_OPTIONS}.attemptToConvertTypes`,
|
||||
itemIndex,
|
||||
false,
|
||||
);
|
||||
const ignoreTypeErrors = this.getNodeParameter(
|
||||
`${INPUT_OPTIONS}.ignoreTypeErrors`,
|
||||
itemIndex,
|
||||
false,
|
||||
);
|
||||
|
||||
// Fields listed here will explicitly overwrite original fields
|
||||
const newItem: INodeExecutionData = {
|
||||
json: {},
|
||||
index: itemIndex,
|
||||
// TODO: Ensure we handle sub-execution jumps correctly.
|
||||
// metadata: {
|
||||
// subExecution: {
|
||||
// executionId: 'uhh',
|
||||
// workflowId: 'maybe?',
|
||||
// },
|
||||
// },
|
||||
pairedItem: { item: itemIndex },
|
||||
};
|
||||
try {
|
||||
const newParams = getFieldEntries(this);
|
||||
|
||||
for (const { name, type } of newParams) {
|
||||
if (!item.json.hasOwnProperty(name)) {
|
||||
newItem.json[name] = DEFAULT_PLACEHOLDER;
|
||||
continue;
|
||||
}
|
||||
|
||||
const result =
|
||||
type === 'any'
|
||||
? ({ valid: true, newValue: item.json[name] } as const)
|
||||
: validateFieldType(name, item.json[name], type, {
|
||||
strict: !attemptToConvertTypes,
|
||||
parseStrings: true, // Default behavior is to accept anything as a string, this is a good opportunity for a stricter boundary
|
||||
});
|
||||
|
||||
if (!result.valid) {
|
||||
if (ignoreTypeErrors) {
|
||||
newItem.json[name] = item.json[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new NodeOperationError(this.getNode(), result.errorMessage, {
|
||||
itemIndex,
|
||||
});
|
||||
} else {
|
||||
// If the value is `null` or `undefined`, then `newValue` is not in the returned object
|
||||
if (result.hasOwnProperty('newValue')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
newItem.json[name] = result.newValue;
|
||||
} else {
|
||||
newItem.json[name] = item.json[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items.push(newItem);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
/** todo error case? */
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), error, {
|
||||
itemIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [items];
|
||||
return [getWorkflowInputData.call(this, inputData, newParams)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
import { json as generateSchemaFromExample, type SchemaObject } from 'generate-schema';
|
||||
import type { JSONSchema7 } from 'json-schema';
|
||||
import type { FieldValueOption, FieldType, IWorkflowNodeContext } from 'n8n-workflow';
|
||||
import { jsonParse, NodeOperationError } from 'n8n-workflow';
|
||||
import type {
|
||||
FieldValueOption,
|
||||
FieldType,
|
||||
IWorkflowNodeContext,
|
||||
INodeExecutionData,
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-workflow';
|
||||
import { jsonParse, NodeOperationError, validateFieldType } from 'n8n-workflow';
|
||||
|
||||
import { JSON_EXAMPLE, INPUT_SOURCE, WORKFLOW_INPUTS, VALUES, TYPE_OPTIONS } from './constants';
|
||||
import {
|
||||
JSON_EXAMPLE,
|
||||
INPUT_SOURCE,
|
||||
WORKFLOW_INPUTS,
|
||||
VALUES,
|
||||
TYPE_OPTIONS,
|
||||
INPUT_OPTIONS,
|
||||
DEFAULT_PLACEHOLDER,
|
||||
} from './constants';
|
||||
|
||||
const SUPPORTED_TYPES = TYPE_OPTIONS.map((x) => x.value);
|
||||
|
||||
|
@ -73,3 +87,85 @@ export function getFieldEntries(context: IWorkflowNodeContext): FieldValueOption
|
|||
}
|
||||
throw new NodeOperationError(context.getNode(), result);
|
||||
}
|
||||
|
||||
export function getWorkflowInputData(
|
||||
this: IExecuteFunctions,
|
||||
inputData: INodeExecutionData[],
|
||||
newParams: FieldValueOption[],
|
||||
): INodeExecutionData[] {
|
||||
const items: INodeExecutionData[] = [];
|
||||
|
||||
for (const [itemIndex, item] of inputData.entries()) {
|
||||
const attemptToConvertTypes = this.getNodeParameter(
|
||||
`${INPUT_OPTIONS}.attemptToConvertTypes`,
|
||||
itemIndex,
|
||||
false,
|
||||
);
|
||||
const ignoreTypeErrors = this.getNodeParameter(
|
||||
`${INPUT_OPTIONS}.ignoreTypeErrors`,
|
||||
itemIndex,
|
||||
false,
|
||||
);
|
||||
|
||||
// Fields listed here will explicitly overwrite original fields
|
||||
const newItem: INodeExecutionData = {
|
||||
json: {},
|
||||
index: itemIndex,
|
||||
// TODO: Ensure we handle sub-execution jumps correctly.
|
||||
// metadata: {
|
||||
// subExecution: {
|
||||
// executionId: 'uhh',
|
||||
// workflowId: 'maybe?',
|
||||
// },
|
||||
// },
|
||||
pairedItem: { item: itemIndex },
|
||||
};
|
||||
try {
|
||||
for (const { name, type } of newParams) {
|
||||
if (!item.json.hasOwnProperty(name)) {
|
||||
newItem.json[name] = DEFAULT_PLACEHOLDER;
|
||||
continue;
|
||||
}
|
||||
|
||||
const result =
|
||||
type === 'any'
|
||||
? ({ valid: true, newValue: item.json[name] } as const)
|
||||
: validateFieldType(name, item.json[name], type, {
|
||||
strict: !attemptToConvertTypes,
|
||||
parseStrings: true, // Default behavior is to accept anything as a string, this is a good opportunity for a stricter boundary
|
||||
});
|
||||
|
||||
if (!result.valid) {
|
||||
if (ignoreTypeErrors) {
|
||||
newItem.json[name] = item.json[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new NodeOperationError(this.getNode(), result.errorMessage, {
|
||||
itemIndex,
|
||||
});
|
||||
} else {
|
||||
// If the value is `null` or `undefined`, then `newValue` is not in the returned object
|
||||
if (result.hasOwnProperty('newValue')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
newItem.json[name] = result.newValue;
|
||||
} else {
|
||||
newItem.json[name] = item.json[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items.push(newItem);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
/** todo error case? */
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), error, {
|
||||
itemIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue