Changes
5 changed files (+157/-21)
-
-
@@ -3,7 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0-onlyimport { describe, expect, test } from "bun:test"; import { isSameBytes } from "./helpers"; import { create, type MessageInitShape } from "@bufbuild/protobuf"; import { DateSchema } from "@yamori/proto/yamori/type/v1/date_pb.js"; import { isSameBytes, maskMessage } from "./helpers"; describe("isSameBytes", () => { test("Should return true for same references", () => {
-
@@ -33,3 +36,82 @@expect(isSameBytes(foo, bar)).toBe(false); }); }); describe("maskMessage", () => { test("Should return empty message if mask is empty", () => { expect( maskMessage(DateSchema, { fields: [] }, { year: 2000, month: 1, day: 1, } as MessageInitShape<typeof DateSchema>), ).toEqual({}); }); test("Should mask field", () => { expect( maskMessage(DateSchema, { fields: [DateSchema.field.month.number] }, { year: 2000, month: 1, day: 1, } as MessageInitShape<typeof DateSchema>), ).toEqual({ month: 1, }); }); test("Should return every field on full mask", () => { expect( maskMessage( DateSchema, { fields: [ DateSchema.field.year.number, DateSchema.field.month.number, DateSchema.field.day.number, ], }, { year: 2000, month: 1, day: 1, } as MessageInitShape<typeof DateSchema>, ), ).toEqual({ year: 2000, month: 1, day: 1, }); }); test("Should keep $typeName", () => { const masked = maskMessage( DateSchema, { fields: [DateSchema.field.day.number] }, create(DateSchema, { year: 2000, month: 1, day: 1, }), ); expect(masked.$typeName).not.toBeEmpty(); expect(masked.day).toBe(1); expect(masked.year).toBeEmpty(); expect(masked.month).toBeEmpty(); }); test("Should not mutate input message", () => { const input = create(DateSchema, { year: 2000, month: 1, day: 1, }); maskMessage(DateSchema, { fields: [DateSchema.field.day.number] }, input); expect(input.year).toBe(2000); expect(input.month).toBe(1); expect(input.day).toBe(1); }); });
-
-
-
@@ -1,6 +1,8 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { type DescMessage, type MessageInitShape } from "@bufbuild/protobuf"; export function isSameBytes(a: Uint8Array, b: Uint8Array): boolean { if (a.byteLength !== b.byteLength) { return false;
-
@@ -14,3 +16,18 @@ }return true; } export function maskMessage< Message extends DescMessage, Data extends Readonly<MessageInitShape<Message>>, >(schema: Message, mask: { fields: readonly number[] }, message: Data): Data { const init: Data = { ...message }; for (const field of schema.fields) { if (!mask.fields.includes(field.number)) { delete init[field.localName as keyof Data]; } } return init; }
-
-
-
@@ -0,0 +1,19 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { create, type MessageInitShape } from "@bufbuild/protobuf"; import { LeaveSchema, type Leave } from "@yamori/proto/yamori/work_record/v1/leave_pb.js"; import { type LeaveReadMask } from "@yamori/proto/yamori/work_record/v1/leave_read_mask_pb.js"; import { maskMessage } from "../../../helpers"; export function maskLeave( mask: LeaveReadMask | undefined, leave: MessageInitShape<typeof LeaveSchema>, ): Leave { if (!mask) { return create(LeaveSchema, leave); } return create(LeaveSchema, maskMessage(LeaveSchema, mask, leave)); }
-
-
-
@@ -0,0 +1,30 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { create, type MessageInitShape } from "@bufbuild/protobuf"; import { WorkspaceSchema, type Workspace, } from "@yamori/proto/yamori/workspace/v1/workspace_pb.js"; import { type WorkspaceReadMask } from "@yamori/proto/yamori/workspace/v1/workspace_read_mask_pb.js"; import { maskMessage } from "../../../helpers"; import { maskLeave } from "../../work_record/v1/leave"; export function maskWorkspace( mask: WorkspaceReadMask | undefined, workspace: MessageInitShape<typeof WorkspaceSchema>, ): Workspace { if (!mask) { return create(WorkspaceSchema, workspace); } const masked = maskMessage(WorkspaceSchema, mask, workspace); return create(WorkspaceSchema, { ...masked, leaveDefinitions: masked.leaveDefinitions && masked.leaveDefinitions.map((leave) => maskLeave(mask.leaveDefinitionsMask, leave)), } as MessageInitShape<typeof WorkspaceSchema>); }
-
-
-
@@ -7,9 +7,9 @@ GetRequestSchema,type GetRequest, } from "@yamori/proto/yamori/workspace/v1/get_request_pb.js"; import { GetResponseSchema } from "@yamori/proto/yamori/workspace/v1/get_response_pb.js"; import { WorkspaceSchema } from "@yamori/proto/yamori/workspace/v1/workspace_pb.js"; import { type Context, type YamoriDB } from "../../../../types"; import { maskWorkspace } from "../workspace"; export async function get(data: Uint8Array, { db }: Context): Promise<Uint8Array> { let req: GetRequest;
-
@@ -83,30 +83,18 @@ }),); } const shouldReturn = (fieldNumber: number): true | undefined => !req.readMask || req.readMask.fields.includes(fieldNumber) || undefined; return toBinary( GetResponseSchema, create(GetResponseSchema, { result: { case: "ok", value: { id: shouldReturn(WorkspaceSchema.field.id.number) && { value: found.id, }, displayName: shouldReturn(WorkspaceSchema.field.displayName.number) && found.displayName, updateKey: shouldReturn(WorkspaceSchema.field.updateKey.number) && { key: found.capabilities.updateKey, }, deletionKey: shouldReturn(WorkspaceSchema.field.deletionKey.number) && { key: found.capabilities.deletionKey, }, workerAddKey: shouldReturn(WorkspaceSchema.field.workerAddKey.number) && { key: found.capabilities.workerAddKey, }, }, value: maskWorkspace(req.readMask, { id: { value: found.id }, displayName: found.displayName, updateKey: { key: found.capabilities.updateKey }, deletionKey: { key: found.capabilities.deletionKey }, workerAddKey: { key: found.capabilities.workerAddKey }, }), }, }), );
-