mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 16:44:07 -08:00
🔨 Refactor ORM operations as helpers
This commit is contained in:
parent
cfc8aa75ac
commit
9249c6d622
|
@ -585,20 +585,7 @@ class App {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
result.tags = await getConnection()
|
||||
.createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.where(qb => {
|
||||
return "id IN " + qb.subQuery()
|
||||
.select('tagId')
|
||||
.from('workflow_entity', 'workflow_entity')
|
||||
.leftJoin('workflows_tags', 'workflows_tags', 'workflows_tags.workflowId = workflow_entity.id')
|
||||
.where("workflow_entity.id = :id", { id: Number(req.params.id) })
|
||||
.getQuery();
|
||||
})
|
||||
.getRawMany();
|
||||
result.tags = await TagHelpers.getWorkflowTags(req.params.id);
|
||||
|
||||
// Convert to response format in which the id is a string
|
||||
(result as IWorkflowBase as IWorkflowResponse).id = result.id.toString();
|
||||
|
@ -718,40 +705,6 @@ class App {
|
|||
return true;
|
||||
}));
|
||||
|
||||
// Adds a tag to a workflow
|
||||
this.app.post(`/${this.restEndpoint}/workflows/:workflowId/tags/:tagId`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<{ workflowId: number, tagId: number }> => {
|
||||
const workflowId = Number(req.params.workflowId);
|
||||
const tagId = Number(req.params.tagId);
|
||||
|
||||
await TagHelpers.validateId(tagId);
|
||||
await TagHelpers.validateNoRelation(workflowId, tagId);
|
||||
|
||||
await getConnection().createQueryBuilder()
|
||||
.insert()
|
||||
.into('workflows_tags')
|
||||
.values([ { workflowId, tagId } ])
|
||||
.execute();
|
||||
|
||||
return { workflowId, tagId };
|
||||
}));
|
||||
|
||||
// Removes a tag from a workflow
|
||||
this.app.delete(`/${this.restEndpoint}/workflows/:workflowId/tags/:tagId`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<boolean> => {
|
||||
const workflowId = Number(req.params.workflowId);
|
||||
const tagId = Number(req.params.tagId);
|
||||
|
||||
await TagHelpers.validateId(tagId);
|
||||
await TagHelpers.validateRelation(workflowId, tagId);
|
||||
|
||||
await getConnection().createQueryBuilder()
|
||||
.delete()
|
||||
.from('workflows_tags')
|
||||
.where('workflowId = :workflowId AND tagId = :tagId', { workflowId, tagId })
|
||||
.execute();
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
this.app.post(`/${this.restEndpoint}/workflows/run`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<IExecutionPushResponse> => {
|
||||
const workflowData = req.body.workflowData;
|
||||
const runData: IRunData | undefined = req.body.runData;
|
||||
|
@ -801,26 +754,9 @@ class App {
|
|||
|
||||
// Retrieves all tags, with or without usage count
|
||||
this.app.get(`/${this.restEndpoint}/tags`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<Array<{ id: number, name: string, usageCount?: number }>> => {
|
||||
const withUsageCount = req.query.withUsageCount === 'true';
|
||||
|
||||
if (withUsageCount) {
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.addSelect('COUNT(workflow_entity.id)', 'usageCount')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.leftJoin('workflows_tags', 'workflows_tags', 'workflows_tags.tagId = tag_entity.id')
|
||||
.leftJoin('workflow_entity', 'workflow_entity', 'workflows_tags.workflowId = workflow_entity.id')
|
||||
.groupBy('tag_entity.id')
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.groupBy('tag_entity.id')
|
||||
.getRawMany();
|
||||
return req.query.withUsageCount === 'true'
|
||||
? await TagHelpers.getAllTagsWithUsageCount()
|
||||
: await TagHelpers.getAllTags();
|
||||
}));
|
||||
|
||||
// Creates a tag
|
||||
|
@ -828,8 +764,8 @@ class App {
|
|||
TagHelpers.validateRequestBody(req.body);
|
||||
|
||||
const { name } = req.body;
|
||||
await TagHelpers.validateName(name);
|
||||
TagHelpers.validateLength(name);
|
||||
await TagHelpers.validateName(name);
|
||||
|
||||
const newTag: ITagBase = {
|
||||
name,
|
||||
|
@ -855,8 +791,8 @@ class App {
|
|||
TagHelpers.validateRequestBody(req.body);
|
||||
|
||||
const { name } = req.body;
|
||||
await TagHelpers.validateName(name);
|
||||
TagHelpers.validateLength(name);
|
||||
await TagHelpers.validateName(name);
|
||||
|
||||
const id = Number(req.params.id);
|
||||
await TagHelpers.validateId(id);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
FindManyOptions,
|
||||
FindOneOptions,
|
||||
getConnection,
|
||||
In,
|
||||
|
@ -10,17 +9,10 @@ import {
|
|||
ResponseHelper,
|
||||
} from ".";
|
||||
|
||||
/**
|
||||
* Validate whether a tag name exists so that it cannot be used for a tag create or tag update operation.
|
||||
*/
|
||||
export async function validateName(name: string): Promise<void> | never {
|
||||
const findQuery = { where: { name } } as FindOneOptions;
|
||||
const tag = await Db.collections.Tag!.findOne(findQuery);
|
||||
|
||||
if (tag) {
|
||||
throw new ResponseHelper.ResponseError('Tag name already exists.', undefined, 400);
|
||||
}
|
||||
}
|
||||
// ----------------------------------
|
||||
// validators
|
||||
// ----------------------------------
|
||||
|
||||
/**
|
||||
* Validate whether a tag ID exists so that it can be used for a workflow create or tag update operation.
|
||||
|
@ -44,11 +36,14 @@ export function validateLength(name: string): void | never {
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate whether the request body for a create/update operation has a `name` property.
|
||||
* Validate whether a tag name exists so that it cannot be used for a tag create or tag update operation.
|
||||
*/
|
||||
export function validateRequestBody({ name }: { name: string }): void | never {
|
||||
if (!name) {
|
||||
throw new ResponseHelper.ResponseError(`Property 'name' missing from request body.`, undefined, 400);
|
||||
export async function validateName(name: string): Promise<void> | never {
|
||||
const findQuery = { where: { name } } as FindOneOptions;
|
||||
const tag = await Db.collections.Tag!.findOne(findQuery);
|
||||
|
||||
if (tag) {
|
||||
throw new ResponseHelper.ResponseError('Tag name already exists.', undefined, 400);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +51,7 @@ export function validateRequestBody({ name }: { name: string }): void | never {
|
|||
* Validate that a tag and a workflow are not related so that a link can be created.
|
||||
*/
|
||||
export async function validateNoRelation(workflowId: number, tagId: number): Promise<void> | never {
|
||||
const result = await findRelation(workflowId, tagId);
|
||||
const result = await findRelations(workflowId, tagId);
|
||||
|
||||
if (result.length) {
|
||||
throw new ResponseHelper.ResponseError(`Workflow ID ${workflowId} and tag ID ${tagId} are already related.`, undefined, 400);
|
||||
|
@ -67,7 +62,7 @@ export async function validateNoRelation(workflowId: number, tagId: number): Pro
|
|||
* Validate that a tag and a workflow are related so that their link can be deleted.
|
||||
*/
|
||||
export async function validateRelation(workflowId: number, tagId: number): Promise<void> | never {
|
||||
const result = await findRelation(workflowId, tagId);
|
||||
const result = await findRelations(workflowId, tagId);
|
||||
|
||||
if (!result.length) {
|
||||
throw new ResponseHelper.ResponseError(`Workflow ID ${workflowId} and tag ID ${tagId} are not related.`, undefined, 400);
|
||||
|
@ -75,9 +70,70 @@ export async function validateRelation(workflowId: number, tagId: number): Promi
|
|||
}
|
||||
|
||||
/**
|
||||
* Find a relation between a workflow and a tag, if any.
|
||||
* Validate whether the request body for a create/update operation has a `name` property.
|
||||
*/
|
||||
async function findRelation(workflowId: number, tagId: number): Promise<Array<{ workflowId: number, tagId: number }>> {
|
||||
export function validateRequestBody({ name }: { name: string | undefined }): void | never {
|
||||
if (!name) {
|
||||
throw new ResponseHelper.ResponseError(`Property 'name' missing from request body.`, undefined, 400);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// queries
|
||||
// ----------------------------------
|
||||
|
||||
/**
|
||||
* Retrieve all existing tags, whether linked to a workflow or not.
|
||||
*/
|
||||
export async function getAllTags(): Promise<Array<{ id: number; name: string }>> {
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.groupBy('tag_entity.id')
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all existing tags, whether linked to a workflow or not,
|
||||
* including how many workflows each tag is linked to.
|
||||
*/
|
||||
export async function getAllTagsWithUsageCount(): Promise<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
usageCount: number
|
||||
}>> {
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.addSelect('COUNT(workflow_entity.id)', 'usageCount')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.leftJoin('workflows_tags', 'workflows_tags', 'workflows_tags.tagId = tag_entity.id')
|
||||
.leftJoin('workflow_entity', 'workflow_entity', 'workflows_tags.workflowId = workflow_entity.id')
|
||||
.groupBy('tag_entity.id')
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve tag IDs and names, to be used in an API response.
|
||||
*/
|
||||
export async function getTagsForResponseData(
|
||||
tagIds: number[]
|
||||
): Promise<Array<{ id: number; name: string }>> {
|
||||
return await Db.collections.Tag!.find({
|
||||
select: ['id', 'name'],
|
||||
where: { id: In(tagIds) },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find if a workflow and a tag are related.
|
||||
*/
|
||||
async function findRelations(
|
||||
workflowId: number,
|
||||
tagId: number
|
||||
): Promise<Array<{ workflowId: number, tagId: number }>> {
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select()
|
||||
.from('workflows_tags', 'workflows_tags')
|
||||
|
@ -85,6 +141,43 @@ async function findRelation(workflowId: number, tagId: number): Promise<Array<{
|
|||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tags linked to a single workflow.
|
||||
*/
|
||||
export async function getWorkflowTags(
|
||||
workflowId: string
|
||||
): Promise<Array<{ id: number; name: string }>> {
|
||||
return await getConnection().createQueryBuilder()
|
||||
.select('tag_entity.id', 'id')
|
||||
.addSelect('tag_entity.name', 'name')
|
||||
.from('tag_entity', 'tag_entity')
|
||||
.where(qb => {
|
||||
return "id IN " + qb.subQuery()
|
||||
.select('tagId')
|
||||
.from('workflow_entity', 'workflow_entity')
|
||||
.leftJoin('workflows_tags', 'workflows_tags', 'workflows_tags.workflowId = workflow_entity.id')
|
||||
.where("workflow_entity.id = :id", { id: workflowId })
|
||||
.getQuery();
|
||||
})
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// mutations
|
||||
// ----------------------------------
|
||||
|
||||
/**
|
||||
* Link a workflow to one or more tags.
|
||||
*/
|
||||
export async function createTagWorkflowRelations(workflowId: string, tagIds: number[]) {
|
||||
await getConnection().createQueryBuilder()
|
||||
.insert()
|
||||
.into('workflows_tags')
|
||||
.values(tagIds.map(tagId => ({ workflowId, tagId })))
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all tags for a workflow during a tag update operation.
|
||||
*/
|
||||
|
@ -95,24 +188,3 @@ export async function deleteAllTagsForWorkflow(workflowId: string) {
|
|||
.where('workflowId = :id', { id: workflowId })
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a workflow with one or many tags.
|
||||
*/
|
||||
export async function createTagWorkflowRelations(workflowId: string, tagIds: number[]) {
|
||||
await getConnection().createQueryBuilder()
|
||||
.insert()
|
||||
.into('workflows_tags')
|
||||
.values(tagIds.map(tagId => ({ workflowId, tagId })))
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return tag IDs and names, only for use in a response.
|
||||
*/
|
||||
export async function getTagsForResponseData(tagIds: number[]) {
|
||||
return await Db.collections.Tag!.find({
|
||||
select: ['id', 'name'],
|
||||
where: { id: In(tagIds) },
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue