generated from pagopa/io-template-typescript
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fb48327
commit e990a4a
Showing
7 changed files
with
1,541 additions
and
1,452 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { TableEntityResult } from "@azure/data-tables"; | ||
import { describe, test, expect } from "@jest/globals"; | ||
|
||
import { EmailString, FiscalCode } from "@pagopa/ts-commons/lib/strings"; | ||
|
||
import { isEmailAlreadyTaken } from "../index"; | ||
|
||
const mocks = { | ||
email: "citizen@email.test.pagopa.it" as EmailString | ||
}; | ||
|
||
jest.mock("@azure/data-tables", () => ({ | ||
default: { | ||
TableClient: jest.fn().mockImplementation(() => { | ||
return { | ||
listEntities: async function*() { | ||
yield { | ||
partitionKey: "citizen@email.test.pagopa.it", | ||
rowKey: "AAAAAA00A00A000A" | ||
}; | ||
yield { | ||
partitionKey: "citizen@email.test.pagopa.it", | ||
rowKey: "AAAAAA00A00A000A" | ||
}; | ||
} | ||
}; | ||
}) | ||
} | ||
})); | ||
|
||
function generateProfileEmails(count: number) { | ||
return async function*(email: EmailString) { | ||
for (let i = 0; i < count; i++) { | ||
yield { email, fiscalCode: "X" as FiscalCode }; | ||
} | ||
}; | ||
} | ||
|
||
describe("isEmailAlreadyTaken", () => { | ||
test.each([ | ||
{ | ||
entries: 0, | ||
expected: false | ||
}, | ||
{ | ||
entries: 1, | ||
expected: false | ||
}, | ||
{ | ||
entries: 100, | ||
expected: true | ||
}, | ||
{ | ||
entries: 2, | ||
expected: true | ||
} | ||
])( | ||
`${mocks.email} is used by $entries profiles, so isEmailAlreadyTaken should be $expected`, | ||
({ entries, expected }) => { | ||
const result = isEmailAlreadyTaken(mocks.email)({ | ||
profileEmailReader: { | ||
profileEmails: generateProfileEmails(entries) | ||
} | ||
}); | ||
expect(result).resolves.toBe(expected); | ||
} | ||
); | ||
}); |
28 changes: 28 additions & 0 deletions
28
src/utils/unique_email_enforcement/__test__/storage.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { describe, it, expect } from "@jest/globals"; | ||
|
||
import { TableEntityResult } from "@azure/data-tables"; | ||
import { toProfileEmailsAsyncIterator } from "../storage"; | ||
|
||
describe("toProfileEmailsAsyncIterator", () => { | ||
it("emits only valid entities", async () => { | ||
async function* listEntities(): AsyncIterableIterator< | ||
TableEntityResult<unknown> | ||
> { | ||
yield { | ||
partitionKey: "citizen@email.test.pagopa.it", | ||
rowKey: "AAAAAA00A00A000A", | ||
etag: "etag" | ||
}; | ||
yield { | ||
partitionKey: "not-a-valid-email", | ||
rowKey: "AAAAAA00A00A000A", | ||
etag: "etag" | ||
}; | ||
} | ||
let count = 0; | ||
for await (const _ of toProfileEmailsAsyncIterator(listEntities())) { | ||
count++; | ||
} | ||
expect(count).toBe(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { EmailString, FiscalCode } from "@pagopa/ts-commons/lib/strings"; | ||
|
||
import * as t from "io-ts"; | ||
|
||
export const ProfileEmail = t.type({ | ||
email: EmailString, | ||
fiscalCode: FiscalCode | ||
}); | ||
|
||
export type ProfileEmail = t.TypeOf<typeof ProfileEmail>; | ||
|
||
export interface ProfileEmailReader { | ||
profileEmails(email: EmailString): AsyncIterableIterator<ProfileEmail>; | ||
} | ||
|
||
export interface ProfileEmailWriter { | ||
delete(profileEmail: ProfileEmail): Promise<void>; | ||
insert(profileEmail: ProfileEmail): Promise<void>; | ||
} | ||
|
||
interface IsEmailAlreadyTakenDependencies { | ||
readonly profileEmailReader: ProfileEmailReader; | ||
} | ||
|
||
// 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. | ||
export const isEmailAlreadyTaken = (email: EmailString) => async ({ | ||
profileEmailReader: { profileEmails } | ||
}: IsEmailAlreadyTakenDependencies): Promise<boolean> => { | ||
let count = 0; | ||
for await (const _ of profileEmails(email)) { | ||
if (++count > 1) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { TableClient, odata, TableEntityResult } from "@azure/data-tables"; | ||
import { EmailString } from "@pagopa/ts-commons/lib/strings"; | ||
|
||
import { ProfileEmail, ProfileEmailReader, ProfileEmailWriter } from "./index"; | ||
|
||
// Generates AsyncIterable<ProfileEmail> from AsyncIterable<TableEntityResult> | ||
export async function* toProfileEmailsAsyncIterator( | ||
iterator: AsyncIterableIterator<TableEntityResult<unknown>> | ||
): AsyncIterableIterator<ProfileEmail> { | ||
for await (const item of iterator) { | ||
const uniqueEmail = ProfileEmail.decode({ | ||
email: item.partitionKey, | ||
fiscalCode: item.rowKey | ||
}); | ||
if (uniqueEmail._tag === "Right") { | ||
yield uniqueEmail.right; | ||
} | ||
} | ||
} | ||
|
||
export class DataTableProfileEmailsRepository | ||
implements ProfileEmailReader, ProfileEmailWriter { | ||
constructor(private tableClient: TableClient) {} | ||
|
||
// Generates an AsyncIterable<ProfileEmail> | ||
async *profileEmails(email: EmailString) { | ||
return toProfileEmailsAsyncIterator( | ||
this.tableClient.listEntities({ | ||
queryOptions: { | ||
filter: odata`partitionKey eq ${email}` | ||
} | ||
}) | ||
); | ||
} | ||
|
||
async insert(p: ProfileEmail) { | ||
try { | ||
await this.tableClient.createEntity({ | ||
partitionKey: p.email, | ||
rowKey: p.fiscalCode | ||
}); | ||
} catch (cause) { | ||
throw new Error("error inserting ProfileEmail into table storage", { | ||
cause | ||
}); | ||
} | ||
} | ||
|
||
async delete(p: ProfileEmail) { | ||
try { | ||
await this.tableClient.deleteEntity(p.email, p.fiscalCode); | ||
} catch (cause) { | ||
throw new Error("error deleting ProfileEmail from table storage", { | ||
cause | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.