Skip to content

Commit

Permalink
Add UseCases to delete Files, RelationshipTemplates and Tokens (#432)
Browse files Browse the repository at this point in the history
* feat: delete files from backbone

* feat: add isOwn check to tokens and templates

* feat: adapt files, add use cases

* test: add transport tests

* refactor: remove unused error

* test: add runtime tests

* test: fix test

* test: improve tests

* test: fix token decomposition

* fix: sync datawallet

* refactor: test names

* refactor: capitalize test describe block names

Co-authored-by: Britta Stallknecht <[email protected]>

* refactor: test name capitalization

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Britta Stallknecht <[email protected]>
  • Loading branch information
3 people authored Mar 3, 2025
1 parent 23b0ae3 commit 5e81f68
Show file tree
Hide file tree
Showing 19 changed files with 383 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
CreateTokenQRCodeForFileRequest,
CreateTokenQRCodeForFileResponse,
CreateTokenQRCodeForFileUseCase,
DeleteFileRequest,
DeleteFileUseCase,
DownloadFileRequest,
DownloadFileResponse,
DownloadFileUseCase,
Expand All @@ -30,6 +32,7 @@ export class FilesFacade {
@Inject private readonly getFilesUseCase: GetFilesUseCase,
@Inject private readonly downloadFileUseCase: DownloadFileUseCase,
@Inject private readonly getFileUseCase: GetFileUseCase,
@Inject private readonly deleteFileUseCase: DeleteFileUseCase,
@Inject private readonly createQRCodeForFileUseCase: CreateQRCodeForFileUseCase,
@Inject private readonly createTokenForFileUseCase: CreateTokenForFileUseCase,
@Inject private readonly createTokenQRCodeForFileUseCase: CreateTokenQRCodeForFileUseCase
Expand All @@ -55,6 +58,10 @@ export class FilesFacade {
return await this.uploadOwnFileUseCase.execute(request);
}

public async deleteFile(request: DeleteFileRequest): Promise<Result<void>> {
return await this.deleteFileUseCase.execute(request);
}

public async createQRCodeForFile(request: CreateQRCodeForFileRequest): Promise<Result<CreateQRCodeForFileResponse>> {
return await this.createQRCodeForFileUseCase.execute(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
CreateTokenQRCodeForOwnTemplateRequest,
CreateTokenQRCodeForOwnTemplateResponse,
CreateTokenQRCodeForOwnTemplateUseCase,
DeleteRelationshipTemplateRequest,
DeleteRelationshipTemplateUseCase,
GetRelationshipTemplateRequest,
GetRelationshipTemplateUseCase,
GetRelationshipTemplatesRequest,
Expand All @@ -26,6 +28,7 @@ export class RelationshipTemplatesFacade {
@Inject private readonly loadPeerRelationshipTemplateUseCase: LoadPeerRelationshipTemplateUseCase,
@Inject private readonly getRelationshipTemplatesUseCase: GetRelationshipTemplatesUseCase,
@Inject private readonly getRelationshipTemplateUseCase: GetRelationshipTemplateUseCase,
@Inject private readonly deleteRelationshipTemplateUseCase: DeleteRelationshipTemplateUseCase,
@Inject private readonly createQRCodeForOwnTemplateUseCase: CreateQRCodeForOwnTemplateUseCase,
@Inject private readonly createTokenQRCodeForOwnTemplateUseCase: CreateTokenQRCodeForOwnTemplateUseCase,
@Inject private readonly createTokenForOwnTemplateUseCase: CreateTokenForOwnTemplateUseCase
Expand All @@ -47,6 +50,10 @@ export class RelationshipTemplatesFacade {
return await this.getRelationshipTemplateUseCase.execute(request);
}

public async deleteRelationshipTemplate(request: DeleteRelationshipTemplateRequest): Promise<Result<void>> {
return await this.deleteRelationshipTemplateUseCase.execute(request);
}

public async createQRCodeForOwnTemplate(request: CreateQRCodeForOwnTemplateRequest): Promise<Result<CreateQRCodeForOwnTemplateResponse>> {
return await this.createQRCodeForOwnTemplateUseCase.execute(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { TokenDTO } from "../../../types";
import {
CreateOwnTokenRequest,
CreateOwnTokenUseCase,
DeleteTokenRequest,
DeleteTokenUseCase,
GetQRCodeForTokenRequest,
GetQRCodeForTokenResponse,
GetQRCodeForTokenUseCase,
Expand All @@ -21,6 +23,7 @@ export class TokensFacade {
@Inject private readonly loadPeerTokenUseCase: LoadPeerTokenUseCase,
@Inject private readonly getTokensUseCase: GetTokensUseCase,
@Inject private readonly getTokenUseCase: GetTokenUseCase,
@Inject private readonly deleteTokenUseCase: DeleteTokenUseCase,
@Inject private readonly getQRCodeForTokenUseCase: GetQRCodeForTokenUseCase
) {}

Expand All @@ -40,6 +43,10 @@ export class TokensFacade {
return await this.getTokenUseCase.execute(request);
}

public async deleteToken(request: DeleteTokenRequest): Promise<Result<void>> {
return await this.deleteTokenUseCase.execute(request);
}

public async getQRCodeForToken(request: GetQRCodeForTokenRequest): Promise<Result<GetQRCodeForTokenResponse>> {
return await this.getQRCodeForTokenUseCase.execute(request);
}
Expand Down
69 changes: 69 additions & 0 deletions packages/runtime/src/useCases/common/Schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20284,6 +20284,29 @@ export const CreateTokenQRCodeForFileRequest: any = {
}
}

export const DeleteFileRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/DeleteFileRequest",
"definitions": {
"DeleteFileRequest": {
"type": "object",
"properties": {
"fileId": {
"$ref": "#/definitions/FileIdString"
}
},
"required": [
"fileId"
],
"additionalProperties": false
},
"FileIdString": {
"type": "string",
"pattern": "FIL[A-Za-z0-9]{17}"
}
}
}

export const GetFileRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/GetFileRequest",
Expand Down Expand Up @@ -21574,6 +21597,29 @@ export const CreateTokenQRCodeForOwnTemplateRequest: any = {
}
}

export const DeleteRelationshipTemplateRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/DeleteRelationshipTemplateRequest",
"definitions": {
"DeleteRelationshipTemplateRequest": {
"type": "object",
"properties": {
"templateId": {
"$ref": "#/definitions/RelationshipTemplateIdString"
}
},
"required": [
"templateId"
],
"additionalProperties": false
},
"RelationshipTemplateIdString": {
"type": "string",
"pattern": "RLT[A-Za-z0-9]{17}"
}
}
}

export const GetRelationshipTemplateRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/GetRelationshipTemplateRequest",
Expand Down Expand Up @@ -21839,6 +21885,29 @@ export const CreateOwnTokenRequest: any = {
}
}

export const DeleteTokenRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/DeleteTokenRequest",
"definitions": {
"DeleteTokenRequest": {
"type": "object",
"properties": {
"tokenId": {
"$ref": "#/definitions/TokenIdString"
}
},
"required": [
"tokenId"
],
"additionalProperties": false
},
"TokenIdString": {
"type": "string",
"pattern": "TOK[A-Za-z0-9]{17}"
}
}
}

export const GetQRCodeForTokenRequest: any = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/GetQRCodeForTokenRequest",
Expand Down
37 changes: 37 additions & 0 deletions packages/runtime/src/useCases/transport/files/DeleteFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Result } from "@js-soft/ts-utils";
import { CoreId } from "@nmshd/core-types";
import { AccountController, File, FileController } from "@nmshd/transport";
import { Inject } from "@nmshd/typescript-ioc";
import { FileIdString, RuntimeErrors, SchemaRepository, SchemaValidator, UseCase } from "../../common";

export interface DeleteFileRequest {
fileId: FileIdString;
}

class Validator extends SchemaValidator<DeleteFileRequest> {
public constructor(@Inject schemaRepository: SchemaRepository) {
super(schemaRepository.getSchema("DeleteFileRequest"));
}
}

export class DeleteFileUseCase extends UseCase<DeleteFileRequest, void> {
public constructor(
@Inject private readonly fileController: FileController,
@Inject private readonly accountController: AccountController,
@Inject validator: Validator
) {
super(validator);
}

protected async executeInternal(request: DeleteFileRequest): Promise<Result<void>> {
const file = await this.fileController.getFile(CoreId.from(request.fileId));
if (!file) {
return Result.fail(RuntimeErrors.general.recordNotFound(File));
}

await this.fileController.deleteFile(file);
await this.accountController.syncDatawallet();

return Result.ok(undefined);
}
}
1 change: 1 addition & 0 deletions packages/runtime/src/useCases/transport/files/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./CreateQRCodeForFile";
export * from "./CreateTokenForFile";
export * from "./CreateTokenQRCodeForFile";
export * from "./DeleteFile";
export * from "./DownloadFile";
export * from "./FileMapper";
export * from "./GetFile";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Result } from "@js-soft/ts-utils";
import { CoreId } from "@nmshd/core-types";
import { AccountController, RelationshipTemplate, RelationshipTemplateController } from "@nmshd/transport";
import { Inject } from "@nmshd/typescript-ioc";
import { RelationshipTemplateIdString, RuntimeErrors, SchemaRepository, SchemaValidator, UseCase } from "../../common";

