UNPKG

@n8n/n8n-benchmark

Version:

Cli for running benchmark tests for n8n

166 lines (138 loc) 5.76 kB
import type { AuthenticatedN8nApiClient } from '@/n8n-api-client/authenticated-n8n-api-client'; import { CredentialApiClient } from '@/n8n-api-client/credentials-api-client'; import { DataTableApiClient } from '@/n8n-api-client/data-table-api-client'; import type { Workflow, Credential, DataTable } from '@/n8n-api-client/n8n-api-client.types'; import { ProjectApiClient } from '@/n8n-api-client/project-api-client'; import { WorkflowApiClient } from '@/n8n-api-client/workflows-api-client'; import type { LoadableScenarioData } from '@/scenario/scenario-data-loader'; /** * Imports scenario data into an n8n instance */ export class ScenarioDataImporter { private readonly workflowApiClient: WorkflowApiClient; private readonly credentialApiClient: CredentialApiClient; private readonly dataTableApiClient: DataTableApiClient; private readonly projectApiClient: ProjectApiClient; constructor(n8nApiClient: AuthenticatedN8nApiClient) { this.workflowApiClient = new WorkflowApiClient(n8nApiClient); this.credentialApiClient = new CredentialApiClient(n8nApiClient); this.dataTableApiClient = new DataTableApiClient(n8nApiClient); this.projectApiClient = new ProjectApiClient(n8nApiClient); } private replaceValuesInObject(obj: unknown, searchText: string, targetText: string) { if (Array.isArray(obj)) { obj.map((item) => this.replaceValuesInObject(item, searchText, targetText)); } else if (typeof obj === 'object' && obj !== null) { for (const [key, value] of Object.entries(obj)) { if (typeof value === 'string' && value === searchText) { (obj as Record<string, unknown>)[key] = targetText; } else { this.replaceValuesInObject(value, searchText, targetText); } } } } async importTestScenarioData(data: LoadableScenarioData) { const existingWorkflows = await this.workflowApiClient.getAllWorkflows(); const existingCredentials = await this.credentialApiClient.getAllCredentials(); const existingDataTables = await this.dataTableApiClient.getAllDataTables(); for (const credential of data.credentials) { const createdCredential = await this.importCredentials({ existingCredentials, credential }); // We need to update the id and name of the credential in the workflows for (const workflow of data.workflows) { this.replaceValuesInObject(workflow, credential.id, createdCredential.id); this.replaceValuesInObject(workflow, credential.name, createdCredential.name); } } for (const workflow of data.workflows) { await this.importWorkflow({ existingWorkflows, workflow }); } let dataTableId: string | undefined; if (data.dataTable) { dataTableId = await this.importDataTable({ existingDataTables, dataTable: data.dataTable }); } return { dataTableId }; } /** * Imports a single credential into n8n removing any existing credentials with the same name * @param opts * @returns */ private async importCredentials(opts: { existingCredentials: Credential[]; credential: Credential; }) { const existingCredentials = this.findExistingCredentials( opts.existingCredentials, opts.credential, ); if (existingCredentials.length > 0) { for (const toDelete of existingCredentials) { await this.credentialApiClient.deleteCredential(toDelete.id); } } return await this.credentialApiClient.createCredential({ ...opts.credential, name: this.getBenchmarkCredentialName(opts.credential), }); } private async importDataTable(opts: { existingDataTables: DataTable[]; dataTable: DataTable }) { const { existingDataTables, dataTable } = opts; const projectId = await this.projectApiClient.getPersonalProject(); const existingTable = existingDataTables.find( (dt) => dt.name === this.getBenchmarkDataTableName(dataTable), ); if (existingTable) { await this.dataTableApiClient.deleteDataTable(projectId, existingTable.id); } const { id } = await this.dataTableApiClient.createDataTable(projectId, { ...dataTable, name: this.getBenchmarkDataTableName(dataTable), }); return id; } /** * Imports a single workflow into n8n removing any existing workflows with the same name */ private async importWorkflow(opts: { existingWorkflows: Workflow[]; workflow: Workflow }) { const existingWorkflows = this.findExistingWorkflows(opts.existingWorkflows, opts.workflow); if (existingWorkflows.length > 0) { for (const toDelete of existingWorkflows) { await this.workflowApiClient.archiveWorkflow(toDelete.id); await this.workflowApiClient.deleteWorkflow(toDelete.id); } } const createdWorkflow = await this.workflowApiClient.createWorkflow({ ...opts.workflow, name: this.getBenchmarkWorkflowName(opts.workflow), }); return await this.workflowApiClient.activateWorkflow(createdWorkflow); } private findExistingCredentials( existingCredentials: Credential[], credentialToImport: Credential, ): Credential[] { const benchmarkCredentialName = this.getBenchmarkCredentialName(credentialToImport); return existingCredentials.filter( (existingCredential) => existingCredential.name === benchmarkCredentialName, ); } private findExistingWorkflows( existingWorkflows: Workflow[], workflowToImport: Workflow, ): Workflow[] { const benchmarkWorkflowName = this.getBenchmarkWorkflowName(workflowToImport); return existingWorkflows.filter( (existingWorkflow) => existingWorkflow.name === benchmarkWorkflowName, ); } private getBenchmarkCredentialName(credential: Credential) { return `[BENCHMARK] ${credential.name}`; } private getBenchmarkWorkflowName(workflow: Workflow) { return `[BENCHMARK] ${workflow.name}`; } private getBenchmarkDataTableName(dataTable: DataTable) { return `[BENCHMARK] ${dataTable.name}`; } }