feat(core): Add support to import/export tags (#3130)

* Export and Import Workflow Tags

Support exporting and importing tags of workflows via frontend and cli.

On export, all tag data is included in the json.
- id
- name
- updatedAt
- createdAt

When importing a workflow json to n8n we:
- first check if a tag with the same id and createdAt date exists in the
  database, then we can assume the tag is identical. Changes on the name
  of the tag are now preserved.
- check if a tag with the same name exists on the database.
- create a new tag with the given name.

* clean up fe export

* remove usage count

* return updatedat, createdat

* fix tags import

* move logic from workflow package

* refactor import

* check for tags before import

* update checks on type

* fix on import

* fix build issues

* fix type issue

* remove unnessary ?

* update tag helpers so only name is required

* fix tag import

* add don't replace existing tags

* fix build issue

* address comments

* fix with promise.all

* update setting tags

* update check

* fix existing check

* add helper

* fix duplication

* fix multiple same tags bug

* fix db bugs

* add more validation on workflow type

* fix validation

* disable importing tags on copy paste

Co-authored-by: Luca Berneking <l.berneking@mittwald.de>
This commit is contained in:
Mutasem Aldmour
2022-06-02 12:39:42 +02:00
committed by GitHub
parent 042b8daf1c
commit 15a20d257d
10 changed files with 179 additions and 22 deletions

View File

@@ -17,15 +17,34 @@ import glob from 'fast-glob';
import { UserSettings } from 'n8n-core';
import { EntityManager, getConnection } from 'typeorm';
import { getLogger } from '../../src/Logger';
import { Db, ICredentialsDb } from '../../src';
import { Db, ICredentialsDb, IWorkflowToImport } from '../../src';
import { SharedWorkflow } from '../../src/databases/entities/SharedWorkflow';
import { WorkflowEntity } from '../../src/databases/entities/WorkflowEntity';
import { Role } from '../../src/databases/entities/Role';
import { User } from '../../src/databases/entities/User';
import { setTagsForImport } from '../../src/TagHelpers';
const FIX_INSTRUCTION =
'Please fix the database by running ./packages/cli/bin/n8n user-management:reset';
function assertHasWorkflowsToImport(workflows: unknown): asserts workflows is IWorkflowToImport[] {
if (!Array.isArray(workflows)) {
throw new Error(
'File does not seem to contain workflows. Make sure the workflows are contained in an array.',
);
}
for (const workflow of workflows) {
if (
typeof workflow !== 'object' ||
!Object.prototype.hasOwnProperty.call(workflow, 'nodes') ||
!Object.prototype.hasOwnProperty.call(workflow, 'connections')
) {
throw new Error('File does not seem to contain valid workflows.');
}
}
}
export class ImportWorkflowsCommand extends Command {
static description = 'Import workflows';
@@ -82,7 +101,8 @@ export class ImportWorkflowsCommand extends Command {
// Make sure the settings exist
await UserSettings.prepareUserSettings();
const credentials = (await Db.collections.Credentials.find()) ?? [];
const credentials = await Db.collections.Credentials.find();
const tags = await Db.collections.Tag.find();
let totalImported = 0;
@@ -111,6 +131,10 @@ export class ImportWorkflowsCommand extends Command {
});
}
if (Object.prototype.hasOwnProperty.call(workflow, 'tags')) {
await setTagsForImport(transactionManager, workflow, tags);
}
await this.storeWorkflow(workflow, user);
}
});
@@ -121,13 +145,9 @@ export class ImportWorkflowsCommand extends Command {
const workflows = JSON.parse(fs.readFileSync(flags.input, { encoding: 'utf8' }));
totalImported = workflows.length;
assertHasWorkflowsToImport(workflows);
if (!Array.isArray(workflows)) {
throw new Error(
'File does not seem to contain workflows. Make sure the workflows are contained in an array.',
);
}
totalImported = workflows.length;
await getConnection().transaction(async (transactionManager) => {
this.transactionManager = transactionManager;
@@ -139,6 +159,10 @@ export class ImportWorkflowsCommand extends Command {
});
}
if (Object.prototype.hasOwnProperty.call(workflow, 'tags')) {
await setTagsForImport(transactionManager, workflow, tags);
}
await this.storeWorkflow(workflow, user);
}
});