export interface DeleteRelationshipTemplateRequest {
templateId: RelationshipTemplateIdString;
}

class Validator extends SchemaValidator<DeleteRelationshipTemplateRequest> {
public constructor(@Inject schemaRepository: SchemaRepository) {
super(schemaRepository.getSchema("DeleteRelationshipTemplateRequest"));
}
}

export class DeleteRelationshipTemplateUseCase extends UseCase<DeleteRelationshipTemplateRequest, void> {
public constructor(
@Inject private readonly relationshipTemplateController: RelationshipTemplateController,
@Inject private readonly accountController: AccountController,
@Inject validator: Validator
) {
super(validator);
}

protected async executeInternal(request: DeleteRelationshipTemplateRequest): Promise<Result<void>> {
const template = await this.relationshipTemplateController.getRelationshipTemplate(CoreId.from(request.templateId));
if (!template) {
return Result.fail(RuntimeErrors.general.recordNotFound(RelationshipTemplate));
}

await this.relationshipTemplateController.deleteRelationshipTemplate(template);
await this.accountController.syncDatawallet();

return Result.ok(undefined);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./CreateOwnRelationshipTemplate";
export * from "./CreateQRCodeForOwnRelationshipTemplate";
export * from "./CreateTokenForOwnRelationshipTemplate";
export * from "./CreateTokenQRCodeForOwnRelationshipTemplate";
export * from "./DeleteRelationshipTemplate";
export * from "./GetRelationshipTemplate";
export * from "./GetRelationshipTemplates";
export * from "./LoadPeerRelationshipTemplate";
Expand Down
37 changes: 37 additions & 0 deletions packages/runtime/src/useCases/transport/tokens/DeleteToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Result } from "@js-soft/ts-utils";
import { CoreId } from "@nmshd/core-types";
import { AccountController, Token, TokenController } from "@nmshd/transport";
import { Inject } from "@nmshd/typescript-ioc";
import { RuntimeErrors, SchemaRepository, SchemaValidator, TokenIdString, UseCase } from "../../common";

export interface DeleteTokenRequest {
tokenId: TokenIdString;
}

class Validator extends SchemaValidator<DeleteTokenRequest> {
public constructor(@Inject schemaRepository: SchemaRepository) {
super(schemaRepository.getSchema("DeleteTokenRequest"));
}
}

export class DeleteTokenUseCase extends UseCase<DeleteTokenRequest, void> {
public constructor(
@Inject private readonly tokenController: TokenController,
@Inject private readonly accountController: AccountController,
@Inject validator: Validator
) {
super(validator);
}

protected async executeInternal(request: DeleteTokenRequest): Promise<Result<void>> {
const token = await this.tokenController.getToken(CoreId.from(request.tokenId));
if (!token) {
return Result.fail(RuntimeErrors.general.recordNotFound(Token));
}

await this.tokenController.delete(token);
await this.accountController.syncDatawallet();

return Result.ok(undefined);
}
}
1 change: 1 addition & 0 deletions packages/runtime/src/useCases/transport/tokens/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./CreateOwnToken";
export * from "./DeleteToken";
export * from "./GetQRCodeForToken";
export * from "./GetToken";
export * from "./GetTokens";
Expand Down
20 changes: 20 additions & 0 deletions packages/runtime/test/transport/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,26 @@ describe("Upload big files", () => {
});
});

