feat(core): Initial workflow history API (#7234)

Github issue / Community forum post (link here to close automatically):
This commit is contained in:
Val
2023-09-27 15:22:39 +01:00
committed by GitHub
parent 5c57e2ccc3
commit 0083a9e45d
14 changed files with 474 additions and 17 deletions

View File

@@ -0,0 +1,76 @@
import { Authorized, RestController, Get, Middleware } from '@/decorators';
import { WorkflowHistoryRequest } from '@/requests';
import { Service } from 'typedi';
import {
HistoryVersionNotFoundError,
SharedWorkflowNotFoundError,
WorkflowHistoryService,
} from './workflowHistory.service.ee';
import { Request, Response, NextFunction } from 'express';
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
import { NotFoundError } from '@/ResponseHelper';
import { paginationListQueryMiddleware } from '@/middlewares/listQuery/pagination';
const DEFAULT_TAKE = 20;
@Service()
@Authorized()
@RestController('/workflow-history')
export class WorkflowHistoryController {
constructor(private readonly historyService: WorkflowHistoryService) {}
@Middleware()
workflowHistoryLicense(_req: Request, res: Response, next: NextFunction) {
if (!isWorkflowHistoryLicensed()) {
res.status(403);
res.send('Workflow History license data not found');
return;
}
next();
}
@Middleware()
workflowHistoryEnabled(_req: Request, res: Response, next: NextFunction) {
if (!isWorkflowHistoryEnabled()) {
res.status(403);
res.send('Workflow History is disabled');
return;
}
next();
}
@Get('/workflow/:workflowId', { middlewares: [paginationListQueryMiddleware] })
async getList(req: WorkflowHistoryRequest.GetList) {
try {
return await this.historyService.getList(
req.user,
req.params.workflowId,
req.query.take ?? DEFAULT_TAKE,
req.query.skip ?? 0,
);
} catch (e) {
if (e instanceof SharedWorkflowNotFoundError) {
throw new NotFoundError('Could not find workflow');
}
throw e;
}
}
@Get('/workflow/:workflowId/version/:versionId')
async getVersion(req: WorkflowHistoryRequest.GetVersion) {
try {
return await this.historyService.getVersion(
req.user,
req.params.workflowId,
req.params.versionId,
);
} catch (e) {
if (e instanceof SharedWorkflowNotFoundError) {
throw new NotFoundError('Could not find workflow');
} else if (e instanceof HistoryVersionNotFoundError) {
throw new NotFoundError('Could not find version');
}
throw e;
}
}
}

View File

@@ -0,0 +1,78 @@
import type { SharedWorkflow } from '@/databases/entities/SharedWorkflow';
import type { User } from '@/databases/entities/User';
import type { WorkflowEntity } from '@/databases/entities/WorkflowEntity';
import type { WorkflowHistory } from '@/databases/entities/WorkflowHistory';
import { SharedWorkflowRepository } from '@/databases/repositories';
import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
import { Service } from 'typedi';
import { isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';
export class SharedWorkflowNotFoundError extends Error {}
export class HistoryVersionNotFoundError extends Error {}
@Service()
export class WorkflowHistoryService {
constructor(
private readonly workflowHistoryRepository: WorkflowHistoryRepository,
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
) {}
private async getSharedWorkflow(user: User, workflowId: string): Promise<SharedWorkflow | null> {
return this.sharedWorkflowRepository.findOne({
where: {
...(!user.isOwner && { userId: user.id }),
workflowId,
},
});
}
async getList(
user: User,
workflowId: string,
take: number,
skip: number,
): Promise<Array<Omit<WorkflowHistory, 'nodes' | 'connections'>>> {
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
if (!sharedWorkflow) {
throw new SharedWorkflowNotFoundError();
}
return this.workflowHistoryRepository.find({
where: {
workflowId: sharedWorkflow.workflowId,
},
take,
skip,
select: ['workflowId', 'versionId', 'authors', 'createdAt', 'updatedAt'],
order: { createdAt: 'DESC' },
});
}
async getVersion(user: User, workflowId: string, versionId: string): Promise<WorkflowHistory> {
const sharedWorkflow = await this.getSharedWorkflow(user, workflowId);
if (!sharedWorkflow) {
throw new SharedWorkflowNotFoundError();
}
const hist = await this.workflowHistoryRepository.findOne({
where: {
workflowId: sharedWorkflow.workflowId,
versionId,
},
});
if (!hist) {
throw new HistoryVersionNotFoundError();
}
return hist;
}
async saveVersion(user: User, workflow: WorkflowEntity) {
if (isWorkflowHistoryEnabled()) {
await this.workflowHistoryRepository.insert({
authors: user.firstName + ' ' + user.lastName,
connections: workflow.connections,
nodes: workflow.nodes,
versionId: workflow.versionId,
workflowId: workflow.id,
});
}
}
}

View File

@@ -5,3 +5,7 @@ export function isWorkflowHistoryLicensed() {
const license = Container.get(License);
return license.isWorkflowHistoryLicensed();
}
export function isWorkflowHistoryEnabled() {
return isWorkflowHistoryLicensed();
}

View File

@@ -1,7 +0,0 @@
import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
import { Service } from 'typedi';
@Service()
export class WorkflowHistoryService {
constructor(private readonly workflowHistoryRepository: WorkflowHistoryRepository) {}
}

View File

@@ -33,6 +33,8 @@ import { WorkflowRepository } from '@/databases/repositories';
import { RoleService } from '@/services/role.service';
import { OwnershipService } from '@/services/ownership.service';
import { isStringArray, isWorkflowIdValid } from '@/utils';
import { isWorkflowHistoryLicensed } from './workflowHistory/workflowHistoryHelper.ee';
import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee';
export class WorkflowsService {
static async getSharing(
@@ -298,6 +300,10 @@ export class WorkflowsService {
);
}
if (isWorkflowHistoryLicensed()) {
await Container.get(WorkflowHistoryService).saveVersion(user, shared.workflow);
}
const relations = config.getEnv('workflowTagsDisabled') ? [] : ['tags'];
// We sadly get nothing back from "update". Neither if it updated a record