mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
extract workflow inputs to generic function
This commit is contained in:
parent
4ae18fa25f
commit
dacd1907f3
|
@ -95,7 +95,7 @@ export class DynamicNodeParametersController {
|
|||
|
||||
@Post('/local-resource-mapper-fields')
|
||||
async getLocalResourceMappingFields(req: DynamicNodeParametersRequest.ResourceMapperFields) {
|
||||
const { path, methodName, credentials, currentNodeParameters, nodeTypeAndVersion } = req.body;
|
||||
const { path, methodName, currentNodeParameters, nodeTypeAndVersion } = req.body;
|
||||
|
||||
if (!methodName) throw new BadRequestError('Missing `methodName` in request body');
|
||||
|
||||
|
@ -106,8 +106,6 @@ export class DynamicNodeParametersController {
|
|||
path,
|
||||
additionalData,
|
||||
nodeTypeAndVersion,
|
||||
currentNodeParameters,
|
||||
credentials,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -193,8 +193,6 @@ export class DynamicNodeParametersService {
|
|||
path: string,
|
||||
additionalData: IWorkflowExecuteAdditionalData,
|
||||
nodeTypeAndVersion: INodeTypeNameVersion,
|
||||
currentNodeParameters: INodeParameters,
|
||||
credentials?: INodeCredentials,
|
||||
): Promise<ResourceMapperFields> {
|
||||
const nodeType = this.getNodeType(nodeTypeAndVersion);
|
||||
const method = this.getMethod('localResourceMapping', methodName, nodeType);
|
||||
|
|
|
@ -5,8 +5,8 @@ import type {
|
|||
IWorkflowExecuteAdditionalData,
|
||||
NodeParameterValueType,
|
||||
ILocalLoadOptionsFunctions,
|
||||
FieldValueOption,
|
||||
IWorkflowLoader,
|
||||
INode,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
|
||||
|
@ -16,7 +16,7 @@ export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
|
|||
private workflowLoader: IWorkflowLoader,
|
||||
) {}
|
||||
|
||||
async getWorkflowInputValues(): Promise<FieldValueOption[]> {
|
||||
async getExecuteWorkflowTriggerNode(): Promise<INode | undefined> {
|
||||
const { value } = this.getCurrentNodeParameter('workflowId') as INodeParameterResourceLocator;
|
||||
|
||||
const workflowId = value as string;
|
||||
|
@ -26,17 +26,7 @@ export class LocalLoadOptionsContext implements ILocalLoadOptionsFunctions {
|
|||
|
||||
const workflow = await this.workflowLoader.get(workflowId);
|
||||
|
||||
workflow.nodes.find((node) => node.type === 'n8n-nodes-base.start');
|
||||
|
||||
// TODO: load the inputs from the workflow
|
||||
const dummyFields = [
|
||||
{ name: 'field1', type: 'string' as const },
|
||||
{ name: 'field2', type: 'number' as const },
|
||||
{ name: 'field3', type: 'boolean' as const },
|
||||
{ name: 'field4', type: 'any' as const },
|
||||
];
|
||||
|
||||
return dummyFields;
|
||||
return workflow.nodes.find((node) => node.type === 'n8n-nodes-base.executeWorkflowTrigger');
|
||||
}
|
||||
|
||||
getCurrentNodeParameter(parameterPath: string): NodeParameterValueType | object | undefined {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type {
|
||||
FieldValueOption,
|
||||
ILocalLoadOptionsFunctions,
|
||||
INode,
|
||||
ResourceMapperField,
|
||||
ResourceMapperFields,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -8,7 +8,16 @@ import type {
|
|||
export async function getWorkflowInputs(
|
||||
this: ILocalLoadOptionsFunctions,
|
||||
): Promise<ResourceMapperFields> {
|
||||
const workflowInputFields = (await this.getWorkflowInputValues()) as FieldValueOption[];
|
||||
const executeWorkflowTriggerNode = (await this.getExecuteWorkflowTriggerNode()) as INode;
|
||||
|
||||
// const fieldValues = getFieldEntries(executeWorkflowTriggerNode);
|
||||
|
||||
const workflowInputFields = [
|
||||
{ name: 'field1', type: 'string' as const },
|
||||
{ name: 'field2', type: 'number' as const },
|
||||
{ name: 'field3', type: 'boolean' as const },
|
||||
{ name: 'field4', type: 'any' as const },
|
||||
];
|
||||
|
||||
const fields: ResourceMapperField[] = workflowInputFields.map((currentWorkflowInput) => {
|
||||
const field: ResourceMapperField = {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { json as generateSchemaFromExample, type SchemaObject } from 'generate-schema';
|
||||
import type { JSONSchema7 } from 'json-schema';
|
||||
import {
|
||||
type INodeExecutionData,
|
||||
NodeConnectionType,
|
||||
|
@ -8,113 +6,18 @@ import {
|
|||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
validateFieldType,
|
||||
type FieldType,
|
||||
jsonParse,
|
||||
FieldValueOption,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const INPUT_SOURCE = 'inputSource';
|
||||
const WORKFLOW_INPUTS = 'workflowInputs';
|
||||
const INPUT_OPTIONS = 'inputOptions';
|
||||
const VALUES = 'values';
|
||||
const JSON_EXAMPLE = 'jsonExample';
|
||||
const TYPE_OPTIONS: Array<{ name: string; value: FieldType | 'any' }> = [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
// Intentional omission of `dateTime`, `time`, `string-alphanumeric`, `form-fields`, `jwt` and `url`
|
||||
];
|
||||
const SUPPORTED_TYPES = TYPE_OPTIONS.map((x) => x.value);
|
||||
|
||||
const DEFAULT_PLACEHOLDER = null;
|
||||
|
||||
function parseJsonSchema(schema: JSONSchema7): FieldValueOption[] | string {
|
||||
if (!schema?.properties) {
|
||||
return 'Invalid JSON schema. Missing key `properties` in schema';
|
||||
}
|
||||
|
||||
if (typeof schema.properties !== 'object') {
|
||||
return 'Invalid JSON schema. Key `properties` is not an object';
|
||||
}
|
||||
|
||||
const result: FieldValueOption[] = [];
|
||||
for (const [name, v] of Object.entries(schema.properties)) {
|
||||
if (typeof v !== 'object') {
|
||||
return `Invalid JSON schema. Value for property '${name}' is not an object`;
|
||||
}
|
||||
|
||||
const type = v?.type;
|
||||
|
||||
if (type === 'null') {
|
||||
result.push({ name, type: 'any' });
|
||||
} else if (Array.isArray(type)) {
|
||||
// Schema allows an array of types, but we don't
|
||||
return `Invalid JSON schema. Array of types for property '${name}' is not supported by n8n. Either provide a single type or use type 'any' to allow any type`;
|
||||
} else if (typeof type !== 'string') {
|
||||
return `Invalid JSON schema. Unexpected non-string type ${type} for property '${name}'`;
|
||||
} else if (!SUPPORTED_TYPES.includes(type as never)) {
|
||||
return `Invalid JSON schema. Unsupported type ${type} for property '${name}'. Supported types are ${JSON.stringify(SUPPORTED_TYPES, null, 1)}`;
|
||||
} else {
|
||||
result.push({ name, type: type as FieldType });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJsonExample(context: IExecuteFunctions): JSONSchema7 {
|
||||
const jsonString = context.getNodeParameter(JSON_EXAMPLE, 0, '') as string;
|
||||
const json = jsonParse<SchemaObject>(jsonString);
|
||||
|
||||
return generateSchemaFromExample(json) as JSONSchema7;
|
||||
}
|
||||
|
||||
function getFieldEntries(context: IExecuteFunctions): FieldValueOption[] {
|
||||
const inputSource = context.getNodeParameter(INPUT_SOURCE, 0) as string;
|
||||
let result: FieldValueOption[] | string = 'Internal Error: Invalid input source';
|
||||
try {
|
||||
if (inputSource === WORKFLOW_INPUTS) {
|
||||
result = context.getNodeParameter(
|
||||
`${WORKFLOW_INPUTS}.${VALUES}`,
|
||||
0,
|
||||
[],
|
||||
) as FieldValueOption[];
|
||||
} else if (inputSource === JSON_EXAMPLE) {
|
||||
const schema = parseJsonExample(context);
|
||||
result = parseJsonSchema(schema);
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
result =
|
||||
e && typeof e === 'object' && 'message' in e && typeof e.message === 'string'
|
||||
? e.message
|
||||
: `Unknown error occurred: ${JSON.stringify(e)}`;
|
||||
}
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
throw new NodeOperationError(context.getNode(), result);
|
||||
}
|
||||
import {
|
||||
INPUT_SOURCE,
|
||||
WORKFLOW_INPUTS,
|
||||
JSON_EXAMPLE,
|
||||
VALUES,
|
||||
INPUT_OPTIONS,
|
||||
DEFAULT_PLACEHOLDER,
|
||||
TYPE_OPTIONS,
|
||||
} from '../constants';
|
||||
import { getFieldEntries } from '../GenericFunctions';
|
||||
|
||||
export class ExecuteWorkflowTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import { json as generateSchemaFromExample, type SchemaObject } from 'generate-schema';
|
||||
import type { JSONSchema7 } from 'json-schema';
|
||||
import type { FieldValueOption, FieldType, IExecuteFunctions } from 'n8n-workflow';
|
||||
import { jsonParse, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { JSON_EXAMPLE, INPUT_SOURCE, WORKFLOW_INPUTS, VALUES, TYPE_OPTIONS } from './constants';
|
||||
|
||||
const SUPPORTED_TYPES = TYPE_OPTIONS.map((x) => x.value);
|
||||
|
||||
function parseJsonSchema(schema: JSONSchema7): FieldValueOption[] | string {
|
||||
if (!schema?.properties) {
|
||||
return 'Invalid JSON schema. Missing key `properties` in schema';
|
||||
}
|
||||
|
||||
if (typeof schema.properties !== 'object') {
|
||||
return 'Invalid JSON schema. Key `properties` is not an object';
|
||||
}
|
||||
|
||||
const result: FieldValueOption[] = [];
|
||||
for (const [name, v] of Object.entries(schema.properties)) {
|
||||
if (typeof v !== 'object') {
|
||||
return `Invalid JSON schema. Value for property '${name}' is not an object`;
|
||||
}
|
||||
|
||||
const type = v?.type;
|
||||
|
||||
if (type === 'null') {
|
||||
result.push({ name, type: 'any' });
|
||||
} else if (Array.isArray(type)) {
|
||||
// Schema allows an array of types, but we don't
|
||||
return `Invalid JSON schema. Array of types for property '${name}' is not supported by n8n. Either provide a single type or use type 'any' to allow any type`;
|
||||
} else if (typeof type !== 'string') {
|
||||
return `Invalid JSON schema. Unexpected non-string type ${type} for property '${name}'`;
|
||||
} else if (!SUPPORTED_TYPES.includes(type as never)) {
|
||||
return `Invalid JSON schema. Unsupported type ${type} for property '${name}'. Supported types are ${JSON.stringify(SUPPORTED_TYPES, null, 1)}`;
|
||||
} else {
|
||||
result.push({ name, type: type as FieldType });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJsonExample(context: IExecuteFunctions): JSONSchema7 {
|
||||
const jsonString = context.getNodeParameter(JSON_EXAMPLE, 0, '') as string;
|
||||
const json = jsonParse<SchemaObject>(jsonString);
|
||||
|
||||
return generateSchemaFromExample(json) as JSONSchema7;
|
||||
}
|
||||
|
||||
export function getFieldEntries(context: IExecuteFunctions): FieldValueOption[] {
|
||||
const inputSource = context.getNodeParameter(INPUT_SOURCE, 0);
|
||||
let result: FieldValueOption[] | string = 'Internal Error: Invalid input source';
|
||||
try {
|
||||
if (inputSource === WORKFLOW_INPUTS) {
|
||||
result = context.getNodeParameter(
|
||||
`${WORKFLOW_INPUTS}.${VALUES}`,
|
||||
0,
|
||||
[],
|
||||
) as FieldValueOption[];
|
||||
} else if (inputSource === JSON_EXAMPLE) {
|
||||
const schema = parseJsonExample(context);
|
||||
result = parseJsonSchema(schema);
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
result =
|
||||
e && typeof e === 'object' && 'message' in e && typeof e.message === 'string'
|
||||
? e.message
|
||||
: `Unknown error occurred: ${JSON.stringify(e)}`;
|
||||
}
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
throw new NodeOperationError(context.getNode(), result);
|
||||
}
|
36
packages/nodes-base/nodes/ExecuteWorkflow/constants.ts
Normal file
36
packages/nodes-base/nodes/ExecuteWorkflow/constants.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import type { FieldType } from 'n8n-workflow';
|
||||
|
||||
export const INPUT_SOURCE = 'inputSource';
|
||||
export const WORKFLOW_INPUTS = 'workflowInputs';
|
||||
export const INPUT_OPTIONS = 'inputOptions';
|
||||
export const VALUES = 'values';
|
||||
export const JSON_EXAMPLE = 'jsonExample';
|
||||
export const TYPE_OPTIONS: Array<{ name: string; value: FieldType | 'any' }> = [
|
||||
{
|
||||
name: 'Allow Any Type',
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
name: 'String',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
name: 'Number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
name: 'Boolean',
|
||||
value: 'boolean',
|
||||
},
|
||||
{
|
||||
name: 'Array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
name: 'Object',
|
||||
value: 'object',
|
||||
},
|
||||
// Intentional omission of `dateTime`, `time`, `string-alphanumeric`, `form-fields`, `jwt` and `url`
|
||||
];
|
||||
|
||||
export const DEFAULT_PLACEHOLDER = null;
|
|
@ -1073,7 +1073,7 @@ export interface ILoadOptionsFunctions extends FunctionsBase {
|
|||
export type FieldValueOption = { name: string; type: FieldType | 'any' };
|
||||
|
||||
export interface ILocalLoadOptionsFunctions {
|
||||
getWorkflowInputValues(): Promise<FieldValueOption[]>;
|
||||
getExecuteWorkflowTriggerNode(): Promise<INode | undefined>;
|
||||
}
|
||||
|
||||
export interface IWorkflowLoader {
|
||||
|
|
Loading…
Reference in a new issue