refactor(core): Continue moving typeorm operators to repositories (no-changelog) (#8186)
Follow-up to: #8163
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Service } from 'typedi';
|
||||
import type { FindOptionsWhere } from 'typeorm';
|
||||
import { DataSource, In, Not, Repository } from 'typeorm';
|
||||
import { SharedCredentials } from '../entities/SharedCredentials';
|
||||
import type { User } from '../entities/User';
|
||||
@@ -50,4 +51,13 @@ export class SharedCredentialsRepository extends Repository<SharedCredentials> {
|
||||
|
||||
return sharings.map((s) => s.credentialsId);
|
||||
}
|
||||
|
||||
async findSharings(userIds: string[], roleId?: string) {
|
||||
const where: FindOptionsWhere<SharedCredentials> = { userId: In(userIds) };
|
||||
|
||||
// If credential sharing is not enabled, get only credentials owned by this user
|
||||
if (roleId) where.roleId = roleId;
|
||||
|
||||
return this.find({ where });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Service } from 'typedi';
|
||||
import { DataSource, type FindOptionsWhere, Repository, In, Not } from 'typeorm';
|
||||
import { DataSource, Repository, In, Not } from 'typeorm';
|
||||
import type { EntityManager, FindOptionsWhere } from 'typeorm';
|
||||
import { SharedWorkflow } from '../entities/SharedWorkflow';
|
||||
import { type User } from '../entities/User';
|
||||
import type { Scope } from '@n8n/permissions';
|
||||
import type { Role } from '../entities/Role';
|
||||
import type { WorkflowEntity } from '../entities/WorkflowEntity';
|
||||
|
||||
@Service()
|
||||
export class SharedWorkflowRepository extends Repository<SharedWorkflow> {
|
||||
@@ -72,4 +74,55 @@ export class SharedWorkflowRepository extends Repository<SharedWorkflow> {
|
||||
async makeOwnerOfAllWorkflows(user: User, role: Role) {
|
||||
return this.update({ userId: Not(user.id), roleId: role.id }, { user });
|
||||
}
|
||||
|
||||
async getSharing(
|
||||
user: User,
|
||||
workflowId: string,
|
||||
options: { allowGlobalScope: true; globalScope: Scope } | { allowGlobalScope: false },
|
||||
relations: string[] = ['workflow'],
|
||||
): Promise<SharedWorkflow | null> {
|
||||
const where: FindOptionsWhere<SharedWorkflow> = { workflowId };
|
||||
|
||||
// Omit user from where if the requesting user has relevant
|
||||
// global workflow permissions. This allows the user to
|
||||
// access workflows they don't own.
|
||||
if (!options.allowGlobalScope || !user.hasGlobalScope(options.globalScope)) {
|
||||
where.userId = user.id;
|
||||
}
|
||||
|
||||
return this.findOne({ where, relations });
|
||||
}
|
||||
|
||||
async getSharedWorkflows(
|
||||
user: User,
|
||||
options: {
|
||||
relations?: string[];
|
||||
workflowIds?: string[];
|
||||
},
|
||||
): Promise<SharedWorkflow[]> {
|
||||
return this.find({
|
||||
where: {
|
||||
...(!['owner', 'admin'].includes(user.globalRole.name) && { userId: user.id }),
|
||||
...(options.workflowIds && { workflowId: In(options.workflowIds) }),
|
||||
},
|
||||
...(options.relations && { relations: options.relations }),
|
||||
});
|
||||
}
|
||||
|
||||
async share(transaction: EntityManager, workflow: WorkflowEntity, users: User[], roleId: string) {
|
||||
const newSharedWorkflows = users.reduce<SharedWorkflow[]>((acc, user) => {
|
||||
if (user.isPending) {
|
||||
return acc;
|
||||
}
|
||||
const entity: Partial<SharedWorkflow> = {
|
||||
workflowId: workflow.id,
|
||||
userId: user.id,
|
||||
roleId,
|
||||
};
|
||||
acc.push(this.create(entity));
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return transaction.save(newSharedWorkflows);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Service } from 'typedi';
|
||||
import type { EntityManager } from 'typeorm';
|
||||
import { DataSource, In, Repository } from 'typeorm';
|
||||
import { TagEntity } from '../entities/TagEntity';
|
||||
import type { WorkflowEntity } from '../entities/WorkflowEntity';
|
||||
import intersection from 'lodash/intersection';
|
||||
|
||||
@Service()
|
||||
export class TagRepository extends Repository<TagEntity> {
|
||||
@@ -14,4 +17,57 @@ export class TagRepository extends Repository<TagEntity> {
|
||||
where: { id: In(tagIds) },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tags on workflow to import while ensuring all tags exist in the database,
|
||||
* either by matching incoming to existing tags or by creating them first.
|
||||
*/
|
||||
async setTags(tx: EntityManager, dbTags: TagEntity[], workflow: WorkflowEntity) {
|
||||
if (!workflow?.tags?.length) return;
|
||||
|
||||
for (let i = 0; i < workflow.tags.length; i++) {
|
||||
const importTag = workflow.tags[i];
|
||||
|
||||
if (!importTag.name) continue;
|
||||
|
||||
const identicalMatch = dbTags.find(
|
||||
(dbTag) =>
|
||||
dbTag.id === importTag.id &&
|
||||
dbTag.createdAt &&
|
||||
importTag.createdAt &&
|
||||
dbTag.createdAt.getTime() === new Date(importTag.createdAt).getTime(),
|
||||
);
|
||||
|
||||
if (identicalMatch) {
|
||||
workflow.tags[i] = identicalMatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nameMatch = dbTags.find((dbTag) => dbTag.name === importTag.name);
|
||||
|
||||
if (nameMatch) {
|
||||
workflow.tags[i] = nameMatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
const tagEntity = this.create(importTag);
|
||||
|
||||
workflow.tags[i] = await tx.save<TagEntity>(tagEntity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workflow IDs that have certain tags.
|
||||
* Intersection! e.g. workflow needs to have all provided tags.
|
||||
*/
|
||||
async getWorkflowIdsViaTags(tags: string[]): Promise<string[]> {
|
||||
const dbTags = await this.find({
|
||||
where: { name: In(tags) },
|
||||
relations: ['workflows'],
|
||||
});
|
||||
|
||||
const workflowIdsPerTag = dbTags.map((tag) => tag.workflows.map((workflow) => workflow.id));
|
||||
|
||||
return intersection(...workflowIdsPerTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Service } from 'typedi';
|
||||
import { DataSource, In, Not, Repository } from 'typeorm';
|
||||
import type { EntityManager, FindManyOptions } from 'typeorm';
|
||||
import { DataSource, In, IsNull, Not, Repository } from 'typeorm';
|
||||
import { User } from '../entities/User';
|
||||
import type { ListQuery } from '@/requests';
|
||||
|
||||
@Service()
|
||||
export class UserRepository extends Repository<User> {
|
||||
@@ -18,4 +20,68 @@ export class UserRepository extends Repository<User> {
|
||||
async deleteAllExcept(user: User) {
|
||||
await this.delete({ id: Not(user.id) });
|
||||
}
|
||||
|
||||
async getByIds(transaction: EntityManager, ids: string[]) {
|
||||
return transaction.find(User, { where: { id: In(ids) } });
|
||||
}
|
||||
|
||||
async findManyByEmail(emails: string[]) {
|
||||
return this.find({
|
||||
where: { email: In(emails) },
|
||||
relations: ['globalRole'],
|
||||
select: ['email', 'password', 'id'],
|
||||
});
|
||||
}
|
||||
|
||||
async deleteMany(userIds: string[]) {
|
||||
return this.delete({ id: In(userIds) });
|
||||
}
|
||||
|
||||
async findNonShellUser(email: string) {
|
||||
return this.findOne({
|
||||
where: {
|
||||
email,
|
||||
password: Not(IsNull()),
|
||||
},
|
||||
relations: ['authIdentities', 'globalRole'],
|
||||
});
|
||||
}
|
||||
|
||||
async toFindManyOptions(listQueryOptions?: ListQuery.Options, globalOwnerRoleId?: string) {
|
||||
const findManyOptions: FindManyOptions<User> = {};
|
||||
|
||||
if (!listQueryOptions) {
|
||||
findManyOptions.relations = ['globalRole', 'authIdentities'];
|
||||
return findManyOptions;
|
||||
}
|
||||
|
||||
const { filter, select, take, skip } = listQueryOptions;
|
||||
|
||||
if (select) findManyOptions.select = select;
|
||||
if (take) findManyOptions.take = take;
|
||||
if (skip) findManyOptions.skip = skip;
|
||||
|
||||
if (take && !select) {
|
||||
findManyOptions.relations = ['globalRole', 'authIdentities'];
|
||||
}
|
||||
|
||||
if (take && select && !select?.id) {
|
||||
findManyOptions.select = { ...findManyOptions.select, id: true }; // pagination requires id
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
const { isOwner, ...otherFilters } = filter;
|
||||
|
||||
findManyOptions.where = otherFilters;
|
||||
|
||||
if (isOwner !== undefined && globalOwnerRoleId) {
|
||||
findManyOptions.relations = ['globalRole'];
|
||||
findManyOptions.where.globalRole = {
|
||||
id: isOwner ? globalOwnerRoleId : Not(globalOwnerRoleId),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return findManyOptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,4 +198,16 @@ export class WorkflowRepository extends Repository<WorkflowEntity> {
|
||||
.innerJoin(WebhookEntity, 'webhook_entity', 'workflow.id = webhook_entity.workflowId')
|
||||
.execute() as Promise<Array<{ id: string; name: string }>>;
|
||||
}
|
||||
|
||||
async updateActiveState(workflowId: string, newState: boolean) {
|
||||
return this.update({ id: workflowId }, { active: newState });
|
||||
}
|
||||
|
||||
async deactivateAll() {
|
||||
return this.update({ active: true }, { active: false });
|
||||
}
|
||||
|
||||
async findByActiveState(activeState: boolean) {
|
||||
return this.findBy({ active: activeState });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user