refactor: Use string ids on Credentials, Workflows, Tags, and Executions DB entities (#5041)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-01-02 17:42:32 +01:00
committed by GitHub
parent 8bee04cd2a
commit ee28213538
83 changed files with 468 additions and 645 deletions

View File

@@ -92,7 +92,7 @@ EEWorkflowController.get(
relations.push('tags');
}
const workflow = await EEWorkflows.get({ id: parseInt(workflowId, 10) }, { relations });
const workflow = await EEWorkflows.get({ id: workflowId }, { relations });
if (!workflow) {
throw new ResponseHelper.NotFoundError(`Workflow with ID "${workflowId}" does not exist`);
@@ -108,7 +108,7 @@ EEWorkflowController.get(
EEWorkflows.addOwnerAndSharings(workflow);
await EEWorkflows.addCredentialsToWorkflow(workflow, req.user);
return EEWorkflows.entityToResponse(workflow);
return workflow;
}),
);
@@ -189,7 +189,7 @@ EEWorkflowController.post(
await ExternalHooks().run('workflow.afterCreate', [savedWorkflow]);
void InternalHooksManager.getInstance().onWorkflowCreated(req.user.id, newWorkflow, false);
return EEWorkflows.entityToResponse(savedWorkflow);
return savedWorkflow;
}),
);
@@ -205,7 +205,7 @@ EEWorkflowController.get(
return workflows.map((workflow) => {
EEWorkflows.addOwnerAndSharings(workflow);
workflow.nodes = [];
return EEWorkflows.entityToResponse(workflow);
return workflow;
});
}),
);
@@ -230,7 +230,7 @@ EEWorkflowController.patch(
forceSave,
);
return EEWorkflows.entityToResponse(updatedWorkflow);
return updatedWorkflow;
}),
);
@@ -244,11 +244,7 @@ EEWorkflowController.post(
Object.assign(workflow, req.body.workflowData);
if (workflow.id !== undefined) {
const safeWorkflow = await EEWorkflows.preventTampering(
workflow,
workflow.id.toString(),
req.user,
);
const safeWorkflow = await EEWorkflows.preventTampering(workflow, workflow.id, req.user);
req.body.workflowData.nodes = safeWorkflow.nodes;
}

View File

@@ -106,7 +106,7 @@ workflowsController.post(
await ExternalHooks().run('workflow.afterCreate', [savedWorkflow]);
void InternalHooksManager.getInstance().onWorkflowCreated(req.user.id, newWorkflow, false);
return WorkflowsService.entityToResponse(savedWorkflow);
return savedWorkflow;
}),
);
@@ -116,8 +116,7 @@ workflowsController.post(
workflowsController.get(
'/',
ResponseHelper.send(async (req: WorkflowRequest.GetAll) => {
const workflows = await WorkflowsService.getMany(req.user, req.query.filter);
return workflows.map((workflow) => WorkflowsService.entityToResponse(workflow));
return WorkflowsService.getMany(req.user, req.query.filter);
}),
);
@@ -218,7 +217,7 @@ workflowsController.get(
);
}
return WorkflowsService.entityToResponse(shared.workflow);
return shared.workflow;
}),
);
@@ -244,7 +243,7 @@ workflowsController.patch(
['owner'],
);
return WorkflowsService.entityToResponse(updatedWorkflow);
return updatedWorkflow;
}),
);

View File

