mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
import {
|
|
BreakingChangeLightReportResult,
|
|
BreakingChangeReportQueryDto,
|
|
BreakingChangeVersion,
|
|
BreakingChangeWorkflowRuleResult,
|
|
WorkflowMigrationResult,
|
|
} from '@n8n/api-types';
|
|
import { AuthenticatedRequest } from '@n8n/db';
|
|
import { Get, RestController, GlobalScope, Query, Post, Param } from '@n8n/decorators';
|
|
import { NotFoundError } from '@n8n/errors';
|
|
import { Response } from 'express';
|
|
|
|
import { BreakingChangeMigrationService } from './breaking-changes.migration.service';
|
|
import { RuleRegistry } from './breaking-changes.rule-registry.service';
|
|
import { MigrationFindingQueryService } from './query/migration-finding-query.service';
|
|
import { MigrationFindingSyncService } from './sync/migration-finding-sync.service';
|
|
|
|
/** The version the report targets when the request names none. */
|
|
const DEFAULT_TARGET_VERSION: BreakingChangeVersion = 'v2';
|
|
|
|
@RestController('/breaking-changes')
|
|
export class BreakingChangesController {
|
|
constructor(
|
|
private readonly migrationService: BreakingChangeMigrationService,
|
|
private readonly syncService: MigrationFindingSyncService,
|
|
private readonly queryService: MigrationFindingQueryService,
|
|
private readonly ruleRegistry: RuleRegistry,
|
|
) {}
|
|
|
|
/**
|
|
* The report overview, read from the finding table. A first read, or a
|
|
* changed rule set, fills the table before the read.
|
|
*/
|
|
@Get('/report')
|
|
@GlobalScope('breakingChanges:list')
|
|
async getDetectionReport(
|
|
_req: AuthenticatedRequest,
|
|
_res: Response,
|
|
@Query query: BreakingChangeReportQueryDto,
|
|
): Promise<BreakingChangeLightReportResult> {
|
|
const version = query.version ?? DEFAULT_TARGET_VERSION;
|
|
await this.syncService.syncIfStale(version);
|
|
return await this.queryService.getLightReport(version);
|
|
}
|
|
|
|
/** Re-scans every workflow, updates the finding table, and returns the fresh overview. */
|
|
@Post('/report/refresh')
|
|
@GlobalScope('breakingChanges:list')
|
|
async regenerate(
|
|
_req: AuthenticatedRequest,
|
|
_res: Response,
|
|
@Query query: BreakingChangeReportQueryDto,
|
|
): Promise<BreakingChangeLightReportResult> {
|
|
const version = query.version ?? DEFAULT_TARGET_VERSION;
|
|
await this.syncService.sync(version);
|
|
return await this.queryService.getLightReport(version);
|
|
}
|
|
|
|
/**
|
|
* The detail of one workflow rule, read from the finding table like the
|
|
* overview. The same stale check runs first, so a deep link on a fresh
|
|
* instance is not empty.
|
|
*/
|
|
@Get('/report/:ruleId')
|
|
@GlobalScope('breakingChanges:list')
|
|
async getDetectionReportForRule(
|
|
_req: AuthenticatedRequest,
|
|
_res: Response,
|
|
@Param('ruleId') ruleId: string,
|
|
): Promise<BreakingChangeWorkflowRuleResult> {
|
|
// The page names the rule but not the version, so the rule decides.
|
|
const rule = this.ruleRegistry.getRule(ruleId);
|
|
if (!rule) {
|
|
throw new NotFoundError(`Breaking change rule with ID '${ruleId}' not found.`);
|
|
}
|
|
const version = rule.getMetadata().version;
|
|
await this.syncService.syncIfStale(version);
|
|
return await this.queryService.getRuleFindings(version, ruleId);
|
|
}
|
|
|
|
/**
|
|
* Apply the rule's automated migration to a single workflow, saving the
|
|
* rewritten workflow as a new version.
|
|
*/
|
|
@Post('/report/:ruleId/workflows/:workflowId/migrate')
|
|
@GlobalScope('breakingChanges:migrate')
|
|
async migrateWorkflow(
|
|
req: AuthenticatedRequest,
|
|
_res: Response,
|
|
@Param('ruleId') ruleId: string,
|
|
@Param('workflowId') workflowId: string,
|
|
): Promise<WorkflowMigrationResult> {
|
|
return await this.migrationService.migrateWorkflow(ruleId, workflowId, req.user);
|
|
}
|
|
}
|