Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC-5628 cyclic data deletion #4557

Merged
merged 116 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 113 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
ca59c48
first commit
WojciechGrancow Oct 19, 2023
5838b81
add some tests
WojciechGrancow Oct 20, 2023
7df7c23
add test cases and services
WojciechGrancow Oct 27, 2023
1b07d3f
add new (almost empty for now) batch deletion app
bn-pass Oct 30, 2023
dec5292
refactor config vars
bn-pass Oct 31, 2023
29c58a0
add optional env var for specifying delay between the API calls
bn-pass Oct 31, 2023
a853ea7
add usecases and test cases
WojciechGrancow Nov 1, 2023
72c84b4
Merge branch 'main' into BC-5521-implementation-KNL-Module
WojciechGrancow Nov 1, 2023
c56ae3d
fix importing
WojciechGrancow Nov 1, 2023
cad4491
add type in uc
WojciechGrancow Nov 2, 2023
b80ff0e
fix import
WojciechGrancow Nov 2, 2023
5d764d0
add references service that'll load all the references to the data we…
bn-pass Nov 2, 2023
c226c6d
fix most of issue form review
WojciechGrancow Nov 3, 2023
31a225c
Merge branch 'BC-5521-implementation-KNL-Module' into BC-5629-batch-d…
bn-pass Nov 3, 2023
fdbe8c9
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 3, 2023
78ed3b9
add deletion API client with just a single method for now that allows…
bn-pass Nov 4, 2023
3acf266
refactor the env vars for configurting the Admin API
bn-pass Nov 4, 2023
f2ac1bc
add exporting DeletionClientConfig
bn-pass Nov 4, 2023
b69741a
move references service to the deletion module
bn-pass Nov 4, 2023
0c02d6d
delete unused code
bn-pass Nov 4, 2023
30412f9
add batch deletion service that makes it possible ot queue deletion f…
bn-pass Nov 4, 2023
13f7b57
move some parts of the interface to the interface subdir
bn-pass Nov 4, 2023
61a0814
add an interface for the batch deletion summary
bn-pass Nov 4, 2023
6c72715
move some interfaces to a separate subdir
bn-pass Nov 4, 2023
46395c0
refactor the batch deletion summary interface
bn-pass Nov 4, 2023
f204220
add uc for the batch deletion
bn-pass Nov 4, 2023
3ecc282
remove unused annotation
bn-pass Nov 6, 2023
78dedbf
refactor deletion client implementation
bn-pass Nov 6, 2023
6c66cf6
add batch deletion service implementation
bn-pass Nov 6, 2023
1c8cae9
add UC for the batch deletion
bn-pass Nov 7, 2023
90aca19
add a console app for the deletion module and a console command to ma…
bn-pass Nov 7, 2023
20a04e9
remove no longer used app, add param to make it possible to define de…
bn-pass Nov 7, 2023
9e0d281
remove no longer used separate batch-deletion module (it became a par…
bn-pass Nov 7, 2023
ab14b19
fix invalid key
bn-pass Nov 7, 2023
4d13aa5
remove no longer used config vars
bn-pass Nov 7, 2023
ee43ec2
remove no longer used commands
bn-pass Nov 7, 2023
8dbeeb7
remove no longer used Nest cli config
bn-pass Nov 7, 2023
8018c6e
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 7, 2023
71d0b40
remove no longer used code
bn-pass Nov 7, 2023
4c5de4e
change name of the method that prepares default headers
bn-pass Nov 7, 2023
02c5ed4
add builders for most of the interfaces
bn-pass Nov 7, 2023
df4124e
add builders for the remaining interfaces
bn-pass Nov 7, 2023
0d0fdb2
add type in catch clause
bn-pass Nov 7, 2023
62695f5
do some adjustments, move PushDeletionRequestsOptions interface to a …
bn-pass Nov 7, 2023
a1c7477
remove unused import
bn-pass Nov 7, 2023
141d5d2
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 7, 2023
5d19f5d
rollback
bn-pass Nov 7, 2023
a63c594
remove unnecessary indent
bn-pass Nov 7, 2023
2f52adc
remove unnecessary indents
bn-pass Nov 7, 2023
9001592
remove empty line
bn-pass Nov 7, 2023
da9c2a6
remove repeated imports
bn-pass Nov 7, 2023
66daec1
refactor some imports to omit calling Configuration.get() on every su…
bn-pass Nov 7, 2023
2bffa6d
add builder for the DeletionRequestOutput class
bn-pass Nov 8, 2023
06ef50b
add unit tests for the batch deletion service
bn-pass Nov 8, 2023
5317dae
add unit tests for the BatchDeletionUc
bn-pass Nov 8, 2023
e9d5559
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 8, 2023
a1b117b
modify env keys for the Admin API client configuration, refactor the …
bn-pass Nov 8, 2023
5dd40a7
Merge branch 'BC-5629-batch-deletion-mechanism' of https://github.com…
bn-pass Nov 8, 2023
aa378b0
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 8, 2023
ebfe6bd
fix invalid import, remove unused undefined arg
bn-pass Nov 8, 2023
9f62aa7
add comment to ignore console.ts file for coverage
bn-pass Nov 8, 2023
8706f50
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 8, 2023
abc6656
move deletion client config interface to a separate file, refactor fu…
bn-pass Nov 8, 2023
e057fe0
Merge branch 'BC-5629-batch-deletion-mechanism' of https://github.com…
bn-pass Nov 8, 2023
638d1e9
fix invalid import
bn-pass Nov 8, 2023
a5180ae
add more test cases to the deletion client unit tests
bn-pass Nov 8, 2023
e1c9312
change invalid import
bn-pass Nov 8, 2023
b35aa9c
fix invalid import
bn-pass Nov 8, 2023
667427f
add builder for the PushDeletionRequestsOptions class, add unit tests…
bn-pass Nov 8, 2023
02bd3a2
rename the file containing the deletion module console to deletion.co…
bn-pass Nov 8, 2023
88b0b28
remove deletion.console.ts from the sonar.coverage.exclusions param a…
bn-pass Nov 8, 2023
7f06aa6
add deletion.console.ts file to the coverage exclusions (another try …
bn-pass Nov 8, 2023
4e26016
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 9, 2023
009bdd1
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 9, 2023
4d18313
change name of the file containing the deletion console app
bn-pass Nov 9, 2023
b12527c
Merge branch 'BC-5629-batch-deletion-mechanism' of https://github.com…
bn-pass Nov 9, 2023
d17c097
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 9, 2023
acf51b9
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 9, 2023
cca5fff
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 10, 2023
199f582
add deletion client method that allows for triggering a deletion requ…
bn-pass Nov 10, 2023
a3037ea
fix some imports
bn-pass Nov 13, 2023
1217640
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 13, 2023
d8ff82f
move default value for the ADMIN_API_CLIENT object to default.schema.…
bn-pass Nov 13, 2023
5624dc5
move default for the BASE_URL
bn-pass Nov 13, 2023
8ec624f
move Deletion module console app to the apps/ dir
bn-pass Nov 13, 2023
e87ede7
add separate functino to log error and set exit code
bn-pass Nov 13, 2023
abc83e5
add handling of the case that only CR chars are used as a line separa…
bn-pass Nov 13, 2023
3cd6e25
add use of the BatchDeletionSummaryBuilder in place of an anonymous o…
bn-pass Nov 13, 2023
3bf6813
fix some imports/exports
bn-pass Nov 13, 2023
28753a5
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 14, 2023
30cfe39
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 14, 2023
d61d6c8
Merge branch 'main' into BC-5629-batch-deletion-mechanism
bn-pass Nov 14, 2023
1c5051d
Merge branch 'BC-5629-batch-deletion-mechanism' into BC-5628-cyclic-d…
bn-pass Nov 15, 2023
ba8a8b9
add an interface for the deletion execution trigger result, add build…
bn-pass Nov 15, 2023
a148651
add use case for the deletion executions
bn-pass Nov 15, 2023
b7472d1
add new interface for the deletion execution trigger options, add bui…
bn-pass Nov 15, 2023
af2268f
add console command for triggering the deletion execution
bn-pass Nov 15, 2023
5e954a7
add Admin API client secret provisioned by 1Password
bn-pass Nov 15, 2023
71dc318
Merge branch 'main' into BC-5628-cyclic-data-deletion
bn-pass Nov 16, 2023
c228695
add data deletion trigger cronjob
bn-pass Nov 16, 2023
ec511e8
Merge branch 'BC-5628-cyclic-data-deletion' of https://github.com/hpi…
bn-pass Nov 16, 2023
4be9d00
add metadata to the data deletion trigger CronJob
bn-pass Nov 16, 2023
6bea44c
add task to add data deletion trigger CronJob
bn-pass Nov 16, 2023
ae51bf1
Merge branch 'main' into BC-5628-cyclic-data-deletion
bn-pass Nov 17, 2023
bce7b5a
Merge branch 'main' into BC-5628-cyclic-data-deletion
bn-pass Nov 21, 2023
046589f
rewrite HTTP client execution to try/catch block, modify types of ret…
bn-pass Nov 22, 2023
f269a64
modify the solution to not catch the client's exception in the use ca…
bn-pass Nov 22, 2023
014c17a
add more test cases
bn-pass Nov 22, 2023
49029ce
delete unnecessary cronjob label
bn-pass Nov 22, 2023
537dc70
merge labels into metadata
bn-pass Nov 22, 2023
982d83d
BC-5628 - move logic of errors
SevenWaysDP Nov 22, 2023
1d80735
Merge branch 'main' into BC-5628-cyclic-data-deletion
bn-pass Nov 22, 2023
61560e9
adjusted the second client method according to Sergej's proposal
bn-pass Nov 22, 2023
d1142da
Update apps/server/src/modules/deletion/client/deletion.client.ts
bn-pass Nov 22, 2023
56bfa00
remove unnecessary label
bn-pass Nov 22, 2023
0f379a0
Merge branch 'main' into BC-5628-cyclic-data-deletion
WojciechGrancow Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ansible/roles/schulcloud-server-core/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
template: onepassword.yml.j2
when: ONEPASSWORD_OPERATOR is defined and ONEPASSWORD_OPERATOR|bool

- name: Admin API client secret (from 1Password)
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: onepassword-admin-api-client.yml.j2
when: ONEPASSWORD_OPERATOR is defined and ONEPASSWORD_OPERATOR|bool

- name: remove old migration Job
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
Expand Down Expand Up @@ -108,6 +115,12 @@
namespace: "{{ NAMESPACE }}"
template: api-delete-s3-files-cronjob.yml.j2

- name: Data deletion trigger CronJob
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
namespace: "{{ NAMESPACE }}"
template: data-deletion-trigger-cronjob.yml.j2

- name: AMQPFileStorageDeployment
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: {{ NAMESPACE }}
labels:
app: data-deletion-trigger
app.kubernetes.io/part-of: schulcloud-verbund
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: data-deletion-trigger
app.kubernetes.io/component: data-deletion
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
name: data-deletion-trigger-cronjob
spec:
concurrencyPolicy: Forbid
schedule: "{{ SERVER_DATA_DELETION_TRIGGER_CRONJOB_SCHEDULE|default("@hourly", true) }}"
jobTemplate:
metadata:
labels:
app: data-deletion-trigger
app.kubernetes.io/part-of: schulcloud-verbund
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: data-deletion-trigger
app.kubernetes.io/component: data-deletion
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
spec:
template:
spec:
containers:
- name: data-deletion-trigger-cronjob
image: {{ SCHULCLOUD_SERVER_IMAGE }}:{{ SCHULCLOUD_SERVER_IMAGE_TAG }}
envFrom:
- secretRef:
name: admin-api-client-secret
command: ['/bin/sh', '-c']
args: ['npm run nest:start:deletion-console -- execution trigger']
resources:
limits:
cpu: {{ API_CPU_LIMITS|default("2000m", true) }}
memory: {{ API_MEMORY_LIMITS|default("2Gi", true) }}
requests:
cpu: {{ API_CPU_REQUESTS|default("100m", true) }}
memory: {{ API_MEMORY_REQUESTS|default("150Mi", true) }}
restartPolicy: OnFailure
metadata:
labels:
app: data-deletion-trigger
cronjob: data-deletion-trigger
app.kubernetes.io/part-of: schulcloud-verbund
bn-pass marked this conversation as resolved.
Show resolved Hide resolved
app.kubernetes.io/version: {{ SCHULCLOUD_SERVER_IMAGE_TAG }}
app.kubernetes.io/name: data-deletion-trigger
app.kubernetes.io/component: data-deletion
app.kubernetes.io/managed-by: ansible
git.branch: {{ SCHULCLOUD_SERVER_BRANCH_NAME }}
git.repo: {{ SCHULCLOUD_SERVER_REPO_NAME }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: admin-api-client-secret
namespace: {{ NAMESPACE }}
spec:
itemPath: "vaults/{{ ONEPASSWORD_OPERATOR_VAULT }}/items/admin-api-client"
70 changes: 69 additions & 1 deletion apps/server/src/modules/deletion/client/deletion.client.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { of } from 'rxjs';
import { of, throwError } from 'rxjs';
import { AxiosResponse } from 'axios';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
Expand Down Expand Up @@ -51,6 +51,23 @@ describe(DeletionClient.name, () => {
});

describe('queueDeletionRequest', () => {
describe('when sending the HTTP request failed', () => {
const setup = () => {
const input = DeletionRequestInputBuilder.build('user', '652f1625e9bc1a13bdaae48b');

const error = new Error('unknown error');
httpService.post.mockReturnValueOnce(throwError(() => error));

return { input };
};

it('should catch and throw an error', async () => {
const { input } = setup();

await expect(client.queueDeletionRequest(input)).rejects.toThrow(Error);
});
});

describe('when received valid response with expected HTTP status code', () => {
const setup = () => {
const input = DeletionRequestInputBuilder.build('user', '652f1625e9bc1a13bdaae48b');
Expand Down Expand Up @@ -151,4 +168,55 @@ describe(DeletionClient.name, () => {
});
});
});

describe('executeDeletions', () => {
describe('when sending the HTTP request failed', () => {
const setup = () => {
const error = new Error('unknown error');
httpService.post.mockReturnValueOnce(throwError(() => error));
};

it('should catch and throw an error', async () => {
setup();

await expect(client.executeDeletions()).rejects.toThrow(Error);
});
});

describe('when received valid response with expected HTTP status code', () => {
const setup = () => {
const limit = 10;

const response: AxiosResponse<DeletionRequestOutput> = axiosResponseFactory.build({
status: 204,
});

httpService.post.mockReturnValueOnce(of(response));

return { limit };
};

it('should return proper output', async () => {
const { limit } = setup();

await expect(client.executeDeletions(limit)).resolves.not.toThrow();
});
});

describe('when received invalid HTTP status code in a response', () => {
const setup = () => {
const response: AxiosResponse<DeletionRequestOutput> = axiosResponseFactory.build({
status: 200,
});

httpService.post.mockReturnValueOnce(of(response));
};

it('should throw an exception', async () => {
setup();

await expect(client.executeDeletions()).rejects.toThrow(Error);
});
});
});
});
95 changes: 64 additions & 31 deletions apps/server/src/modules/deletion/client/deletion.client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { firstValueFrom } from 'rxjs';
import { AxiosResponse } from 'axios';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { BadGatewayException, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DeletionRequestInput, DeletionRequestOutput, DeletionClientConfig } from './interface';
import { ErrorUtils } from '@src/core/error/utils';
import { firstValueFrom } from 'rxjs';
import { DeletionClientConfig, DeletionRequestInput, DeletionRequestOutput } from './interface';

@Injectable()
export class DeletionClient {
Expand All @@ -13,6 +13,8 @@ export class DeletionClient {

private readonly postDeletionRequestsEndpoint: string;

private readonly postDeletionExecutionsEndpoint: string;

constructor(
private readonly httpService: HttpService,
private readonly configService: ConfigService<DeletionClientConfig, true>
Expand All @@ -22,36 +24,67 @@ export class DeletionClient {

// Prepare the POST /deletionRequests endpoint beforehand to not do it on every client call.
this.postDeletionRequestsEndpoint = new URL('/admin/api/v1/deletionRequests', this.baseUrl).toString();
this.postDeletionExecutionsEndpoint = new URL('/admin/api/v1/deletionExecutions', this.baseUrl).toString();
}

async queueDeletionRequest(input: DeletionRequestInput): Promise<DeletionRequestOutput> {
const request = this.httpService.post(this.postDeletionRequestsEndpoint, input, this.defaultHeaders());

return firstValueFrom(request)
.then((resp: AxiosResponse<DeletionRequestOutput>) => {
// Throw an error if any other status code (other than expected "202 Accepted" is returned).
if (resp.status !== 202) {
throw new Error(`invalid HTTP status code in a response from the server - ${resp.status} instead of 202`);
}

// Throw an error if server didn't return a requestId in a response (and it is
// required as it gives client the reference to the created deletion request).
if (!resp.data.requestId) {
throw new Error('no valid requestId returned from the server');
}

// Throw an error if server didn't return a deletionPlannedAt timestamp so the user
// will not be aware after which date the deletion request's execution will begin.
if (!resp.data.deletionPlannedAt) {
throw new Error('no valid deletionPlannedAt returned from the server');
}

return resp.data;
})
.catch((err: Error) => {
// Throw an error if sending/processing deletion request by the client failed in any way.
throw new Error(`failed to send/process a deletion request: ${err.toString()}`);
});
try {
const request = this.httpService.post<DeletionRequestOutput>(
this.postDeletionRequestsEndpoint,
input,
this.defaultHeaders()
);

const resp = await firstValueFrom(request);

// Throw an error if any other status code (other than expected "202 Accepted" is returned).
if (resp.status !== 202) {
throw new Error(`invalid HTTP status code in a response from the server - ${resp.status} instead of 202`);
}

// Throw an error if server didn't return a requestId in a response (and it is
// required as it gives client the reference to the created deletion request).
if (!resp.data.requestId) {
throw new Error('no valid requestId returned from the server');
}

// Throw an error if server didn't return a deletionPlannedAt timestamp so the user
// will not be aware after which date the deletion request's execution will begin.
if (!resp.data.deletionPlannedAt) {
throw Error('no valid deletionPlannedAt returned from the server');
}

return resp.data;
} catch (err) {
// Throw an error if sending deletion request has failed.
throw new BadGatewayException('DeletionClient:queueDeletionRequest', ErrorUtils.createHttpExceptionOptions(err));
}
}

async executeDeletions(limit?: number): Promise<void> {
let requestConfig = {};

if (limit && limit > 0) {
requestConfig = { ...this.defaultHeaders(), params: { limit } };
} else {
requestConfig = { ...this.defaultHeaders() };
}

try {
const request = this.httpService.post(this.postDeletionExecutionsEndpoint, null, requestConfig);

const resp = await firstValueFrom(request);

if (resp.status !== 204) {
// Throw an error if any other status code (other than expected "204 No Content" is returned).
const err = new Error(`invalid HTTP status code in a response from the server - ${resp.status} instead of 204`);

throw new BadGatewayException('DeletionClient:executeDeletions', ErrorUtils.createHttpExceptionOptions(err));
}
} catch (err: unknown) {
// Throw an error if sending deletion request(s) execution trigger has failed.
bn-pass marked this conversation as resolved.
Show resolved Hide resolved
throw new BadGatewayException('DeletionClient:executeDeletions', ErrorUtils.createHttpExceptionOptions(err));
}
}

private apiKeyHeader() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DeletionExecutionTriggerResult, DeletionExecutionTriggerStatus } from '../interface';
import { DeletionExecutionTriggerResultBuilder } from './deletion-execution-trigger-result.builder';

describe(DeletionExecutionTriggerResultBuilder.name, () => {
describe(DeletionExecutionTriggerResultBuilder.buildSuccess.name, () => {
describe('when called', () => {
const setup = () => {
const expectedOutput: DeletionExecutionTriggerResult = { status: DeletionExecutionTriggerStatus.SUCCESS };

return { expectedOutput };
};

it('should return valid object indicating success', () => {
const { expectedOutput } = setup();

const output = DeletionExecutionTriggerResultBuilder.buildSuccess();

expect(output).toStrictEqual(expectedOutput);
});
});
});

describe(DeletionExecutionTriggerResultBuilder.buildFailure.name, () => {
describe('when called with proper arguments', () => {
const setup = () => {
const error = new Error('test error message');

const expectedOutput: DeletionExecutionTriggerResult = {
status: DeletionExecutionTriggerStatus.FAILURE,
error: error.toString(),
};

return { error, expectedOutput };
};

it('should return valid object with expected values', () => {
const { error, expectedOutput } = setup();

const output = DeletionExecutionTriggerResultBuilder.buildFailure(error);

expect(output).toStrictEqual(expectedOutput);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DeletionExecutionTriggerResult, DeletionExecutionTriggerStatus } from '../interface';

export class DeletionExecutionTriggerResultBuilder {
private static build(status: DeletionExecutionTriggerStatus, error?: string): DeletionExecutionTriggerResult {
const output: DeletionExecutionTriggerResult = { status };

if (error) {
output.error = error;
}

return output;
}

static buildSuccess(): DeletionExecutionTriggerResult {
return this.build(DeletionExecutionTriggerStatus.SUCCESS);
}

static buildFailure(err: Error): DeletionExecutionTriggerResult {
return this.build(DeletionExecutionTriggerStatus.FAILURE, err.toString());
}
}
2 changes: 2 additions & 0 deletions apps/server/src/modules/deletion/console/builder/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './push-delete-requests-options.builder';
export * from './trigger-deletion-execution-options.builder';
export * from './deletion-execution-trigger-result.builder';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TriggerDeletionExecutionOptions } from '../interface';
import { TriggerDeletionExecutionOptionsBuilder } from './trigger-deletion-execution-options.builder';

describe(TriggerDeletionExecutionOptionsBuilder.name, () => {
describe(TriggerDeletionExecutionOptionsBuilder.build.name, () => {
describe('when called with proper arguments', () => {
const setup = () => {
const limit = 1000;

const expectedOutput: TriggerDeletionExecutionOptions = { limit };

return { limit, expectedOutput };
};

it('should return valid object with expected values', () => {
const { limit, expectedOutput } = setup();

const output = TriggerDeletionExecutionOptionsBuilder.build(limit);

expect(output).toStrictEqual(expectedOutput);
});
});
});
});
Loading
Loading