mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
refactor(editor): Fix remaining FE type check errors (no-changelog) (#9607)
Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
parent
1e15f73b0d
commit
22bdb0568e
|
@ -240,7 +240,7 @@ const validationError = computed<string | null>(() => {
|
|||
|
||||
if (error) {
|
||||
if ('messageKey' in error) {
|
||||
return t(error.messageKey, error.options as object);
|
||||
return t(error.messageKey, error.options);
|
||||
} else if ('message' in error) {
|
||||
return error.message;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
|
|||
* https://github.com/Matt-Esch/string-template/index.js
|
||||
*/
|
||||
export default function () {
|
||||
const isReplacementGroup = (target: object, key: string): target is Record<string, unknown> =>
|
||||
key in target;
|
||||
|
||||
function template(
|
||||
value: string | ((...args: unknown[]) => string),
|
||||
...args: Array<string | object>
|
||||
|
@ -15,21 +18,23 @@ export default function () {
|
|||
}
|
||||
|
||||
const str = value;
|
||||
let replacements: object = args;
|
||||
if (args.length === 1 && typeof args[0] === 'object') {
|
||||
args = args[0] as unknown as Array<string | object>;
|
||||
replacements = args[0];
|
||||
}
|
||||
|
||||
if (!args?.hasOwnProperty) {
|
||||
args = {} as unknown as Array<string | object>;
|
||||
if (!replacements?.hasOwnProperty) {
|
||||
replacements = {};
|
||||
}
|
||||
|
||||
return str.replace(RE_NARGS, (match, _, i, index: number) => {
|
||||
let result: string | object | null;
|
||||
return str.replace(RE_NARGS, (match, _, group: string, index: number): string => {
|
||||
let result: string | null;
|
||||
|
||||
if (str[index - 1] === '{' && str[index + match.length] === '}') {
|
||||
return i;
|
||||
return `${group}`;
|
||||
} else {
|
||||
result = Object.hasOwn(args, i) ? args[i] : null;
|
||||
result = isReplacementGroup(replacements, group) ? `${replacements[group]}` : null;
|
||||
|
||||
if (result === null || result === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ export const t = function (
|
|||
|
||||
// only support flat keys
|
||||
if (lang[path] !== undefined) {
|
||||
return format(lang[path], options);
|
||||
return format(lang[path], ...(options ? [options] : []));
|
||||
}
|
||||
|
||||
return '';
|
||||
|
@ -44,8 +44,8 @@ export async function use(l: string) {
|
|||
} catch (e) {}
|
||||
}
|
||||
|
||||
export const i18n = function (fn: N8nLocaleTranslateFn) {
|
||||
export function i18n(fn: N8nLocaleTranslateFn) {
|
||||
i18nHandler = fn || i18nHandler;
|
||||
};
|
||||
}
|
||||
|
||||
export default { use, t, i18n };
|
||||
|
|
|
@ -2,7 +2,7 @@ import { t } from '../locale';
|
|||
|
||||
export default {
|
||||
methods: {
|
||||
t(path: string, options: object) {
|
||||
t(path: string, options: string[]) {
|
||||
return t.call(this, path, options);
|
||||
},
|
||||
},
|
||||
|
|
7
packages/design-system/src/shims-types.d.ts
vendored
7
packages/design-system/src/shims-types.d.ts
vendored
|
@ -1,7 +0,0 @@
|
|||
import * as Vue from 'vue';
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$style: Record<string, string>;
|
||||
}
|
||||
}
|
13
packages/design-system/src/shims-vue.d.ts
vendored
13
packages/design-system/src/shims-vue.d.ts
vendored
|
@ -1,4 +1,11 @@
|
|||
declare module '*.vue' {
|
||||
import Vue from 'vue';
|
||||
export default Vue;
|
||||
export {};
|
||||
|
||||
/**
|
||||
* @docs https://vuejs.org/guide/typescript/options-api.html#augmenting-global-properties
|
||||
*/
|
||||
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties {
|
||||
$style: Record<string, string>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import type { N8nLocaleTranslateFnOptions } from 'n8n-design-system/types/i18n';
|
||||
|
||||
export type Rule = { name: string; config?: unknown };
|
||||
|
||||
export type RuleGroup = {
|
||||
rules: Array<Rule | RuleGroup>;
|
||||
defaultError?: { messageKey: string; options?: unknown };
|
||||
defaultError?: { messageKey: string; options?: N8nLocaleTranslateFnOptions };
|
||||
};
|
||||
|
||||
export type Validatable = string | number | boolean | null | undefined;
|
||||
|
@ -13,8 +15,8 @@ export type IValidator<T = unknown> = {
|
|||
config: T,
|
||||
) =>
|
||||
| false
|
||||
| { message: string; options?: unknown }
|
||||
| { messageKey: string; options?: unknown }
|
||||
| { message: string; options?: N8nLocaleTranslateFnOptions }
|
||||
| { messageKey: string; options?: N8nLocaleTranslateFnOptions }
|
||||
| null;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export type N8nLocaleTranslateFn = (path: string, options: object) => string;
|
||||
export type N8nLocaleTranslateFnOptions = string[] | Record<string, unknown>;
|
||||
|
||||
export type N8nLocaleTranslateFn = (path: string, options?: N8nLocaleTranslateFnOptions) => string;
|
||||
|
||||
export type N8nLocale = Record<string, string | ((...args: unknown[]) => string)>;
|
||||
|
|
|
@ -134,16 +134,6 @@ export type EndpointStyle = {
|
|||
hoverMessage?: string;
|
||||
};
|
||||
|
||||
export type EndpointMeta = {
|
||||
__meta?: {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
index: number;
|
||||
totalEndpoints: number;
|
||||
endpointLabelLength: number;
|
||||
};
|
||||
};
|
||||
|
||||
export interface IUpdateInformation<T extends NodeParameterValueType = NodeParameterValueType> {
|
||||
name: string;
|
||||
key?: string;
|
||||
|
@ -410,19 +400,6 @@ export interface IExecutionResponse extends IExecutionBase {
|
|||
executedNode?: string;
|
||||
}
|
||||
|
||||
export interface IExecutionShortResponse {
|
||||
id: string;
|
||||
workflowData: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
mode: WorkflowExecuteMode;
|
||||
finished: boolean;
|
||||
startedAt: Date;
|
||||
stoppedAt: Date;
|
||||
executionTime?: number;
|
||||
}
|
||||
|
||||
export interface IExecutionsListResponse {
|
||||
count: number;
|
||||
results: ExecutionSummary[];
|
||||
|
@ -432,6 +409,7 @@ export interface IExecutionsListResponse {
|
|||
export interface IExecutionsCurrentSummaryExtended {
|
||||
id: string;
|
||||
finished?: boolean;
|
||||
status: ExecutionStatus;
|
||||
mode: WorkflowExecuteMode;
|
||||
retryOf?: string | null;
|
||||
retrySuccessId?: string | null;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { faker } from '@faker-js/faker';
|
||||
import type { ProjectListItem, ProjectSharingData, ProjectType } from '@/types/projects.types';
|
||||
import type {
|
||||
Project,
|
||||
ProjectListItem,
|
||||
ProjectSharingData,
|
||||
ProjectType,
|
||||
} from '@/types/projects.types';
|
||||
import { ProjectTypes } from '@/types/projects.types';
|
||||
|
||||
export const createProjectSharingData = (projectType?: ProjectType): ProjectSharingData => ({
|
||||
|
@ -19,3 +24,16 @@ export const createProjectListItem = (projectType?: ProjectType): ProjectListIte
|
|||
updatedAt: faker.date.recent().toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
export function createTestProject(data: Partial<Project>): Project {
|
||||
return {
|
||||
id: faker.string.uuid(),
|
||||
name: faker.lorem.words({ min: 1, max: 3 }),
|
||||
createdAt: faker.date.past().toISOString(),
|
||||
updatedAt: faker.date.recent().toISOString(),
|
||||
type: 'team',
|
||||
relations: [],
|
||||
scopes: [],
|
||||
...data,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { EnvironmentVariable } from '@/Interface';
|
|||
|
||||
export const variableFactory = Factory.extend<EnvironmentVariable>({
|
||||
id(i: number) {
|
||||
return i;
|
||||
return `${i}`;
|
||||
},
|
||||
key() {
|
||||
return `${faker.lorem.word()}`.toUpperCase();
|
||||
|
|
|
@ -31,7 +31,7 @@ export async function deleteDestinationFromDb(context: IRestApiContext, destinat
|
|||
export async function sendTestMessageToDestination(
|
||||
context: IRestApiContext,
|
||||
destination: ApiMessageEventBusDestinationOptions,
|
||||
) {
|
||||
): Promise<boolean> {
|
||||
const data: IDataObject = {
|
||||
...destination,
|
||||
};
|
||||
|
|
|
@ -5,9 +5,9 @@ import type {
|
|||
INodePropertyOptions,
|
||||
INodeTypeDescription,
|
||||
INodeTypeNameVersion,
|
||||
ResourceMapperFields,
|
||||
} from 'n8n-workflow';
|
||||
import axios from 'axios';
|
||||
import type { ResourceMapperFields } from 'n8n-workflow/src/Interfaces';
|
||||
|
||||
export async function getNodeTypes(baseUrl: string) {
|
||||
const { data } = await axios.get(baseUrl + 'types/nodes.json', { withCredentials: true });
|
||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
|||
} from '@/Interface';
|
||||
import { get } from '@/utils/apiUtils';
|
||||
|
||||
function stringifyArray(arr: number[]) {
|
||||
function stringifyArray(arr: string[]) {
|
||||
return arr.join(',');
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ export async function getCollections(
|
|||
|
||||
export async function getWorkflows(
|
||||
apiEndpoint: string,
|
||||
query: { page: number; limit: number; categories: number[]; search: string },
|
||||
query: { page: number; limit: number; categories: string[]; search: string },
|
||||
headers?: RawAxiosRequestHeaders,
|
||||
): Promise<{
|
||||
totalWorkflows: number;
|
||||
|
|
|
@ -1115,8 +1115,6 @@ export default defineComponent({
|
|||
oauthTokenData: {} as CredentialInformation,
|
||||
};
|
||||
|
||||
this.credentialsStore.enableOAuthCredential(credential);
|
||||
|
||||
// Close the window
|
||||
if (oauthPopup) {
|
||||
oauthPopup.close();
|
||||
|
@ -1164,7 +1162,7 @@ export default defineComponent({
|
|||
|
||||
this.credentialData = {
|
||||
...this.credentialData,
|
||||
scopes,
|
||||
scopes: scopes as unknown as CredentialInformation,
|
||||
homeProject,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { ICredentialsResponse, IUser, IUserListAction } from '@/Interface';
|
||||
import type { ICredentialsResponse, IUserListAction } from '@/Interface';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
|
@ -157,22 +157,35 @@ export default defineComponent({
|
|||
credentialOwnerName(): string {
|
||||
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);
|
||||
},
|
||||
credentialDataHomeProject(): ProjectSharingData | undefined {
|
||||
const credentialContainsProjectSharingData = (
|
||||
data: ICredentialDataDecryptedObject,
|
||||
): data is { homeProject: ProjectSharingData } => {
|
||||
return 'homeProject' in data;
|
||||
};
|
||||
|
||||
return this.credentialData && credentialContainsProjectSharingData(this.credentialData)
|
||||
? this.credentialData.homeProject
|
||||
: undefined;
|
||||
},
|
||||
isCredentialSharedWithCurrentUser(): boolean {
|
||||
return (this.credentialData.sharedWithProjects ?? []).some((sharee: IUser) => {
|
||||
return sharee.id === this.usersStore.currentUser?.id;
|
||||
if (!Array.isArray(this.credentialData.sharedWithProjects)) return false;
|
||||
|
||||
return this.credentialData.sharedWithProjects.some((sharee) => {
|
||||
return typeof sharee === 'object' && 'id' in sharee
|
||||
? sharee.id === this.usersStore.currentUser?.id
|
||||
: false;
|
||||
});
|
||||
},
|
||||
projects(): ProjectListItem[] {
|
||||
return this.projectsStore.personalProjects.filter(
|
||||
(project) =>
|
||||
project.id !== this.credential?.homeProject?.id &&
|
||||
project.id !== this.credentialData?.homeProject?.id,
|
||||
project.id !== this.credentialDataHomeProject?.id,
|
||||
);
|
||||
},
|
||||
homeProject(): ProjectSharingData | undefined {
|
||||
return (
|
||||
this.credential?.homeProject ?? (this.credentialData?.homeProject as ProjectSharingData)
|
||||
);
|
||||
return this.credential?.homeProject ?? this.credentialDataHomeProject;
|
||||
},
|
||||
isHomeTeamProject(): boolean {
|
||||
return this.homeProject?.type === ProjectTypes.Team;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div
|
||||
:class="$style.wrapper"
|
||||
:style="iconStyleData"
|
||||
@click="(e) => $emit('click')"
|
||||
@click="() => $emit('click')"
|
||||
@mouseover="showTooltip = true"
|
||||
@mouseleave="showTooltip = false"
|
||||
>
|
||||
|
@ -126,7 +126,7 @@ export default defineComponent({
|
|||
|
||||
const restUrl = this.rootStore.getRestUrl;
|
||||
|
||||
if (nodeType.icon) {
|
||||
if (typeof nodeType.icon === 'string') {
|
||||
const [type, path] = nodeType.icon.split(':');
|
||||
const returnData: NodeIconData = {
|
||||
type,
|
||||
|
|
|
@ -207,6 +207,10 @@ const isWorkflowHistoryButtonDisabled = computed(() => {
|
|||
return isNewWorkflow.value;
|
||||
});
|
||||
|
||||
const workflowTagIds = computed(() => {
|
||||
return (props.workflow.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id));
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.workflow.id,
|
||||
() => {
|
||||
|
@ -601,7 +605,7 @@ function showCreateWorkflowSuccessToast(id?: string) {
|
|||
<TagsContainer
|
||||
v-else
|
||||
:key="workflow.id"
|
||||
:tag-ids="workflow.tags"
|
||||
:tag-ids="workflowTagIds"
|
||||
:clickable="true"
|
||||
:responsive="true"
|
||||
data-test-id="workflow-tags"
|
||||
|
|
|
@ -88,7 +88,9 @@ function onSelected(item: INodeCreateElement) {
|
|||
|
||||
const icon = item.properties.iconUrl
|
||||
? `${baseUrl}${item.properties.iconUrl}`
|
||||
: item.properties.icon?.split(':')[1];
|
||||
: typeof item.properties.icon === 'string'
|
||||
? item.properties.icon?.split(':')[1]
|
||||
: undefined;
|
||||
|
||||
const transformedActions = nodeActions?.map((a) =>
|
||||
transformNodeType(a, item.properties.displayName, 'action'),
|
||||
|
|
|
@ -18,7 +18,7 @@ export const mockSimplifiedNodeType = (
|
|||
): SimplifiedNodeType => ({
|
||||
displayName: 'Sample DisplayName',
|
||||
name: 'sampleName',
|
||||
icon: 'sampleIcon',
|
||||
icon: 'fa:sampleIcon',
|
||||
iconUrl: 'https://example.com/icon.png',
|
||||
group: ['group1', 'group2'],
|
||||
description: 'Sample description',
|
||||
|
|
|
@ -36,7 +36,7 @@ import { useI18n } from '@/composables/useI18n';
|
|||
import { useKeyboardNavigation } from './useKeyboardNavigation';
|
||||
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import type { INodeInputFilter, NodeConnectionType } from 'n8n-workflow';
|
||||
import type { INodeInputFilter, NodeConnectionType, Themed } from 'n8n-workflow';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
|
||||
interface ViewStack {
|
||||
|
@ -48,7 +48,7 @@ interface ViewStack {
|
|||
info?: string;
|
||||
nodeIcon?: {
|
||||
iconType?: string;
|
||||
icon?: string;
|
||||
icon?: Themed<string>;
|
||||
color?: string;
|
||||
};
|
||||
iconUrl?: string;
|
||||
|
|
|
@ -58,7 +58,7 @@ import {
|
|||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import type { SimplifiedNodeType } from '@/Interface';
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { INodeTypeDescription, Themed } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
|
||||
|
@ -74,7 +74,7 @@ export interface NodeViewItem {
|
|||
properties: {
|
||||
name?: string;
|
||||
title?: string;
|
||||
icon?: string;
|
||||
icon?: Themed<string>;
|
||||
iconProps?: {
|
||||
color?: string;
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IVersionNode } from '@/Interface';
|
||||
import type { IVersionNode, SimplifiedNodeType } from '@/Interface';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { getBadgeIconUrl, getNodeIcon, getNodeIconUrl } from '@/utils/nodeTypesUtils';
|
||||
|
@ -30,7 +30,7 @@ interface NodeIconSource {
|
|||
}
|
||||
|
||||
type Props = {
|
||||
nodeType?: INodeTypeDescription | IVersionNode | null;
|
||||
nodeType?: INodeTypeDescription | SimplifiedNodeType | IVersionNode | null;
|
||||
size?: number;
|
||||
disabled?: boolean;
|
||||
circle?: boolean;
|
||||
|
|
|
@ -460,7 +460,6 @@ export default defineComponent({
|
|||
parameters: {},
|
||||
} as INodeParameters,
|
||||
nodeValuesInitialized: false, // Used to prevent nodeValues from being overwritten by defaults on reopening ndv
|
||||
|
||||
nodeSettings: [] as INodeProperties[],
|
||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
||||
CUSTOM_NODES_DOCS_URL,
|
||||
|
@ -469,7 +468,7 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
watch: {
|
||||
node(newNode, oldNode) {
|
||||
node() {
|
||||
this.setNodeValues();
|
||||
},
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@ import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
|||
type Tab = 'settings' | 'params';
|
||||
type Props = {
|
||||
modelValue?: Tab;
|
||||
nodeType?: INodeTypeDescription;
|
||||
nodeType?: INodeTypeDescription | null;
|
||||
pushRef?: string;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import ProjectTabs from '@/components/Projects/ProjectTabs.vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { createTestProject } from '@/__tests__/data/projects';
|
||||
import { useProjectsStore } from '@/stores/projects.store';
|
||||
|
||||
vi.mock('vue-router', () => {
|
||||
|
@ -54,15 +55,14 @@ describe('ProjectTabs', () => {
|
|||
|
||||
it('should render project tabs if use has permissions', () => {
|
||||
route.params.projectId = '123';
|
||||
projectsStore.currentProject = {
|
||||
id: '123',
|
||||
type: 'team',
|
||||
name: 'Project',
|
||||
relations: [],
|
||||
scopes: ['project:update'],
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
vi.mocked(useProjectsStore).mockImplementationOnce(
|
||||
() =>
|
||||
({
|
||||
currentProject: createTestProject({
|
||||
scopes: ['project:update'],
|
||||
}),
|
||||
}) as ReturnType<typeof useProjectsStore>,
|
||||
);
|
||||
const { getByText } = renderComponent();
|
||||
|
||||
expect(getByText('Workflows')).toBeInTheDocument();
|
||||
|
@ -72,15 +72,14 @@ describe('ProjectTabs', () => {
|
|||
|
||||
it('should render project tabs', () => {
|
||||
route.params.projectId = '123';
|
||||
projectsStore.currentProject = {
|
||||
id: '123',
|
||||
type: 'team',
|
||||
name: 'Project',
|
||||
relations: [],
|
||||
scopes: ['project:read'],
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
vi.mocked(useProjectsStore).mockImplementationOnce(
|
||||
() =>
|
||||
({
|
||||
currentProject: createTestProject({
|
||||
scopes: ['project:read'],
|
||||
}),
|
||||
}) as ReturnType<typeof useProjectsStore>,
|
||||
);
|
||||
const { queryByText, getByText } = renderComponent();
|
||||
|
||||
expect(getByText('Workflows')).toBeInTheDocument();
|
||||
|
|
|
@ -486,7 +486,7 @@ export default defineComponent({
|
|||
this.eventBus.on('refreshList', this.refreshList);
|
||||
window.addEventListener('resize', this.setWidth);
|
||||
|
||||
useNDVStore().$subscribe((mutation, state) => {
|
||||
useNDVStore().$subscribe((_mutation, _state) => {
|
||||
// Update the width when main panel dimension change
|
||||
this.setWidth();
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ import MappingModeSelect from './MappingModeSelect.vue';
|
|||
import MatchingColumnsSelect from './MatchingColumnsSelect.vue';
|
||||
import MappingFields from './MappingFields.vue';
|
||||
import { fieldCannotBeDeleted, parseResourceMapperFieldName } from '@/utils/nodeTypesUtils';
|
||||
import { isResourceMapperValue } from '@/utils/typeGuards';
|
||||
import { isFullExecutionResponse, isResourceMapperValue } from '@/utils/typeGuards';
|
||||
import { i18n as locale } from '@/plugins/i18n';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
|
@ -78,7 +78,12 @@ watch(
|
|||
watch(
|
||||
() => workflowsStore.getWorkflowExecution,
|
||||
async (data) => {
|
||||
if (data?.status === 'success' && state.paramValue.mappingMode === 'autoMapInputData') {
|
||||
if (
|
||||
data &&
|
||||
isFullExecutionResponse(data) &&
|
||||
data.status === 'success' &&
|
||||
state.paramValue.mappingMode === 'autoMapInputData'
|
||||
) {
|
||||
await initFetching(true);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -176,12 +176,14 @@ import ParameterInputList from '@/components/ParameterInputList.vue';
|
|||
import type { IMenuItem, INodeUi, IUpdateInformation } from '@/Interface';
|
||||
import type {
|
||||
IDataObject,
|
||||
INodeCredentials,
|
||||
NodeParameterValue,
|
||||
MessageEventBusDestinationOptions,
|
||||
INodeParameters,
|
||||
NodeParameterValueType,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
deepCopy,
|
||||
messageEventBusDestinationTypeNames,
|
||||
defaultMessageEventBusDestinationOptions,
|
||||
defaultMessageEventBusDestinationWebhookOptions,
|
||||
MessageEventBusDestinationTypeNames,
|
||||
|
@ -246,7 +248,7 @@ export default defineComponent({
|
|||
showRemoveConfirm: false,
|
||||
typeSelectValue: '',
|
||||
typeSelectPlaceholder: 'Destination Type',
|
||||
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions),
|
||||
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions) as INodeParameters,
|
||||
webhookDescription: webhookModalDescription,
|
||||
sentryDescription: sentryModalDescription,
|
||||
syslogDescription: syslogModalDescription,
|
||||
|
@ -261,7 +263,7 @@ export default defineComponent({
|
|||
...mapStores(useUIStore, useLogStreamingStore, useNDVStore, useWorkflowsStore),
|
||||
typeSelectOptions(): Array<{ value: string; label: BaseTextKey }> {
|
||||
const options: Array<{ value: string; label: BaseTextKey }> = [];
|
||||
for (const t of Object.values(MessageEventBusDestinationTypeNames)) {
|
||||
for (const t of messageEventBusDestinationTypeNames) {
|
||||
if (t === MessageEventBusDestinationTypeNames.abstract) {
|
||||
continue;
|
||||
}
|
||||
|
@ -325,7 +327,8 @@ export default defineComponent({
|
|||
if (arg.name === this.destination.id) {
|
||||
if ('credentials' in arg.properties) {
|
||||
this.unchanged = false;
|
||||
this.nodeParameters.credentials = arg.properties.credentials as INodeCredentials;
|
||||
this.nodeParameters.credentials = arg.properties
|
||||
.credentials as NodeParameterValueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -350,7 +353,7 @@ export default defineComponent({
|
|||
this.workflowsStore.removeNode(this.node);
|
||||
this.ndvStore.activeNodeName = options.id ?? 'thisshouldnothappen';
|
||||
this.workflowsStore.addNode(destinationToFakeINodeUi(options));
|
||||
this.nodeParameters = options;
|
||||
this.nodeParameters = options as INodeParameters;
|
||||
this.logStreamingStore.items[this.destination.id].destination = options;
|
||||
},
|
||||
onTypeSelectInput(destinationType: MessageEventBusDestinationTypeNames) {
|
||||
|
@ -448,7 +451,7 @@ export default defineComponent({
|
|||
if (deleteConfirmed !== MODAL_CONFIRM) {
|
||||
return;
|
||||
} else {
|
||||
this.eventBus.emit('remove', this.destination.id);
|
||||
this.callEventBus('remove', this.destination.id);
|
||||
this.uiStore.closeModal(LOG_STREAM_MODAL_KEY);
|
||||
this.uiStore.stateIsDirty = false;
|
||||
}
|
||||
|
@ -456,10 +459,12 @@ export default defineComponent({
|
|||
onModalClose() {
|
||||
if (!this.hasOnceBeenSaved) {
|
||||
this.workflowsStore.removeNode(this.node);
|
||||
this.logStreamingStore.removeDestination(this.nodeParameters.id!);
|
||||
if (this.nodeParameters.id) {
|
||||
this.logStreamingStore.removeDestination(this.nodeParameters.id.toString());
|
||||
}
|
||||
}
|
||||
this.ndvStore.activeNodeName = null;
|
||||
this.eventBus.emit('closing', this.destination.id);
|
||||
this.callEventBus('closing', this.destination.id);
|
||||
this.uiStore.stateIsDirty = false;
|
||||
},
|
||||
async saveDestination() {
|
||||
|
@ -471,10 +476,12 @@ export default defineComponent({
|
|||
this.hasOnceBeenSaved = true;
|
||||
this.testMessageSent = false;
|
||||
this.unchanged = true;
|
||||
this.eventBus.emit('destinationWasSaved', this.destination.id);
|
||||
this.callEventBus('destinationWasSaved', this.destination.id);
|
||||
this.uiStore.stateIsDirty = false;
|
||||
|
||||
const destinationType = (this.nodeParameters.__type ?? 'unknown')
|
||||
const destinationType = (
|
||||
this.nodeParameters.__type ? `${this.nodeParameters.__type}` : 'unknown'
|
||||
)
|
||||
.replace('$$MessageEventBusDestination', '')
|
||||
.toLowerCase();
|
||||
|
||||
|
@ -503,6 +510,11 @@ export default defineComponent({
|
|||
});
|
||||
}
|
||||
},
|
||||
callEventBus(event: string, data: unknown) {
|
||||
if (this.eventBus) {
|
||||
this.eventBus.emit(event, data);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -184,7 +184,7 @@ export default defineComponent({
|
|||
},
|
||||
isSelected(): boolean {
|
||||
return (
|
||||
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data.name) !==
|
||||
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data?.name) !==
|
||||
undefined
|
||||
);
|
||||
},
|
||||
|
|
|
@ -12,6 +12,8 @@ import { useUsersStore } from '@/stores/users.store';
|
|||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { testingNodeTypes, mockNodeTypesToArray } from '@/__tests__/defaults';
|
||||
import { setupServer } from '@/__tests__/server';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { IConnections } from 'n8n-workflow';
|
||||
|
||||
const renderComponent = createComponentRenderer(WorkflowLMChatModal, {
|
||||
props: {
|
||||
|
@ -23,25 +25,25 @@ const renderComponent = createComponentRenderer(WorkflowLMChatModal, {
|
|||
async function createPiniaWithAINodes(options = { withConnections: true, withAgentNode: true }) {
|
||||
const { withConnections, withAgentNode } = options;
|
||||
const workflowId = uuid();
|
||||
const connections: IConnections = {
|
||||
'Chat Trigger': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Agent',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const workflow = createTestWorkflow({
|
||||
id: workflowId,
|
||||
name: 'Test Workflow',
|
||||
connections: withConnections
|
||||
? {
|
||||
'Chat Trigger': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Agent',
|
||||
type: 'main',
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
: {},
|
||||
active: true,
|
||||
...(withConnections ? { connections } : {}),
|
||||
nodes: [
|
||||
createTestNode({
|
||||
name: 'Chat Trigger',
|
||||
|
|
|
@ -160,14 +160,14 @@ import { useRoute } from 'vue-router';
|
|||
// eslint-disable-next-line unused-imports/no-unused-imports, @typescript-eslint/no-unused-vars
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
|
||||
export interface IResource {
|
||||
export type IResource = {
|
||||
id: string;
|
||||
name: string;
|
||||
value: string;
|
||||
updatedAt?: string;
|
||||
createdAt?: string;
|
||||
homeProject?: ProjectSharingData;
|
||||
}
|
||||
};
|
||||
|
||||
interface IFilters {
|
||||
search: string;
|
||||
|
@ -291,11 +291,11 @@ export default defineComponent({
|
|||
case 'lastUpdated':
|
||||
return props.sortFns.lastUpdated
|
||||
? props.sortFns.lastUpdated(a, b)
|
||||
: new Date(b.updatedAt).valueOf() - new Date(a.updatedAt).valueOf();
|
||||
: new Date(b.updatedAt ?? '').valueOf() - new Date(a.updatedAt ?? '').valueOf();
|
||||
case 'lastCreated':
|
||||
return props.sortFns.lastCreated
|
||||
? props.sortFns.lastCreated(a, b)
|
||||
: new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf();
|
||||
: new Date(b.createdAt ?? '').valueOf() - new Date(a.createdAt ?? '').valueOf();
|
||||
case 'nameAsc':
|
||||
return props.sortFns.nameAsc
|
||||
? props.sortFns.nameAsc(a, b)
|
||||
|
|
|
@ -69,7 +69,7 @@ describe('useCanvasPanning()', () => {
|
|||
const { onMouseDown, onMouseMove, onMouseUp } = useCanvasPanning(elementRef);
|
||||
|
||||
onMouseDown(new MouseEvent('mousedown', { button: 1 }), true);
|
||||
onMouseUp(new MouseEvent('mouseup'));
|
||||
onMouseUp();
|
||||
|
||||
expect(removeEventListenerSpy).toHaveBeenCalledWith('mousemove', onMouseMove);
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import { createPinia, setActivePinia } from 'pinia';
|
|||
import { createTestNode } from '@/__tests__/mocks';
|
||||
import type { Connection } from '@vue-flow/core';
|
||||
import type { IConnection } from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
describe('useCanvasOperations', () => {
|
||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
|
@ -200,9 +201,9 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
const connection: Connection = {
|
||||
source: nodeA.id,
|
||||
sourceHandle: 'outputs/main/0',
|
||||
sourceHandle: `outputs/${NodeConnectionType.Main}/0`,
|
||||
target: nodeB.id,
|
||||
targetHandle: 'inputs/main/0',
|
||||
targetHandle: `inputs/${NodeConnectionType.Main}/0`,
|
||||
};
|
||||
|
||||
vi.spyOn(workflowsStore, 'getNodeById').mockReturnValueOnce(nodeA).mockReturnValueOnce(nodeB);
|
||||
|
@ -211,8 +212,8 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
expect(addConnectionSpy).toHaveBeenCalledWith({
|
||||
connection: [
|
||||
{ index: 0, node: nodeA.name, type: 'main' },
|
||||
{ index: 0, node: nodeB.name, type: 'main' },
|
||||
{ index: 0, node: nodeA.name, type: NodeConnectionType.Main },
|
||||
{ index: 0, node: nodeB.name, type: NodeConnectionType.Main },
|
||||
],
|
||||
});
|
||||
expect(uiStore.stateIsDirty).toBe(true);
|
||||
|
@ -269,9 +270,9 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
const connection: Connection = {
|
||||
source: nodeA.id,
|
||||
sourceHandle: 'outputs/main/0',
|
||||
sourceHandle: `outputs/${NodeConnectionType.Main}/0`,
|
||||
target: nodeB.id,
|
||||
targetHandle: 'inputs/main/0',
|
||||
targetHandle: `inputs/${NodeConnectionType.Main}/0`,
|
||||
};
|
||||
|
||||
vi.spyOn(workflowsStore, 'getNodeById').mockReturnValueOnce(nodeA).mockReturnValueOnce(nodeB);
|
||||
|
@ -280,8 +281,8 @@ describe('useCanvasOperations', () => {
|
|||
|
||||
expect(removeConnectionSpy).toHaveBeenCalledWith({
|
||||
connection: [
|
||||
{ index: 0, node: nodeA.name, type: 'main' },
|
||||
{ index: 0, node: nodeB.name, type: 'main' },
|
||||
{ index: 0, node: nodeA.name, type: NodeConnectionType.Main },
|
||||
{ index: 0, node: nodeB.name, type: NodeConnectionType.Main },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -294,8 +295,8 @@ describe('useCanvasOperations', () => {
|
|||
.mockImplementation(() => {});
|
||||
|
||||
const connection: [IConnection, IConnection] = [
|
||||
{ node: 'sourceNode', type: 'type', index: 1 },
|
||||
{ node: 'targetNode', type: 'type', index: 2 },
|
||||
{ node: 'sourceNode', type: NodeConnectionType.Main, index: 1 },
|
||||
{ node: 'targetNode', type: NodeConnectionType.Main, index: 2 },
|
||||
];
|
||||
|
||||
canvasOperations.revertDeleteConnection(connection);
|
||||
|
|
|
@ -24,7 +24,7 @@ export function useCanvasPanning(
|
|||
/**
|
||||
* Updates the canvas offset position based on the mouse movement
|
||||
*/
|
||||
function panCanvas(e: MouseEvent) {
|
||||
function panCanvas(e: MouseEvent | TouchEvent) {
|
||||
const offsetPosition = uiStore.nodeViewOffsetPosition;
|
||||
|
||||
const [x, y] = getMousePosition(e);
|
||||
|
|
|
@ -15,6 +15,7 @@ import { useSettingsStore } from '@/stores/settings.store';
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useTelemetry } from './useTelemetry';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { isFullExecutionResponse } from '@/utils/typeGuards';
|
||||
|
||||
export const useExecutionDebugging = () => {
|
||||
const telemetry = useTelemetry();
|
||||
|
@ -131,7 +132,7 @@ export const useExecutionDebugging = () => {
|
|||
|
||||
telemetry.track('User clicked debug execution button', {
|
||||
instance_id: useRootStore().instanceId,
|
||||
exec_status: execution.status,
|
||||
exec_status: isFullExecutionResponse(execution) ? execution.status : '',
|
||||
override_pinned_data: pinnableNodes.length === pinnings,
|
||||
all_exec_data_imported: missingNodeNames.length === 0,
|
||||
});
|
||||
|
|
|
@ -271,7 +271,7 @@ export function useNodeHelpers() {
|
|||
}
|
||||
}
|
||||
|
||||
function updateNodeParameterIssues(node: INodeUi, nodeType?: INodeTypeDescription): void {
|
||||
function updateNodeParameterIssues(node: INodeUi, nodeType?: INodeTypeDescription | null): void {
|
||||
const localNodeType = nodeType ?? nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
||||
|
||||
if (localNodeType === null) {
|
||||
|
|
|
@ -530,6 +530,7 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
|
|||
const executionData: IExecutionsCurrentSummaryExtended = {
|
||||
id: pushData.executionId,
|
||||
finished: false,
|
||||
status: 'running',
|
||||
mode: pushData.mode,
|
||||
startedAt: pushData.startedAt,
|
||||
retryOf: pushData.retryOf,
|
||||
|
|
|
@ -39,7 +39,6 @@ import type {
|
|||
IWorkflowData,
|
||||
IWorkflowDataUpdate,
|
||||
IWorkflowDb,
|
||||
IWorkflowTemplateNode,
|
||||
TargetItem,
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
|
@ -308,11 +307,7 @@ function getNodes(): INodeUi[] {
|
|||
}
|
||||
|
||||
// Returns a workflow instance.
|
||||
function getWorkflow(
|
||||
nodes: Array<INodeUi | IWorkflowTemplateNode>,
|
||||
connections: IConnections,
|
||||
copyData?: boolean,
|
||||
): Workflow {
|
||||
function getWorkflow(nodes: INodeUi[], connections: IConnections, copyData?: boolean): Workflow {
|
||||
return useWorkflowsStore().getWorkflow(nodes, connections, copyData);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { IFakeDoor } from '@/Interface';
|
||||
import { FAKE_DOOR_FEATURES } from '@/constants';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
|
||||
export function compileFakeDoorFeatures(): IFakeDoor[] {
|
||||
const store = useUIStore();
|
||||
|
@ -20,7 +21,7 @@ export function compileFakeDoorFeatures(): IFakeDoor[] {
|
|||
if (loggingFeature) {
|
||||
loggingFeature.actionBoxTitle += '.cloud';
|
||||
loggingFeature.linkURL += '&edition=cloud';
|
||||
loggingFeature.infoText = '';
|
||||
loggingFeature.infoText = '' as BaseTextKey;
|
||||
}
|
||||
|
||||
return fakeDoorFeatures;
|
||||
|
|
|
@ -50,7 +50,8 @@ app.mount('#app');
|
|||
if (!import.meta.env.PROD) {
|
||||
// Make sure that we get all error messages properly displayed
|
||||
// as long as we are not in production mode
|
||||
window.onerror = (message, source, lineno, colno, error) => {
|
||||
window.onerror = (message, _source, _lineno, _colno, error) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
if (message.toString().includes('ResizeObserver')) {
|
||||
// That error can apparently be ignored and can probably
|
||||
// not do anything about it anyway
|
||||
|
|
|
@ -29,6 +29,7 @@ import { useCanvasStore } from '@/stores/canvas.store';
|
|||
import type { EndpointSpec } from '@jsplumb/common';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import type { N8nEndpointLabelLength } from '@/plugins/jsplumb/N8nPlusEndpointType';
|
||||
import { isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
|
||||
const createAddInputEndpointSpec = (
|
||||
connectionName: NodeConnectionType,
|
||||
|
@ -119,9 +120,9 @@ export const nodeBase = defineComponent({
|
|||
},
|
||||
methods: {
|
||||
__addEndpointTestingData(endpoint: Endpoint, type: string, inputIndex: number) {
|
||||
if (window?.Cypress && 'canvas' in endpoint.endpoint) {
|
||||
if (window?.Cypress && 'canvas' in endpoint.endpoint && this.instance) {
|
||||
const canvas = endpoint.endpoint.canvas;
|
||||
this.instance.setAttribute(canvas, 'data-endpoint-name', this.data.name);
|
||||
this.instance.setAttribute(canvas, 'data-endpoint-name', this.data?.name ?? '');
|
||||
this.instance.setAttribute(canvas, 'data-input-index', inputIndex.toString());
|
||||
this.instance.setAttribute(canvas, 'data-endpoint-type', type);
|
||||
}
|
||||
|
@ -216,7 +217,11 @@ export const nodeBase = defineComponent({
|
|||
spacerIndexes,
|
||||
)[rootTypeIndex];
|
||||
|
||||
const scope = NodeViewUtils.getEndpointScope(inputName as NodeConnectionType);
|
||||
if (!isValidNodeConnectionType(inputName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scope = NodeViewUtils.getEndpointScope(inputName);
|
||||
|
||||
const newEndpointData: EndpointOptions = {
|
||||
uuid: NodeViewUtils.getInputEndpointUUID(this.nodeId, inputName, typeIndex),
|
||||
|
@ -252,15 +257,15 @@ export const nodeBase = defineComponent({
|
|||
};
|
||||
|
||||
const endpoint = this.instance?.addEndpoint(
|
||||
this.$refs[this.data.name] as Element,
|
||||
this.$refs[this.data?.name ?? ''] as Element,
|
||||
newEndpointData,
|
||||
) as Endpoint;
|
||||
this.__addEndpointTestingData(endpoint, 'input', typeIndex);
|
||||
if (inputConfiguration.displayName || nodeTypeData.inputNames?.[i]) {
|
||||
if (inputConfiguration.displayName ?? nodeTypeData.inputNames?.[i]) {
|
||||
// Apply input names if they got set
|
||||
endpoint.addOverlay(
|
||||
NodeViewUtils.getInputNameOverlay(
|
||||
inputConfiguration.displayName || nodeTypeData.inputNames[i],
|
||||
inputConfiguration.displayName ?? nodeTypeData.inputNames?.[i] ?? '',
|
||||
inputName,
|
||||
inputConfiguration.required,
|
||||
),
|
||||
|
@ -288,7 +293,7 @@ export const nodeBase = defineComponent({
|
|||
// }
|
||||
});
|
||||
if (sortedInputs.length === 0) {
|
||||
this.instance.manage(this.$refs[this.data.name] as Element);
|
||||
this.instance?.manage(this.$refs[this.data?.name ?? ''] as Element);
|
||||
}
|
||||
},
|
||||
getSpacerIndexes(
|
||||
|
@ -349,6 +354,10 @@ export const nodeBase = defineComponent({
|
|||
[key: string]: number;
|
||||
} = {};
|
||||
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.outputs = NodeHelpers.getNodeOutputs(this.workflow, this.data, nodeTypeData) || [];
|
||||
|
||||
// TODO: There are still a lot of references of "main" in NodesView and
|
||||
|
@ -380,7 +389,7 @@ export const nodeBase = defineComponent({
|
|||
|
||||
const endpointLabelLength = getEndpointLabelLength(maxLabelLength);
|
||||
|
||||
this.outputs.forEach((value, i) => {
|
||||
this.outputs.forEach((_value, i) => {
|
||||
const outputConfiguration = outputConfigurations[i];
|
||||
|
||||
const outputName: ConnectionTypes = outputConfiguration.type;
|
||||
|
@ -419,7 +428,11 @@ export const nodeBase = defineComponent({
|
|||
outputsOfSameRootType.length,
|
||||
)[rootTypeIndex];
|
||||
|
||||
const scope = NodeViewUtils.getEndpointScope(outputName as NodeConnectionType);
|
||||
if (!isValidNodeConnectionType(outputName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scope = NodeViewUtils.getEndpointScope(outputName);
|
||||
|
||||
const newEndpointData: EndpointOptions = {
|
||||
uuid: NodeViewUtils.getOutputEndpointUUID(this.nodeId, outputName, typeIndex),
|
||||
|
@ -448,13 +461,17 @@ export const nodeBase = defineComponent({
|
|||
...this.__getOutputConnectionStyle(outputName, outputConfiguration, nodeTypeData),
|
||||
};
|
||||
|
||||
const endpoint = this.instance.addEndpoint(
|
||||
this.$refs[this.data.name] as Element,
|
||||
const endpoint = this.instance?.addEndpoint(
|
||||
this.$refs[this.data?.name ?? ''] as Element,
|
||||
newEndpointData,
|
||||
);
|
||||
|
||||
if (!endpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.__addEndpointTestingData(endpoint, 'output', typeIndex);
|
||||
if (outputConfiguration.displayName) {
|
||||
if (outputConfiguration.displayName && isValidNodeConnectionType(outputName)) {
|
||||
// Apply output names if they got set
|
||||
const overlaySpec = NodeViewUtils.getOutputNameOverlay(
|
||||
outputConfiguration.displayName,
|
||||
|
@ -514,6 +531,10 @@ export const nodeBase = defineComponent({
|
|||
plusEndpointData.cssClass = `${plusEndpointData.cssClass} ${outputConfiguration?.category}`;
|
||||
}
|
||||
|
||||
if (!this.instance || !this.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const plusEndpoint = this.instance.addEndpoint(
|
||||
this.$refs[this.data.name] as Element,
|
||||
plusEndpointData,
|
||||
|
@ -556,7 +577,7 @@ export const nodeBase = defineComponent({
|
|||
};
|
||||
}
|
||||
|
||||
if (!Object.values(NodeConnectionType).includes(connectionType as NodeConnectionType)) {
|
||||
if (!isValidNodeConnectionType(connectionType)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -614,13 +635,13 @@ export const nodeBase = defineComponent({
|
|||
};
|
||||
}
|
||||
|
||||
if (!Object.values(NodeConnectionType).includes(connectionType as NodeConnectionType)) {
|
||||
if (!isValidNodeConnectionType(connectionType)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return createSupplementalConnectionType(connectionType);
|
||||
},
|
||||
touchEnd(e: MouseEvent) {
|
||||
touchEnd(_e: MouseEvent) {
|
||||
const deviceSupport = useDeviceSupport();
|
||||
if (deviceSupport.isTouchDevice) {
|
||||
if (this.uiStore.isActionActive('dragActive')) {
|
||||
|
@ -651,7 +672,7 @@ export const nodeBase = defineComponent({
|
|||
this.$emit('deselectAllNodes');
|
||||
}
|
||||
|
||||
if (this.uiStore.isNodeSelected(this.data.name)) {
|
||||
if (this.uiStore.isNodeSelected(this.data?.name ?? '')) {
|
||||
this.$emit('deselectNode', this.name);
|
||||
} else {
|
||||
this.$emit('nodeSelected', this.name);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import type { RouteLocation } from 'vue-router';
|
||||
import { hasPermission } from '@/utils/rbac/permissions';
|
||||
import type { RouteConfig } from '@/types/router';
|
||||
import type { PermissionTypeOptions } from '@/types/rbac';
|
||||
|
||||
export const userHelpers = defineComponent({
|
||||
|
@ -16,7 +15,7 @@ export const userHelpers = defineComponent({
|
|||
return this.canUserAccessRoute(this.$route);
|
||||
},
|
||||
|
||||
canUserAccessRoute(route: RouteLocation & RouteConfig) {
|
||||
canUserAccessRoute(route: RouteLocation) {
|
||||
const middleware = route.meta?.middleware;
|
||||
const middlewareOptions = route.meta?.middlewareOptions;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import type {
|
|||
IExecuteData,
|
||||
INodeTypeData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import { WorkflowDataProxy } from 'n8n-workflow';
|
||||
import { createTestWorkflowObject } from '@/__tests__/mocks';
|
||||
|
||||
|
@ -91,7 +92,7 @@ const connections: IConnections = {
|
|||
[
|
||||
{
|
||||
node: 'Function',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
|
@ -102,7 +103,7 @@ const connections: IConnections = {
|
|||
[
|
||||
{
|
||||
node: 'Rename',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
|
@ -113,7 +114,7 @@ const connections: IConnections = {
|
|||
[
|
||||
{
|
||||
node: 'End',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -14,8 +14,8 @@ beforeEach(() => {
|
|||
describe('variablesCompletions', () => {
|
||||
test('should return completions for $vars prefix', () => {
|
||||
environmentsStore.variables = [
|
||||
{ key: 'VAR1', value: 'Value1', id: 1 },
|
||||
{ key: 'VAR2', value: 'Value2', id: 2 },
|
||||
{ key: 'VAR1', value: 'Value1', id: '1' },
|
||||
{ key: 'VAR2', value: 'Value2', id: '2' },
|
||||
];
|
||||
|
||||
const state = EditorState.create({ doc: '$vars.', selection: { anchor: 6 } });
|
||||
|
@ -39,7 +39,7 @@ describe('variablesCompletions', () => {
|
|||
});
|
||||
|
||||
test('should escape special characters in matcher', () => {
|
||||
environmentsStore.variables = [{ key: 'VAR1', value: 'Value1', id: 1 }];
|
||||
environmentsStore.variables = [{ key: 'VAR1', value: 'Value1', id: '1' }];
|
||||
|
||||
const state = EditorState.create({ doc: '$vars.', selection: { anchor: 6 } });
|
||||
const context = new CompletionContext(state, 6, true);
|
||||
|
@ -49,7 +49,7 @@ describe('variablesCompletions', () => {
|
|||
});
|
||||
|
||||
test('should return completions for custom matcher', () => {
|
||||
environmentsStore.variables = [{ key: 'VAR1', value: 'Value1', id: 1 }];
|
||||
environmentsStore.variables = [{ key: 'VAR1', value: 'Value1', id: '1' }];
|
||||
|
||||
const state = EditorState.create({ doc: '$custom.', selection: { anchor: 8 } });
|
||||
const context = new CompletionContext(state, 8, true);
|
||||
|
|
|
@ -9,7 +9,7 @@ import EnterpriseEdition from '@/components/EnterpriseEdition.ee.vue';
|
|||
import RBAC from '@/components/RBAC.vue';
|
||||
import ParameterInputList from '@/components/ParameterInputList.vue';
|
||||
|
||||
export const GlobalComponentsPlugin: Plugin<{}> = {
|
||||
export const GlobalComponentsPlugin: Plugin = {
|
||||
install(app) {
|
||||
const messageService = useMessage();
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const GlobalComponentsPlugin: Plugin<{}> = {
|
|||
app.component('ParameterInputList', ParameterInputList);
|
||||
|
||||
app.use(ElementPlus);
|
||||
app.use(N8nPlugin);
|
||||
app.use(N8nPlugin, {});
|
||||
|
||||
// app.use(ElLoading);
|
||||
// app.use(ElNotification);
|
||||
|
|
|
@ -169,7 +169,7 @@ export class N8nConnector extends AbstractConnector {
|
|||
|
||||
targetGap: number;
|
||||
|
||||
overrideTargetEndpoint: Endpoint | null;
|
||||
overrideTargetEndpoint: Endpoint;
|
||||
|
||||
getEndpointOffset?: (e: Endpoint) => number | null;
|
||||
|
||||
|
@ -517,7 +517,7 @@ export class N8nConnector extends AbstractConnector {
|
|||
}
|
||||
|
||||
resetTargetEndpoint() {
|
||||
this.overrideTargetEndpoint = null;
|
||||
this.overrideTargetEndpoint = null as unknown as Endpoint;
|
||||
}
|
||||
|
||||
_computeBezier(paintInfo: N8nConnectorPaintGeometry) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { Plugin } from 'vue';
|
|||
import VueTouchEvents from 'vue3-touch-events';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
export const GlobalDirectivesPlugin: Plugin<{}> = {
|
||||
export const GlobalDirectivesPlugin: Plugin = {
|
||||
install(app) {
|
||||
app.use(VueTouchEvents);
|
||||
app.directive('on-click-outside', vOnClickOutside);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Plugin } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import { locale } from 'n8n-design-system';
|
||||
import { locale, type N8nLocaleTranslateFn } from 'n8n-design-system';
|
||||
import type { INodeProperties, INodePropertyCollection, INodePropertyOptions } from 'n8n-workflow';
|
||||
|
||||
import type { INodeTranslationHeaders } from '@/Interface';
|
||||
|
@ -22,6 +22,8 @@ export const i18nInstance = createI18n({
|
|||
messages: { en: englishBaseText },
|
||||
});
|
||||
|
||||
type BaseTextOptions = { adjustToNumber?: number; interpolate?: Record<string, string | number> };
|
||||
|
||||
export class I18nClass {
|
||||
private baseTextCache = new Map<string, string>();
|
||||
|
||||
|
@ -48,10 +50,7 @@ export class I18nClass {
|
|||
/**
|
||||
* Render a string of base text, i.e. a string with a fixed path to the localized value. Optionally allows for [interpolation](https://kazupon.github.io/vue-i18n/guide/formatting.html#named-formatting) when the localized value contains a string between curly braces.
|
||||
*/
|
||||
baseText(
|
||||
key: BaseTextKey,
|
||||
options?: { adjustToNumber?: number; interpolate?: Record<string, string | number> },
|
||||
): string {
|
||||
baseText(key: BaseTextKey, options?: BaseTextOptions): string {
|
||||
// Create a unique cache key
|
||||
const cacheKey = `${key}-${JSON.stringify(options)}`;
|
||||
|
||||
|
@ -438,11 +437,10 @@ export function addHeaders(headers: INodeTranslationHeaders, language: string) {
|
|||
|
||||
export const i18n: I18nClass = new I18nClass();
|
||||
|
||||
export const I18nPlugin: Plugin<{}> = {
|
||||
export const I18nPlugin: Plugin = {
|
||||
async install(app) {
|
||||
locale.i18n((key: string, options?: { interpolate: Record<string, unknown> }) =>
|
||||
i18nInstance.global.t(key, options?.interpolate || {}),
|
||||
);
|
||||
locale.i18n(((key: string, options?: BaseTextOptions) =>
|
||||
i18nInstance.global.t(key, options?.interpolate ?? {})) as N8nLocaleTranslateFn);
|
||||
|
||||
app.config.globalProperties.$locale = i18n;
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ function addIcon(icon: IconDefinition) {
|
|||
library.add(icon);
|
||||
}
|
||||
|
||||
export const FontAwesomePlugin: Plugin<{}> = {
|
||||
export const FontAwesomePlugin: Plugin = {
|
||||
install: (app) => {
|
||||
addIcon(faAngleDoubleLeft);
|
||||
addIcon(faAngleDown);
|
||||
|
|
|
@ -32,8 +32,6 @@ export class N8nPlusEndpoint extends EndpointRepresentation<ComputedN8nPlusEndpo
|
|||
|
||||
messageOverlay: Overlay | null;
|
||||
|
||||
canvas: HTMLElement | null;
|
||||
|
||||
constructor(endpoint: Endpoint, params: N8nPlusEndpointParams) {
|
||||
super(endpoint, params);
|
||||
|
||||
|
@ -41,7 +39,6 @@ export class N8nPlusEndpoint extends EndpointRepresentation<ComputedN8nPlusEndpo
|
|||
this.label = '';
|
||||
this.stalkOverlay = null;
|
||||
this.messageOverlay = null;
|
||||
this.canvas = null;
|
||||
|
||||
this.unbindEvents();
|
||||
this.bindEvents();
|
||||
|
@ -111,7 +108,6 @@ export class N8nPlusEndpoint extends EndpointRepresentation<ComputedN8nPlusEndpo
|
|||
this[`${fnKey}Class`]('long-stalk');
|
||||
|
||||
if (this.label) {
|
||||
// @ts-expect-error: Overlay interface is missing the `canvas` property
|
||||
stalkOverlay.canvas.setAttribute('data-label', this.label);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ import * as N8nPlusEndpointRenderer from '@/plugins/jsplumb/N8nPlusEndpointRende
|
|||
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
||||
import * as N8nAddInputEndpointRenderer from '@/plugins/jsplumb/N8nAddInputEndpointRenderer';
|
||||
import { N8nAddInputEndpointHandler } from '@/plugins/jsplumb/N8nAddInputEndpointType';
|
||||
import type { AbstractConnector } from '@jsplumb/core';
|
||||
import { Connectors, EndpointFactory } from '@jsplumb/core';
|
||||
import type { Constructable } from '@jsplumb/util';
|
||||
|
||||
export const JsPlumbPlugin: Plugin<{}> = {
|
||||
export const JsPlumbPlugin: Plugin = {
|
||||
install: () => {
|
||||
Connectors.register(N8nConnector.type, N8nConnector);
|
||||
Connectors.register(N8nConnector.type, N8nConnector as Constructable<AbstractConnector>);
|
||||
|
||||
N8nPlusEndpointRenderer.register();
|
||||
EndpointFactory.registerHandler(N8nPlusEndpointHandler);
|
||||
|
|
|
@ -120,7 +120,7 @@ export class Telemetry {
|
|||
}
|
||||
}
|
||||
|
||||
page(route: Route) {
|
||||
page(route: RouteLocation) {
|
||||
if (this.rudderStack) {
|
||||
if (route.path === this.previousPath) {
|
||||
// avoid duplicate requests query is changed for example on search page
|
||||
|
@ -128,8 +128,8 @@ export class Telemetry {
|
|||
}
|
||||
this.previousPath = route.path;
|
||||
|
||||
const pageName = route.name;
|
||||
let properties: { [key: string]: string } = {};
|
||||
const pageName = String(route.name);
|
||||
let properties: Record<string, unknown> = {};
|
||||
if (route.meta?.telemetry && typeof route.meta.telemetry.getProperties === 'function') {
|
||||
properties = route.meta.telemetry.getProperties(route);
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ export class Telemetry {
|
|||
|
||||
export const telemetry = new Telemetry();
|
||||
|
||||
export const TelemetryPlugin: Plugin<{}> = {
|
||||
export const TelemetryPlugin: Plugin = {
|
||||
install(app) {
|
||||
app.config.globalProperties.$telemetry = telemetry;
|
||||
},
|
||||
|
|
|
@ -14,7 +14,7 @@ import { useSSOStore } from '@/stores/sso.store';
|
|||
import { EnterpriseEditionFeature, VIEWS, EDITABLE_CANVAS_VIEWS } from '@/constants';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { middleware } from '@/utils/rbac/middleware';
|
||||
import type { RouteConfig, RouterMiddleware } from '@/types/router';
|
||||
import type { RouterMiddleware } from '@/types/router';
|
||||
import { initializeCore } from '@/init';
|
||||
import { tryToParseNumber } from '@/utils/typesUtils';
|
||||
import { projectsRoutes } from '@/routes/projects.routes';
|
||||
|
@ -60,17 +60,17 @@ const WorkerView = async () => await import('./views/WorkerView.vue');
|
|||
const WorkflowHistory = async () => await import('@/views/WorkflowHistory.vue');
|
||||
const WorkflowOnboardingView = async () => await import('@/views/WorkflowOnboardingView.vue');
|
||||
|
||||
function getTemplatesRedirect(defaultRedirect: VIEWS[keyof VIEWS]) {
|
||||
function getTemplatesRedirect(defaultRedirect: VIEWS[keyof VIEWS]): { name: string } | false {
|
||||
const settingsStore = useSettingsStore();
|
||||
const isTemplatesEnabled: boolean = settingsStore.isTemplatesEnabled;
|
||||
if (!isTemplatesEnabled) {
|
||||
return { name: defaultRedirect || VIEWS.NOT_FOUND };
|
||||
return { name: `${defaultRedirect}` || VIEWS.NOT_FOUND };
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export const routes = [
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home/workflows',
|
||||
|
@ -742,7 +742,7 @@ export const routes = [
|
|||
},
|
||||
},
|
||||
},
|
||||
] as Array<RouteRecordRaw & RouteConfig>;
|
||||
];
|
||||
|
||||
function withCanvasReadOnlyMeta(route: RouteRecordRaw) {
|
||||
if (!route.meta) {
|
||||
|
@ -759,7 +759,7 @@ function withCanvasReadOnlyMeta(route: RouteRecordRaw) {
|
|||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.DEV ? '/' : window.BASE_PATH ?? '/'),
|
||||
scrollBehavior(to: RouteLocationNormalized & RouteConfig, from, savedPosition) {
|
||||
scrollBehavior(to: RouteLocationNormalized, _, savedPosition) {
|
||||
// saved position == null means the page is NOT visited from history (back button)
|
||||
if (savedPosition === null && to.name === VIEWS.TEMPLATES && to.meta?.setScrollPosition) {
|
||||
// for templates view, reset scroll position in this case
|
||||
|
@ -769,7 +769,7 @@ const router = createRouter({
|
|||
routes: routes.map(withCanvasReadOnlyMeta),
|
||||
});
|
||||
|
||||
router.beforeEach(async (to: RouteLocationNormalized & RouteConfig, from, next) => {
|
||||
router.beforeEach(async (to: RouteLocationNormalized, from, next) => {
|
||||
try {
|
||||
/**
|
||||
* Initialize application core
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import type { Connection, Endpoint, EndpointRepresentation, AbstractConnector, Overlay } from '@jsplumb/core';
|
||||
import type {
|
||||
Connection,
|
||||
Endpoint,
|
||||
EndpointRepresentation,
|
||||
AbstractConnector,
|
||||
Overlay,
|
||||
} from '@jsplumb/core';
|
||||
import type { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { N8nEndpointLabelLength } from '@/plugins/jsplumb/N8nPlusEndpointType';
|
||||
|
||||
declare module '@jsplumb/core' {
|
||||
interface EndpointRepresentation {
|
||||
|
@ -27,8 +34,9 @@ declare module '@jsplumb/core' {
|
|||
nodeName: string;
|
||||
nodeId: string;
|
||||
index: number;
|
||||
nodeType?: string;
|
||||
totalEndpoints: number;
|
||||
endpointLabelLength: number;
|
||||
endpointLabelLength?: N8nEndpointLabelLength;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,3 +1,21 @@
|
|||
/**
|
||||
* Modules
|
||||
*/
|
||||
|
||||
declare module 'vue-agile';
|
||||
|
||||
/**
|
||||
* File types
|
||||
*/
|
||||
|
||||
declare module '*.json';
|
||||
declare module '*.svg';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.jpeg';
|
||||
declare module '*.gif';
|
||||
declare module '*.webp';
|
||||
|
||||
declare module 'v3-infinite-loading' {
|
||||
import { Plugin, DefineComponent } from 'vue';
|
||||
|
36
packages/editor-ui/src/shims-vue.d.ts
vendored
36
packages/editor-ui/src/shims-vue.d.ts
vendored
|
@ -1,6 +1,16 @@
|
|||
import 'vue-router';
|
||||
import type { I18nClass } from '@/plugins/i18n';
|
||||
import type { Route } from 'vue-router';
|
||||
import type { Route, RouteLocation } from 'vue-router';
|
||||
import type { Telemetry } from '@/plugins/telemetry';
|
||||
import type { VIEWS } from '@/constants';
|
||||
import type { IPermissions } from '@/Interface';
|
||||
import type { MiddlewareOptions, RouterMiddlewareType } from '@/types/router';
|
||||
|
||||
export {};
|
||||
|
||||
/**
|
||||
* @docs https://vuejs.org/guide/typescript/options-api.html#augmenting-global-properties
|
||||
*/
|
||||
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomOptions {
|
||||
|
@ -17,6 +27,26 @@ declare module 'vue' {
|
|||
}
|
||||
|
||||
/**
|
||||
* @docs https://vuejs.org/guide/typescript/options-api.html#augmenting-global-properties
|
||||
* @docs https://router.vuejs.org/guide/advanced/meta
|
||||
*/
|
||||
export {};
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
nodeView?: boolean;
|
||||
templatesEnabled?: boolean;
|
||||
getRedirect?:
|
||||
| (() => { name: string } | false)
|
||||
| ((defaultRedirect: VIEWS[keyof VIEWS]) => { name: string } | false);
|
||||
permissions?: IPermissions;
|
||||
middleware?: RouterMiddlewareType[];
|
||||
middlewareOptions?: Partial<MiddlewareOptions>;
|
||||
telemetry?: {
|
||||
disabled?: true;
|
||||
pageCategory?: string;
|
||||
getProperties?: (route: RouteLocation) => Record<string, unknown>;
|
||||
};
|
||||
scrollOffset?: number;
|
||||
setScrollPosition?: (position: number) => void;
|
||||
readOnlyCanvas?: boolean;
|
||||
}
|
||||
}
|
||||
|
|
18
packages/editor-ui/src/shims.d.ts
vendored
18
packages/editor-ui/src/shims.d.ts
vendored
|
@ -1,11 +1,8 @@
|
|||
import { VNode, ComponentPublicInstance } from 'vue';
|
||||
import { PartialDeep } from 'type-fest';
|
||||
import { ExternalHooks } from '@/types/externalHooks';
|
||||
import type { VNode, ComponentPublicInstance } from 'vue';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { ExternalHooks } from '@/types/externalHooks';
|
||||
|
||||
declare module 'markdown-it-link-attributes';
|
||||
declare module 'markdown-it-emoji';
|
||||
declare module 'markdown-it-task-lists';
|
||||
declare module 'vue-agile';
|
||||
export {};
|
||||
|
||||
declare global {
|
||||
interface ImportMeta {
|
||||
|
@ -37,10 +34,3 @@ declare global {
|
|||
findLast(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.svg';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.jpeg';
|
||||
declare module '*.gif';
|
||||
declare module '*.webp';
|
||||
|
|
|
@ -19,6 +19,7 @@ import { MANUAL_TRIGGER_NODE_TYPE, START_NODE_TYPE } from '@/constants';
|
|||
import type {
|
||||
BeforeStartEventParams,
|
||||
BrowserJsPlumbInstance,
|
||||
ConstrainFunction,
|
||||
DragStopEventParams,
|
||||
} from '@jsplumb/browser-ui';
|
||||
import { newInstance } from '@jsplumb/browser-ui';
|
||||
|
@ -307,14 +308,14 @@ export const useCanvasStore = defineStore('canvas', () => {
|
|||
filter: '.node-description, .node-description .node-name, .node-description .node-subtitle',
|
||||
},
|
||||
});
|
||||
jsPlumbInstanceRef.value?.setDragConstrainFunction((pos: PointXY) => {
|
||||
jsPlumbInstanceRef.value?.setDragConstrainFunction(((pos: PointXY) => {
|
||||
const isReadOnly = uiStore.isReadOnlyView;
|
||||
if (isReadOnly) {
|
||||
// Do not allow to move nodes in readOnly mode
|
||||
return null;
|
||||
}
|
||||
return pos;
|
||||
});
|
||||
}) as ConstrainFunction);
|
||||
}
|
||||
|
||||
const jsPlumbInstance = computed(() => jsPlumbInstanceRef.value as BrowserJsPlumbInstance);
|
||||
|
|
|
@ -236,9 +236,6 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
|
|||
};
|
||||
}
|
||||
},
|
||||
enableOAuthCredential(credential: ICredentialsResponse): void {
|
||||
// enable oauth event to track change between modals
|
||||
},
|
||||
async fetchCredentialTypes(forceFetch: boolean): Promise<void> {
|
||||
if (this.allCredentialTypes.length > 0 && !forceFetch) {
|
||||
return;
|
||||
|
|
|
@ -201,7 +201,7 @@ export const useLogStreamingStore = defineStore('logStreaming', {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
async sendTestMessage(destination: MessageEventBusDestinationOptions) {
|
||||
async sendTestMessage(destination: MessageEventBusDestinationOptions): Promise<boolean> {
|
||||
if (!hasDestinationId(destination)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -233,6 +233,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||
isDragging: false,
|
||||
type: '',
|
||||
data: '',
|
||||
dimensions: null,
|
||||
activeTarget: null,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -155,7 +155,7 @@ export const usePostHog = defineStore('posthog', () => {
|
|||
trackExperimentsDebounced(featureFlags.value);
|
||||
} else {
|
||||
// depend on client side evaluation if serverside evaluation fails
|
||||
window.posthog?.onFeatureFlags?.((keys: string[], map: FeatureFlags) => {
|
||||
window.posthog?.onFeatureFlags?.((_, map: FeatureFlags) => {
|
||||
featureFlags.value = map;
|
||||
|
||||
// must be debounced because it is called multiple times by posthog
|
||||
|
|
|
@ -32,6 +32,7 @@ export const useRBACStore = defineStore(STORES.RBAC, () => {
|
|||
license: {},
|
||||
logStreaming: {},
|
||||
saml: {},
|
||||
securityAudit: {},
|
||||
});
|
||||
|
||||
function addGlobalRole(role: IRole) {
|
||||
|
|
|
@ -274,7 +274,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
|||
|
||||
this.setSettings(settings);
|
||||
this.settings.communityNodesEnabled = settings.communityNodesEnabled;
|
||||
this.setAllowedModules(settings.allowedModules as { builtIn?: string; external?: string });
|
||||
this.setAllowedModules(settings.allowedModules);
|
||||
this.setSaveDataErrorExecution(settings.saveDataErrorExecution);
|
||||
this.setSaveDataSuccessExecution(settings.saveDataSuccessExecution);
|
||||
this.setSaveManualExecutions(settings.saveManualExecutions);
|
||||
|
|
|
@ -63,7 +63,7 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||
return (id: string): null | ITemplatesCollection => this.collections[id];
|
||||
},
|
||||
getCategoryById() {
|
||||
return (id: string): null | ITemplatesCategory => this.categories[id];
|
||||
return (id: string): null | ITemplatesCategory => this.categories[id as unknown as number];
|
||||
},
|
||||
getSearchedCollections() {
|
||||
return (query: ITemplatesQuery) => {
|
||||
|
@ -121,7 +121,7 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||
* Constructs URLSearchParams object based on the default parameters for the template repository
|
||||
* and provided additional parameters
|
||||
*/
|
||||
websiteTemplateRepositoryParameters(roleOverride?: string) {
|
||||
websiteTemplateRepositoryParameters(_roleOverride?: string) {
|
||||
const rootStore = useRootStore();
|
||||
const userStore = useUsersStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
@ -131,8 +131,12 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||
utm_n8n_version: rootStore.versionCli,
|
||||
utm_awc: String(workflowsStore.activeWorkflows.length),
|
||||
};
|
||||
const userRole: string | undefined =
|
||||
userStore.currentUserCloudInfo?.role ?? userStore.currentUser?.personalizationAnswers?.role;
|
||||
const userRole: string | null | undefined =
|
||||
userStore.currentUserCloudInfo?.role ??
|
||||
(userStore.currentUser?.personalizationAnswers &&
|
||||
'role' in userStore.currentUser.personalizationAnswers
|
||||
? userStore.currentUser.personalizationAnswers.role
|
||||
: undefined);
|
||||
|
||||
if (userRole) {
|
||||
defaultParameters.utm_user_role = userRole;
|
||||
|
|
|
@ -2,9 +2,7 @@ import type {
|
|||
NavigationGuardNext,
|
||||
NavigationGuardWithThis,
|
||||
RouteLocationNormalized,
|
||||
RouteLocation,
|
||||
} from 'vue-router';
|
||||
import type { IPermissions } from '@/Interface';
|
||||
import type {
|
||||
AuthenticatedPermissionOptions,
|
||||
CustomPermissionOptions,
|
||||
|
@ -32,24 +30,6 @@ export type MiddlewareOptions = {
|
|||
role: RolePermissionOptions;
|
||||
};
|
||||
|
||||
export interface RouteConfig {
|
||||
meta: {
|
||||
nodeView?: boolean;
|
||||
templatesEnabled?: boolean;
|
||||
getRedirect?: () => { name: string } | false;
|
||||
permissions?: IPermissions;
|
||||
middleware?: RouterMiddlewareType[];
|
||||
middlewareOptions?: Partial<MiddlewareOptions>;
|
||||
telemetry?: {
|
||||
disabled?: true;
|
||||
getProperties: (route: RouteLocation) => object;
|
||||
};
|
||||
scrollOffset?: number;
|
||||
setScrollPosition?: (position: number) => void;
|
||||
readOnlyCanvas?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type RouterMiddlewareReturnType = ReturnType<NavigationGuardWithThis<undefined>>;
|
||||
|
||||
export interface RouterMiddleware<RouterMiddlewareOptions = {}> {
|
||||
|
|
|
@ -5,6 +5,7 @@ import type {
|
|||
ITemplatesNode,
|
||||
IVersionNode,
|
||||
NodeAuthenticationOption,
|
||||
SimplifiedNodeType,
|
||||
} from '@/Interface';
|
||||
import {
|
||||
CORE_NODES_CATEGORY,
|
||||
|
@ -451,21 +452,21 @@ export const getThemedValue = <T extends string>(
|
|||
};
|
||||
|
||||
export const getNodeIcon = (
|
||||
nodeType: INodeTypeDescription | IVersionNode,
|
||||
nodeType: INodeTypeDescription | SimplifiedNodeType | IVersionNode,
|
||||
theme: AppliedThemeOption = 'light',
|
||||
): string | null => {
|
||||
return getThemedValue(nodeType.icon, theme);
|
||||
};
|
||||
|
||||
export const getNodeIconUrl = (
|
||||
nodeType: INodeTypeDescription | IVersionNode,
|
||||
nodeType: INodeTypeDescription | SimplifiedNodeType | IVersionNode,
|
||||
theme: AppliedThemeOption = 'light',
|
||||
): string | null => {
|
||||
return getThemedValue(nodeType.iconUrl, theme);
|
||||
};
|
||||
|
||||
export const getBadgeIconUrl = (
|
||||
nodeType: INodeTypeDescription,
|
||||
nodeType: INodeTypeDescription | SimplifiedNodeType,
|
||||
theme: AppliedThemeOption = 'light',
|
||||
): string | null => {
|
||||
return getThemedValue(nodeType.badgeIconUrl, theme);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { isNumber, isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
import { NODE_OUTPUT_DEFAULT_KEY, STICKY_NODE_TYPE } from '@/constants';
|
||||
import type { EndpointMeta, EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
||||
import type { EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
||||
import type { ArrayAnchorSpec, ConnectorSpec, OverlaySpec, PaintStyle } from '@jsplumb/common';
|
||||
import type { Connection, Endpoint, SelectOptions } from '@jsplumb/core';
|
||||
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
||||
|
@ -73,7 +73,7 @@ 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 & EndpointMeta) {
|
||||
getEndpointOffset(endpoint: Endpoint) {
|
||||
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;
|
||||
|
@ -320,7 +320,7 @@ export const getOutputNameOverlay = (
|
|||
options: {
|
||||
id: OVERLAY_OUTPUT_NAME_LABEL,
|
||||
visible: true,
|
||||
create: (ep: Endpoint & EndpointMeta) => {
|
||||
create: (ep: Endpoint) => {
|
||||
const label = document.createElement('div');
|
||||
label.innerHTML = labelText;
|
||||
label.classList.add('node-output-endpoint-label');
|
||||
|
@ -1120,7 +1120,7 @@ export const getPlusEndpoint = (
|
|||
): Endpoint | undefined => {
|
||||
const endpoints = getJSPlumbEndpoints(node, instance);
|
||||
return endpoints.find(
|
||||
(endpoint: Endpoint & EndpointMeta) =>
|
||||
(endpoint: Endpoint) =>
|
||||
endpoint.endpoint.type === 'N8nPlus' && endpoint?.__meta?.index === outputIndex,
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ describe('templateTransforms', () => {
|
|||
getNodeType: vitest.fn(),
|
||||
};
|
||||
const node = newWorkflowTemplateNode({
|
||||
id: 'twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
credentials: {
|
||||
twitterOAuth1Api: 'old1',
|
||||
|
@ -40,6 +41,7 @@ describe('templateTransforms', () => {
|
|||
getNodeType: vitest.fn(),
|
||||
};
|
||||
const node = newWorkflowTemplateNode({
|
||||
id: 'twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
});
|
||||
const toReplaceWith = {
|
||||
|
|
|
@ -7,6 +7,7 @@ export const newWorkflowTemplateNode = ({
|
|||
type,
|
||||
...optionalOpts
|
||||
}: Pick<IWorkflowTemplateNode, 'type'> &
|
||||
Pick<IWorkflowTemplateNode, 'id'> &
|
||||
Partial<IWorkflowTemplateNode>): IWorkflowTemplateNode => ({
|
||||
type,
|
||||
name: faker.commerce.productName(),
|
||||
|
@ -306,7 +307,6 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
|||
export const fullCreateApiEndpointTemplate = {
|
||||
id: 1750,
|
||||
name: 'Creating an API endpoint',
|
||||
recentViews: 9899,
|
||||
totalViews: 13265,
|
||||
createdAt: '2022-07-06T14:45:19.659Z',
|
||||
description:
|
||||
|
@ -393,7 +393,6 @@ export const fullCreateApiEndpointTemplate = {
|
|||
},
|
||||
},
|
||||
},
|
||||
lastUpdatedBy: 1,
|
||||
workflowInfo: {
|
||||
nodeCount: 2,
|
||||
nodeTypes: {},
|
||||
|
|
|
@ -5,7 +5,7 @@ import type {
|
|||
TriggerPanelDefinition,
|
||||
} from 'n8n-workflow';
|
||||
import { nodeConnectionTypes } from 'n8n-workflow';
|
||||
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
import type { IExecutionResponse, ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
import type { jsPlumbDOMElement } from '@jsplumb/browser-ui';
|
||||
import type { Connection } from '@jsplumb/core';
|
||||
|
||||
|
@ -73,3 +73,9 @@ export function isTriggerPanelObject(
|
|||
): triggerPanel is TriggerPanelDefinition {
|
||||
return triggerPanel !== undefined && typeof triggerPanel === 'object' && triggerPanel !== null;
|
||||
}
|
||||
|
||||
export function isFullExecutionResponse(
|
||||
execution: IExecutionResponse | null,
|
||||
): execution is IExecutionResponse {
|
||||
return !!execution && 'status' in execution;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
import type { ICredentialsResponse, ICredentialTypeMap } from '@/Interface';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import type { IResource } from '@/components/layouts/ResourcesListLayout.vue';
|
||||
import ResourcesListLayout from '@/components/layouts/ResourcesListLayout.vue';
|
||||
import CredentialCard from '@/components/CredentialCard.vue';
|
||||
import type { ICredentialType } from 'n8n-workflow';
|
||||
|
@ -106,8 +107,14 @@ export default defineComponent({
|
|||
useExternalSecretsStore,
|
||||
useProjectsStore,
|
||||
),
|
||||
allCredentials(): ICredentialsResponse[] {
|
||||
return this.credentialsStore.allCredentials;
|
||||
allCredentials(): IResource[] {
|
||||
return this.credentialsStore.allCredentials.map((credential) => ({
|
||||
id: credential.id,
|
||||
name: credential.name,
|
||||
value: '',
|
||||
updatedAt: credential.updatedAt,
|
||||
createdAt: credential.createdAt,
|
||||
}));
|
||||
},
|
||||
allCredentialTypes(): ICredentialType[] {
|
||||
return this.credentialsStore.allCredentialTypes;
|
||||
|
|
|
@ -2178,7 +2178,11 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
return await this.importWorkflowData(workflowData!, 'paste', false);
|
||||
if (!workflowData) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await this.importWorkflowData(workflowData, 'paste', false);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2205,7 +2209,7 @@ export default defineComponent({
|
|||
|
||||
// Imports the given workflow data into the current workflow
|
||||
async importWorkflowData(
|
||||
workflowData: IWorkflowToShare,
|
||||
workflowData: IWorkflowDataUpdate,
|
||||
source: string,
|
||||
importTags = true,
|
||||
): Promise<void> {
|
||||
|
@ -2342,7 +2346,7 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
|
||||
removeUnknownCredentials(workflow: IWorkflowToShare) {
|
||||
removeUnknownCredentials(workflow: IWorkflowDataUpdate) {
|
||||
if (!workflow?.nodes) return;
|
||||
|
||||
for (const node of workflow.nodes) {
|
||||
|
|
|
@ -93,6 +93,7 @@ describe('SetupWorkflowFromTemplateView store', () => {
|
|||
|
||||
const templatesStore = useTemplatesStore();
|
||||
const workflow = testData.newFullOneNodeTemplate({
|
||||
id: 'workflow',
|
||||
name: 'Test',
|
||||
type: 'n8n-nodes-base.httpRequest',
|
||||
typeVersion: 1,
|
||||
|
|
|
@ -15,6 +15,7 @@ const objToMap = <TKey extends string, T>(obj: Record<TKey, T>) => {
|
|||
describe('useCredentialSetupState', () => {
|
||||
const nodesByName = {
|
||||
Twitter: {
|
||||
id: 'twitter',
|
||||
name: 'Twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
position: [720, -220],
|
||||
|
@ -58,12 +59,14 @@ describe('useCredentialSetupState', () => {
|
|||
it('returns credentials grouped when the credential names are the same', () => {
|
||||
const [node1, node2] = [
|
||||
newWorkflowTemplateNode({
|
||||
id: 'twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
credentials: {
|
||||
twitterOAuth1Api: 'credential',
|
||||
},
|
||||
}) as IWorkflowTemplateNodeWithCredentials,
|
||||
newWorkflowTemplateNode({
|
||||
id: 'telegram',
|
||||
type: 'n8n-nodes-base.telegram',
|
||||
credentials: {
|
||||
telegramApi: 'credential',
|
||||
|
|
|
@ -122,6 +122,16 @@ export default defineComponent({
|
|||
TemplateList,
|
||||
TemplatesView,
|
||||
},
|
||||
beforeRouteLeave(_to, _from, next) {
|
||||
const contentArea = document.getElementById('content');
|
||||
if (contentArea) {
|
||||
// When leaving this page, store current scroll position in route data
|
||||
this.$route.meta?.setScrollPosition?.(contentArea.scrollTop);
|
||||
}
|
||||
|
||||
this.trackSearch();
|
||||
next();
|
||||
},
|
||||
setup() {
|
||||
const { callDebounced } = useDebounce();
|
||||
|
||||
|
@ -406,21 +416,6 @@ export default defineComponent({
|
|||
}, 0);
|
||||
},
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
const contentArea = document.getElementById('content');
|
||||
if (contentArea) {
|
||||
// When leaving this page, store current scroll position in route data
|
||||
if (
|
||||
this.$route.meta?.setScrollPosition &&
|
||||
typeof this.$route.meta.setScrollPosition === 'function'
|
||||
) {
|
||||
this.$route.meta.setScrollPosition(contentArea.scrollTop);
|
||||
}
|
||||
}
|
||||
|
||||
this.trackSearch();
|
||||
next();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ const resourceToEnvironmentVariable = (data: IResource): EnvironmentVariable =>
|
|||
return {
|
||||
id: data.id,
|
||||
key: data.name,
|
||||
value: data.value,
|
||||
value: 'value' in data ? data.value : '',
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { createUser } from '@/__tests__/data/users';
|
|||
import { createProjectListItem } from '@/__tests__/data/projects';
|
||||
import { useRBACStore } from '@/stores/rbac.store';
|
||||
import { DELETE_USER_MODAL_KEY } from '@/constants';
|
||||
import { expect } from 'vitest';
|
||||
import * as usersApi from '@/api/users';
|
||||
|
||||
const wrapperComponentWithModal = {
|
||||
components: { SettingsUsersView, ModalRoot, DeleteUserModal },
|
||||
|
@ -34,31 +34,34 @@ const loggedInUser = createUser();
|
|||
const users = Array.from({ length: 3 }, createUser);
|
||||
const personalProjects = Array.from({ length: 3 }, createProjectListItem);
|
||||
|
||||
let pinia: ReturnType<typeof createPinia>;
|
||||
let projectsStore: ReturnType<typeof useProjectsStore>;
|
||||
let usersStore: ReturnType<typeof useUsersStore>;
|
||||
let rbacStore: ReturnType<typeof useRBACStore>;
|
||||
|
||||
describe('SettingsUsersView', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
pinia = createPinia();
|
||||
setActivePinia(pinia);
|
||||
projectsStore = useProjectsStore();
|
||||
usersStore = useUsersStore();
|
||||
rbacStore = useRBACStore();
|
||||
|
||||
vi.spyOn(rbacStore, 'hasScope').mockReturnValue(true);
|
||||
vi.spyOn(usersStore, 'fetchUsers').mockImplementation(async () => await Promise.resolve());
|
||||
vi.spyOn(usersApi, 'getUsers').mockResolvedValue(users);
|
||||
vi.spyOn(usersStore, 'allUsers', 'get').mockReturnValue(users);
|
||||
vi.spyOn(usersStore, 'getUserById', 'get').mockReturnValue(() => loggedInUser);
|
||||
vi.spyOn(projectsStore, 'getAllProjects').mockImplementation(
|
||||
async () => await Promise.resolve(),
|
||||
);
|
||||
vi.spyOn(projectsStore, 'personalProjects', 'get').mockReturnValue(personalProjects);
|
||||
|
||||
usersStore.currentUserId = loggedInUser.id;
|
||||
});
|
||||
|
||||
it('should show confirmation modal before deleting user and delete with transfer', async () => {
|
||||
const deleteUserSpy = vi.spyOn(usersStore, 'deleteUser').mockImplementation(async () => {});
|
||||
|
||||
const { getByTestId } = renderComponent();
|
||||
const { getByTestId } = renderComponent({ pinia });
|
||||
|
||||
const userListItem = getByTestId(`user-list-item-${users[0].email}`);
|
||||
expect(userListItem).toBeInTheDocument();
|
||||
|
@ -98,7 +101,7 @@ describe('SettingsUsersView', () => {
|
|||
it('should show confirmation modal before deleting user and delete without transfer', async () => {
|
||||
const deleteUserSpy = vi.spyOn(usersStore, 'deleteUser').mockImplementation(async () => {});
|
||||
|
||||
const { getByTestId } = renderComponent();
|
||||
const { getByTestId } = renderComponent({ pinia });
|
||||
|
||||
const userListItem = getByTestId(`user-list-item-${users[0].email}`);
|
||||
expect(userListItem).toBeInTheDocument();
|
||||
|
|
|
@ -14,11 +14,7 @@
|
|||
"types": [
|
||||
"vitest/globals",
|
||||
"unplugin-icons/types/vue",
|
||||
"./src/shims.d.ts",
|
||||
"./src/shims-vue.d.ts",
|
||||
"./src/v3-infinite-loading.d.ts",
|
||||
"../workflow/src/types.d.ts",
|
||||
"../design-system/src/shims-markdown-it.d.ts"
|
||||
"../design-system/src/shims-modules.d.ts"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
|
|
|
@ -77,8 +77,12 @@ const plugins = [
|
|||
}),
|
||||
vue(),
|
||||
];
|
||||
if (process.env.ENABLE_TYPE_CHECKING === 'true') {
|
||||
plugins.push(checker({ vueTsc: true }));
|
||||
|
||||
if (!process.env.VITEST) {
|
||||
plugins.push({
|
||||
...checker({ vueTsc: true }),
|
||||
apply: 'build'
|
||||
});
|
||||
}
|
||||
|
||||
const { SENTRY_AUTH_TOKEN: authToken, RELEASE: release } = process.env;
|
||||
|
|
|
@ -116,21 +116,21 @@ export interface IUser {
|
|||
lastName: string;
|
||||
}
|
||||
|
||||
export type ProjectSharingData = {
|
||||
id: string;
|
||||
name: string | null;
|
||||
type: 'personal' | 'team' | 'public';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export interface ICredentialsDecrypted {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
data?: ICredentialDataDecryptedObject;
|
||||
homeProject?: {
|
||||
id: string;
|
||||
name: string | null;
|
||||
type: 'personal' | 'team' | 'public';
|
||||
};
|
||||
sharedWithProjects?: Array<{
|
||||
id: string;
|
||||
name: string | null;
|
||||
type: 'personal' | 'team' | 'public';
|
||||
}>;
|
||||
homeProject?: ProjectSharingData;
|
||||
sharedWithProjects?: ProjectSharingData[];
|
||||
}
|
||||
|
||||
export interface ICredentialsEncrypted {
|
||||
|
@ -340,7 +340,13 @@ export interface ICredentialData {
|
|||
}
|
||||
|
||||
// The encrypted credentials which the nodes can access
|
||||
export type CredentialInformation = string | number | boolean | IDataObject | IDataObject[];
|
||||
export type CredentialInformation =
|
||||
| string
|
||||
| string[]
|
||||
| number
|
||||
| boolean
|
||||
| IDataObject
|
||||
| IDataObject[];
|
||||
|
||||
// The encrypted credentials which the nodes can access
|
||||
export interface ICredentialDataDecryptedObject {
|
||||
|
@ -1530,7 +1536,7 @@ export interface INodeIssueObjectProperty {
|
|||
export interface INodeIssueData {
|
||||
node: string;
|
||||
type: INodeIssueTypes;
|
||||
value: boolean | string | string[] | INodeIssueObjectProperty;
|
||||
value: null | boolean | string | string[] | INodeIssueObjectProperty;
|
||||
}
|
||||
|
||||
export interface INodeIssues {
|
||||
|
@ -1571,7 +1577,7 @@ export interface INodeTypeBaseDescription {
|
|||
icon?: Themed<Icon>;
|
||||
iconColor?: NodeIconColor;
|
||||
iconUrl?: Themed<string>;
|
||||
badgeIconUrl?: string;
|
||||
badgeIconUrl?: Themed<string>;
|
||||
group: string[];
|
||||
description: string;
|
||||
documentationUrl?: string;
|
||||
|
|
|
@ -21,6 +21,13 @@ export const enum MessageEventBusDestinationTypeNames {
|
|||
syslog = '$$MessageEventBusDestinationSyslog',
|
||||
}
|
||||
|
||||
export const messageEventBusDestinationTypeNames = [
|
||||
MessageEventBusDestinationTypeNames.abstract,
|
||||
MessageEventBusDestinationTypeNames.webhook,
|
||||
MessageEventBusDestinationTypeNames.sentry,
|
||||
MessageEventBusDestinationTypeNames.syslog,
|
||||
];
|
||||
|
||||
// ===============================
|
||||
// Event Message Interfaces
|
||||
// ===============================
|
||||
|
|
Loading…
Reference in a new issue