Skip to content

Commit

Permalink
Enrich metadata of File with tags (#419)
Browse files Browse the repository at this point in the history
* feat: add tags to files

* fix: tests

* test: add test to load file with tags

* fix: add tags to FileDVO

* fix: add tags to getFilesQuery

* test: add test for custom query

* chore: make tags optional in DTO and DVO
  • Loading branch information
sebbi08 authored Feb 13, 2025
1 parent cabb8dd commit 4821dcc
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/runtime/src/dataViews/transport/FileDVO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IdentityDVO } from "./IdentityDVO";
export interface FileDVO extends DataViewObject {
type: "FileDVO";
filename: string;
tags?: string[];
filesize: number;
createdAt: string;
createdBy: IdentityDVO;
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/types/transport/FileDTO.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface FileDTO {
id: string;
filename: string;
tags?: string[];
filesize: number;
createdAt: string;
createdBy: string;
Expand Down
25 changes: 25 additions & 0 deletions packages/runtime/src/useCases/common/Schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20436,6 +20436,19 @@ export const GetFilesRequest: any = {
}
}
]
},
"tags": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": false
Expand Down Expand Up @@ -20548,6 +20561,12 @@ export const UploadOwnFileRequest: any = {
},
"description": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
Expand Down Expand Up @@ -20587,6 +20606,12 @@ export const UploadOwnFileValidatableRequest: any = {
"description": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"content": {
"type": "object"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class FileMapper {
return {
id: file.id.toString(),
filename: file.cache.filename,
tags: file.cache.tags,
filesize: file.cache.filesize,
createdAt: file.cache.createdAt.toString(),
createdBy: file.cache.createdBy.toString(),
Expand Down
18 changes: 18 additions & 0 deletions packages/runtime/src/useCases/transport/files/GetFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface GetFilesQuery {
mimetype?: string | string[];
title?: string | string[];
isOwn?: string | string[];
tags?: string | string[];
}

export interface GetFilesRequest {
Expand All @@ -43,6 +44,7 @@ export class GetFilesUseCase extends UseCase<GetFilesRequest, FileDTO[]> {
[nameof<FileDTO>((c) => c.filesize)]: true,
[nameof<FileDTO>((c) => c.mimetype)]: true,
[nameof<FileDTO>((c) => c.title)]: true,
[nameof<FileDTO>((c) => c.tags)]: true,
[nameof<FileDTO>((c) => c.isOwn)]: true
},
alias: {
Expand All @@ -55,7 +57,23 @@ export class GetFilesUseCase extends UseCase<GetFilesRequest, FileDTO[]> {
[nameof<FileDTO>((c) => c.filesize)]: `${nameof<File>((f) => f.cache)}.${nameof<CachedFile>((c) => c.filesize)}`,
[nameof<FileDTO>((c) => c.mimetype)]: `${nameof<File>((f) => f.cache)}.${nameof<CachedFile>((c) => c.mimetype)}`,
[nameof<FileDTO>((c) => c.title)]: `${nameof<File>((f) => f.cache)}.${nameof<CachedFile>((c) => c.title)}`,
[nameof<FileDTO>((c) => c.tags)]: `${nameof<File>((f) => f.cache)}.${nameof<CachedFile>((c) => c.tags)}`,
[nameof<FileDTO>((c) => c.isOwn)]: nameof<File>((f) => f.isOwn)
},
custom: {
// content.tags
[`${nameof<FileDTO>((x) => x.tags)}`]: (query: any, input: string | string[]) => {
if (typeof input === "string") {
query[`${nameof<FileDTO>((x) => x.tags)}`] = { $contains: input };
return;
}
const allowedTags = [];
for (const tag of input) {
const tagQuery = { [`${nameof<FileDTO>((x) => x.tags)}`]: { $contains: tag } };
allowedTags.push(tagQuery);
}
query["$or"] = allowedTags;
}
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface UploadOwnFileRequest {
expiresAt?: ISO8601DateTimeString;
title?: string;
description?: string;
tags?: string[];
}

export interface UploadOwnFileValidatableRequest extends Omit<UploadOwnFileRequest, "content"> {
Expand Down Expand Up @@ -74,7 +75,8 @@ export class UploadOwnFileUseCase extends UseCase<UploadOwnFileRequest, FileDTO>
description: request.description,
filename: request.filename,
mimetype: request.mimetype,
expiresAt: CoreDate.from(request.expiresAt ?? "9999-12-31T00:00:00.000Z")
expiresAt: CoreDate.from(request.expiresAt ?? "9999-12-31T00:00:00.000Z"),
tags: request.tags
});

await this.accountController.syncDatawallet();
Expand Down
52 changes: 52 additions & 0 deletions packages/runtime/test/transport/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ describe("File upload", () => {
expect(CoreDate.from(file.expiresAt).isSame(defaultDate)).toBe(true);
});

test("can upload a file with tags", async () => {
const response = await transportServices1.files.uploadOwnFile(await makeUploadRequest({ tags: ["tag1", "tag2"] }));
expect(response).toBeSuccessful();

expect(response.value.tags).toStrictEqual(["tag1", "tag2"]);
});

test("cannot upload a file with expiry date in the past", async () => {
const response = await transportServices1.files.uploadOwnFile(await makeUploadRequest({ expiresAt: "1970" }));
expect(response).toBeAnError("'expiresAt' must be in the future", "error.runtime.validation.invalidPropertyValue");
Expand All @@ -136,6 +143,39 @@ describe("Get file", () => {
expect(response.value).toMatchObject(file);
});

test("can get file by tags", async () => {
const uploadFileResult = await transportServices1.files.uploadOwnFile(
await makeUploadRequest({
tags: ["aTag", "anotherTag"]
})
);
const file = uploadFileResult.value;

const uploadFileResult2 = await transportServices1.files.uploadOwnFile(
await makeUploadRequest({
tags: ["aThirdTag", "aFourthTag"]
})
);
const file2 = uploadFileResult2.value;

const getResult = await transportServices1.files.getFiles({ query: { tags: ["aTag"] } });

expect(getResult).toBeSuccessful();
expect(getResult.value[0].id).toStrictEqual(file.id);

const getResult2 = await transportServices1.files.getFiles({ query: { tags: ["aTag", "anotherTag"] } });

expect(getResult2).toBeSuccessful();
expect(getResult2.value[0].id).toStrictEqual(file.id);

const getResult3 = await transportServices1.files.getFiles({ query: { tags: ["aTag", "aThirdTag"] } });

expect(getResult3).toBeSuccessful();
const result3Ids = getResult3.value.map((file) => file.id);
expect(result3Ids).toContain(file.id);
expect(result3Ids).toContain(file2.id);
});

test("accessing not existing file id causes an error", async () => {
const response = await transportServices1.files.getFile({ id: UNKNOWN_FILE_ID });
expect(response).toBeAnError("File not found. Make sure the ID exists and the record is not expired.", "error.runtime.recordNotFound");
Expand Down Expand Up @@ -295,6 +335,18 @@ describe("Load peer file with token reference", () => {
expect(response.value).toContainEqual({ ...file, isOwn: false });
});

test("should load a peer file with its tags", async () => {
const uploadOwnFileResult = await transportServices1.files.uploadOwnFile(
await makeUploadRequest({
tags: ["tag1", "tag2"]
})
);
const token = (await transportServices1.files.createTokenForFile({ fileId: uploadOwnFileResult.value.id })).value;
const loadFileResult = await transportServices2.files.getOrLoadFile({ reference: token.truncatedReference });

expect(loadFileResult.value.tags).toStrictEqual(["tag1", "tag2"]);
});

test("cannot pass token id as truncated token reference", async () => {
const file = await uploadFile(transportServices1);
const token = (await transportServices1.files.createTokenForFile({ fileId: file.id })).value;
Expand Down
4 changes: 3 additions & 1 deletion packages/transport/src/modules/files/FileController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ export class FileController extends TransportController {
plaintextHash: plaintextHash,
secretKey: fileDownloadSecretKey,
filemodified: input.filemodified,
mimetype: input.mimetype
mimetype: input.mimetype,
tags: input.tags
});

const serializedMetadata = metadata.serialize();
Expand All @@ -218,6 +219,7 @@ export class FileController extends TransportController {
title: input.title,
description: input.description,
filename: input.filename,
tags: input.tags,
filesize: fileSize,
filemodified: input.filemodified,
cipherKey: fileDownloadSecretKey,
Expand Down
6 changes: 6 additions & 0 deletions packages/transport/src/modules/files/local/CachedFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FileMetadata } from "../transmission/FileMetadata";
export interface ICachedFile extends ISerializable {
title?: string;
filename: string;
tags?: string[];
filesize: number;
filemodified?: CoreDate;
mimetype: string;
Expand Down Expand Up @@ -37,6 +38,10 @@ export class CachedFile extends Serializable implements ICachedFile {
@serialize()
public filename: string;

@validate({ nullable: true })
@serialize({ type: String })
public tags?: string[];

@validate()
@serialize()
public filesize: number;
Expand Down Expand Up @@ -112,6 +117,7 @@ export class CachedFile extends Serializable implements ICachedFile {
cipherKey: metadata.secretKey,
filemodified: metadata.filemodified,
filename: metadata.filename,
tags: metadata.tags,
filesize: metadata.filesize,
plaintextHash: metadata.plaintextHash,
deletedAt: backboneResponse.deletedAt ? CoreDate.from(backboneResponse.deletedAt) : undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ISendFileParameters extends ISerializable {
expiresAt: ICoreDate;
filemodified?: ICoreDate;
buffer: ICoreBuffer;
tags?: string[];
}

@type("SendFileParameters")
Expand Down Expand Up @@ -42,6 +43,10 @@ export class SendFileParameters extends Serializable implements ISendFileParamet
@serialize()
public buffer: CoreBuffer;

@validate({ nullable: true })
@serialize({ type: String })
public tags?: string[];

public static from(value: ISendFileParameters): SendFileParameters {
return this.fromAny(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface IFileMetadata extends ISerializable {
title?: string;
description?: string;
filename: string;
tags?: string[];

plaintextHash: ICoreHash;
secretKey: ICryptoSecretKey;
Expand All @@ -30,6 +31,10 @@ export class FileMetadata extends Serializable implements IFileMetadata {
@serialize()
public filename: string;

@validate({ nullable: true })
@serialize({ type: String })
public tags?: string[];

@validate()
@serialize()
public plaintextHash: CoreHash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe("FileController", function () {
expect(receivedFile.cache?.expiresAt).toStrictEqual(sentFile.cache!.expiresAt);
expect(sentFile.cache!.description).toBe(receivedFile.cache!.description);
expect(sentFile.cache!.title).toBe(receivedFile.cache!.title);
expect(sentFile.cache!.tags).toStrictEqual(receivedFile.cache!.tags);
expect(sentFile.cache!.filemodified?.toString()).toBe(receivedFile.cache!.filemodified?.toString());
expect(sentFile.cache!.filename).toBe(receivedFile.cache!.filename);
expect(sentFile.cache!.filesize).toBe(receivedFile.cache!.filesize);
Expand Down
3 changes: 2 additions & 1 deletion packages/transport/test/testHelpers/TestUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,8 @@ export class TestUtil {
filename: "Test.bin",
filemodified: CoreDate.from("2019-09-30T00:00:00.000Z"),
mimetype: "application/json",
expiresAt: CoreDate.utc().add({ minutes: 5 })
expiresAt: CoreDate.utc().add({ minutes: 5 }),
tags: ["tag1", "tag2"]
};

const file = await from.files.sendFile(params);
Expand Down

0 comments on commit 4821dcc

Please sign in to comment.