Skip to content

Commit

Permalink
feat(uee): add DataTableProfileEmailsRepository error cause
Browse files Browse the repository at this point in the history
  • Loading branch information
lucacavallaro authored Jan 11, 2024
1 parent d1af3b3 commit 70abae2
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 52 deletions.
3 changes: 1 addition & 2 deletions src/utils/unique_email_enforcement/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ describe("isEmailAlreadyTaken", () => {
({ entries, expected }) => {
const result = isEmailAlreadyTaken(mocks.email)({
profileEmails: {
list: generateProfileEmails(entries),
get: jest.fn()
list: generateProfileEmails(entries)
}
});
expect(result).resolves.toBe(expected);
Expand Down
21 changes: 0 additions & 21 deletions src/utils/unique_email_enforcement/__tests__/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,6 @@ const tableClient = new MockedTableClient(
);

describe("DataTableProfileEmailsRepository", () => {
describe("get", () => {
it.each(["citizen@email.test.pagopa.it", "CITIZEN@EMAIL.TEST.PAGOPA.IT"])(
"normalizes input e-mail addresses",
async email => {
const repo = new DataTableProfileEmailsRepository(tableClient);
const profileEmail = ProfileEmail.decode({
email,
fiscalCode: "RLDBSV36A78Y792X"
});
if (E.isRight(profileEmail)) {
await repo.get(profileEmail.right);
expect(tableClient.getEntity).toHaveBeenCalledWith(
"citizen@email.test.pagopa.it",
"RLDBSV36A78Y792X"
);
}
expect.hasAssertions();
}
);
});

describe("list", () => {
it("normalizes input e-mail address", async () => {
const repo = new DataTableProfileEmailsRepository(tableClient);
Expand Down
22 changes: 21 additions & 1 deletion src/utils/unique_email_enforcement/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export const ProfileEmail = t.type({
export type ProfileEmail = t.TypeOf<typeof ProfileEmail>;

export interface IProfileEmailReader {
readonly get: (p: ProfileEmail) => Promise<ProfileEmail>;
readonly list: (filter: EmailString) => AsyncIterableIterator<ProfileEmail>;
}

Expand All @@ -19,6 +18,27 @@ export interface IProfileEmailWriter {
readonly insert: (p: ProfileEmail) => Promise<void>;
}

type ProfileEmailWriterErrorCause =
| "ENTITY_NOT_FOUND"
| "DUPLICATE_ENTITY"
| "STORAGE_ERROR";

/* eslint-disable functional/prefer-readonly-type */
export class ProfileEmailWriterError extends Error {
public name = "ProfileEmailWriterError";
public cause: ProfileEmailWriterErrorCause;

constructor(message: string, cause: ProfileEmailWriterErrorCause) {
super(message);
this.cause = cause;
}

public static is(u: unknown): u is ProfileEmailWriterError {
return u instanceof Error && u.name === "ProfileEmailWriterError";
}
}
/* eslint-enable functional/prefer-readonly-type */

// Checks if the given e-mail is already taken
// profileEmails returns all the ProfileEmail records that shares
// the same e-mail. If count(records) >= 1 then the e-mail is already taken.
Expand Down
48 changes: 20 additions & 28 deletions src/utils/unique_email_enforcement/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import * as E from "fp-ts/lib/Either";
import * as t from "io-ts";
import { flow } from "fp-ts/lib/function";

import { TableClient, odata } from "@azure/data-tables";
import { TableClient, odata, RestError } from "@azure/data-tables";
import { EmailString } from "@pagopa/ts-commons/lib/strings";

import {
ProfileEmail,
IProfileEmailReader,
IProfileEmailWriter
IProfileEmailWriter,
ProfileEmailWriterError
} from "./index";

const TableEntity = t.type({
Expand All @@ -33,30 +34,13 @@ const ProfileEmailToTableEntity = new t.Type<ProfileEmail, TableEntity>(
})
);

const isRestError = (u: unknown): u is RestError =>
u instanceof Error && u.name === "RestError";

export class DataTableProfileEmailsRepository
implements IProfileEmailReader, IProfileEmailWriter {
constructor(private readonly tableClient: TableClient) {}

public async get(p: ProfileEmail): Promise<ProfileEmail> {
try {
const entity = await this.tableClient.getEntity(
p.email.toLowerCase(),
p.fiscalCode
);
const profileEmail = ProfileEmailToTableEntity.decode(entity);
if (E.isLeft(profileEmail)) {
throw new Error(`can't parse a profile email from the given entity`, {
cause: "parsing"
});
}
return profileEmail.right;
} catch {
throw new Error(
`unable to get a profile entity from ${this.tableClient.tableName} table`
);
}
}

// Generates an AsyncIterable<ProfileEmail>
public async *list(filter: EmailString): AsyncIterableIterator<ProfileEmail> {
const queryOptions = {
Expand Down Expand Up @@ -89,9 +73,12 @@ export class DataTableProfileEmailsRepository
try {
const entity = ProfileEmailToTableEntity.encode(p);
await this.tableClient.createEntity(entity);
} catch {
throw new Error(
`unable to insert a new profile entity into ${this.tableClient.tableName} table`
} catch (e) {
throw new ProfileEmailWriterError(
`unable to insert a new profile entity into ${this.tableClient.tableName} table`,
isRestError(e) && e.statusCode === 409
? "DUPLICATE_ENTITY"
: "STORAGE_ERROR"
);
}
}
Expand All @@ -100,9 +87,14 @@ export class DataTableProfileEmailsRepository
try {
const entity = ProfileEmailToTableEntity.encode(p);
await this.tableClient.deleteEntity(entity.partitionKey, entity.rowKey);
} catch {
throw new Error(
`unable to delete the specified entity from ${this.tableClient.tableName} table`
} catch (e) {
throw new ProfileEmailWriterError(
`unable to delete the specified entity from ${this.tableClient.tableName} table`,
isRestError(e) &&
e.statusCode === 404 &&
e.message.includes(`"ResourceNotFound"`)
? "ENTITY_NOT_FOUND"
: "STORAGE_ERROR"
);
}
}
Expand Down

0 comments on commit 70abae2

Please sign in to comment.