diff --git a/packages/cli/src/databases/entities/index.ts b/packages/cli/src/databases/entities/index.ts index be73a0ed63..3f445e835a 100644 --- a/packages/cli/src/databases/entities/index.ts +++ b/packages/cli/src/databases/entities/index.ts @@ -21,6 +21,7 @@ import { SharedCredentials } from './shared-credentials'; import { SharedWorkflow } from './shared-workflow'; import { TagEntity } from './tag-entity'; import { TestDefinition } from './test-definition.ee'; +import { TestMetric } from './test-metric.ee'; import { User } from './user'; import { Variables } from './variables'; import { WebhookEntity } from './webhook-entity'; @@ -60,4 +61,5 @@ export const entities = { ApiKey, ProcessedData, TestDefinition, + TestMetric, }; diff --git a/packages/cli/src/databases/entities/test-definition.ee.ts b/packages/cli/src/databases/entities/test-definition.ee.ts index dd39ebef02..77f8ca2bdc 100644 --- a/packages/cli/src/databases/entities/test-definition.ee.ts +++ b/packages/cli/src/databases/entities/test-definition.ee.ts @@ -1,7 +1,8 @@ -import { Column, Entity, Index, ManyToOne, RelationId } from '@n8n/typeorm'; +import { Column, Entity, Index, ManyToOne, OneToMany, RelationId } from '@n8n/typeorm'; import { Length } from 'class-validator'; import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; +import type { TestMetric } from '@/databases/entities/test-metric.ee'; import { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { WithTimestampsAndStringId } from './abstract-entity'; @@ -53,4 +54,7 @@ export class TestDefinition extends WithTimestampsAndStringId { @RelationId((test: TestDefinition) => test.annotationTag) annotationTagId: string; + + @OneToMany('TestMetric', 'testDefinition') + metrics: TestMetric[]; } diff --git a/packages/cli/src/databases/entities/test-metric.ee.ts b/packages/cli/src/databases/entities/test-metric.ee.ts new file mode 100644 index 0000000000..bc3dc07f7a --- /dev/null +++ b/packages/cli/src/databases/entities/test-metric.ee.ts @@ -0,0 +1,29 @@ +import { Column, Entity, Index, ManyToOne } from '@n8n/typeorm'; +import { Length } from 'class-validator'; + +import { WithTimestampsAndStringId } from '@/databases/entities/abstract-entity'; +import { TestDefinition } from '@/databases/entities/test-definition.ee'; + +/** + * Entity representing a Test Metric + * It represents a single metric that can be retrieved from evaluation workflow execution result + */ +@Entity() +@Index(['testDefinition']) +export class TestMetric extends WithTimestampsAndStringId { + /** + * Name of the metric. + * This will be used as a property name to extract metric value from the evaluation workflow execution result object + */ + @Column({ length: 255 }) + @Length(1, 255, { + message: 'Metric name must be $constraint1 to $constraint2 characters long.', + }) + name: string; + + /** + * Relation to test definition + */ + @ManyToOne('TestDefinition', 'metrics') + testDefinition: TestDefinition; +} diff --git a/packages/cli/src/databases/migrations/common/1732271325258-CreateTestMetricTable.ts b/packages/cli/src/databases/migrations/common/1732271325258-CreateTestMetricTable.ts new file mode 100644 index 0000000000..33460d8fb1 --- /dev/null +++ b/packages/cli/src/databases/migrations/common/1732271325258-CreateTestMetricTable.ts @@ -0,0 +1,24 @@ +import type { MigrationContext, ReversibleMigration } from '@/databases/types'; + +const testMetricEntityTableName = 'test_metric'; + +export class CreateTestMetricTable1732271325258 implements ReversibleMigration { + async up({ schemaBuilder: { createTable, column } }: MigrationContext) { + await createTable(testMetricEntityTableName) + .withColumns( + column('id').varchar(36).primary.notNull, + column('name').varchar(255).notNull, + column('testDefinitionId').varchar(36).notNull, + ) + .withIndexOn('testDefinitionId') + .withForeignKey('testDefinitionId', { + tableName: 'test_definition', + columnName: 'id', + onDelete: 'CASCADE', + }).withTimestamps; + } + + async down({ schemaBuilder: { dropTable } }: MigrationContext) { + await dropTable(testMetricEntityTableName); + } +} diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts index 4b219e9a26..534c4404fa 100644 --- a/packages/cli/src/databases/migrations/mysqldb/index.ts +++ b/packages/cli/src/databases/migrations/mysqldb/index.ts @@ -71,6 +71,7 @@ import { AddMissingPrimaryKeyOnAnnotationTagMapping1728659839644 } from '../comm import { UpdateProcessedDataValueColumnToText1729607673464 } from '../common/1729607673464-UpdateProcessedDataValueColumnToText'; import { CreateTestDefinitionTable1730386903556 } from '../common/1730386903556-CreateTestDefinitionTable'; import { AddDescriptionToTestDefinition1731404028106 } from '../common/1731404028106-AddDescriptionToTestDefinition'; +import { CreateTestMetricTable1732271325258 } from '../common/1732271325258-CreateTestMetricTable'; export const mysqlMigrations: Migration[] = [ InitialMigration1588157391238, @@ -144,4 +145,5 @@ export const mysqlMigrations: Migration[] = [ CreateTestDefinitionTable1730386903556, AddDescriptionToTestDefinition1731404028106, MigrateTestDefinitionKeyToString1731582748663, + CreateTestMetricTable1732271325258, ]; diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index 689840b937..8a771490d9 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -71,6 +71,7 @@ import { AddMissingPrimaryKeyOnAnnotationTagMapping1728659839644 } from '../comm import { UpdateProcessedDataValueColumnToText1729607673464 } from '../common/1729607673464-UpdateProcessedDataValueColumnToText'; import { CreateTestDefinitionTable1730386903556 } from '../common/1730386903556-CreateTestDefinitionTable'; import { AddDescriptionToTestDefinition1731404028106 } from '../common/1731404028106-AddDescriptionToTestDefinition'; +import { CreateTestMetricTable1732271325258 } from '../common/1732271325258-CreateTestMetricTable'; export const postgresMigrations: Migration[] = [ InitialMigration1587669153312, @@ -144,4 +145,5 @@ export const postgresMigrations: Migration[] = [ CreateTestDefinitionTable1730386903556, AddDescriptionToTestDefinition1731404028106, MigrateTestDefinitionKeyToString1731582748663, + CreateTestMetricTable1732271325258, ]; diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts index 5c7c6d9a07..80e5d8491d 100644 --- a/packages/cli/src/databases/migrations/sqlite/index.ts +++ b/packages/cli/src/databases/migrations/sqlite/index.ts @@ -68,6 +68,7 @@ import { CreateProcessedDataTable1726606152711 } from '../common/1726606152711-C import { SeparateExecutionCreationFromStart1727427440136 } from '../common/1727427440136-SeparateExecutionCreationFromStart'; import { UpdateProcessedDataValueColumnToText1729607673464 } from '../common/1729607673464-UpdateProcessedDataValueColumnToText'; import { CreateTestDefinitionTable1730386903556 } from '../common/1730386903556-CreateTestDefinitionTable'; +import { CreateTestMetricTable1732271325258 } from '../common/1732271325258-CreateTestMetricTable'; const sqliteMigrations: Migration[] = [ InitialMigration1588102412422, @@ -138,6 +139,7 @@ const sqliteMigrations: Migration[] = [ CreateTestDefinitionTable1730386903556, AddDescriptionToTestDefinition1731404028106, MigrateTestDefinitionKeyToString1731582748663, + CreateTestMetricTable1732271325258, ]; export { sqliteMigrations }; diff --git a/packages/cli/src/databases/repositories/test-definition.repository.ee.ts b/packages/cli/src/databases/repositories/test-definition.repository.ee.ts index ecd4bdde34..e705af278c 100644 --- a/packages/cli/src/databases/repositories/test-definition.repository.ee.ts +++ b/packages/cli/src/databases/repositories/test-definition.repository.ee.ts @@ -45,7 +45,7 @@ export class TestDefinitionRepository extends Repository { id: In(accessibleWorkflowIds), }, }, - relations: ['annotationTag'], + relations: ['annotationTag', 'metrics'], }); } diff --git a/packages/cli/src/databases/repositories/test-metric.repository.ee.ts b/packages/cli/src/databases/repositories/test-metric.repository.ee.ts new file mode 100644 index 0000000000..01635ef8f7 --- /dev/null +++ b/packages/cli/src/databases/repositories/test-metric.repository.ee.ts @@ -0,0 +1,11 @@ +import { DataSource, Repository } from '@n8n/typeorm'; +import { Service } from 'typedi'; + +import { TestMetric } from '@/databases/entities/test-metric.ee'; + +@Service() +export class TestMetricRepository extends Repository { + constructor(dataSource: DataSource) { + super(TestMetric, dataSource.manager); + } +} diff --git a/packages/cli/src/evaluation/test-definition.service.ee.ts b/packages/cli/src/evaluation/test-definition.service.ee.ts index d32a2e153d..55b7339ebe 100644 --- a/packages/cli/src/evaluation/test-definition.service.ee.ts +++ b/packages/cli/src/evaluation/test-definition.service.ee.ts @@ -10,7 +10,7 @@ import type { ListQuery } from '@/requests'; type TestDefinitionLike = Omit< Partial, - 'workflow' | 'evaluationWorkflow' | 'annotationTag' + 'workflow' | 'evaluationWorkflow' | 'annotationTag' | 'metrics' > & { workflow?: { id: string }; evaluationWorkflow?: { id: string };