first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,173 @@
import type { CredentialPayload } from '@n8n/backend-test-utils';
import type { Project, User, ICredentialsDb } from '@n8n/db';
import {
CredentialsEntity,
CredentialsRepository,
ProjectRepository,
SharedCredentialsRepository,
} from '@n8n/db';
import { Container } from '@n8n/di';
import type { CredentialSharingRole } from '@n8n/permissions';
export async function encryptCredentialData(
credential: CredentialsEntity,
): Promise<ICredentialsDb> {
const { createCredentialsFromCredentialsEntity } = await import('@/credentials-helper');
const coreCredential = createCredentialsFromCredentialsEntity(credential, true);
// @ts-ignore
coreCredential.setData(credential.data);
return Object.assign(credential, coreCredential.getDataToSave());
}
export async function decryptCredentialData(credential: ICredentialsDb): Promise<unknown> {
const { createCredentialsFromCredentialsEntity } = await import('@/credentials-helper');
const coreCredential = createCredentialsFromCredentialsEntity(credential);
return coreCredential.getData();
}
const emptyAttributes = {
name: 'test',
type: 'test',
data: '',
};
export async function createManyCredentials(
amount: number,
attributes: Partial<CredentialsEntity> = emptyAttributes,
) {
return await Promise.all(
Array(amount)
.fill(0)
.map(async () => await createCredentials(attributes)),
);
}
export async function createCredentials(
attributes: Partial<CredentialsEntity> = emptyAttributes,
project?: Project,
) {
const credentialsRepository = Container.get(CredentialsRepository);
const credentials = await credentialsRepository.save(credentialsRepository.create(attributes));
if (project) {
await Container.get(SharedCredentialsRepository).save(
Container.get(SharedCredentialsRepository).create({
project,
credentials,
role: 'credential:owner',
}),
);
}
return credentials;
}
/**
* Save a credential to the test DB, sharing it with a user.
*/
export async function saveCredential(
credentialPayload: CredentialPayload,
options:
| { user: User; role: CredentialSharingRole }
| {
project: Project;
role: CredentialSharingRole;
},
) {
const role = options.role;
const newCredential = new CredentialsEntity();
Object.assign(newCredential, credentialPayload);
await encryptCredentialData(newCredential);
const savedCredential = await Container.get(CredentialsRepository).save(newCredential);
savedCredential.data = newCredential.data;
if ('user' in options) {
const user = options.user;
const personalProject = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(
user.id,
);
await Container.get(SharedCredentialsRepository).save({
user,
credentials: savedCredential,
role,
project: personalProject,
});
} else {
const project = options.project;
await Container.get(SharedCredentialsRepository).save({
credentials: savedCredential,
role,
project,
});
}
return savedCredential;
}
export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) {
const newSharedCredentials = await Promise.all(
users.map(async (user) => {
const personalProject = await Container.get(
ProjectRepository,
).getPersonalProjectForUserOrFail(user.id);
return Container.get(SharedCredentialsRepository).create({
credentialsId: credential.id,
role: 'credential:user',
projectId: personalProject.id,
});
}),
);
return await Container.get(SharedCredentialsRepository).save(newSharedCredentials);
}
export async function shareCredentialWithProjects(
credential: CredentialsEntity,
projects: Project[],
) {
const newSharedCredentials = await Promise.all(
projects.map(async (project) => {
return Container.get(SharedCredentialsRepository).create({
credentialsId: credential.id,
role: 'credential:user',
projectId: project.id,
});
}),
);
return await Container.get(SharedCredentialsRepository).save(newSharedCredentials);
}
export function affixRoleToSaveCredential(role: CredentialSharingRole) {
return async (
credentialPayload: CredentialPayload,
options: { user: User } | { project: Project },
) => await saveCredential(credentialPayload, { ...options, role });
}
export async function getAllCredentials() {
return await Container.get(CredentialsRepository).find();
}
export const getCredentialById = async (id: string) =>
await Container.get(CredentialsRepository).findOneBy({ id });
export async function getAllSharedCredentials() {
return await Container.get(SharedCredentialsRepository).find();
}
export async function getCredentialSharings(credential: CredentialsEntity) {
return await Container.get(SharedCredentialsRepository).findBy({
credentialsId: credential.id,
});
}
@@ -0,0 +1,43 @@
import type { CreateDataTableColumnDto } from '@n8n/api-types';
import { randomName } from '@n8n/backend-test-utils';
import type { Project } from '@n8n/db';
import { Container } from '@n8n/di';
import type { DataTableRows } from 'n8n-workflow';
import { DataTableColumnRepository } from '@/modules/data-table/data-table-column.repository';
import { DataTableRowsRepository } from '@/modules/data-table/data-table-rows.repository';
import { DataTableRepository } from '@/modules/data-table/data-table.repository';
export const createDataTable = async (
project: Project,
options: {
name?: string;
columns?: CreateDataTableColumnDto[];
data?: DataTableRows;
updatedAt?: Date;
} = {},
) => {
const dataTableRepository = Container.get(DataTableRepository);
const dataTable = await dataTableRepository.createDataTable(
project.id,
options.name ?? randomName(),
options.columns ?? [],
);
if (options.updatedAt) {
await dataTableRepository.update(dataTable.id, {
updatedAt: options.updatedAt,
});
dataTable.updatedAt = options.updatedAt;
}
if (options.data) {
const dataTableColumnRepository = Container.get(DataTableColumnRepository);
const columns = await dataTableColumnRepository.getColumns(dataTable.id);
const dataTableRowsRepository = Container.get(DataTableRowsRepository);
await dataTableRowsRepository.insertRows(dataTable.id, options.data, columns, 'count');
}
return dataTable;
};
@@ -0,0 +1,71 @@
import { TestRunRepository, TestCaseExecutionRepository } from '@n8n/db';
import type {
TestRun,
TestCaseExecution,
AggregatedTestRunMetrics,
TestCaseExecutionErrorCode,
TestRunErrorCode,
} from '@n8n/db';
import { Container } from '@n8n/di';
import type { IDataObject } from 'n8n-workflow';
/**
* Creates a test run for a workflow
*/
export const createTestRun = async (
workflowId: string,
options: {
status?: TestRun['status'];
runAt?: Date | null;
completedAt?: Date | null;
metrics?: AggregatedTestRunMetrics;
errorCode?: TestRunErrorCode;
errorDetails?: IDataObject;
} = {},
) => {
const testRunRepository = Container.get(TestRunRepository);
const testRun = testRunRepository.create({
workflow: { id: workflowId },
status: options.status ?? 'new',
runAt: options.runAt ?? null,
completedAt: options.completedAt ?? null,
metrics: options.metrics ?? {},
errorCode: options.errorCode,
errorDetails: options.errorDetails,
});
return await testRunRepository.save(testRun);
};
/**
* Creates a test case execution for a test run
*/
export const createTestCaseExecution = async (
testRunId: string,
options: {
status?: TestCaseExecution['status'];
runAt?: Date | null;
completedAt?: Date | null;
metrics?: Record<string, number>;
errorCode?: TestCaseExecutionErrorCode;
errorDetails?: IDataObject;
executionId?: string;
pastExecutionId?: string;
} = {},
) => {
const testCaseExecutionRepository = Container.get(TestCaseExecutionRepository);
const testCaseExecution = testCaseExecutionRepository.create({
testRun: { id: testRunId },
status: options.status ?? 'success',
runAt: options.runAt ?? null,
completedAt: options.completedAt ?? null,
metrics: options.metrics ?? {},
errorCode: options.errorCode,
errorDetails: options.errorDetails,
executionId: options.executionId,
});
return await testCaseExecutionRepository.save(testCaseExecution);
};
@@ -0,0 +1,136 @@
import { mockInstance } from '@n8n/backend-test-utils';
import type { ExecutionEntity, ExecutionData } from '@n8n/db';
import {
ExecutionDataRepository,
ExecutionMetadataRepository,
ExecutionRepository,
AnnotationTagRepository,
} from '@n8n/db';
import { Container } from '@n8n/di';
import type { AnnotationVote, ExecutionStatus, IWorkflowBase } from 'n8n-workflow';
import { ExecutionService } from '@/executions/execution.service';
import { Telemetry } from '@/telemetry';
mockInstance(Telemetry);
export async function createManyExecutions(
amount: number,
workflow: IWorkflowBase,
callback: (workflow: IWorkflowBase) => Promise<ExecutionEntity>,
) {
const executionsRequests = [...Array(amount)].map(async (_) => await callback(workflow));
return await Promise.all(executionsRequests);
}
/**
* Store a execution in the DB and assign it to a workflow.
*/
export async function createExecution(
attributes: Partial<
Omit<ExecutionEntity, 'metadata'> &
ExecutionData & { metadata: Array<{ key: string; value: string }> }
>,
workflow: IWorkflowBase,
) {
const {
data,
finished,
mode,
startedAt,
stoppedAt,
waitTill,
status,
deletedAt,
metadata,
createdAt,
} = attributes;
const execution = await Container.get(ExecutionRepository).save({
finished: finished ?? true,
mode: mode ?? 'manual',
createdAt: createdAt ?? new Date(),
startedAt: startedAt === undefined ? new Date() : startedAt,
...(workflow !== undefined && { workflowId: workflow.id }),
stoppedAt: stoppedAt ?? new Date(),
waitTill: waitTill ?? null,
status: status ?? 'success',
deletedAt,
});
if (metadata?.length) {
const metadataToSave = metadata.map(({ key, value }) => ({
key,
value,
execution: { id: execution.id },
}));
await Container.get(ExecutionMetadataRepository).save(metadataToSave);
}
await Container.get(ExecutionDataRepository).save({
data: data ?? '[]',
workflowData: workflow ?? {},
executionId: execution.id,
});
return execution;
}
/**
* Store a successful execution in the DB and assign it to a workflow.
*/
export async function createSuccessfulExecution(workflow: IWorkflowBase) {
return await createExecution({ finished: true, status: 'success' }, workflow);
}
/**
* Store an error execution in the DB and assign it to a workflow.
*/
export async function createErrorExecution(workflow: IWorkflowBase) {
return await createExecution(
{ finished: false, stoppedAt: new Date(), status: 'error' },
workflow,
);
}
/**
* Store a waiting execution in the DB and assign it to a workflow.
*/
export async function createWaitingExecution(workflow: IWorkflowBase) {
return await createExecution(
{ finished: false, waitTill: new Date(), status: 'waiting' },
workflow,
);
}
/**
* Store an execution with a given status in the DB and assign it to a workflow.
*/
export async function createdExecutionWithStatus(workflow: IWorkflowBase, status: ExecutionStatus) {
const execution: Partial<ExecutionEntity> = {
status,
finished: status === 'success' ? true : false,
stoppedAt: ['crashed', 'error'].includes(status) ? new Date() : undefined,
waitTill: status === 'waiting' ? new Date() : undefined,
};
return await createExecution(execution, workflow);
}
export async function annotateExecution(
executionId: string,
annotation: { vote?: AnnotationVote | null; tags?: string[] },
sharedWorkflowIds: string[],
) {
await Container.get(ExecutionService).annotate(executionId, annotation, sharedWorkflowIds);
}
export async function getAllExecutions() {
return await Container.get(ExecutionRepository).find();
}
export async function createAnnotationTags(annotationTags: string[]) {
const tagRepository = Container.get(AnnotationTagRepository);
return await tagRepository.save(annotationTags.map((name) => tagRepository.create({ name })));
}
@@ -0,0 +1,29 @@
import { randomName } from '@n8n/backend-test-utils';
import type { Folder, Project, TagEntity } from '@n8n/db';
import { FolderRepository } from '@n8n/db';
import { Container } from '@n8n/di';
export const createFolder = async (
project: Project,
options: {
name?: string;
parentFolder?: Folder;
tags?: TagEntity[];
updatedAt?: Date;
createdAt?: Date;
} = {},
) => {
const folderRepository = Container.get(FolderRepository);
const folder = await folderRepository.save(
folderRepository.create({
name: options.name ?? randomName(),
homeProject: project,
parentFolder: options.parentFolder ?? null,
tags: options.tags ?? [],
updatedAt: options.updatedAt ?? new Date(),
createdAt: options.updatedAt ?? new Date(),
}),
);
return folder;
};
@@ -0,0 +1,185 @@
import { RoleCacheService } from '@/services/role-cache.service';
import { Role, RoleRepository, Scope, ScopeRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { Scope as ScopeType } from '@n8n/permissions';
/**
* Creates a test role with given parameters
*/
export async function createRole(overrides: Partial<Role> = {}): Promise<Role> {
const roleRepository = Container.get(RoleRepository);
const roleCacheService = Container.get(RoleCacheService);
const defaultRole: Partial<Role> = {
slug: `test-role-${Math.random().toString(36).substring(7)}`,
displayName: `Test Role ${Math.random().toString(36).substring(7)}`,
description: 'A test role for integration testing',
systemRole: false,
roleType: 'project',
scopes: [],
};
const roleData = { ...defaultRole, ...overrides };
const role = Object.assign(new Role(), roleData);
const createdRole = await roleRepository.save(role);
// Force refresh the role cache to include the newly created role (important when running concurrent tests)
await roleCacheService.refreshCache();
return createdRole;
}
/**
* Creates a system role (cannot be deleted/modified)
*/
export async function createSystemRole(overrides: Partial<Role> = {}): Promise<Role> {
return await createRole({
systemRole: true,
slug: `system-role-${Math.random().toString(36).substring(7)}`,
displayName: 'System Test Role',
...overrides,
});
}
/**
* Creates a custom role with specific scopes
*/
export async function createCustomRoleWithScopes(
scopes: Scope[],
overrides: Partial<Role> = {},
): Promise<Role> {
return await createRole({
scopes,
systemRole: false,
...overrides,
});
}
/**
* Creates a custom role with specific scope slugs (using existing permission system scopes)
*/
export async function createCustomRoleWithScopeSlugs(
scopeSlugs: string[],
overrides: Partial<Role> = {},
): Promise<Role> {
const scopeRepository = Container.get(ScopeRepository);
// Find existing scopes by their slugs
const scopes = await scopeRepository.findByList(scopeSlugs);
if (scopes.length !== scopeSlugs.length) {
const missingScopes = scopeSlugs.filter((slug) => !scopes.some((scope) => scope.slug === slug));
throw new Error(
`Could not find all scopes. Expected ${scopeSlugs.length}, found ${scopes.length}, missing: ${missingScopes.join(', ')}`,
);
}
return await createRole({
scopes,
systemRole: false,
...overrides,
});
}
/**
* Creates a test scope with given parameters
*/
export async function createScope(overrides: Partial<Scope> = {}): Promise<Scope> {
const scopeRepository = Container.get(ScopeRepository);
const defaultScope: Partial<Scope> = {
slug: `test:scope:${Math.random().toString(36).substring(7)}` as ScopeType,
displayName: 'Test Scope',
description: 'A test scope for integration testing',
};
const scopeData = { ...defaultScope, ...overrides };
const scope = Object.assign(new Scope(), scopeData);
return await scopeRepository.save(scope);
}
/**
* Creates multiple test scopes
*/
export async function createScopes(
count: number,
baseOverrides: Partial<Scope> = {},
): Promise<Scope[]> {
const scopes: Scope[] = [];
for (let i = 0; i < count; i++) {
const scope = await createScope({
slug: `test:scope:${i}:${Math.random().toString(36).substring(7)}` as ScopeType,
displayName: `Test Scope ${i}`,
...baseOverrides,
});
scopes.push(scope);
}
return scopes;
}
/**
* Creates predefined test scopes for common test scenarios
*/
export async function createTestScopes(): Promise<{
readScope: Scope;
writeScope: Scope;
deleteScope: Scope;
adminScope: Scope;
}> {
const [readScope, writeScope, deleteScope, adminScope] = await Promise.all([
createScope({
slug: 'test:read' as ScopeType,
displayName: 'Test Read',
description: 'Test read access',
}),
createScope({
slug: 'test:write' as ScopeType,
displayName: 'Test Write',
description: 'Test write access',
}),
createScope({
slug: 'test:delete' as ScopeType,
displayName: 'Test Delete',
description: 'Test delete access',
}),
createScope({
slug: 'test:admin' as ScopeType,
displayName: 'Test Admin',
description: 'Test admin access',
}),
]);
return { readScope, writeScope, deleteScope, adminScope };
}
/**
* Cleans up test roles and scopes
*/
export async function cleanupRolesAndScopes(): Promise<void> {
const roleRepository = Container.get(RoleRepository);
const scopeRepository = Container.get(ScopeRepository);
// Delete test roles (excluding system roles for safety)
const testRoles = await roleRepository
.createQueryBuilder('role')
.where('role.slug LIKE :testPattern', { testPattern: 'test-role-%' })
.orWhere('role.slug LIKE :systemPattern', { systemPattern: 'system-role-%' })
.getMany();
for (const role of testRoles) {
await roleRepository.delete({ slug: role.slug });
}
// Delete test scopes
const testScopes = await scopeRepository
.createQueryBuilder('scope')
.where('scope.slug LIKE :pattern', { pattern: 'test:%' })
.getMany();
for (const scope of testScopes) {
await scopeRepository.delete({ slug: scope.slug });
}
}
@@ -0,0 +1,53 @@
import { randomName } from '@n8n/backend-test-utils';
import type { TagEntity, WorkflowEntity } from '@n8n/db';
import { generateNanoId, TagRepository, WorkflowTagMappingRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { IWorkflowBase } from 'n8n-workflow';
export async function createTag(attributes: Partial<TagEntity> = {}, workflow?: IWorkflowBase) {
const { name } = attributes;
const tag = await Container.get(TagRepository).save({
id: generateNanoId(),
name: name ?? randomName(),
...attributes,
});
if (workflow) {
const mappingRepository = Container.get(WorkflowTagMappingRepository);
const mapping = mappingRepository.create({ tagId: tag.id, workflowId: workflow.id });
await mappingRepository.save(mapping);
}
return tag;
}
export async function updateTag(tag: TagEntity, attributes: Partial<TagEntity>) {
const tagRepository = Container.get(TagRepository);
const updatedTag = tagRepository.merge(tag, attributes);
return await tagRepository.save(updatedTag);
}
export async function assignTagToWorkflow(tag: TagEntity, workflow: WorkflowEntity) {
const mappingRepository = Container.get(WorkflowTagMappingRepository);
// Check if mapping already exists
const existingMapping = await mappingRepository.findOne({
where: {
tagId: tag.id,
workflowId: workflow.id,
},
});
if (existingMapping) {
return existingMapping;
}
// Create new mapping
const mapping = mappingRepository.create({
tagId: tag.id,
workflowId: workflow.id,
});
return await mappingRepository.save(mapping);
}
@@ -0,0 +1,214 @@
import { randomEmail, randomName, randomValidPassword } from '@n8n/backend-test-utils';
import {
AuthIdentity,
AuthIdentityRepository,
GLOBAL_ADMIN_ROLE,
GLOBAL_CHAT_USER_ROLE,
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
type Role,
UserRepository,
} from '@n8n/db';
import { type User } from '@n8n/db';
import { Container } from '@n8n/di';
import type { ApiKeyScope } from '@n8n/permissions';
import { getApiKeyScopesForRole } from '@n8n/permissions';
import { hash } from 'bcryptjs';
import { MfaService } from '@/mfa/mfa.service';
import { TOTPService } from '@/mfa/totp.service';
import { PublicApiKeyService } from '@/services/public-api-key.service';
import type { DeepPartial } from '@n8n/typeorm';
type ApiKeyOptions = {
expiresAt?: number | null;
scopes?: ApiKeyScope[];
};
// pre-computed bcrypt hash for the string 'password', using `await hash('password', 10)`
const passwordHash = '$2a$10$njedH7S6V5898mj6p0Jr..IGY9Ms.qNwR7RbSzzX9yubJocKfvGGK';
// A null password value means that no password will be set in the database
// rendering the user as pending, an undefined value means we default
// to 'password' as password.
// Also we are hashing the plaintext password here if necessary
async function handlePasswordSetup(password: string | null | undefined): Promise<string | null> {
if (password === undefined) {
return passwordHash;
} else if (password === null) {
return null;
}
return await hash(password, 1);
}
/** Store a new user object, defaulting to a `member` */
export async function newUser(attributes: DeepPartial<User> = {}): Promise<User> {
const { email, password, firstName, lastName, role, lastActiveAt, ...rest } = attributes;
return Container.get(UserRepository).create({
email: email ?? randomEmail(),
password: await handlePasswordSetup(password),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
role: role ?? GLOBAL_MEMBER_ROLE,
lastActiveAt: lastActiveAt ?? new Date(),
...rest,
});
}
/** Store a user object in the DB */
export async function createUser(attributes: DeepPartial<User> = {}): Promise<User> {
const userInstance = await newUser(attributes);
const { user } = await Container.get(UserRepository).createUserWithProject(userInstance);
return user;
}
export async function createLdapUser(attributes: DeepPartial<User>, ldapId: string): Promise<User> {
const user = await createUser(attributes);
await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId, 'ldap'));
return user;
}
export async function createUserWithMfaEnabled(
data: { numberOfRecoveryCodes: number } = { numberOfRecoveryCodes: 10 },
) {
const email = randomEmail();
const password = randomValidPassword();
const toptService = new TOTPService();
const secret = toptService.generateSecret();
const mfaService = Container.get(MfaService);
const recoveryCodes = mfaService.generateRecoveryCodes(data.numberOfRecoveryCodes);
const { encryptedSecret, encryptedRecoveryCodes } = mfaService.encryptSecretAndRecoveryCodes(
secret,
recoveryCodes,
);
const user = await createUser({
mfaEnabled: true,
password,
email,
});
await Container.get(UserRepository).update(user.id, {
mfaSecret: encryptedSecret,
mfaRecoveryCodes: encryptedRecoveryCodes,
});
user.mfaSecret = encryptedSecret;
user.mfaRecoveryCodes = encryptedRecoveryCodes;
return {
user,
rawPassword: password,
rawSecret: secret,
rawRecoveryCodes: recoveryCodes,
};
}
export const addApiKey = async (
user: User,
{ expiresAt = null, scopes = [] }: { expiresAt?: number | null; scopes?: ApiKeyScope[] } = {},
) => {
return await Container.get(PublicApiKeyService).createPublicApiKeyForUser(user, {
label: randomName(),
expiresAt,
scopes: scopes.length ? scopes : getApiKeyScopesForRole(user),
});
};
export async function createOwnerWithApiKey({ expiresAt = null, scopes = [] }: ApiKeyOptions = {}) {
const owner = await createOwner();
const apiKey = await addApiKey(owner, { expiresAt, scopes });
owner.apiKeys = [apiKey];
return owner;
}
export async function createMemberWithApiKey({
expiresAt = null,
scopes = [],
}: ApiKeyOptions = {}) {
const member = await createMember();
const apiKey = await addApiKey(member, { expiresAt, scopes });
member.apiKeys = [apiKey];
return member;
}
export async function createAdminWithApiKey({ expiresAt = null, scopes = [] }: ApiKeyOptions = {}) {
const member = await createAdmin();
const apiKey = await addApiKey(member, { expiresAt, scopes });
member.apiKeys = [apiKey];
return member;
}
export async function createOwner() {
return await createUser({ role: GLOBAL_OWNER_ROLE });
}
export async function createMember() {
return await createUser({ role: GLOBAL_MEMBER_ROLE });
}
export async function createAdmin() {
return await createUser({ role: GLOBAL_ADMIN_ROLE });
}
export async function createChatUser() {
return await createUser({ role: GLOBAL_CHAT_USER_ROLE });
}
export async function createUserShell(role: Role): Promise<User> {
const shell: DeepPartial<User> = { role };
if (role.slug !== GLOBAL_OWNER_ROLE.slug) {
shell.email = randomEmail();
}
const { user } = await Container.get(UserRepository).createUserWithProject(shell);
return user;
}
/**
* Create many users in the DB, defaulting to a `member`.
*/
export async function createManyUsers(
amount: number,
attributes: DeepPartial<User> = {},
): Promise<User[]> {
const result = await Promise.all(
Array(amount)
.fill(0)
.map(async () => {
const userInstance = await newUser(attributes);
return await Container.get(UserRepository).createUserWithProject(userInstance);
}),
);
return result.map((result) => result.user);
}
export const getAllUsers = async () =>
await Container.get(UserRepository).find({
relations: ['authIdentities', 'role'],
});
export const getUserById = async (id: string) =>
await Container.get(UserRepository).findOneOrFail({
where: { id },
relations: ['authIdentities', 'role'],
});
export const getLdapIdentities = async () =>
await Container.get(AuthIdentityRepository).find({
where: { providerType: 'ldap' },
relations: { user: true },
});
export async function getGlobalOwner() {
return await Container.get(UserRepository).findOneOrFail({
where: { role: { slug: GLOBAL_OWNER_ROLE.slug } },
relations: ['role'],
});
}
@@ -0,0 +1,55 @@
import type { Project } from '@n8n/db';
import { generateNanoId, VariablesRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { randomString } from 'n8n-workflow';
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
export async function createVariable(key = randomString(5), value = randomString(5)) {
const result = await Container.get(VariablesRepository).save({
id: generateNanoId(),
key,
value,
});
await Container.get(VariablesService).updateCache();
return result;
}
export async function createProjectVariable(
key = randomString(5),
value = randomString(5),
project: Project,
) {
const result = await Container.get(VariablesRepository).save({
id: generateNanoId(),
key,
value,
project,
});
await Container.get(VariablesService).updateCache();
return result;
}
export async function getVariableByIdOrFail(id: string) {
return await Container.get(VariablesRepository).findOneOrFail({
where: { id },
relations: ['project'],
});
}
export async function getVariableByKey(key: string) {
return await Container.get(VariablesRepository).findOne({
where: {
key,
},
});
}
export async function getVariableById(id: string) {
return await Container.get(VariablesRepository).findOne({
where: {
id,
},
});
}
@@ -0,0 +1,46 @@
import type { WorkflowHistory } from '@n8n/db';
import { WorkflowHistoryRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { v4 as uuid } from 'uuid';
export async function createWorkflowHistoryItem(
workflowId: string,
data?: Partial<WorkflowHistory>,
) {
return await Container.get(WorkflowHistoryRepository).save({
authors: 'John Smith',
connections: {},
nodes: [
{
id: 'uuid-1234',
name: 'Start',
parameters: {},
position: [-20, 260],
type: 'n8n-nodes-base.manualTrigger',
typeVersion: 1,
},
],
versionId: uuid(),
workflowPublishHistory: [],
autosaved: false,
...(data ?? {}),
workflowId,
});
}
export async function createManyWorkflowHistoryItems(
workflowId: string,
count: number,
time?: Date,
) {
const baseTime = (time ?? new Date()).valueOf();
return await Promise.all(
[...Array(count)].map(
async (_, i) =>
await createWorkflowHistoryItem(workflowId, {
createdAt: new Date(baseTime + i),
updatedAt: new Date(baseTime + i),
}),
),
);
}
@@ -0,0 +1,32 @@
import type { WorkflowHistory, WorkflowPublishHistory } from '@n8n/db';
import { WorkflowPublishHistoryRepository } from '@n8n/db';
import { Container } from '@n8n/di';
export async function createWorkflowPublishHistoryItem(
workflowHistory: Pick<WorkflowHistory, 'workflowId' | 'versionId'>,
data?: Partial<WorkflowPublishHistory>,
) {
return await Container.get(WorkflowPublishHistoryRepository).save({
workflowId: workflowHistory.workflowId,
versionId: workflowHistory.versionId,
event: 'activated',
userId: null,
...(data ?? {}),
});
}
export async function createManyWorkflowPublishHistoryItems(
workflowHistory: WorkflowHistory,
count: number,
time?: Date,
) {
const baseTime = (time ?? new Date()).valueOf();
return await Promise.all(
[...Array(count)].map(
async (_, i) =>
await createWorkflowPublishHistoryItem(workflowHistory, {
createdAt: new Date(baseTime + i),
}),
),
);
}
@@ -0,0 +1,21 @@
import { StatisticsNames, type WorkflowStatistics } from '@n8n/db';
import { WorkflowStatisticsRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { Workflow } from 'n8n-workflow';
export async function createWorkflowStatisticsItem(
workflowId: Workflow['id'],
data?: Partial<WorkflowStatistics>,
) {
const entity = Container.get(WorkflowStatisticsRepository).create({
count: 0,
latestEvent: new Date().toISOString(),
name: StatisticsNames.manualSuccess,
...(data ?? {}),
workflowId,
});
await Container.get(WorkflowStatisticsRepository).insert(entity);
return entity;
}