refactor(core): Continue moving typeorm operators to repositories (no-changelog) (#8212)

Follow-up to: https://github.com/n8n-io/n8n/pull/8186
This commit is contained in:
Iván Ovejero 2024-01-05 13:06:24 +01:00 committed by GitHub
parent bed04ec122
commit 23a4ac96c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 47 deletions

View file

@ -420,18 +420,13 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
* Get the IDs of the workflows that have been shared with the user. * Get the IDs of the workflows that have been shared with the user.
* Returns all IDs if user has the 'workflow:read' scope. * Returns all IDs if user has the 'workflow:read' scope.
*/ */
export async function getSharedWorkflowIds(user: User, roles?: RoleNames[]): Promise<string[]> { export async function getSharedWorkflowIds(user: User, roleNames?: RoleNames[]): Promise<string[]> {
const where: FindOptionsWhere<SharedWorkflow> = {}; const where: FindOptionsWhere<SharedWorkflow> = {};
if (!user.hasGlobalScope('workflow:read')) { if (!user.hasGlobalScope('workflow:read')) {
where.userId = user.id; where.userId = user.id;
} }
if (roles?.length) { if (roleNames?.length) {
const roleIds = await Container.get(RoleRepository) const roleIds = await Container.get(RoleRepository).getIdsInScopeWorkflowByNames(roleNames);
.find({
select: ['id'],
where: { name: In(roles), scope: 'workflow' },
})
.then((role) => role.map(({ id }) => id));
where.roleId = In(roleIds); where.roleId = In(roleIds);
} }

View file

@ -1,4 +1,3 @@
import { In } from 'typeorm';
import { User } from '@db/entities/User'; import { User } from '@db/entities/User';
import { SharedCredentials } from '@db/entities/SharedCredentials'; import { SharedCredentials } from '@db/entities/SharedCredentials';
import { SharedWorkflow } from '@db/entities/SharedWorkflow'; import { SharedWorkflow } from '@db/entities/SharedWorkflow';
@ -214,10 +213,11 @@ export class UsersController {
// Prevents issues with unique key constraints since user being assigned // Prevents issues with unique key constraints since user being assigned
// workflows and credentials might be a sharee // workflows and credentials might be a sharee
await transactionManager.delete(SharedWorkflow, { await this.sharedWorkflowRepository.deleteByIds(
user: transferee, transactionManager,
workflowId: In(sharedWorkflowIds), sharedWorkflowIds,
}); transferee,
);
// Transfer ownership of owned workflows // Transfer ownership of owned workflows
await transactionManager.update( await transactionManager.update(
@ -239,10 +239,11 @@ export class UsersController {
// Prevents issues with unique key constraints since user being assigned // Prevents issues with unique key constraints since user being assigned
// workflows and credentials might be a sharee // workflows and credentials might be a sharee
await transactionManager.delete(SharedCredentials, { await this.sharedCredentialsRepository.deleteByIds(
user: transferee, transactionManager,
credentialsId: In(sharedCredentialIds), sharedCredentialIds,
}); transferee,
);
// Transfer ownership of owned credentials // Transfer ownership of owned credentials
await transactionManager.update( await transactionManager.update(

View file

@ -10,7 +10,6 @@ import { EECredentialsService as EECredentials } from './credentials.service.ee'
import { OwnershipService } from '@/services/ownership.service'; import { OwnershipService } from '@/services/ownership.service';
import { Container } from 'typedi'; import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
@ -38,10 +37,10 @@ EECredentialsController.get(
const { id: credentialId } = req.params; const { id: credentialId } = req.params;
const includeDecryptedData = req.query.includeData === 'true'; const includeDecryptedData = req.query.includeData === 'true';
let credential = (await EECredentials.get( let credential = await Container.get(CredentialsRepository).findOne({
{ id: credentialId }, where: { id: credentialId },
{ relations: ['shared', 'shared.role', 'shared.user'] }, relations: ['shared', 'shared.role', 'shared.user'],
)) as CredentialsEntity; });
if (!credential) { if (!credential) {
throw new NotFoundError( throw new NotFoundError(

View file

@ -1,5 +1,5 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { DataSource, Repository } from 'typeorm'; import { DataSource, In, Repository } from 'typeorm';
import type { RoleNames, RoleScopes } from '../entities/Role'; import type { RoleNames, RoleScopes } from '../entities/Role';
import { Role } from '../entities/Role'; import { Role } from '../entities/Role';
import { User } from '../entities/User'; import { User } from '../entities/User';
@ -32,4 +32,11 @@ export class RoleRepository extends Repository<Role> {
return acc; return acc;
}, {}); }, {});
} }
async getIdsInScopeWorkflowByNames(roleNames: RoleNames[]) {
return this.find({
select: ['id'],
where: { name: In(roleNames), scope: 'workflow' },
}).then((role) => role.map(({ id }) => id));
}
} }

View file

@ -1,5 +1,5 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import type { FindOptionsWhere } from 'typeorm'; import type { EntityManager, FindOptionsWhere } from 'typeorm';
import { DataSource, In, Not, Repository } from 'typeorm'; import { DataSource, In, Not, Repository } from 'typeorm';
import { SharedCredentials } from '../entities/SharedCredentials'; import { SharedCredentials } from '../entities/SharedCredentials';
import type { User } from '../entities/User'; import type { User } from '../entities/User';
@ -60,4 +60,11 @@ export class SharedCredentialsRepository extends Repository<SharedCredentials> {
return this.find({ where }); return this.find({ where });
} }
async deleteByIds(transaction: EntityManager, sharedCredentialsIds: string[], user?: User) {
return transaction.delete(SharedCredentials, {
user,
credentialsId: In(sharedCredentialsIds),
});
}
} }

View file

@ -1,6 +1,6 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { DataSource, Repository, In, Not } from 'typeorm'; import { DataSource, Repository, In, Not } from 'typeorm';
import type { EntityManager, FindOptionsWhere } from 'typeorm'; import type { EntityManager, FindOptionsSelect, FindOptionsWhere } from 'typeorm';
import { SharedWorkflow } from '../entities/SharedWorkflow'; import { SharedWorkflow } from '../entities/SharedWorkflow';
import { type User } from '../entities/User'; import { type User } from '../entities/User';
import type { Scope } from '@n8n/permissions'; import type { Scope } from '@n8n/permissions';
@ -125,4 +125,20 @@ export class SharedWorkflowRepository extends Repository<SharedWorkflow> {
return transaction.save(newSharedWorkflows); return transaction.save(newSharedWorkflows);
} }
async findWithFields(workflowIds: string[], { fields }: { fields: string[] }) {
return this.find({
where: {
workflowId: In(workflowIds),
},
select: fields as FindOptionsSelect<SharedWorkflow>,
});
}
async deleteByIds(transaction: EntityManager, sharedWorkflowIds: string[], user?: User) {
return transaction.delete(SharedWorkflow, {
user,
workflowId: In(sharedWorkflowIds),
});
}
} }

View file

@ -54,8 +54,14 @@ export class WorkflowRepository extends Repository<WorkflowEntity> {
}); });
} }
async findByIds(workflowIds: string[]) { async findByIds(workflowIds: string[], { fields }: { fields?: string[] } = {}) {
return this.find({ where: { id: In(workflowIds) } }); const options: FindManyOptions<WorkflowEntity> = {
where: { id: In(workflowIds) },
};
if (fields?.length) options.select = fields as FindOptionsSelect<WorkflowEntity>;
return this.find(options);
} }
async getActiveTriggerCount() { async getActiveTriggerCount() {

View file

@ -225,18 +225,13 @@ export class SourceControlImportService {
const ownerWorkflowRole = await this.getWorkflowOwnerRole(); const ownerWorkflowRole = await this.getWorkflowOwnerRole();
const workflowRunner = this.activeWorkflowRunner; const workflowRunner = this.activeWorkflowRunner;
const candidateIds = candidates.map((c) => c.id); const candidateIds = candidates.map((c) => c.id);
const existingWorkflows = await Container.get(WorkflowRepository).find({ const existingWorkflows = await Container.get(WorkflowRepository).findByIds(candidateIds, {
where: { fields: ['id', 'name', 'versionId', 'active'],
id: In(candidateIds),
},
select: ['id', 'name', 'versionId', 'active'],
});
const allSharedWorkflows = await Container.get(SharedWorkflowRepository).find({
where: {
workflowId: In(candidateIds),
},
select: ['workflowId', 'roleId', 'userId'],
}); });
const allSharedWorkflows = await Container.get(SharedWorkflowRepository).findWithFields(
candidateIds,
{ fields: ['workflowId', 'roleId', 'userId'] },
);
const cachedOwnerIds = new Map<string, string>(); const cachedOwnerIds = new Map<string, string>();
const importWorkflowsResult = await Promise.all( const importWorkflowsResult = await Promise.all(
candidates.map(async (candidate) => { candidates.map(async (candidate) => {

View file

@ -15,7 +15,6 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import type { EntityManager } from 'typeorm';
import { UserRepository } from '@/databases/repositories/user.repository'; import { UserRepository } from '@/databases/repositories/user.repository';
@Service() @Service()
@ -46,13 +45,6 @@ export class EnterpriseWorkflowService {
return { ownsWorkflow: true, workflow }; return { ownsWorkflow: true, workflow };
} }
async share(transaction: EntityManager, workflow: WorkflowEntity, shareWithIds: string[]) {
const users = await this.userRepository.getByIds(transaction, shareWithIds);
const role = await this.roleService.findWorkflowEditorRole();
await this.sharedWorkflowRepository.share(transaction, workflow, users, role.id);
}
addOwnerAndSharings(workflow: WorkflowWithSharingsAndCredentials): void { addOwnerAndSharings(workflow: WorkflowWithSharingsAndCredentials): void {
workflow.ownedBy = null; workflow.ownedBy = null;
workflow.sharedWith = []; workflow.sharedWith = [];

View file

@ -35,6 +35,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import type { RoleNames } from '@/databases/entities/Role'; import type { RoleNames } from '@/databases/entities/Role';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error'; import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { CredentialsService } from '../credentials/credentials.service'; import { CredentialsService } from '../credentials/credentials.service';
import { UserRepository } from '@/databases/repositories/user.repository';
export const workflowsController = express.Router(); export const workflowsController = express.Router();
@ -428,7 +429,10 @@ workflowsController.put(
); );
if (newShareeIds.length) { if (newShareeIds.length) {
await Container.get(EnterpriseWorkflowService).share(trx, workflow!, newShareeIds); const users = await Container.get(UserRepository).getByIds(trx, newShareeIds);
const role = await Container.get(RoleService).findWorkflowEditorRole();
await Container.get(SharedWorkflowRepository).share(trx, workflow!, users, role.id);
} }
}); });