mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 03:12:15 +00:00
feat(editor): Display schema preview for unexecuted nodes (#12901)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { ref } from 'vue';
|
||||
import type { Optional, Primitives, Schema, INodeUi } from '@/Interface';
|
||||
import type { Optional, Primitives, Schema, INodeUi, SchemaType } from '@/Interface';
|
||||
import {
|
||||
type ITaskDataConnections,
|
||||
type IDataObject,
|
||||
@@ -13,6 +13,8 @@ import { isObj } from '@/utils/typeGuards';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { isPresent, shorten } from '@/utils/typesUtils';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import type { JSONSchema7, JSONSchema7Definition, JSONSchema7TypeName } from 'json-schema';
|
||||
import { isObject } from '@/utils/objectUtils';
|
||||
|
||||
export function useDataSchema() {
|
||||
function getSchema(
|
||||
@@ -67,6 +69,58 @@ export function useDataSchema() {
|
||||
return getSchema(merge({}, head, ...tail, head), undefined, excludeValues);
|
||||
}
|
||||
|
||||
function getSchemaForJsonSchema(schema: JSONSchema7 | JSONSchema7Definition, path = ''): Schema {
|
||||
if (typeof schema !== 'object') {
|
||||
return {
|
||||
type: 'null',
|
||||
path,
|
||||
value: 'null',
|
||||
};
|
||||
}
|
||||
if (schema.type === 'array') {
|
||||
return {
|
||||
type: 'array',
|
||||
value: isObject(schema.items)
|
||||
? [{ ...getSchemaForJsonSchema(schema.items, `${path}[0]`), key: '0' }]
|
||||
: [],
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
if (schema.type === 'object') {
|
||||
const properties = schema.properties ?? {};
|
||||
const value = Object.entries(properties).map(([key, propSchema]) => {
|
||||
const newPath = path ? `${path}.${key}` : `.${key}`;
|
||||
const transformed = getSchemaForJsonSchema(propSchema, newPath);
|
||||
return { ...transformed, key };
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'object',
|
||||
value,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
||||
return {
|
||||
type: JsonSchemaTypeToSchemaType(type),
|
||||
value: '',
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
function JsonSchemaTypeToSchemaType(type: JSONSchema7TypeName | undefined): SchemaType {
|
||||
switch (type) {
|
||||
case undefined:
|
||||
return 'undefined';
|
||||
case 'integer':
|
||||
return 'number';
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the data of the main input
|
||||
function getMainInputData(
|
||||
connectionsData: ITaskDataConnections,
|
||||
@@ -164,6 +218,7 @@ export function useDataSchema() {
|
||||
return {
|
||||
getSchema,
|
||||
getSchemaForExecutionData,
|
||||
getSchemaForJsonSchema,
|
||||
getNodeInputData,
|
||||
getInputDataWithPinned,
|
||||
filterSchema,
|
||||
@@ -177,6 +232,7 @@ export type SchemaNode = {
|
||||
connectedOutputIndexes: number[];
|
||||
itemsCount: number;
|
||||
schema: Schema;
|
||||
preview: boolean;
|
||||
};
|
||||
|
||||
export type RenderItem = {
|
||||
@@ -190,6 +246,7 @@ export type RenderItem = {
|
||||
icon: string;
|
||||
collapsable?: boolean;
|
||||
nodeType?: INodeUi['type'];
|
||||
preview?: boolean;
|
||||
type: 'item';
|
||||
};
|
||||
|
||||
@@ -201,6 +258,7 @@ export type RenderHeader = {
|
||||
nodeType: INodeTypeDescription;
|
||||
itemCount: number | null;
|
||||
type: 'header';
|
||||
preview?: boolean;
|
||||
};
|
||||
|
||||
type Renders = RenderHeader | RenderItem;
|
||||
@@ -227,6 +285,15 @@ const emptyItem = (): RenderItem => ({
|
||||
type: 'item',
|
||||
});
|
||||
|
||||
const dummyItem = (): RenderItem => ({
|
||||
id: `dummy-${window.crypto.randomUUID()}`,
|
||||
icon: '',
|
||||
level: 1,
|
||||
title: '...',
|
||||
type: 'item',
|
||||
preview: true,
|
||||
});
|
||||
|
||||
const isDataEmpty = (schema: Schema) => {
|
||||
// Utilize the generated schema instead of looping over the entire data again
|
||||
// The schema for empty data is { type: 'object', value: [] }
|
||||
@@ -264,12 +331,14 @@ export const useFlattenSchema = () => {
|
||||
depth = 0,
|
||||
prefix = '',
|
||||
level = 0,
|
||||
preview,
|
||||
}: {
|
||||
schema: Schema;
|
||||
node?: { name: string; type: string };
|
||||
depth?: number;
|
||||
prefix?: string;
|
||||
level?: number;
|
||||
preview?: boolean;
|
||||
}): RenderItem[] => {
|
||||
// only show empty item for the first level
|
||||
if (isDataEmpty(schema) && depth <= 0) {
|
||||
@@ -299,6 +368,7 @@ export const useFlattenSchema = () => {
|
||||
collapsable: true,
|
||||
nodeType: node.type,
|
||||
type: 'item',
|
||||
preview,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,6 +386,7 @@ export const useFlattenSchema = () => {
|
||||
depth,
|
||||
prefix: itemPrefix,
|
||||
level: level + 1,
|
||||
preview,
|
||||
});
|
||||
})
|
||||
.flat(),
|
||||
@@ -334,6 +405,7 @@ export const useFlattenSchema = () => {
|
||||
collapsable: false,
|
||||
nodeType: node.type,
|
||||
type: 'item',
|
||||
preview,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -356,6 +428,7 @@ export const useFlattenSchema = () => {
|
||||
itemCount: item.itemsCount,
|
||||
info: additionalInfo(item.node),
|
||||
type: 'header',
|
||||
preview: item.preview,
|
||||
});
|
||||
|
||||
headerIds.value.add(item.node.name);
|
||||
@@ -370,9 +443,20 @@ export const useFlattenSchema = () => {
|
||||
}
|
||||
|
||||
acc.push(...flattenSchema(item));
|
||||
|
||||
if (item.preview) {
|
||||
acc.push(dummyItem());
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
return { closedNodes, toggleLeaf, toggleNode, flattenSchema, flattenMultipleSchemas };
|
||||
return {
|
||||
closedNodes,
|
||||
toggleLeaf,
|
||||
toggleNode,
|
||||
flattenSchema,
|
||||
flattenMultipleSchemas,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user