describe("Delete file", () => {
test("accessing invalid file id causes an error", async () => {
const response = await transportServices1.files.deleteFile({ fileId: UNKNOWN_FILE_ID });
expect(response).toBeAnError("File not found. Make sure the ID exists and the record is not expired.", "error.runtime.recordNotFound");
});

test("successfully delete file", async () => {
const uploadResponse = await transportServices1.files.uploadOwnFile(await makeUploadRequest());
expect(uploadResponse).toBeSuccessful();
const file = uploadResponse.value;
expect(file).toBeDefined();

const deleteFileResponse = await transportServices1.files.deleteFile({ fileId: file.id });
expect(deleteFileResponse).toBeSuccessful();

const getFileResponse = await transportServices1.files.getFile({ id: file.id });
expect(getFileResponse).toBeAnError("File not found. Make sure the ID exists and the record is not expired.", "error.runtime.recordNotFound");
});
});

describe("Files query", () => {
test("files can be queried by their attributes", async () => {
const file = await uploadFile(transportServices1);
Expand Down
24 changes: 24 additions & 0 deletions packages/runtime/test/transport/relationshipTemplates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const serviceProvider = new RuntimeServiceProvider();
let runtimeServices1: TestRuntimeServices;
let runtimeServices2: TestRuntimeServices;

const UNKNOWN_TEMPLATE_ID = "RLTXXXXXXXXXXXXXXXXX";

beforeAll(async () => {
const runtimeServices = await serviceProvider.launch(2);
runtimeServices1 = runtimeServices[0];
Expand Down Expand Up @@ -247,6 +249,28 @@ describe("RelationshipTemplate Tests", () => {
expect(createQRCodeWithoutPersonalizationResult).toBeAnError(/.*/, "error.runtime.relationshipTemplates.personalizationMustBeInherited");
});
});

describe("Delete template", () => {
test("accessing invalid template id causes an error", async () => {
const response = await runtimeServices1.transport.relationshipTemplates.deleteRelationshipTemplate({ templateId: UNKNOWN_TEMPLATE_ID });
expect(response).toBeAnError("RelationshipTemplate not found. Make sure the ID exists and the record is not expired.", "error.runtime.recordNotFound");
});

test("successfully delete template", async () => {
const template = (
await runtimeServices1.transport.relationshipTemplates.createOwnRelationshipTemplate({
content: emptyRelationshipTemplateContent,
expiresAt: DateTime.utc().plus({ minutes: 1 }).toString()
})
).value;

const deleteTemplateResponse = await runtimeServices1.transport.relationshipTemplates.deleteRelationshipTemplate({ templateId: template.id });
expect(deleteTemplateResponse).toBeSuccessful();

const getTemplateResponse = await runtimeServices1.transport.relationshipTemplates.getRelationshipTemplate({ id: template.id });
expect(getTemplateResponse).toBeAnError("RelationshipTemplate not found. Make sure the ID exists and the record is not expired.", "error.runtime.recordNotFound");
});
});
});

describe("Serialization Errors", () => {
Expand Down
Loading

0 comments on commit 5e81f68

Please sign in to comment.