mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Fix type errors for various utils files (no-changelog) (#9480)
This commit is contained in:
@@ -130,6 +130,14 @@ export type EndpointStyle = {
|
||||
hoverMessage?: string;
|
||||
};
|
||||
|
||||
export type EndpointMeta = {
|
||||
__meta?: {
|
||||
index: number;
|
||||
totalEndpoints: number;
|
||||
endpointLabelLength: number;
|
||||
};
|
||||
};
|
||||
|
||||
export interface IUpdateInformation<
|
||||
T extends NodeParameterValueType =
|
||||
| string
|
||||
|
||||
@@ -14,6 +14,7 @@ import { uuid } from '@jsplumb/util';
|
||||
import { defaultMockNodeTypes } from '@/__tests__/defaults';
|
||||
import type { INodeUi, ITag, IUsedCredential, IWorkflowDb, WorkflowMetadata } from '@/Interface';
|
||||
import type { ProjectSharingData } from '@/features/projects/projects.types';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
export function createTestNodeTypes(data: INodeTypeData = {}): INodeTypes {
|
||||
const getResolvedKey = (key: string) => {
|
||||
@@ -103,3 +104,27 @@ export function createTestNode(
|
||||
...node,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTestRouteLocation({
|
||||
path = '',
|
||||
params = {},
|
||||
fullPath = path,
|
||||
hash = '',
|
||||
matched = [],
|
||||
redirectedFrom = undefined,
|
||||
name = path,
|
||||
meta = {},
|
||||
query = {},
|
||||
}: Partial<RouteLocationNormalized> = {}): RouteLocationNormalized {
|
||||
return {
|
||||
path,
|
||||
params,
|
||||
fullPath,
|
||||
hash,
|
||||
matched,
|
||||
redirectedFrom,
|
||||
name,
|
||||
meta,
|
||||
query,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export type Resolvable = {
|
||||
resolved: unknown;
|
||||
state: ResolvableState;
|
||||
error: Error | null;
|
||||
fullError?: Error;
|
||||
} & Range;
|
||||
|
||||
export type Resolved = Resolvable;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import {
|
||||
inferProjectIdFromRoute,
|
||||
inferResourceTypeFromRoute,
|
||||
inferResourceIdFromRoute,
|
||||
} from '../rbacUtils';
|
||||
import { createTestRouteLocation } from '@/__tests__/mocks';
|
||||
|
||||
describe('rbacUtils', () => {
|
||||
describe('inferProjectIdFromRoute()', () => {
|
||||
it('should infer project ID from route correctly', () => {
|
||||
const route = { path: '/dashboard/projects/123/settings' } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ path: '/dashboard/projects/123/settings' });
|
||||
const projectId = inferProjectIdFromRoute(route);
|
||||
expect(projectId).toBe('123');
|
||||
});
|
||||
|
||||
it('should return undefined for project ID if not found', () => {
|
||||
const route = { path: '/dashboard/settings' } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ path: '/dashboard/settings' });
|
||||
const projectId = inferProjectIdFromRoute(route);
|
||||
expect(projectId).toBeUndefined();
|
||||
});
|
||||
@@ -29,15 +29,15 @@ describe('rbacUtils', () => {
|
||||
['/variables', 'variable'],
|
||||
['/users', 'user'],
|
||||
['/source-control', 'sourceControl'],
|
||||
['/external-secrets', 'externalSecretsStore'],
|
||||
['/external-secrets', 'externalSecret'],
|
||||
])('should infer resource type from %s correctly to %s', (path, type) => {
|
||||
const route = { path } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ path });
|
||||
const resourceType = inferResourceTypeFromRoute(route);
|
||||
expect(resourceType).toBe(type);
|
||||
});
|
||||
|
||||
it('should return undefined for resource type if not found', () => {
|
||||
const route = { path: '/dashboard/settings' } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ path: '/dashboard/settings' });
|
||||
const resourceType = inferResourceTypeFromRoute(route);
|
||||
expect(resourceType).toBeUndefined();
|
||||
});
|
||||
@@ -45,19 +45,19 @@ describe('rbacUtils', () => {
|
||||
|
||||
describe('inferResourceIdFromRoute()', () => {
|
||||
it('should infer resource ID from params.id', () => {
|
||||
const route = { params: { id: 'abc123' } } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ params: { id: 'abc123' } });
|
||||
const resourceId = inferResourceIdFromRoute(route);
|
||||
expect(resourceId).toBe('abc123');
|
||||
});
|
||||
|
||||
it('should infer resource ID from params.name if id is not present', () => {
|
||||
const route = { params: { name: 'my-resource' } } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ params: { name: 'my-resource' } });
|
||||
const resourceId = inferResourceIdFromRoute(route);
|
||||
expect(resourceId).toBe('my-resource');
|
||||
});
|
||||
|
||||
it('should return undefined for resource ID if neither id nor name is present', () => {
|
||||
const route = { params: {} } as RouteLocationNormalized;
|
||||
const route = createTestRouteLocation({ params: {} });
|
||||
const resourceId = inferResourceIdFromRoute(route);
|
||||
expect(resourceId).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import type { SourceControlStatus } from '@/Interface';
|
||||
import { beforeEach } from 'vitest';
|
||||
import { aggregateSourceControlFiles } from '@/utils/sourceControlUtils';
|
||||
|
||||
describe('sourceControlUtils', () => {
|
||||
describe('aggregateSourceControlFiles()', () => {
|
||||
let status: SourceControlStatus;
|
||||
|
||||
beforeEach(() => {
|
||||
status = {
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
conflicted: [],
|
||||
created: [],
|
||||
current: 'main',
|
||||
deleted: [],
|
||||
detached: false,
|
||||
files: [],
|
||||
modified: [],
|
||||
not_added: [],
|
||||
renamed: [],
|
||||
staged: [],
|
||||
tracking: null,
|
||||
};
|
||||
});
|
||||
|
||||
it('should be empty array if no files', () => {
|
||||
expect(aggregateSourceControlFiles(status)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should contain list of conflicted, created, deleted, modified, and renamed files', () => {
|
||||
status.files = [
|
||||
{ path: 'conflicted.json', index: 'A', working_dir: '' },
|
||||
{ path: 'created.json', index: 'A', working_dir: '' },
|
||||
{ path: 'deleted.json', index: 'A', working_dir: '' },
|
||||
{ path: 'modified.json', index: 'A', working_dir: '' },
|
||||
{ path: 'renamed.json', index: 'A', working_dir: '' },
|
||||
];
|
||||
|
||||
status.conflicted.push('conflicted.json');
|
||||
status.created.push('created.json');
|
||||
status.deleted.push('deleted.json');
|
||||
status.modified.push('modified.json');
|
||||
status.renamed.push('renamed.json');
|
||||
status.staged = status.files.map((file) => file.path);
|
||||
|
||||
expect(aggregateSourceControlFiles(status)).toEqual([
|
||||
{ path: 'conflicted.json', status: 'conflicted', staged: true },
|
||||
{ path: 'created.json', status: 'created', staged: true },
|
||||
{ path: 'deleted.json', status: 'deleted', staged: true },
|
||||
{ path: 'modified.json', status: 'modified', staged: true },
|
||||
{ path: 'renamed.json', status: 'renamed', staged: true },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import type { IZoomConfig } from '@/Interface';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { ConnectionDetachedParams } from '@jsplumb/core';
|
||||
import type { IConnection } from 'n8n-workflow';
|
||||
import type { Route } from 'vue-router';
|
||||
import type { RouteLocation } from 'vue-router';
|
||||
|
||||
/*
|
||||
Constants and utility functions mainly used by canvas store
|
||||
@@ -52,7 +52,7 @@ export const scaleReset = (config: IZoomConfig): IZoomConfig => {
|
||||
return applyScale(1 / config.scale)(config);
|
||||
};
|
||||
|
||||
export const getNodeViewTab = (route: Route): string | null => {
|
||||
export const getNodeViewTab = (route: RouteLocation): string | null => {
|
||||
if (route.meta?.nodeView) {
|
||||
return MAIN_HEADER_TABS.WORKFLOW;
|
||||
} else if (
|
||||
|
||||
@@ -143,8 +143,8 @@ export const getMainAuthField = (nodeType: INodeTypeDescription | null): INodePr
|
||||
credentialDependencies.find(
|
||||
(prop) =>
|
||||
prop.name === MAIN_AUTH_FIELD_NAME &&
|
||||
!prop.options?.find((option) => option.value === 'none'),
|
||||
) || null;
|
||||
!prop.options?.find((option) => 'value' in option && option.value === 'none'),
|
||||
) ?? null;
|
||||
// If there is a field name `authentication`, use it
|
||||
// Otherwise, try to find alternative main auth field
|
||||
const mainAuthFiled =
|
||||
@@ -166,7 +166,7 @@ const findAlternativeAuthField = (
|
||||
if (cred.displayOptions?.show) {
|
||||
for (const fieldName in cred.displayOptions.show) {
|
||||
dependentAuthFieldValues[fieldName] = (dependentAuthFieldValues[fieldName] || []).concat(
|
||||
(cred.displayOptions.show[fieldName] || []).map((val) => (val ? val.toString() : '')),
|
||||
(cred.displayOptions.show[fieldName] ?? []).map((val) => (val ? val.toString() : '')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,11 @@ const findAlternativeAuthField = (
|
||||
const alternativeAuthField = fields.find((field) => {
|
||||
let required = true;
|
||||
field.options?.forEach((option) => {
|
||||
if (!dependentAuthFieldValues[field.name].includes(option.value)) {
|
||||
if (
|
||||
'value' in option &&
|
||||
typeof option.value === 'string' &&
|
||||
!dependentAuthFieldValues[field.name].includes(option.value)
|
||||
) {
|
||||
required = false;
|
||||
}
|
||||
});
|
||||
@@ -206,21 +210,24 @@ export const getNodeAuthOptions = (
|
||||
if (field.options) {
|
||||
options = options.concat(
|
||||
field.options.map((option) => {
|
||||
const optionValue = 'value' in option ? `${option.value}` : '';
|
||||
|
||||
// Check if credential type associated with this auth option has overwritten properties
|
||||
let hasOverrides = false;
|
||||
const cred = getNodeCredentialForSelectedAuthType(nodeType, option.value);
|
||||
const cred = getNodeCredentialForSelectedAuthType(nodeType, optionValue);
|
||||
if (cred) {
|
||||
hasOverrides =
|
||||
useCredentialsStore().getCredentialTypeByName(cred.name)?.__overwrittenProperties !==
|
||||
undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
name:
|
||||
// Add recommended suffix if credentials have overrides and option is not already recommended
|
||||
hasOverrides && !option.name.endsWith(recommendedSuffix)
|
||||
? `${option.name} ${recommendedSuffix}`
|
||||
: option.name,
|
||||
value: option.value,
|
||||
value: optionValue,
|
||||
// Also add in the display options so we can hide/show the option if necessary
|
||||
displayOptions: field.displayOptions,
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { isNumber } from '@/utils/typeGuards';
|
||||
import { isNumber, isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
import { NODE_OUTPUT_DEFAULT_KEY, STICKY_NODE_TYPE } from '@/constants';
|
||||
import type { EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
||||
import type { EndpointMeta, EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
||||
import type { ArrayAnchorSpec, ConnectorSpec, OverlaySpec, PaintStyle } from '@jsplumb/common';
|
||||
import type { Endpoint, Connection } from '@jsplumb/core';
|
||||
import type { Connection, Endpoint, SelectOptions } from '@jsplumb/core';
|
||||
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
||||
import type {
|
||||
ConnectionTypes,
|
||||
IConnection,
|
||||
ITaskData,
|
||||
INodeExecutionData,
|
||||
NodeInputConnections,
|
||||
INodeTypeDescription,
|
||||
ITaskData,
|
||||
NodeInputConnections,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
|
||||
@@ -72,13 +72,15 @@ export const CONNECTOR_FLOWCHART_TYPE: ConnectorSpec = {
|
||||
alwaysRespectStubs: false,
|
||||
loopbackVerticalLength: NODE_SIZE + GRID_SIZE, // height of vertical segment when looping
|
||||
loopbackMinimum: LOOPBACK_MINIMUM, // minimum length before flowchart loops around
|
||||
getEndpointOffset(endpoint: Endpoint) {
|
||||
getEndpointOffset(endpoint: Endpoint & EndpointMeta) {
|
||||
const indexOffset = 10; // stub offset between different endpoints of same node
|
||||
const index = endpoint?.__meta ? endpoint.__meta.index : 0;
|
||||
const totalEndpoints = endpoint?.__meta ? endpoint.__meta.totalEndpoints : 0;
|
||||
|
||||
const outputOverlay = getOverlay(endpoint, OVERLAY_OUTPUT_NAME_LABEL);
|
||||
const labelOffset = outputOverlay?.label && outputOverlay.label.length > 1 ? 10 : 0;
|
||||
const outputOverlayLabel =
|
||||
outputOverlay && 'label' in outputOverlay ? `${outputOverlay?.label}` : '';
|
||||
const labelOffset = outputOverlayLabel.length > 1 ? 10 : 0;
|
||||
const outputsOffset = totalEndpoints > 3 ? 24 : 0; // avoid intersecting plus
|
||||
|
||||
return index * indexOffset + labelOffset + outputsOffset;
|
||||
@@ -111,6 +113,10 @@ export const CONNECTOR_PAINT_STYLE_DATA: PaintStyle = {
|
||||
stroke: 'var(--color-foreground-dark)',
|
||||
};
|
||||
|
||||
export function isCanvasAugmentedType<T>(overlay: T): overlay is T & { canvas: HTMLElement } {
|
||||
return typeof overlay === 'object' && overlay !== null && 'canvas' in overlay && !!overlay.canvas;
|
||||
}
|
||||
|
||||
export const getConnectorColor = (type: ConnectionTypes, category?: string): string => {
|
||||
if (category === 'error') {
|
||||
return '--color-node-error-output-text-color';
|
||||
@@ -228,15 +234,18 @@ export const getAnchorPosition = (
|
||||
return returnPositions;
|
||||
};
|
||||
|
||||
export const getScope = (type?: string) => {
|
||||
export const getScope = (type?: NodeConnectionType): NodeConnectionType | undefined => {
|
||||
if (!type || type === NodeConnectionType.Main) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
export const getEndpointScope = (endpointType: ConnectionTypes): string | undefined => {
|
||||
if (Object.values(NodeConnectionType).includes(endpointType)) {
|
||||
export const getEndpointScope = (
|
||||
endpointType: NodeConnectionType | string,
|
||||
): NodeConnectionType | undefined => {
|
||||
if (isValidNodeConnectionType(endpointType)) {
|
||||
return getScope(endpointType);
|
||||
}
|
||||
|
||||
@@ -276,7 +285,7 @@ export const getInputNameOverlay = (
|
||||
id: OVERLAY_INPUT_NAME_LABEL,
|
||||
visible: true,
|
||||
location: [-1, -1],
|
||||
create: (component: Endpoint) => {
|
||||
create: (_: Endpoint) => {
|
||||
const label = document.createElement('div');
|
||||
label.innerHTML = labelText;
|
||||
if (required) {
|
||||
@@ -303,20 +312,20 @@ export const getOutputEndpointStyle = (
|
||||
|
||||
export const getOutputNameOverlay = (
|
||||
labelText: string,
|
||||
outputName: ConnectionTypes,
|
||||
outputName: NodeConnectionType,
|
||||
category?: string,
|
||||
): OverlaySpec => ({
|
||||
type: 'Custom',
|
||||
options: {
|
||||
id: OVERLAY_OUTPUT_NAME_LABEL,
|
||||
visible: true,
|
||||
create: (ep: Endpoint) => {
|
||||
create: (ep: Endpoint & EndpointMeta) => {
|
||||
const label = document.createElement('div');
|
||||
label.innerHTML = labelText;
|
||||
label.classList.add('node-output-endpoint-label');
|
||||
|
||||
if (ep?.__meta?.endpointLabelLength) {
|
||||
label.setAttribute('data-endpoint-label-length', ep?.__meta?.endpointLabelLength);
|
||||
label.setAttribute('data-endpoint-label-length', `${ep?.__meta?.endpointLabelLength}`);
|
||||
}
|
||||
label.classList.add(`node-connection-type-${getScope(outputName) ?? 'main'}`);
|
||||
if (outputName !== NodeConnectionType.Main) {
|
||||
@@ -438,8 +447,11 @@ export const showOrHideMidpointArrow = (connection: Connection) => {
|
||||
if (arrow) {
|
||||
arrow.setVisible(isArrowVisible);
|
||||
arrow.setLocation(hasItemsLabel ? 0.6 : 0.5);
|
||||
|
||||
if (isCanvasAugmentedType(arrow)) {
|
||||
connection.instance.repaint(arrow.canvas);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getConnectorLengths = (connection: Connection): [number, number] => {
|
||||
@@ -481,7 +493,9 @@ export const showOrHideItemsLabel = (connection: Connection) => {
|
||||
const isHidden = diffX < MIN_X_TO_SHOW_OUTPUT_LABEL && diffY < MIN_Y_TO_SHOW_OUTPUT_LABEL;
|
||||
|
||||
overlay.setVisible(!isHidden);
|
||||
const innerElement = overlay.canvas?.querySelector('span');
|
||||
const innerElement = isCanvasAugmentedType(overlay)
|
||||
? overlay.canvas.querySelector('span')
|
||||
: undefined;
|
||||
if (innerElement) {
|
||||
if (diffY === 0 || isLoopingBackwards(connection)) {
|
||||
innerElement.classList.add('floating');
|
||||
@@ -812,8 +826,11 @@ export const addClassesToOverlays = ({
|
||||
overlayIds.forEach((overlayId) => {
|
||||
const overlay = getOverlay(connection, overlayId);
|
||||
|
||||
overlay?.canvas?.classList.add(...classNames);
|
||||
if (includeConnector) {
|
||||
if (overlay && isCanvasAugmentedType(overlay)) {
|
||||
overlay.canvas?.classList.add(...classNames);
|
||||
}
|
||||
|
||||
if (includeConnector && isCanvasAugmentedType(connection.connector)) {
|
||||
connection.connector.canvas?.classList.add(...classNames);
|
||||
}
|
||||
});
|
||||
@@ -995,18 +1012,18 @@ export const addConnectionActionsOverlay = (
|
||||
|
||||
export const getOutputEndpointUUID = (
|
||||
nodeId: string,
|
||||
connectionType: ConnectionTypes,
|
||||
connectionType: NodeConnectionType,
|
||||
outputIndex: number,
|
||||
) => {
|
||||
return `${nodeId}${OUTPUT_UUID_KEY}${getScope(connectionType) || ''}${outputIndex}`;
|
||||
return `${nodeId}${OUTPUT_UUID_KEY}${getScope(connectionType) ?? ''}${outputIndex}`;
|
||||
};
|
||||
|
||||
export const getInputEndpointUUID = (
|
||||
nodeId: string,
|
||||
connectionType: ConnectionTypes,
|
||||
connectionType: NodeConnectionType,
|
||||
inputIndex: number,
|
||||
) => {
|
||||
return `${nodeId}${INPUT_UUID_KEY}${getScope(connectionType) || ''}${inputIndex}`;
|
||||
return `${nodeId}${INPUT_UUID_KEY}${getScope(connectionType) ?? ''}${inputIndex}`;
|
||||
};
|
||||
|
||||
export const getFixedNodesList = <T extends { position: XYPosition }>(workflowNodes: T[]): T[] => {
|
||||
@@ -1089,10 +1106,10 @@ export const getJSPlumbEndpoints = (
|
||||
node: INodeUi | null,
|
||||
instance: BrowserJsPlumbInstance,
|
||||
): Endpoint[] => {
|
||||
const nodeEl = instance.getManagedElement(node?.id);
|
||||
if (!node) return [];
|
||||
|
||||
const endpoints = instance?.getEndpoints(nodeEl);
|
||||
return endpoints;
|
||||
const nodeEl = instance.getManagedElement(node?.id);
|
||||
return instance?.getEndpoints(nodeEl);
|
||||
};
|
||||
|
||||
export const getPlusEndpoint = (
|
||||
@@ -1102,8 +1119,7 @@ export const getPlusEndpoint = (
|
||||
): Endpoint | undefined => {
|
||||
const endpoints = getJSPlumbEndpoints(node, instance);
|
||||
return endpoints.find(
|
||||
(endpoint: Endpoint) =>
|
||||
// @ts-ignore
|
||||
(endpoint: Endpoint & EndpointMeta) =>
|
||||
endpoint.endpoint.type === 'N8nPlus' && endpoint?.__meta?.index === outputIndex,
|
||||
);
|
||||
};
|
||||
@@ -1113,7 +1129,7 @@ export const getJSPlumbConnection = (
|
||||
sourceOutputIndex: number,
|
||||
targetNode: INodeUi | null,
|
||||
targetInputIndex: number,
|
||||
connectionType: ConnectionTypes,
|
||||
connectionType: NodeConnectionType,
|
||||
sourceNodeType: INodeTypeDescription | null,
|
||||
instance: BrowserJsPlumbInstance,
|
||||
): Connection | undefined => {
|
||||
@@ -1127,17 +1143,24 @@ export const getJSPlumbConnection = (
|
||||
const sourceEndpoint = getOutputEndpointUUID(sourceId, connectionType, sourceOutputIndex);
|
||||
const targetEndpoint = getInputEndpointUUID(targetId, connectionType, targetInputIndex);
|
||||
|
||||
const sourceNodeOutput = sourceNodeType?.outputs?.[sourceOutputIndex] || NodeConnectionType.Main;
|
||||
const sourceNodeOutput = sourceNodeType?.outputs?.[sourceOutputIndex] ?? NodeConnectionType.Main;
|
||||
const sourceNodeOutputName =
|
||||
typeof sourceNodeOutput === 'string' ? sourceNodeOutput : sourceNodeOutput.name;
|
||||
typeof sourceNodeOutput === 'string'
|
||||
? sourceNodeOutput
|
||||
: 'name' in sourceNodeOutput
|
||||
? `${sourceNodeOutput.name}`
|
||||
: '';
|
||||
const scope = getEndpointScope(sourceNodeOutputName);
|
||||
|
||||
// @ts-ignore
|
||||
const connections = instance?.getConnections({
|
||||
scope,
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
}) as Connection[];
|
||||
} as SelectOptions<Element>);
|
||||
|
||||
if (!Array.isArray(connections)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return connections.find((connection: Connection) => {
|
||||
const uuids = connection.getUuids();
|
||||
|
||||
@@ -11,20 +11,24 @@ export function inferProjectIdFromRoute(to: RouteLocationNormalized): string {
|
||||
|
||||
export function inferResourceTypeFromRoute(to: RouteLocationNormalized): Resource | undefined {
|
||||
const routeParts = to.path.split('/');
|
||||
const routeMap = {
|
||||
const routeMap: Record<string, string> = {
|
||||
workflow: 'workflows',
|
||||
credential: 'credentials',
|
||||
user: 'users',
|
||||
variable: 'variables',
|
||||
sourceControl: 'source-control',
|
||||
externalSecretsStore: 'external-secrets',
|
||||
externalSecret: 'external-secrets',
|
||||
};
|
||||
|
||||
for (const resource of Object.keys(routeMap) as Array<keyof typeof routeMap>) {
|
||||
if (routeParts.includes(routeMap[resource])) {
|
||||
const isResource = (key: string): key is Resource => routeParts.includes(routeMap[key]);
|
||||
|
||||
for (const resource of Object.keys(routeMap)) {
|
||||
if (isResource(resource)) {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function inferResourceIdFromRoute(to: RouteLocationNormalized): string | undefined {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { SourceControlAggregatedFile, SourceControlStatus } from '@/Interface';
|
||||
|
||||
export function aggregateSourceControlFiles(sourceControlStatus: SourceControlStatus) {
|
||||
return sourceControlStatus.files.reduce<SourceControlAggregatedFile[]>((acc, file) => {
|
||||
const staged = sourceControlStatus.staged.includes(file.path);
|
||||
|
||||
let status = '';
|
||||
(
|
||||
['conflicted', 'created', 'deleted', 'modified', 'renamed'] as Array<
|
||||
keyof SourceControlStatus
|
||||
>
|
||||
).forEach((key) => {
|
||||
const filesForStatus = sourceControlStatus[key] as string[];
|
||||
if (filesForStatus.includes(file.path)) {
|
||||
status = key;
|
||||
}
|
||||
});
|
||||
|
||||
acc.push({
|
||||
path: file.path,
|
||||
status,
|
||||
staged,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
@@ -26,12 +26,14 @@ export function createExpressionTelemetryPayload(
|
||||
handlebar_error_count: erroringResolvables.length,
|
||||
short_errors: erroringResolvables.map((r) => r.resolved ?? null),
|
||||
full_errors: erroringResolvables.map((erroringResolvable) => {
|
||||
if (!erroringResolvable.fullError) return null;
|
||||
|
||||
if (erroringResolvable.fullError) {
|
||||
return {
|
||||
...exposeErrorProperties(erroringResolvable.fullError),
|
||||
stack: erroringResolvable.fullError.stack,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -295,17 +295,14 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
export const fullCreateApiEndpointTemplate = {
|
||||
id: 1750,
|
||||
name: 'Creating an API endpoint',
|
||||
views: 13265,
|
||||
recentViews: 9899,
|
||||
totalViews: 13265,
|
||||
createdAt: '2022-07-06T14:45:19.659Z',
|
||||
description:
|
||||
'**Task:**\nCreate a simple API endpoint using the Webhook and Respond to Webhook nodes\n\n**Why:**\nYou can prototype or replace a backend process with a single workflow\n\n**Main use cases:**\nReplace backend logic with a workflow',
|
||||
workflow: {
|
||||
meta: { instanceId: '8c8c5237b8e37b006a7adce87f4369350c58e41f3ca9de16196d3197f69eabcd' },
|
||||
nodes: [
|
||||
{
|
||||
id: 'f80aceed-b676-42aa-bf25-f7a44408b1bc',
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
position: [375, 115],
|
||||
@@ -318,7 +315,6 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: '3b9ec913-0bbe-4906-bf8e-da352b556655',
|
||||
name: 'Note1',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, -25],
|
||||
@@ -331,7 +327,6 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: '9c36dae5-0700-450c-9739-e9f3eff31bfe',
|
||||
name: 'Respond to Webhook',
|
||||
type: 'n8n-nodes-base.respondToWebhook',
|
||||
position: [815, 115],
|
||||
@@ -344,7 +339,6 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: '5a228fcb-78b9-4a28-95d2-d7c9fdf1d4ea',
|
||||
name: 'Create URL string',
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [595, 115],
|
||||
@@ -364,7 +358,6 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'e7971820-45a8-4dc8-ba4c-b3220d65307a',
|
||||
name: 'Note3',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, 275],
|
||||
@@ -383,7 +376,10 @@ export const fullCreateApiEndpointTemplate = {
|
||||
},
|
||||
},
|
||||
lastUpdatedBy: 1,
|
||||
workflowInfo: null,
|
||||
workflowInfo: {
|
||||
nodeCount: 2,
|
||||
nodeTypes: {},
|
||||
},
|
||||
user: { username: 'jon-n8n' },
|
||||
nodes: [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { INodeParameterResourceLocator } from 'n8n-workflow';
|
||||
import type { INodeParameterResourceLocator, NodeConnectionType } from 'n8n-workflow';
|
||||
import { nodeConnectionTypes } from 'n8n-workflow';
|
||||
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
|
||||
/*
|
||||
@@ -49,3 +50,9 @@ export function isDateObject(date: unknown): date is Date {
|
||||
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
|
||||
);
|
||||
}
|
||||
|
||||
export function isValidNodeConnectionType(
|
||||
connectionType: string,
|
||||
): connectionType is NodeConnectionType {
|
||||
return nodeConnectionTypes.includes(connectionType as NodeConnectionType);
|
||||
}
|
||||
|
||||
@@ -77,11 +77,8 @@ export function shorten(s: string, limit: number, keep: number) {
|
||||
export const convertPath = (path: string): string => {
|
||||
// TODO: That can for sure be done fancier but for now it works
|
||||
const placeholder = '*___~#^#~___*';
|
||||
let inBrackets = path.match(/\[(.*?)]/g);
|
||||
let inBrackets: string[] = path.match(/\[(.*?)]/g) ?? [];
|
||||
|
||||
if (inBrackets === null) {
|
||||
inBrackets = [];
|
||||
} else {
|
||||
inBrackets = inBrackets
|
||||
.map((item) => item.slice(1, -1))
|
||||
.map((item) => {
|
||||
@@ -90,7 +87,7 @@ export const convertPath = (path: string): string => {
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
const withoutBrackets = path.replace(/\[(.*?)]/g, placeholder);
|
||||
const pathParts = withoutBrackets.split('.');
|
||||
const allParts = [] as string[];
|
||||
@@ -98,7 +95,7 @@ export const convertPath = (path: string): string => {
|
||||
let index = part.indexOf(placeholder);
|
||||
while (index !== -1) {
|
||||
if (index === 0) {
|
||||
allParts.push(inBrackets!.shift() as string);
|
||||
allParts.push(inBrackets.shift() ?? '');
|
||||
part = part.substr(placeholder.length);
|
||||
} else {
|
||||
allParts.push(part.substr(0, index));
|
||||
|
||||
@@ -135,7 +135,7 @@ function getPersonalizationSurveyV2OrLater(
|
||||
|
||||
const companySize = answers[COMPANY_SIZE_KEY];
|
||||
const companyType = answers[COMPANY_TYPE_KEY];
|
||||
const automationGoal = answers[AUTOMATION_GOAL_KEY];
|
||||
const automationGoal = AUTOMATION_GOAL_KEY in answers ? answers[AUTOMATION_GOAL_KEY] : undefined;
|
||||
|
||||
let codingSkill = null;
|
||||
if (CODING_SKILL_KEY in answers && answers[CODING_SKILL_KEY]) {
|
||||
|
||||
@@ -1704,6 +1704,21 @@ export const enum NodeConnectionType {
|
||||
Main = 'main',
|
||||
}
|
||||
|
||||
export const nodeConnectionTypes: NodeConnectionType[] = [
|
||||
NodeConnectionType.AiAgent,
|
||||
NodeConnectionType.AiChain,
|
||||
NodeConnectionType.AiDocument,
|
||||
NodeConnectionType.AiEmbedding,
|
||||
NodeConnectionType.AiLanguageModel,
|
||||
NodeConnectionType.AiMemory,
|
||||
NodeConnectionType.AiOutputParser,
|
||||
NodeConnectionType.AiRetriever,
|
||||
NodeConnectionType.AiTextSplitter,
|
||||
NodeConnectionType.AiTool,
|
||||
NodeConnectionType.AiVectorStore,
|
||||
NodeConnectionType.Main,
|
||||
];
|
||||
|
||||
export interface INodeInputFilter {
|
||||
// TODO: Later add more filter options like categories, subcatogries,
|
||||
// regex, allow to exclude certain nodes, ... ?
|
||||
|
||||
Reference in New Issue
Block a user