refactor(editor): Move templates api to @n8n/rest-api-client package (no-changelog) (#16542)

This commit is contained in:
Alex Grozav
2025-06-23 14:04:33 +03:00
committed by GitHub
parent 662ac1bb57
commit 9c654dbbf7
59 changed files with 423 additions and 417 deletions

View File

@@ -18,3 +18,6 @@ CHANGELOG.md
**/*.js
**/*.json
**/*.jsonc
# Auto-generated
**/components.d.ts

View File

@@ -14,7 +14,10 @@ export * from './roles';
export * from './settings';
export * from './module-settings';
export * from './sso';
export * from './tags';
export * from './templates';
export * from './ui';
export * from './versions';
export * from './webhooks';
export * from './workflowHistory';
export * from './workflows';

View File

@@ -0,0 +1,7 @@
export interface ITag {
id: string;
name: string;
usageCount?: number;
createdAt?: string;
updatedAt?: string;
}

View File

@@ -0,0 +1,195 @@
import type { RawAxiosRequestHeaders } from 'axios';
import type { INode, INodeCredentialsDetails } from 'n8n-workflow';
import type { VersionNode } from './versions';
import type { WorkflowData } from './workflows';
import { get } from '../utils';
export interface IWorkflowTemplateNode
extends Pick<
INode,
'name' | 'type' | 'position' | 'parameters' | 'typeVersion' | 'webhookId' | 'id' | 'disabled'
> {
// The credentials in a template workflow have a different type than in a regular workflow
credentials?: IWorkflowTemplateNodeCredentials;
}
export interface IWorkflowTemplateNodeCredentials {
[key: string]: string | INodeCredentialsDetails;
}
export interface IWorkflowTemplate {
id: number;
name: string;
workflow: Pick<WorkflowData, 'connections' | 'settings' | 'pinData'> & {
nodes: IWorkflowTemplateNode[];
};
}
export interface ITemplatesNode extends VersionNode {
id: number;
categories?: ITemplatesCategory[];
}
export interface ITemplatesCollection {
id: number;
name: string;
nodes: ITemplatesNode[];
workflows: Array<{ id: number }>;
}
interface ITemplatesImage {
id: number;
url: string;
}
interface ITemplatesCollectionExtended extends ITemplatesCollection {
description: string | null;
image: ITemplatesImage[];
categories: ITemplatesCategory[];
createdAt: string;
}
export interface ITemplatesCollectionFull extends ITemplatesCollectionExtended {
full: true;
}
export interface ITemplatesCollectionResponse extends ITemplatesCollectionExtended {
workflows: ITemplatesWorkflow[];
}
/**
* A template without the actual workflow definition
*/
export interface ITemplatesWorkflow {
id: number;
createdAt: string;
name: string;
nodes: ITemplatesNode[];
totalViews: number;
user: {
username: string;
};
}
export interface ITemplatesWorkflowInfo {
nodeCount: number;
nodeTypes: {
[key: string]: {
count: number;
};
};
}
export type TemplateSearchFacet = {
field_name: string;
sampled: boolean;
stats: {
total_values: number;
};
counts: Array<{
count: number;
highlighted: string;
value: string;
}>;
};
export interface ITemplatesWorkflowResponse extends ITemplatesWorkflow, IWorkflowTemplate {
description: string | null;
image: ITemplatesImage[];
categories: ITemplatesCategory[];
workflowInfo: ITemplatesWorkflowInfo;
}
/**
* A template with also the full workflow definition
*/
export interface ITemplatesWorkflowFull extends ITemplatesWorkflowResponse {
full: true;
}
export interface ITemplatesQuery {
categories: string[];
search: string;
}
export interface ITemplatesCategory {
id: number;
name: string;
}
function stringifyArray(arr: string[]) {
return arr.join(',');
}
export async function testHealthEndpoint(apiEndpoint: string) {
return await get(apiEndpoint, '/health');
}
export async function getCategories(
apiEndpoint: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ categories: ITemplatesCategory[] }> {
return await get(apiEndpoint, '/templates/categories', undefined, headers);
}
export async function getCollections(
apiEndpoint: string,
query: ITemplatesQuery,
headers?: RawAxiosRequestHeaders,
): Promise<{ collections: ITemplatesCollection[] }> {
return await get(
apiEndpoint,
'/templates/collections',
{ category: query.categories, search: query.search },
headers,
);
}
export async function getWorkflows(
apiEndpoint: string,
query: { page: number; limit: number; categories: string[]; search: string },
headers?: RawAxiosRequestHeaders,
): Promise<{
totalWorkflows: number;
workflows: ITemplatesWorkflow[];
filters: TemplateSearchFacet[];
}> {
return await get(
apiEndpoint,
'/templates/search',
{
page: query.page,
rows: query.limit,
category: stringifyArray(query.categories),
search: query.search,
},
headers,
);
}
export async function getCollectionById(
apiEndpoint: string,
collectionId: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ collection: ITemplatesCollectionResponse }> {
return await get(apiEndpoint, `/templates/collections/${collectionId}`, undefined, headers);
}
export async function getTemplateById(
apiEndpoint: string,
templateId: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ workflow: ITemplatesWorkflowResponse }> {
return await get(apiEndpoint, `/templates/workflows/${templateId}`, undefined, headers);
}
export async function getWorkflowTemplate(
apiEndpoint: string,
templateId: string,
headers?: RawAxiosRequestHeaders,
): Promise<IWorkflowTemplate> {
return await get(apiEndpoint, `/workflows/templates/${templateId}`, undefined, headers);
}

View File

@@ -0,0 +1,42 @@
import type { IWorkflowSettings, IConnections, INode, IPinData } from 'n8n-workflow';
import type { ITag } from './tags';
export interface WorkflowMetadata {
onboardingId?: string;
templateId?: string;
instanceId?: string;
templateCredsSetupCompleted?: boolean;
}
// Simple version of n8n-workflow.Workflow
export interface WorkflowData {
id?: string;
name?: string;
active?: boolean;
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
tags?: string[];
pinData?: IPinData;
versionId?: string;
meta?: WorkflowMetadata;
}
export interface WorkflowDataUpdate {
id?: string;
name?: string;
nodes?: INode[];
connections?: IConnections;
settings?: IWorkflowSettings;
active?: boolean;
tags?: ITag[] | string[]; // string[] when store or requested, ITag[] from API response
pinData?: IPinData;
versionId?: string;
meta?: WorkflowMetadata;
parentFolderId?: string;
}
export interface WorkflowDataCreate extends WorkflowDataUpdate {
projectId?: string;
}

View File

@@ -41,15 +41,21 @@ import type {
INodeExecutionData,
INodeProperties,
NodeConnectionType,
INodeCredentialsDetails,
StartNodeData,
IPersonalizationSurveyAnswersV4,
AnnotationVote,
ITaskData,
ISourceData,
} from 'n8n-workflow';
import type { Version, VersionNode } from '@n8n/rest-api-client/api/versions';
import type { Version } from '@n8n/rest-api-client/api/versions';
import type { Cloud, InstanceUsage } from '@n8n/rest-api-client/api/cloudPlans';
import type {
WorkflowMetadata,
WorkflowData,
WorkflowDataCreate,
WorkflowDataUpdate,
} from '@n8n/rest-api-client/api/workflows';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type {
AI_NODE_CREATOR_VIEW,
@@ -203,7 +209,7 @@ export interface IAiData {
}
export interface IStartRunData {
workflowData: IWorkflowData;
workflowData: WorkflowData;
startNodes?: StartNodeData[];
destinationNode?: string;
runData?: IRunData;
@@ -230,49 +236,17 @@ export interface ITableData {
};
}
// Simple version of n8n-workflow.Workflow
export interface IWorkflowData {
id?: string;
name?: string;
active?: boolean;
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
tags?: string[];
pinData?: IPinData;
versionId?: string;
meta?: WorkflowMetadata;
}
export interface IWorkflowDataUpdate {
id?: string;
name?: string;
nodes?: INode[];
connections?: IConnections;
settings?: IWorkflowSettings;
active?: boolean;
tags?: ITag[] | string[]; // string[] when store or requested, ITag[] from API response
pinData?: IPinData;
versionId?: string;
meta?: WorkflowMetadata;
parentFolderId?: string;
}
export interface IWorkflowDataCreate extends IWorkflowDataUpdate {
projectId?: string;
}
/**
* Workflow data with mandatory `templateId`
* This is used to identify sample workflows that we create for onboarding
*/
export interface WorkflowDataWithTemplateId extends Omit<IWorkflowDataCreate, 'meta'> {
export interface WorkflowDataWithTemplateId extends Omit<WorkflowDataCreate, 'meta'> {
meta: WorkflowMetadata & {
templateId: Required<WorkflowMetadata>['templateId'];
};
}
export interface IWorkflowToShare extends IWorkflowDataUpdate {
export interface IWorkflowToShare extends WorkflowDataUpdate {
meta: WorkflowMetadata;
}
@@ -281,38 +255,10 @@ export interface NewWorkflowResponse {
defaultSettings: IWorkflowSettings;
}
export interface IWorkflowTemplateNode
extends Pick<
INodeUi,
'name' | 'type' | 'position' | 'parameters' | 'typeVersion' | 'webhookId' | 'id' | 'disabled'
> {
// The credentials in a template workflow have a different type than in a regular workflow
credentials?: IWorkflowTemplateNodeCredentials;
}
export interface IWorkflowTemplateNodeCredentials {
[key: string]: string | INodeCredentialsDetails;
}
export interface IWorkflowTemplate {
id: number;
name: string;
workflow: Pick<IWorkflowData, 'connections' | 'settings' | 'pinData'> & {
nodes: IWorkflowTemplateNode[];
};
}
export interface INewWorkflowData {
name: string;
}
export interface WorkflowMetadata {
onboardingId?: string;
templateId?: string;
instanceId?: string;
templateCredsSetupCompleted?: boolean;
}
// Almost identical to cli.Interfaces.ts
export interface IWorkflowDb {
id: string;
@@ -627,93 +573,6 @@ export interface IUserPermissions {
};
}
export interface ITemplatesCollection {
id: number;
name: string;
nodes: ITemplatesNode[];
workflows: Array<{ id: number }>;
}
interface ITemplatesImage {
id: number;
url: string;
}
interface ITemplatesCollectionExtended extends ITemplatesCollection {
description: string | null;
image: ITemplatesImage[];
categories: ITemplatesCategory[];
createdAt: string;
}
export interface ITemplatesCollectionFull extends ITemplatesCollectionExtended {
full: true;
}
export interface ITemplatesCollectionResponse extends ITemplatesCollectionExtended {
workflows: ITemplatesWorkflow[];
}
/**
* A template without the actual workflow definition
*/
export interface ITemplatesWorkflow {
id: number;
createdAt: string;
name: string;
nodes: ITemplatesNode[];
totalViews: number;
user: {
username: string;
};
}
export interface ITemplatesWorkflowInfo {
nodeCount: number;
nodeTypes: {
[key: string]: {
count: number;
};
};
}
export type TemplateSearchFacet = {
field_name: string;
sampled: boolean;
stats: {
total_values: number;
};
counts: Array<{
count: number;
highlighted: string;
value: string;
}>;
};
export interface ITemplatesWorkflowResponse extends ITemplatesWorkflow, IWorkflowTemplate {
description: string | null;
image: ITemplatesImage[];
categories: ITemplatesCategory[];
workflowInfo: ITemplatesWorkflowInfo;
}
/**
* A template with also the full workflow definition
*/
export interface ITemplatesWorkflowFull extends ITemplatesWorkflowResponse {
full: true;
}
export interface ITemplatesQuery {
categories: string[];
search: string;
}
export interface ITemplatesCategory {
id: number;
name: string;
}
export type WorkflowCallerPolicyDefaultOption = 'any' | 'none' | 'workflowsFromAList';
export interface IWorkflowSettings extends IWorkflowSettingsWorkflow {
@@ -893,13 +752,6 @@ export type NodeTypeSelectedPayload = {
export interface SubcategorizedNodeTypes {
[subcategory: string]: INodeCreateElement[];
}
export interface ITag {
id: string;
name: string;
usageCount?: number;
createdAt?: string;
updatedAt?: string;
}
export interface ITagRow {
tag?: ITag;
@@ -911,11 +763,6 @@ export interface ITagRow {
canDelete?: boolean;
}
export interface ITemplatesNode extends VersionNode {
id: number;
categories?: ITemplatesCategory[];
}
export interface INodeMetadata {
parametersLastUpdatedAt?: number;
pinnedDataLastUpdatedAt?: number;
@@ -1130,28 +977,6 @@ export interface INodeTypesState {
nodeTypes: NodeTypesByTypeNameAndVersion;
}
export interface ITemplateState {
categories: ITemplatesCategory[];
collections: { [id: string]: ITemplatesCollection };
workflows: { [id: string]: ITemplatesWorkflow | ITemplatesWorkflowFull };
workflowSearches: {
[search: string]: {
workflowIds: string[];
totalWorkflows: number;
loadingMore?: boolean;
categories?: ITemplatesCategory[];
};
};
collectionSearches: {
[search: string]: {
collectionIds: string[];
};
};
currentSessionId: string;
previousSessionId: string;
currentN8nPath: string;
}
export interface IVersionsState {
versionNotificationSettings: IVersionNotificationSettings;
nextVersions: Version[];

View File

@@ -1,5 +1,5 @@
import { Factory } from 'miragejs';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { faker } from '@faker-js/faker';
export const tagFactory = Factory.extend<ITag>({

View File

@@ -1,4 +1,4 @@
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
export const tags: ITag[] = [
{
id: '1',

View File

@@ -1,4 +1,4 @@
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { Model } from 'miragejs';
import type { ModelDefinition } from 'miragejs/-types';

View File

@@ -1,4 +1,4 @@
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { IRestApiContext } from '@n8n/rest-api-client';
import { makeRestApiRequest } from '@n8n/rest-api-client';
import type { CreateOrUpdateTagRequestDto, RetrieveTagQueryDto } from '@n8n/api-types';

View File

@@ -1,86 +0,0 @@
import type { RawAxiosRequestHeaders } from 'axios';
import type {
ITemplatesCategory,
ITemplatesCollection,
ITemplatesQuery,
ITemplatesWorkflow,
ITemplatesCollectionResponse,
ITemplatesWorkflowResponse,
IWorkflowTemplate,
TemplateSearchFacet,
} from '@/Interface';
import { get } from '@n8n/rest-api-client';
function stringifyArray(arr: string[]) {
return arr.join(',');
}
export async function testHealthEndpoint(apiEndpoint: string) {
return await get(apiEndpoint, '/health');
}
export async function getCategories(
apiEndpoint: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ categories: ITemplatesCategory[] }> {
return await get(apiEndpoint, '/templates/categories', undefined, headers);
}
export async function getCollections(
apiEndpoint: string,
query: ITemplatesQuery,
headers?: RawAxiosRequestHeaders,
): Promise<{ collections: ITemplatesCollection[] }> {
return await get(
apiEndpoint,
'/templates/collections',
{ category: query.categories, search: query.search },
headers,
);
}
export async function getWorkflows(
apiEndpoint: string,
query: { page: number; limit: number; categories: string[]; search: string },
headers?: RawAxiosRequestHeaders,
): Promise<{
totalWorkflows: number;
workflows: ITemplatesWorkflow[];
filters: TemplateSearchFacet[];
}> {
return await get(
apiEndpoint,
'/templates/search',
{
page: query.page,
rows: query.limit,
category: stringifyArray(query.categories),
search: query.search,
},
headers,
);
}
export async function getCollectionById(
apiEndpoint: string,
collectionId: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ collection: ITemplatesCollectionResponse }> {
return await get(apiEndpoint, `/templates/collections/${collectionId}`, undefined, headers);
}
export async function getTemplateById(
apiEndpoint: string,
templateId: string,
headers?: RawAxiosRequestHeaders,
): Promise<{ workflow: ITemplatesWorkflowResponse }> {
return await get(apiEndpoint, `/templates/workflows/${templateId}`, undefined, headers);
}
export async function getWorkflowTemplate(
apiEndpoint: string,
templateId: string,
headers?: RawAxiosRequestHeaders,
): Promise<IWorkflowTemplate> {
return await get(apiEndpoint, `/workflows/templates/${templateId}`, undefined, headers);
}

View File

@@ -4,7 +4,7 @@ import { useUsersStore } from '@/stores/users.store';
import { computed, watch, ref, onBeforeUnmount } from 'vue';
import AskAssistantChat from '@n8n/design-system/components/AskAssistantChat/AskAssistantChat.vue';
import { useTelemetry } from '@/composables/useTelemetry';
import type { IWorkflowDataUpdate } from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { nodeViewEventBus } from '@/event-bus';
import { v4 as uuid } from 'uuid';
import { useI18n } from '@n8n/i18n';
@@ -37,7 +37,7 @@ async function onUserMessage(content: string) {
await builderStore.initBuilderChat(content, 'chat');
}
function fixWorkflowStickiesPosition(workflowData: IWorkflowDataUpdate): IWorkflowDataUpdate {
function fixWorkflowStickiesPosition(workflowData: WorkflowDataUpdate): WorkflowDataUpdate {
const STICKY_WIDTH = 480;
const HEADERS_HEIGHT = 40;
const NEW_LINE_HEIGHT = 20;
@@ -76,7 +76,7 @@ function fixWorkflowStickiesPosition(workflowData: IWorkflowDataUpdate): IWorkfl
}
function onInsertWorkflow(code: string) {
let workflowData: IWorkflowDataUpdate;
let workflowData: WorkflowDataUpdate;
try {
workflowData = JSON.parse(code);
} catch (error) {

View File

@@ -6,7 +6,7 @@ import WorkflowTagsDropdown from '@/components/WorkflowTagsDropdown.vue';
import Modal from '@/components/Modal.vue';
import { useSettingsStore } from '@/stores/settings.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { IWorkflowDataUpdate } from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { createEventBus, type EventBus } from '@n8n/utils/event-bus';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
@@ -86,7 +86,7 @@ const save = async (): Promise<void> => {
isSaving.value = true;
try {
let workflowToUpdate: IWorkflowDataUpdate | undefined;
let workflowToUpdate: WorkflowDataUpdate | undefined;
if (currentWorkflowId !== PLACEHOLDER_EMPTY_WORKFLOW_ID) {
const {
createdAt,

View File

@@ -47,10 +47,10 @@ import { computed, ref, useCssModule, useTemplateRef, watch } from 'vue';
import type {
ActionDropdownItem,
FolderShortInfo,
IWorkflowDataUpdate,
IWorkflowDb,
IWorkflowToShare,
} from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
import { useTelemetry } from '@/composables/useTelemetry';
import type { PathItem } from '@n8n/design-system/components/N8nBreadcrumbs/Breadcrumbs.vue';
@@ -407,7 +407,7 @@ async function handleFileImport(): Promise<void> {
if (inputRef?.files && inputRef.files.length !== 0) {
const reader = new FileReader();
reader.onload = () => {
let workflowData: IWorkflowDataUpdate;
let workflowData: WorkflowDataUpdate;
try {
workflowData = JSON.parse(reader.result as string);
} catch (error) {

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import NodeIcon from '@/components/NodeIcon.vue';
import type { ITemplatesNode } from '@/Interface';
import type { ITemplatesNode } from '@n8n/rest-api-client/api/templates';
import { filterTemplateNodes } from '@/utils/nodeTypesUtils';
const props = withDefaults(

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue';
import type { ComponentInstance } from 'vue';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import IntersectionObserver from './IntersectionObserver.vue';
import IntersectionObserved from './IntersectionObserved.vue';
import { createEventBus } from '@n8n/utils/event-bus';

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { onClickOutside } from '@vueuse/core';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { MAX_TAG_NAME_LENGTH } from '@/constants';
import { N8nOption, N8nSelect } from '@n8n/design-system';
import type { EventBus } from '@n8n/utils/event-bus';

View File

@@ -4,7 +4,7 @@ import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
import { useAnnotationTagsStore } from '@/stores/tags.store';
import TagsManager from './TagsManager.vue';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { ANNOTATION_TAGS_MANAGER_MODAL_KEY } from '@/constants';
const i18n = useI18n();

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import TagsView from '@/components/TagsManager/TagsView/TagsView.vue';
import NoTagsView from '@/components/TagsManager/NoTagsView.vue';
import Modal from '@/components/Modal.vue';

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import type { ITag, ITagRow } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { ITagRow } from '@/Interface';
import { useI18n } from '@n8n/i18n';
import TagsTableHeader from '@/components/TagsManager/TagsView/TagsTableHeader.vue';
import TagsTable from '@/components/TagsManager/TagsView/TagsTable.vue';

View File

@@ -4,7 +4,7 @@ import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
import { useTagsStore } from '@/stores/tags.store';
import TagsManager from './TagsManager.vue';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { TAGS_MANAGER_MODAL_KEY } from '@/constants';
const i18n = useI18n();

View File

@@ -2,7 +2,7 @@
import { abbreviateNumber } from '@/utils/typesUtils';
import NodeList from './NodeList.vue';
import TimeAgo from '@/components/TimeAgo.vue';
import type { ITemplatesWorkflow } from '@/Interface';
import type { ITemplatesWorkflow } from '@n8n/rest-api-client/api/templates';
import { useI18n } from '@n8n/i18n';
import type { BaseTextKey } from '@n8n/i18n';

View File

@@ -8,7 +8,7 @@ import type {
ITemplatesCollectionFull,
ITemplatesNode,
ITemplatesWorkflow,
} from '@/Interface';
} from '@n8n/rest-api-client/api/templates';
import { useTemplatesStore } from '@/stores/templates.store';
import TimeAgo from '@/components/TimeAgo.vue';
import { isFullTemplatesCollection, isTemplatesWorkflow } from '@/utils/templates/typeGuards';

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import type { ITemplatesCategory } from '@/Interface';
import type { ITemplatesCategory } from '@n8n/rest-api-client/api/templates';
import { useI18n } from '@n8n/i18n';
interface Props {

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
import TemplateCard from './TemplateCard.vue';
import type { ITemplatesWorkflow } from '@/Interface';
import type { ITemplatesWorkflow } from '@n8n/rest-api-client/api/templates';
import { useI18n } from '@n8n/i18n';
interface Props {

View File

@@ -2,7 +2,7 @@
import Card from '@/components/CollectionWorkflowCard.vue';
import NodeList from '@/components/NodeList.vue';
import { useI18n } from '@n8n/i18n';
import type { ITemplatesCollection } from '@/Interface';
import type { ITemplatesCollection } from '@n8n/rest-api-client/api/templates';
withDefaults(
defineProps<{

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { nextTick, onBeforeMount, onMounted, ref, watch } from 'vue';
import type { ITemplatesCollection } from '@/Interface';
import type { ITemplatesCollection } from '@n8n/rest-api-client/api/templates';
import Card from '@/components/CollectionWorkflowCard.vue';
import TemplatesInfoCard from '@/components/TemplatesInfoCard.vue';
import { VueAgile } from 'vue-agile';

View File

@@ -2,7 +2,8 @@
import { onMounted, onBeforeUnmount, ref, computed, watch } from 'vue';
import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
import type { IWorkflowDb, IWorkflowTemplate } from '@/Interface';
import type { IWorkflowDb } from '@/Interface';
import type { IWorkflowTemplate } from '@n8n/rest-api-client/api/templates';
import { useExecutionsStore } from '@/stores/executions.store';
const props = withDefaults(

View File

@@ -22,7 +22,7 @@ import { useProjectsStore } from '@/stores/projects.store';
import { useTelemetry } from '@/composables/useTelemetry';
import { VIEWS } from '@/constants';
import { SAMPLE_SUBWORKFLOW_TRIGGER_ID, SAMPLE_SUBWORKFLOW_WORKFLOW } from '@/constants.workflows';
import type { IWorkflowDataCreate } from '@/Interface';
import type { WorkflowDataCreate } from '@n8n/rest-api-client/api/workflows';
import { useDocumentVisibility } from '@/composables/useDocumentVisibility';
export interface Props {
@@ -36,7 +36,7 @@ export interface Props {
forceShowExpression?: boolean;
parameterIssues?: string[];
parameter: INodeProperties;
sampleWorkflow?: IWorkflowDataCreate;
sampleWorkflow?: WorkflowDataCreate;
newResourceLabel?: string;
}
@@ -254,7 +254,7 @@ const onAddResourceClicked = async () => {
(w) => w.name && new RegExp(workflowName).test(w.name),
);
const workflow: IWorkflowDataCreate = {
const workflow: WorkflowDataCreate = {
...sampleWorkflow,
name: `${workflowName} ${sampleSubWorkflows.length + 1}`,
};

View File

@@ -2,12 +2,8 @@
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useToast } from '@/composables/useToast';
import type {
ITimeoutHMS,
IWorkflowDataUpdate,
IWorkflowSettings,
IWorkflowShortResponse,
} from '@/Interface';
import type { ITimeoutHMS, IWorkflowSettings, IWorkflowShortResponse } from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import Modal from '@/components/Modal.vue';
import {
EnterpriseEditionFeature,
@@ -298,7 +294,7 @@ const convertToHMS = (num: number): ITimeoutHMS => {
const saveSettings = async () => {
// Set that the active state should be changed
const data: IWorkflowDataUpdate & { settings: IWorkflowSettings } = {
const data: WorkflowDataUpdate & { settings: IWorkflowSettings } = {
settings: workflowSettings.value,
};

View File

@@ -2,7 +2,7 @@
import { computed } from 'vue';
import TagsContainer from './TagsContainer.vue';
import { useTagsStore } from '@/stores/tags.store';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
interface Props {
tagIds: string[];

View File

@@ -14,7 +14,8 @@ import { useRoute, useRouter } from 'vue-router';
import type { BaseTextKey } from '@n8n/i18n';
import type { Scope } from '@n8n/permissions';
import type { BaseFolderItem, BaseResource, ITag, ResourceParentFolder } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { BaseFolderItem, BaseResource, ResourceParentFolder } from '@/Interface';
import { isSharedResource, isResourceSortableByDate } from '@/utils/typeGuards';
import { useN8nLocalStorage } from '@/composables/useN8nLocalStorage';

View File

@@ -11,14 +11,8 @@ import { NodeConnectionTypes, NodeHelpers, UserError } from 'n8n-workflow';
import { useCanvasOperations } from '@/composables/useCanvasOperations';
import type { CanvasConnection, CanvasNode } from '@/types';
import { CanvasConnectionMode } from '@/types';
import type {
ICredentialsResponse,
IExecutionResponse,
INodeUi,
IWorkflowDb,
IWorkflowTemplate,
IWorkflowTemplateNode,
} from '@/Interface';
import type { ICredentialsResponse, IExecutionResponse, INodeUi, IWorkflowDb } from '@/Interface';
import type { IWorkflowTemplate, IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import { RemoveNodeCommand, ReplaceNodeParametersCommand } from '@/models/history';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';

View File

@@ -7,15 +7,14 @@ import type {
AddedNodesAndConnections,
IExecutionResponse,
INodeUi,
ITag,
IUsedCredential,
IWorkflowData,
IWorkflowDataUpdate,
IWorkflowDb,
IWorkflowTemplate,
WorkflowDataWithTemplateId,
XYPosition,
} from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { IWorkflowTemplate } from '@n8n/rest-api-client/api/templates';
import type { WorkflowData, WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { useDataSchema } from '@/composables/useDataSchema';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useI18n } from '@n8n/i18n';
@@ -1627,7 +1626,7 @@ export function useCanvasOperations() {
* Import operations
*/
function removeUnknownCredentials(workflow: IWorkflowDataUpdate) {
function removeUnknownCredentials(workflow: WorkflowDataUpdate) {
if (!workflow?.nodes) return;
for (const node of workflow.nodes) {
@@ -1644,9 +1643,9 @@ export function useCanvasOperations() {
}
async function addImportedNodesToWorkflow(
data: IWorkflowDataUpdate,
data: WorkflowDataUpdate,
{ trackBulk = true, trackHistory = false, viewport = DEFAULT_VIEWPORT_BOUNDARIES } = {},
): Promise<IWorkflowDataUpdate> {
): Promise<WorkflowDataUpdate> {
// Because nodes with the same name maybe already exist, it could
// be needed that they have to be renamed. Also could it be possible
// that nodes are not allowed to be created because they have a create
@@ -1818,7 +1817,7 @@ export function useCanvasOperations() {
}
async function importWorkflowData(
workflowData: IWorkflowDataUpdate,
workflowData: WorkflowDataUpdate,
source: string,
{
importTags = true,
@@ -1831,7 +1830,7 @@ export function useCanvasOperations() {
trackHistory?: boolean;
viewport?: ViewportBoundaries;
} = {},
): Promise<IWorkflowDataUpdate> {
): Promise<WorkflowDataUpdate> {
uiStore.resetLastInteractedWith();
// If it is JSON check if it looks on the first look like data we can use
@@ -1947,7 +1946,7 @@ export function useCanvasOperations() {
}
}
async function importWorkflowTags(workflowData: IWorkflowDataUpdate) {
async function importWorkflowTags(workflowData: WorkflowDataUpdate) {
const allTags = await tagsStore.fetchAll();
const tagNames = new Set(allTags.map((tag) => tag.name));
@@ -1978,8 +1977,8 @@ export function useCanvasOperations() {
workflowsStore.addWorkflowTagIds(tagIds);
}
async function fetchWorkflowDataFromUrl(url: string): Promise<IWorkflowDataUpdate | undefined> {
let workflowData: IWorkflowDataUpdate;
async function fetchWorkflowDataFromUrl(url: string): Promise<WorkflowDataUpdate | undefined> {
let workflowData: WorkflowDataUpdate;
canvasStore.startLoading();
try {
@@ -1994,12 +1993,12 @@ export function useCanvasOperations() {
return workflowData;
}
function getNodesToSave(nodes: INode[]): IWorkflowData {
function getNodesToSave(nodes: INode[]): WorkflowData {
const data = {
nodes: [] as INodeUi[],
connections: {} as IConnections,
pinData: {} as IPinData,
} satisfies IWorkflowData;
} satisfies WorkflowData;
const exportedNodeNames = new Set<string>();

View File

@@ -14,7 +14,8 @@ import type {
} from 'n8n-workflow';
import { useRunWorkflow } from '@/composables/useRunWorkflow';
import type { IExecutionResponse, IStartRunData, IWorkflowData } from '@/Interface';
import type { IExecutionResponse, IStartRunData } from '@/Interface';
import type { WorkflowData } from '@n8n/rest-api-client/api/workflows';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
@@ -244,7 +245,7 @@ describe('useRunWorkflow({ router })', () => {
disabled: false,
},
],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
const result = await runWorkflow({});
@@ -278,7 +279,7 @@ describe('useRunWorkflow({ router })', () => {
pinData: {
Slack: [{ json: { value: 'data2' } }],
},
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
const mockExecutionResponse = { executionId: '123' };
@@ -291,7 +292,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
vi.mocked(workflowsStore).getWorkflowRunData = {
NodeName: [],
};
@@ -328,7 +329,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
vi.mocked(workflowsStore).getWorkflowRunData = {
NodeName: [],
};
@@ -350,7 +351,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
vi.mocked(workflowsStore).getWorkflowRunData = {
NodeName: [],
};
@@ -391,7 +392,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
vi.mocked(workflowsStore).getWorkflowRunData = {
[parentNodeName]: [
@@ -465,7 +466,7 @@ describe('useRunWorkflow({ router })', () => {
} as unknown as Workflow);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
vi.mocked(workflowHelpers).executeData.mockResolvedValue({
data: {},
node: {},
@@ -496,7 +497,7 @@ describe('useRunWorkflow({ router })', () => {
mock<Workflow>({ getChildNodes: vi.fn().mockReturnValue([]) }),
);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ nodes: [] }),
mock<WorkflowData>({ nodes: [] }),
);
const { runWorkflow } = composable;
@@ -527,7 +528,7 @@ describe('useRunWorkflow({ router })', () => {
}),
);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ nodes: [] }),
mock<WorkflowData>({ nodes: [] }),
);
const { runWorkflow } = composable;
@@ -566,7 +567,7 @@ describe('useRunWorkflow({ router })', () => {
}),
);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ nodes: [] }),
mock<WorkflowData>({ nodes: [] }),
);
const { runWorkflow } = composable;
@@ -594,7 +595,7 @@ describe('useRunWorkflow({ router })', () => {
mock<Workflow>({ getChildNodes: vi.fn().mockReturnValue([]) }),
);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ nodes: [] }),
mock<WorkflowData>({ nodes: [] }),
);
// ACT
@@ -625,7 +626,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowsStore).nodesIssuesExist = false;
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue(workflow);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ id: 'workflowId', nodes: [] }),
mock<WorkflowData>({ id: 'workflowId', nodes: [] }),
);
vi.mocked(workflowsStore).getWorkflowRunData = mockRunData;
@@ -738,7 +739,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowsStore).nodesIssuesExist = false;
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue(workflow);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ id: 'workflowId', nodes: [] }),
mock<WorkflowData>({ id: 'workflowId', nodes: [] }),
);
vi.mocked(workflowsStore).getWorkflowRunData = mockRunData;
@@ -767,7 +768,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowsStore).nodesIssuesExist = false;
vi.mocked(workflowHelpers).getCurrentWorkflow.mockReturnValue(workflow);
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue(
mock<IWorkflowData>({ id: 'workflowId', nodes: [] }),
mock<WorkflowData>({ id: 'workflowId', nodes: [] }),
);
vi.mocked(workflowsStore).getWorkflowRunData = mockRunData;
@@ -789,7 +790,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: workflow.id,
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
// Simulate failed execution start
vi.mocked(workflowsStore).runWorkflow.mockRejectedValueOnce(new Error());
@@ -915,7 +916,7 @@ describe('useRunWorkflow({ router })', () => {
vi.mocked(workflowHelpers).getWorkflowDataToSave.mockResolvedValue({
id: 'workflowId',
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
await runWorkflowComposable.runEntireWorkflow('main', 'foo');

View File

@@ -17,7 +17,8 @@ import { VIEWS, WORKFLOW_EXTRACTION_NAME_MODAL_KEY } from '@/constants';
import { useHistoryStore } from '@/stores/history.store';
import { useCanvasOperations } from './useCanvasOperations';
import type { AddedNode, INodeUi, IWorkflowDataCreate, IWorkflowDb } from '@/Interface';
import type { AddedNode, INodeUi, IWorkflowDb } from '@/Interface';
import type { WorkflowDataCreate } from '@n8n/rest-api-client/api/workflows';
import { useI18n } from '@n8n/i18n';
import { PUSH_NODES_OFFSET } from '@/utils/nodeViewUtils';
import { useUIStore } from '@/stores/ui.store';
@@ -128,7 +129,7 @@ export function useWorkflowExtraction() {
selectionChildrenVariables: Map<string, string>,
startNodeName: string,
returnNodeName: string,
): IWorkflowDataCreate {
): WorkflowDataCreate {
const newConnections = Object.fromEntries(
Object.entries(connections).filter(([k]) => nodes.some((x) => x.name === k)),
);
@@ -253,7 +254,7 @@ export function useWorkflowExtraction() {
return [summedUp[0] / summedUp[2], summedUp[1] / summedUp[2]];
}
async function tryCreateWorkflow(workflowData: IWorkflowDataCreate): Promise<IWorkflowDb | null> {
async function tryCreateWorkflow(workflowData: WorkflowDataCreate): Promise<IWorkflowDb | null> {
try {
const createdWorkflow = await workflowsStore.createNewWorkflow(workflowData);

View File

@@ -1,4 +1,5 @@
import type { IExecutionResponse, IWorkflowData, IWorkflowDb } from '@/Interface';
import type { IExecutionResponse, IWorkflowDb } from '@/Interface';
import type { WorkflowData } from '@n8n/rest-api-client/api/workflows';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
@@ -358,7 +359,7 @@ describe('useWorkflowHelpers', () => {
uiStore.stateIsDirty = true;
vi.spyOn(workflowHelpers, 'getWorkflowDataToSave').mockResolvedValue({
nodes: [],
} as unknown as IWorkflowData);
} as unknown as WorkflowData);
expect(await workflowHelpers.checkConflictingWebhooks('12345')).toEqual(null);
});
});

View File

@@ -27,14 +27,13 @@ import type {
ICredentialsResponse,
INodeTypesMaxCount,
INodeUi,
ITag,
IWorkflowData,
IWorkflowDataUpdate,
IWorkflowDb,
TargetItem,
WorkflowTitleStatus,
XYPosition,
} from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { WorkflowData, WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
@@ -510,7 +509,7 @@ export function useWorkflowHelpers() {
nodes.push(nodeData);
}
const data: IWorkflowData = {
const data: WorkflowData = {
name: workflowsStore.workflowName,
nodes,
pinData: workflowsStore.pinnedWorkflowData,
@@ -762,7 +761,7 @@ export function useWorkflowHelpers() {
{ workflowId, active }: { workflowId: string; active?: boolean },
partialData = false,
) {
let data: IWorkflowDataUpdate = {};
let data: WorkflowDataUpdate = {};
const isCurrentWorkflow = workflowId === workflowsStore.workflowId;
if (isCurrentWorkflow) {
@@ -796,7 +795,7 @@ export function useWorkflowHelpers() {
// Updates the position of all the nodes that the top-left node
// is at the given position
function updateNodePositions(
workflowData: IWorkflowData | IWorkflowDataUpdate,
workflowData: WorkflowData | WorkflowDataUpdate,
position: XYPosition,
): void {
if (workflowData.nodes === undefined) {
@@ -827,7 +826,7 @@ export function useWorkflowHelpers() {
}
function removeForeignCredentialsFromWorkflow(
workflow: IWorkflowData | IWorkflowDataUpdate,
workflow: WorkflowData | WorkflowDataUpdate,
usableCredentials: ICredentialsResponse[],
): void {
(workflow.nodes ?? []).forEach((node: INode) => {

View File

@@ -6,7 +6,7 @@ import { createTestingPinia } from '@pinia/testing';
import { setActivePinia } from 'pinia';
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { IWorkflowDataUpdate } from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import { mockedStore } from '@/__tests__/utils';
import { createTestNode, createTestWorkflow, mockNodeTypeDescription } from '@/__tests__/mocks';
import { CHAT_TRIGGER_NODE_TYPE } from 'n8n-workflow';
@@ -22,7 +22,7 @@ vi.mock('@/composables/useMessage', () => {
};
});
const getDuplicateTestWorkflow = (): IWorkflowDataUpdate => ({
const getDuplicateTestWorkflow = (): WorkflowDataUpdate => ({
name: 'Duplicate webhook test',
active: false,
nodes: [

View File

@@ -15,13 +15,9 @@ import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useCanvasStore } from '@/stores/canvas.store';
import type {
ITag,
IUpdateInformation,
IWorkflowDataCreate,
IWorkflowDataUpdate,
NotificationOptions,
} from '@/Interface';
import type { IUpdateInformation, NotificationOptions } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import type { WorkflowDataCreate, WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import type { IDataObject, INode, IWorkflowSettings } from 'n8n-workflow';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useToast } from './useToast';
@@ -134,7 +130,7 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
async function getWorkflowDeactivationInfo(
workflowId: string,
request: IWorkflowDataUpdate,
request: WorkflowDataUpdate,
): Promise<Partial<NotificationOptions> | undefined> {
const missingActivatableTriggerNode =
request.nodes !== undefined && !request.nodes.some(isNodeActivatable);
@@ -187,7 +183,7 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
}
uiStore.addActiveAction('workflowSaving');
const workflowDataRequest: IWorkflowDataUpdate = await getWorkflowDataToSave();
const workflowDataRequest: WorkflowDataUpdate = await getWorkflowDataToSave();
// This can happen if the user has another workflow in the browser history and navigates
// via the browser back button, encountering our warning dialog with the new route already set
if (workflowDataRequest.id !== currentWorkflow) {
@@ -306,14 +302,14 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
openInNewWindow?: boolean;
resetNodeIds?: boolean;
parentFolderId?: string;
data?: IWorkflowDataCreate;
data?: WorkflowDataCreate;
} = {},
redirect = true,
): Promise<boolean> {
try {
uiStore.addActiveAction('workflowSaving');
const workflowDataRequest: IWorkflowDataCreate = data || (await getWorkflowDataToSave());
const workflowDataRequest: WorkflowDataCreate = data || (await getWorkflowDataToSave());
const changedNodes = {} as IDataObject;
if (resetNodeIds) {

View File

@@ -1,8 +1,9 @@
import { NodeConnectionTypes } from 'n8n-workflow';
import type { INodeUi, IWorkflowDataCreate } from './Interface';
import type { INodeUi } from './Interface';
import type { WorkflowDataCreate } from '@n8n/rest-api-client/api/workflows';
export const SAMPLE_SUBWORKFLOW_TRIGGER_ID = 'c055762a-8fe7-4141-a639-df2372f30060';
export const SAMPLE_SUBWORKFLOW_WORKFLOW: IWorkflowDataCreate = {
export const SAMPLE_SUBWORKFLOW_WORKFLOW: WorkflowDataCreate = {
name: 'My Sub-Workflow',
nodes: [
{
@@ -40,7 +41,7 @@ export const SAMPLE_SUBWORKFLOW_WORKFLOW: IWorkflowDataCreate = {
pinData: {},
};
export const SAMPLE_EVALUATION_WORKFLOW: IWorkflowDataCreate = {
export const SAMPLE_EVALUATION_WORKFLOW: WorkflowDataCreate = {
name: 'My Evaluation Sub-Workflow',
nodes: [
{

View File

@@ -9,7 +9,7 @@ import type {
import * as eventsApi from '@n8n/rest-api-client/api/events';
import * as settingsApi from '@n8n/rest-api-client/api/settings';
import * as moduleSettingsApi from '@n8n/rest-api-client/api/module-settings';
import { testHealthEndpoint } from '@/api/templates';
import { testHealthEndpoint } from '@n8n/rest-api-client/api/templates';
import {
INSECURE_CONNECTION_WARNING,
LOCAL_STORAGE_EXPERIMENTAL_DOCKED_NODE_SETTINGS,

View File

@@ -1,6 +1,6 @@
import { createTagsApi } from '@/api/tags';
import { STORES } from '@n8n/stores';
import type { ITag } from '@/Interface';
import type { ITag } from '@n8n/rest-api-client/api/tags';
import { defineStore } from 'pinia';
import { useRootStore } from '@n8n/stores/useRootStore';
import { computed, ref } from 'vue';

View File

@@ -1,8 +1,10 @@
import { defineStore } from 'pinia';
import { TEMPLATES_URLS } from '@/constants';
import { STORES } from '@n8n/stores';
import type { INodeUi } from '@/Interface';
import { useSettingsStore } from './settings.store';
import * as templatesApi from '@n8n/rest-api-client/api/templates';
import type {
INodeUi,
ITemplatesCategory,
ITemplatesCollection,
ITemplatesCollectionFull,
@@ -10,15 +12,35 @@ import type {
ITemplatesWorkflow,
ITemplatesWorkflowFull,
IWorkflowTemplate,
} from '@/Interface';
import { useSettingsStore } from './settings.store';
import * as templatesApi from '@/api/templates';
} from '@n8n/rest-api-client/api/templates';
import { getNodesWithNormalizedPosition } from '@/utils/nodeViewUtils';
import { useRootStore } from '@n8n/stores/useRootStore';
import { useUsersStore } from './users.store';
import { useWorkflowsStore } from './workflows.store';
import { computed, ref } from 'vue';
export interface ITemplateState {
categories: ITemplatesCategory[];
collections: { [id: string]: ITemplatesCollection };
workflows: { [id: string]: ITemplatesWorkflow | ITemplatesWorkflowFull };
workflowSearches: {
[search: string]: {
workflowIds: string[];
totalWorkflows: number;
loadingMore?: boolean;
categories?: ITemplatesCategory[];
};
};
collectionSearches: {
[search: string]: {
collectionIds: string[];
};
};
currentSessionId: string;
previousSessionId: string;
currentN8nPath: string;
}
const TEMPLATES_PAGE_SIZE = 20;
function getSearchKey(query: ITemplatesQuery): string {

View File

@@ -1,7 +1,8 @@
import { computed } from 'vue';
import { defineStore } from 'pinia';
import { saveAs } from 'file-saver';
import type { IWorkflowDataUpdate, IWorkflowDb } from '@/Interface';
import type { IWorkflowDb } from '@/Interface';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import type {
WorkflowHistory,
WorkflowVersion,
@@ -68,7 +69,7 @@ export const useWorkflowHistoryStore = defineStore('workflowHistory', () => {
const newWorkflow = await getNewWorkflow(rootStore.restApiContext, {
name: `${name} (${data.formattedCreatedAt})`,
});
const newWorkflowData: IWorkflowDataUpdate = {
const newWorkflowData: WorkflowDataUpdate = {
nodes,
connections,
name: newWorkflow.name,
@@ -83,7 +84,7 @@ export const useWorkflowHistoryStore = defineStore('workflowHistory', () => {
): Promise<IWorkflowDb> => {
const workflowVersion = await getWorkflowVersion(workflowId, workflowVersionId);
const { connections, nodes } = workflowVersion;
const updateData: IWorkflowDataUpdate = { connections, nodes };
const updateData: WorkflowDataUpdate = { connections, nodes };
if (shouldDeactivate) {
updateData.active = false;

View File

@@ -23,16 +23,18 @@ import type {
IStartRunData,
IUpdateInformation,
IUsedCredential,
IWorkflowDataUpdate,
IWorkflowDb,
IWorkflowsMap,
NodeMetadataMap,
WorkflowMetadata,
IExecutionFlattedResponse,
IWorkflowTemplateNode,
IWorkflowDataCreate,
WorkflowListResource,
} from '@/Interface';
import type { IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import type {
WorkflowMetadata,
WorkflowDataCreate,
WorkflowDataUpdate,
} from '@n8n/rest-api-client/api/workflows';
import { defineStore } from 'pinia';
import type {
IConnection,
@@ -1681,7 +1683,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
* Ensures that the new workflow is not active upon creation.
* If the project ID is not provided in the data, it assigns the current project ID from the project store.
*/
async function createNewWorkflow(sendData: IWorkflowDataCreate): Promise<IWorkflowDb> {
async function createNewWorkflow(sendData: WorkflowDataCreate): Promise<IWorkflowDb> {
// make sure that the new ones are not active
sendData.active = false;
@@ -1714,7 +1716,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
async function updateWorkflow(
id: string,
data: IWorkflowDataUpdate,
data: WorkflowDataUpdate,
forceSave = false,
): Promise<IWorkflowDb> {
if (data.settings === null) {

View File

@@ -17,9 +17,9 @@ import type {
INodeUpdatePropertiesInformation,
IPersonalizationLatestVersion,
IWorkflowDb,
IWorkflowTemplateNode,
NodeFilterType,
} from '@/Interface';
import type { IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import type { ComponentPublicInstance } from 'vue';
import type { useWebhooksStore } from '@/stores/webhooks.store';

View File

@@ -2,9 +2,9 @@ import type {
AppliedThemeOption,
INodeUi,
INodeUpdatePropertiesInformation,
ITemplatesNode,
NodeAuthenticationOption,
} from '@/Interface';
import type { ITemplatesNode } from '@n8n/rest-api-client/api/templates';
import {
CORE_NODES_CATEGORY,
MAIN_AUTH_FIELD_NAME,

View File

@@ -5,7 +5,7 @@ import { vi } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { VIEWS } from '@/constants';
import type { ITemplatesWorkflowFull } from '@/Interface';
import type { ITemplatesWorkflowFull } from '@n8n/rest-api-client/api/templates';
import { Telemetry } from '@/plugins/telemetry';
import type { NodeTypesStore } from '@/stores/nodeTypes.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';

View File

@@ -1,9 +1,6 @@
import type {
INodeUi,
ITemplatesWorkflowFull,
IWorkflowData,
IWorkflowTemplate,
} from '@/Interface';
import type { INodeUi } from '@/Interface';
import type { ITemplatesWorkflowFull, IWorkflowTemplate } from '@n8n/rest-api-client/api/templates';
import type { WorkflowData } from '@n8n/rest-api-client/api/workflows';
import { getNewWorkflow } from '@/api/workflows';
import { VIEWS } from '@/constants';
import type { useRootStore } from '@n8n/stores/useRootStore';
@@ -45,7 +42,7 @@ export async function createWorkflowFromTemplate(opts: {
const nodes = getNodesWithNormalizedPosition(nodesWithCreds) as INodeUi[];
const connections = template.workflow.connections;
const workflowToCreate: IWorkflowData = {
const workflowToCreate: WorkflowData = {
name: workflowData.name,
nodes,
connections,

View File

@@ -1,5 +1,5 @@
import { mock } from 'vitest-mock-extended';
import type { IWorkflowTemplateNode } from '@/Interface';
import type { IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import {
keyFromCredentialTypeAndName,
replaceAllTemplateNodeCredentials,

View File

@@ -1,4 +1,8 @@
import type { IWorkflowTemplateNode, IWorkflowTemplateNodeCredentials } from '@/Interface';
import type {
IWorkflowTemplateNode,
IWorkflowTemplateNodeCredentials,
} from '@n8n/rest-api-client/api/templates';
import type { NodeTypeProvider } from '@/utils/nodeTypes/nodeTypeTransforms';
import { getNodeTypeDisplayableCredentials } from '@/utils/nodes/nodeTransforms';
import type { NormalizedTemplateNodeCredentials } from '@/utils/templates/templateTypes';

View File

@@ -2,7 +2,7 @@ import type {
ITemplatesCollection,
ITemplatesCollectionFull,
ITemplatesWorkflow,
} from '@/Interface';
} from '@n8n/rest-api-client/api/templates';
export function isTemplatesWorkflow(
template: ITemplatesWorkflow | ITemplatesCollection | ITemplatesCollectionFull | null,

View File

@@ -27,15 +27,15 @@ import type {
IExecutionResponse,
INodeUi,
IUpdateInformation,
IWorkflowDataUpdate,
IWorkflowDb,
IWorkflowTemplate,
NodeCreatorOpenSource,
NodeFilterType,
ToggleNodeCreatorOptions,
WorkflowDataWithTemplateId,
XYPosition,
} from '@/Interface';
import type { IWorkflowTemplate } from '@n8n/rest-api-client/api/templates';
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
import type {
Connection,
Dimensions,
@@ -769,7 +769,7 @@ async function onClipboardPaste(plainTextData: string): Promise<void> {
return;
}
let workflowData: IWorkflowDataUpdate | null | undefined = null;
let workflowData: WorkflowDataUpdate | null | undefined = null;
// Check if it is an URL which could contain workflow data
if (plainTextData.match(VALID_WORKFLOW_IMPORT_URL_REGEX)) {
@@ -796,7 +796,7 @@ async function onClipboardPaste(plainTextData: string): Promise<void> {
workflowData = await fetchWorkflowDataFromUrl(plainTextData);
} else {
// Pasted data is possible workflow data
workflowData = jsonParse<IWorkflowDataUpdate | null>(plainTextData, { fallbackValue: null });
workflowData = jsonParse<WorkflowDataUpdate | null>(plainTextData, { fallbackValue: null });
}
if (!workflowData) {
@@ -1048,7 +1048,7 @@ function onRevertDeleteConnection({ connection }: { connection: [IConnection, IC
* Import / Export
*/
async function importWorkflowExact({ workflow: workflowData }: { workflow: IWorkflowDataUpdate }) {
async function importWorkflowExact({ workflow: workflowData }: { workflow: WorkflowDataUpdate }) {
if (!workflowData.nodes || !workflowData.connections) {
throw new Error('Invalid workflow object');
}
@@ -1066,7 +1066,7 @@ async function importWorkflowExact({ workflow: workflowData }: { workflow: IWork
}
async function onImportWorkflowDataEvent(data: IDataObject) {
const workflowData = data.data as IWorkflowDataUpdate;
const workflowData = data.data as WorkflowDataUpdate;
await importWorkflowData(workflowData, 'file', {
viewport: viewportBoundaries.value,
});

View File

@@ -1,9 +1,9 @@
import { faker } from '@faker-js/faker/locale/en';
import type { ICredentialsResponse } from '@/Interface';
import type {
ICredentialsResponse,
ITemplatesWorkflowFull,
IWorkflowTemplateNode,
} from '@/Interface';
} from '@n8n/rest-api-client/api/templates';
export const newFullOneNodeTemplate = (node: IWorkflowTemplateNode): ITemplatesWorkflowFull => ({
full: true,

View File

@@ -3,11 +3,11 @@ import { createTestingPinia } from '@pinia/testing';
import { mock } from 'vitest-mock-extended';
import type { ICredentialType } from 'n8n-workflow';
import type { ICredentialsResponse } from '@/Interface';
import type {
ICredentialsResponse,
ITemplatesWorkflowFull,
IWorkflowTemplateNode,
} from '@/Interface';
} from '@n8n/rest-api-client/api/templates';
import { useTemplatesStore } from '@/stores/templates.store';
import { keyFromCredentialTypeAndName } from '@/utils/templates/templateTransforms';
import { useSetupTemplateStore } from '@/views/SetupWorkflowFromTemplateView/setupTemplate.store';

View File

@@ -1,5 +1,5 @@
import { mock } from 'vitest-mock-extended';
import type { IWorkflowTemplateNode } from '@/Interface';
import type { IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import { keyFromCredentialTypeAndName } from '@/utils/templates/templateTransforms';
import type { IWorkflowTemplateNodeWithCredentials } from '@/utils/templates/templateTransforms';
import type { CredentialUsages } from '@/views/SetupWorkflowFromTemplateView/useCredentialSetupState';

View File

@@ -3,7 +3,7 @@ import { computed, onMounted, ref, watch } from 'vue';
import TemplateDetails from '@/components/TemplateDetails.vue';
import TemplateList from '@/components/TemplateList.vue';
import TemplatesView from './TemplatesView.vue';
import type { ITemplatesWorkflow } from '@/Interface';
import type { ITemplatesWorkflow } from '@n8n/rest-api-client/api/templates';
import { VIEWS } from '@/constants';
import { useTemplatesStore } from '@/stores/templates.store';
import { useTemplateWorkflow } from '@/utils/templates/templateActions';

View File

@@ -5,7 +5,7 @@ import TemplateFilters from '@/components/TemplateFilters.vue';
import TemplateList from '@/components/TemplateList.vue';
import TemplatesView from '@/views/TemplatesView.vue';
import type { ITemplatesCategory } from '@/Interface';
import type { ITemplatesCategory } from '@n8n/rest-api-client/api/templates';
import type { IDataObject } from 'n8n-workflow';
import { CREATOR_HUB_URL, VIEWS } from '@/constants';
import { useSettingsStore } from '@/stores/settings.store';