@@ -42,7 +42,8 @@ export class EEWorkflowsService extends WorkflowsService {
transaction: EntityManager,
workflowId: string,
): Promise<SharedWorkflow[]> {
const workflow = await transaction.findOne(WorkflowEntity, workflowId, {
const workflow = await transaction.findOne(WorkflowEntity, {
where: { id: workflowId },
relations: ['shared'],
});
return workflow?.shared ?? [];
@@ -54,8 +55,8 @@ export class EEWorkflowsService extends WorkflowsService {
userIds: string[],
): Promise<DeleteResult> {
return transaction.delete(SharedWorkflow, {
workflow: { id: workflowId },
user: { id: Not(In(userIds)) },
workflowId,
userId: Not(In(userIds)),
});
}
@@ -113,7 +114,7 @@ export class EEWorkflowsService extends WorkflowsService {
): Promise<void> {
workflow.usedCredentials = [];
const userCredentials = await EECredentials.getAll(currentUser, { disableGlobalRole: true });
const credentialIdsUsedByWorkflow = new Set<number>();
const credentialIdsUsedByWorkflow = new Set<string>();
workflow.nodes.forEach((node) => {
if (!node.credentials) {
return;
@@ -123,8 +124,7 @@ export class EEWorkflowsService extends WorkflowsService {
if (!credential?.id) {
return;
}
const credentialId = parseInt(credential.id, 10);
credentialIdsUsedByWorkflow.add(credentialId);
credentialIdsUsedByWorkflow.add(credential.id);
});
});
const workflowCredentials = await EECredentials.getMany({
@@ -133,11 +133,11 @@ export class EEWorkflowsService extends WorkflowsService {
},
relations: ['shared', 'shared.user', 'shared.role'],
});
const userCredentialIds = userCredentials.map((credential) => credential.id.toString());
const userCredentialIds = userCredentials.map((credential) => credential.id);
workflowCredentials.forEach((credential) => {
const credentialId = credential.id.toString();
const credentialId = credential.id;
const workflowCredential: CredentialUsedByWorkflow = {
id: credential.id.toString(),
id: credentialId,
name: credential.name,
type: credential.type,
currentUserHasAccess: userCredentialIds.includes(credentialId),
@@ -190,23 +190,24 @@ export class EEWorkflowsService extends WorkflowsService {
relations: ['shared', 'shared.user', 'shared.role'],
});
const userCredentials = await EECredentials.getAll(currentUser, { disableGlobalRole: true });
const userCredentialIds = userCredentials.map((credential) => credential.id.toString());
const userCredentialIds = userCredentials.map((credential) => credential.id);
const credentialsMap: Record<string, CredentialUsedByWorkflow> = {};
usedWorkflowsCredentials.forEach((credential) => {
credentialsMap[credential.id.toString()] = {
id: credential.id.toString(),
const credentialId = credential.id;
credentialsMap[credentialId] = {
id: credentialId,
name: credential.name,
type: credential.type,
currentUserHasAccess: userCredentialIds.includes(credential.id.toString()),
currentUserHasAccess: userCredentialIds.includes(credentialId),
sharedWith: [],
ownedBy: null,
};
credential.shared?.forEach(({ user, role }) => {
const { id, email, firstName, lastName } = user;
if (role.name === 'owner') {
credentialsMap[credential.id.toString()].ownedBy = { id, email, firstName, lastName };
credentialsMap[credentialId].ownedBy = { id, email, firstName, lastName };
} else {
credentialsMap[credential.id.toString()].sharedWith?.push({
credentialsMap[credentialId].sharedWith?.push({
id,
email,
firstName,
@@ -230,10 +231,9 @@ export class EEWorkflowsService extends WorkflowsService {
return;
}
Object.keys(node.credentials).forEach((credentialType) => {
const credentialId = parseInt(node.credentials?.[credentialType].id ?? '', 10);
const matchedCredential = allowedCredentials.find(
(credential) => credential.id === credentialId,
);
const credentialId = node.credentials?.[credentialType].id;
if (credentialId === undefined) return;
const matchedCredential = allowedCredentials.find(({ id }) => id === credentialId);
if (!matchedCredential) {
throw new Error('The workflow contains credentials that you do not have access to');
}
@@ -242,7 +242,7 @@ export class EEWorkflowsService extends WorkflowsService {
}
static async preventTampering(workflow: WorkflowEntity, workflowId: string, user: User) {
const previousVersion = await EEWorkflowsService.get({ id: parseInt(workflowId, 10) });
const previousVersion = await EEWorkflowsService.get({ id: workflowId });
if (!previousVersion) {
throw new ResponseHelper.NotFoundError('Workflow not found');

View File

@@ -1,6 +1,6 @@
import { validate as jsonSchemaValidate } from 'jsonschema';
import { INode, IPinData, JsonObject, jsonParse, LoggerProxy, Workflow } from 'n8n-workflow';
import { FindManyOptions, FindOneOptions, In, ObjectLiteral } from 'typeorm';
import { FindConditions, In } from 'typeorm';
import pick from 'lodash.pick';
import { v4 as uuid } from 'uuid';
import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner';
@@ -16,7 +16,7 @@ import { validateEntity } from '@/GenericHelpers';
import { ExternalHooks } from '@/ExternalHooks';
import * as TagHelpers from '@/TagHelpers';
import { WorkflowRequest } from '@/requests';
import { IWorkflowDb, IWorkflowExecutionDataProcess, IWorkflowResponse } from '@/Interfaces';
import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
import { NodeTypes } from '@/NodeTypes';
import { WorkflowRunner } from '@/WorkflowRunner';
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
@@ -25,7 +25,7 @@ import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { isSharingEnabled, whereClause } from '@/UserManagement/UserManagementHelper';
export interface IGetWorkflowsQueryFilter {
id?: number | string;
id?: string;
name?: string;
active?: boolean;
}
@@ -45,28 +45,20 @@ const allowedWorkflowsQueryFilterFields = Object.keys(schemaGetWorkflowsQueryFil
export class WorkflowsService {
static async getSharing(
user: User,
workflowId: number | string,
workflowId: string,
relations: string[] = ['workflow'],
{ allowGlobalOwner } = { allowGlobalOwner: true },
): Promise<SharedWorkflow | undefined> {
const options: FindOneOptions<SharedWorkflow> & { where: ObjectLiteral } = {
where: {
workflow: { id: workflowId },
},
};
const where: FindConditions<SharedWorkflow> = { workflowId };
// Omit user from where if the requesting user is the global
// owner. This allows the global owner to view and delete
// workflows they don't own.
if (!allowGlobalOwner || user.globalRole.name !== 'owner') {
options.where.user = { id: user.id };
where.userId = user.id;
}
if (relations?.length) {
options.relations = relations;
}
return Db.collections.SharedWorkflow.findOne(options);
return Db.collections.SharedWorkflow.findOne({ where, relations });
}
/**
@@ -120,11 +112,6 @@ export class WorkflowsService {
return getSharedWorkflowIds(user, roles);
}
static entityToResponse(entity: WorkflowEntity): IWorkflowResponse {
const { id, ...rest } = entity;
return { ...rest, id: id.toString() };
}
static async getMany(user: User, rawFilter: string): Promise<WorkflowEntity[]> {
const sharedWorkflowIds = await this.getWorkflowIdsForUser(user, ['owner']);
if (sharedWorkflowIds.length === 0) {
@@ -181,16 +168,14 @@ export class WorkflowsService {
relations.push('shared', 'shared.user', 'shared.role');
}
const query: FindManyOptions<WorkflowEntity> = {
return Db.collections.Workflow.find({
select: isSharingEnabled() ? [...fields, 'versionId'] : fields,
relations,
where: {
id: In(sharedWorkflowIds),
...filter,
},
};
return Db.collections.Workflow.find(query);
});
}
static async update(
@@ -310,17 +295,11 @@ export class WorkflowsService {
}
}
const options: FindManyOptions<WorkflowEntity> = {
relations: ['tags'],
};
if (config.getEnv('workflowTagsDisabled')) {
delete options.relations;
}
const relations = config.getEnv('workflowTagsDisabled') ? [] : ['tags'];
// We sadly get nothing back from "update". Neither if it updated a record
// nor the new value. So query now the hopefully updated entry.
const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, options);
const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, { relations });
if (updatedWorkflow === undefined) {
throw new ResponseHelper.BadRequestError(