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

BO44 - create DAL utilities to store localized fields #267

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions src/constants/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const defaultLanguage = "en" as const;

export const supportedLanguages = ["it", "en"] as const;

export type SupportedLanguages = (typeof supportedLanguages)[number];
46 changes: 29 additions & 17 deletions src/data/firestore/common/create.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test, describe, expect } from "vitest";
import { createDocument } from "./create";
import { getDocumentById } from "./get-by-id";
import { testConverter, type TestDoc, type TestModel } from "./test-model";
import { describe, expect, test } from "vitest";
import createDocument from "./create";
import { defaultLanguage } from "~/constants/i18n";
import {testConverter, type TestDoc, type TestModel} from "./test-model";
import type { FirestoreDataConverter } from "firebase-admin/firestore";

const testCollection = "create-test";
Expand All @@ -11,29 +11,18 @@ describe("Create a new document", () => {
const modelToSave: TestModel = {
lastUpdate: new Date("2024-01-01"),
name: "Test Document",
description: "Test description",
};
const result = await createDocument(
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);
expect(result).toMatchObject({
status: "success",
data: expect.any(String),
});

const getResult = await getDocumentById(
testCollection,
testConverter,
result.data as string,
);
expect(getResult).toMatchObject({
status: "success",
data: expect.objectContaining({
...modelToSave,
id: result.data,
}),
});
});

