Skip to content

Commit

Permalink
refactor(core): Move more typeorm operators to repositories (no-cha…
Browse files Browse the repository at this point in the history
…ngelog) (n8n-io#8143)

Follow-up to n8n-io#8139
  • Loading branch information
ivov authored Dec 22, 2023
1 parent 4007163 commit a59d78d
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 56 deletions.
10 changes: 6 additions & 4 deletions packages/cli/src/credentials/credentials.controller.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';

export const EECredentialsController = express.Router();

Expand Down Expand Up @@ -155,10 +156,11 @@ EECredentialsController.put(
let newShareeIds: string[] = [];
await Db.transaction(async (trx) => {
// remove all sharings that are not supposed to exist anymore
const { affected } = await EECredentials.pruneSharings(trx, credentialId, [
...ownerIds,
...shareWithIds,
]);
const { affected } = await Container.get(CredentialsRepository).pruneSharings(
trx,
credentialId,
[...ownerIds, ...shareWithIds],
);
if (affected) amountRemoved = affected;

const sharings = await EECredentials.getSharings(trx, credentialId);
Expand Down
17 changes: 2 additions & 15 deletions packages/cli/src/credentials/credentials.service.ee.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { DeleteResult, EntityManager, FindOptionsWhere } from 'typeorm';
import { In, Not } from 'typeorm';
import type { EntityManager, FindOptionsWhere } from 'typeorm';
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SharedCredentials } from '@db/entities/SharedCredentials';
import type { SharedCredentials } from '@db/entities/SharedCredentials';
import type { User } from '@db/entities/User';
import { UserService } from '@/services/user.service';
import { CredentialsService, type CredentialsGetSharedOptions } from './credentials.service';
Expand Down Expand Up @@ -62,18 +61,6 @@ export class EECredentialsService extends CredentialsService {
return credential?.shared ?? [];
}

static async pruneSharings(
transaction: EntityManager,
credentialId: string,
userIds: string[],
): Promise<DeleteResult> {
const conditions: FindOptionsWhere<SharedCredentials> = {
credentialsId: credentialId,
userId: Not(In(userIds)),
};
return transaction.delete(SharedCredentials, conditions);
}

static async share(
transaction: EntityManager,
credential: CredentialsEntity,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import { Service } from 'typedi';
import { DataSource, Repository } from 'typeorm';
import {
DataSource,
In,
Not,
Repository,
type DeleteResult,
type EntityManager,
type FindOptionsWhere,
Like,
} from 'typeorm';
import { CredentialsEntity } from '../entities/CredentialsEntity';
import { SharedCredentials } from '../entities/SharedCredentials';

@Service()
export class CredentialsRepository extends Repository<CredentialsEntity> {
constructor(dataSource: DataSource) {
super(CredentialsEntity, dataSource.manager);
}

async pruneSharings(
transaction: EntityManager,
credentialId: string,
userIds: string[],
): Promise<DeleteResult> {
const conditions: FindOptionsWhere<SharedCredentials> = {
credentialsId: credentialId,
userId: Not(In(userIds)),
};
return transaction.delete(SharedCredentials, conditions);
}

async findStartingWith(credentialName: string) {
return this.find({
select: ['name'],
where: { name: Like(`${credentialName}%`) },
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,13 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
await this.delete(batch);
} while (executionIds.length > 0);
}

async getIdsSince(date: Date) {
return this.find({
select: ['id'],
where: {
startedAt: MoreThanOrEqual(DateUtils.mixedDateToUtcDatetimeString(date)),
},
}).then((executions) => executions.map(({ id }) => id));
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { Service } from 'typedi';
import { DataSource, Repository } from 'typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { ExecutionData } from '../entities/ExecutionData';

@Service()
export class ExecutionDataRepository extends Repository<ExecutionData> {
constructor(dataSource: DataSource) {
super(ExecutionData, dataSource.manager);
}

async findByExecutionIds(executionIds: string[]) {
return this.find({
select: ['workflowData'],
where: {
executionId: In(executionIds),
},
}).then((executionData) => executionData.map(({ workflowData }) => workflowData));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,11 @@ export class WorkflowRepository extends Repository<WorkflowEntity> {

return { workflows, count };
}

async findStartingWith(workflowName: string): Promise<Array<{ name: string }>> {
return this.find({
select: ['name'],
where: { name: Like(`${workflowName}%`) },
});
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Service } from 'typedi';
import { DataSource, Repository } from 'typeorm';
import { DataSource, LessThan, Repository } from 'typeorm';
import { WorkflowHistory } from '../entities/WorkflowHistory';

@Service()
export class WorkflowHistoryRepository extends Repository<WorkflowHistory> {
constructor(dataSource: DataSource) {
super(WorkflowHistory, dataSource.manager);
}

async deleteEarlierThan(date: Date) {
return this.delete({ createdAt: LessThan(date) });
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { In, MoreThanOrEqual } from 'typeorm';
import { DateUtils } from 'typeorm/util/DateUtils';
import { Service } from 'typedi';
import type { IWorkflowBase } from 'n8n-workflow';
import config from '@/config';
Expand Down Expand Up @@ -119,23 +117,9 @@ export class CredentialsRiskReporter implements RiskReporter {

date.setDate(date.getDate() - days);

const executionIds = await this.executionRepository
.find({
select: ['id'],
where: {
startedAt: MoreThanOrEqual(DateUtils.mixedDateToUtcDatetimeString(date) as Date),
},
})
.then((executions) => executions.map(({ id }) => id));

return this.executionDataRepository
.find({
select: ['workflowData'],
where: {
executionId: In(executionIds),
},
})
.then((executionData) => executionData.map(({ workflowData }) => workflowData));
const executionIds = await this.executionRepository.getIdsSince(date);

return this.executionDataRepository.findByExecutionIds(executionIds);
}

/**
Expand Down
6 changes: 1 addition & 5 deletions packages/cli/src/services/naming.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Service } from 'typedi';
import { Like } from 'typeorm';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';

Expand All @@ -21,10 +20,7 @@ export class NamingService {
private async getUniqueName(requestedName: string, entity: 'workflow' | 'credential') {
const repository = entity === 'workflow' ? this.workflowRepository : this.credentialsRepository;

const found: Array<{ name: string }> = await repository.find({
select: ['name'],
where: { name: Like(`${requestedName}%`) },
});
const found = await repository.findStartingWith(requestedName);

if (found.length === 0) return requestedName;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Service } from 'typedi';
import { LessThan } from 'typeorm';
import { DateTime } from 'luxon';
import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
import { WORKFLOW_HISTORY_PRUNE_INTERVAL } from './constants';
Expand Down Expand Up @@ -38,8 +37,6 @@ export class WorkflowHistoryManager {
}
const pruneDateTime = DateTime.now().minus({ hours: pruneHours }).toJSDate();

await this.workflowHistoryRepo.delete({
createdAt: LessThan(pruneDateTime),
});
await this.workflowHistoryRepo.deleteEarlierThan(pruneDateTime);
}
}
14 changes: 8 additions & 6 deletions packages/cli/test/unit/services/naming.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ describe('NamingService', () => {

describe('getUniqueWorkflowName()', () => {
test('should return requested name if already unique', async () => {
workflowRepository.find.mockResolvedValue([]);
workflowRepository.findStartingWith.mockResolvedValue([]);

const name = await namingService.getUniqueWorkflowName('foo');

expect(name).toEqual('foo');
});

test('should return requested name suffixed if already existing once', async () => {
workflowRepository.find.mockResolvedValue([{ name: 'foo' }] as WorkflowEntity[]);
workflowRepository.findStartingWith.mockResolvedValue([{ name: 'foo' }] as WorkflowEntity[]);

const name = await namingService.getUniqueWorkflowName('foo');

Expand All @@ -35,7 +35,7 @@ describe('NamingService', () => {
test('should return requested name with incremented suffix if already suffixed', async () => {
const existingNames = [{ name: 'foo' }, { name: 'foo 2' }] as WorkflowEntity[];

workflowRepository.find.mockResolvedValue(existingNames);
workflowRepository.findStartingWith.mockResolvedValue(existingNames);

const name = await namingService.getUniqueWorkflowName('foo');

Expand All @@ -51,15 +51,17 @@ describe('NamingService', () => {

describe('getUniqueCredentialName()', () => {
test('should return requested name if already unique', async () => {
credentialsRepository.find.mockResolvedValue([]);
credentialsRepository.findStartingWith.mockResolvedValue([]);

const name = await namingService.getUniqueCredentialName('foo');

expect(name).toEqual('foo');
});

test('should return requested name suffixed if already existing once', async () => {
credentialsRepository.find.mockResolvedValue([{ name: 'foo' }] as CredentialsEntity[]);
credentialsRepository.findStartingWith.mockResolvedValue([
{ name: 'foo' },
] as CredentialsEntity[]);

const name = await namingService.getUniqueCredentialName('foo');

Expand All @@ -69,7 +71,7 @@ describe('NamingService', () => {
test('should return requested name with incremented suffix if already suffixed', async () => {
const existingNames = [{ name: 'foo' }, { name: 'foo 2' }] as CredentialsEntity[];

credentialsRepository.find.mockResolvedValue(existingNames);
credentialsRepository.findStartingWith.mockResolvedValue(existingNames);

const name = await namingService.getUniqueCredentialName('foo');

Expand Down

0 comments on commit a59d78d

Please sign in to comment.