test("Should return an error if id is present", async () => {
Expand All @@ -46,6 +35,7 @@ describe("Create a new document", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);
expect(result).toMatchObject({
status: "error",
Expand Down Expand Up @@ -73,6 +63,7 @@ describe("Create a new document", () => {
testCollection,
fakeConverter,
modelToSave,
defaultLanguage,
);
expect(result).toMatchObject({
status: "error",
Expand All @@ -93,6 +84,7 @@ describe("Create a new document", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);
expect(result).toMatchObject({
status: "error",
Expand All @@ -102,4 +94,24 @@ describe("Create a new document", () => {
},
});
});

test("Should return an error if passed language is not default one", async () => {
const modelToSave: TestModel = {
lastUpdate: new Date("2024-01-01"),
name: "Test Document",
};
const result = await createDocument(
testCollection,
testConverter,
modelToSave,
"it",
);
expect(result).toMatchObject({
status: "error",
data: {
code: `${testCollection}/create-error:default-language-is-required`,
message: "Default language is required",
},
});
});
});
24 changes: 18 additions & 6 deletions src/data/firestore/common/create.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import type {
DocumentData,
FirestoreDataConverter,
} from "firebase-admin/firestore";
import { firestoreInstance } from "~/firebase/server";
import type { BaseModel } from "~/models/base-model";
import type { ServerResponse } from "~/models/server-response/server-response.type";
import { defaultLanguage, type SupportedLanguages } from "~/constants/i18n";
import type {DocumentData, FirestoreDataConverter} from "firebase-admin/firestore";

export const createDocument = async <
const createDocument = async <
M extends BaseModel,
D extends DocumentData,
C extends string,
>(
collection: C,
converter: FirestoreDataConverter<M, D>,
model: M,
currentLanguage: SupportedLanguages,
): Promise<
ServerResponse<
string,
`${C}/create-error` | `${C}/create-error:id-is-present`
| `${C}/create-error`
| `${C}/create-error:id-is-present`
| `${C}/create-error:default-language-is-required`
>
> => {
try {
if (currentLanguage !== defaultLanguage) {
return {
status: "error",
data: {
code: `${collection}/create-error:default-language-is-required`,
message: "Default language is required",
},
};
}
if (model.id) {
return {
status: "error",
Expand Down Expand Up @@ -49,3 +59,5 @@ export const createDocument = async <
};
}
};

export default createDocument;
35 changes: 3 additions & 32 deletions src/data/firestore/common/delete.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { describe, expect, test } from "vitest";
import { deleteDocument } from "./delete";
import { createDocument } from "./create";
import { getDocumentById } from "./get-by-id";
import { testConverter, type TestModel } from "./test-model";
import createDocument from "./create";
import { defaultLanguage } from "~/constants/i18n";

const testCollection = "delete-test";

Expand All @@ -17,22 +17,9 @@ describe("deleteDocument", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);

const get = await getDocumentById(
testCollection,
testConverter,
result.data as string,
);

expect(get).toMatchObject({
status: "success",
data: expect.objectContaining({
...modelToSave,
id: result.data,
}),
});

const deleteResult = await deleteDocument(
testCollection,
result.data as string,
Expand All @@ -42,22 +29,6 @@ describe("deleteDocument", () => {
status: "success",
data: null,
});

const getAfterDelete = await getDocumentById(
testCollection,
testConverter,
result.data as string,
);

expect(getAfterDelete).toMatchObject({
status: "error",
data: {
code: `${testCollection}/get-by-id-error:not-found`,
message: `Document with id ${
result.data as string
} not found in collection delete-test`,
},
});
});

test("should return an error if id is missing", async () => {
Expand Down
5 changes: 4 additions & 1 deletion src/data/firestore/common/get-by-id.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { describe, expect, test } from "vitest";
import { createDocument } from "./create";
import { getDocumentById } from "./get-by-id";
import { testConverter, type TestDoc, type TestModel } from "./test-model";
import type { FirestoreDataConverter } from "firebase-admin/firestore";
import createDocument from "./create";
import { defaultLanguage } from "~/constants/i18n";

const testCollection = "get-by-id-test";

Expand All @@ -17,6 +18,7 @@ describe("Get by id", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);

const get = await getDocumentById(
Expand Down Expand Up @@ -84,6 +86,7 @@ describe("Get by id", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);

const fakeConverter: FirestoreDataConverter<TestModel, TestDoc> = {
Expand Down
34 changes: 20 additions & 14 deletions src/data/firestore/common/test-model.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
import type {LocalizedFirestoreDocument} from "~/data/firestore/docs/model-document-mapper";
import {type FirestoreDataConverter, type QueryDocumentSnapshot, Timestamp} from "firebase-admin/firestore";
import {
QueryDocumentSnapshot,
Timestamp,
type FirestoreDataConverter,
} from "firebase-admin/firestore";
import type { FirestoreDocument } from "~/data/model-document-mapper";
localizedFieldFromFirestore,
localizedFieldToFirestore
} from "~/data/firestore/docs/converters/localization-converter.ts";
import omit from "~/data/firestore/docs/utils.ts";

export type TestModel = {
id?: string;
name: string;
description?: string;
lastUpdate: Date;
};

export type TestDoc = FirestoreDocument<TestModel>;
type TestDocLocalizedFields = "description";

export type TestDoc = LocalizedFirestoreDocument<TestModel, TestDocLocalizedFields>

export const testConverter: FirestoreDataConverter<TestModel, TestDoc> = {
toFirestore: (company: TestModel): TestDoc => {
return {
...company,
lastUpdate: Timestamp.fromDate(company.lastUpdate),
toFirestore: (model: TestModel): TestDoc => {
const rest = omit(model, ["id", "description"]);
const doc: TestDoc = {
...rest,
lastUpdate: Timestamp.fromDate(model.lastUpdate),
};
return localizedFieldToFirestore(model, "en", ["description"], doc);
},
fromFirestore: (snapshot: QueryDocumentSnapshot): TestModel => {
const data = snapshot.data();
const model = localizedFieldFromFirestore<TestModel, TestDocLocalizedFields>(snapshot, "en", ["description"]);
return {
id: snapshot.id,
...data,
...model,
lastUpdate: data.lastUpdate.toDate(),
} as TestModel;
};
},
};
};
5 changes: 4 additions & 1 deletion src/data/firestore/common/update.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { test, describe, expect } from "vitest";
import { updateDocument } from "./update";
import { getDocumentById } from "./get-by-id";
import { createDocument } from "./create";
import { testConverter, type TestDoc, type TestModel } from "./test-model";
import type { FirestoreDataConverter } from "firebase-admin/firestore";
import createDocument from "./create";
import { defaultLanguage } from "~/constants/i18n";

const testCollection = "update-test";

Expand All @@ -18,6 +19,7 @@ describe("Update a new document", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);

const modelToUpdate = {
Expand Down Expand Up @@ -88,6 +90,7 @@ describe("Update a new document", () => {
testCollection,
testConverter,
modelToSave,
defaultLanguage,
);

const modelToUpdate = {
Expand Down
Loading
Loading