From 7d71b815de8de4c180e997d7773fddd1f8e233f9 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Sun, 3 Mar 2024 21:26:16 -0500 Subject: [PATCH 1/8] feat: add RepoQuerierLocal to connect dashboard to repository --- .../dashboard-api/adapters/drivens/index.ts | 3 ++- .../adapters/drivens/repo-querier-local-adapter.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/services/dashboard-api/adapters/drivens/repo-querier-local-adapter.ts diff --git a/src/services/dashboard-api/adapters/drivens/index.ts b/src/services/dashboard-api/adapters/drivens/index.ts index 8bbd5a5..2b77826 100644 --- a/src/services/dashboard-api/adapters/drivens/index.ts +++ b/src/services/dashboard-api/adapters/drivens/index.ts @@ -1,2 +1,3 @@ export * from './control-authenticator-stub-adapter'; -export * from './repo-querier-stub-adapter'; +export * from './repo-querier-local-adapter'; +export * from './repo-querier-stub-adapter'; \ No newline at end of file diff --git a/src/services/dashboard-api/adapters/drivens/repo-querier-local-adapter.ts b/src/services/dashboard-api/adapters/drivens/repo-querier-local-adapter.ts new file mode 100644 index 0000000..e9fba09 --- /dev/null +++ b/src/services/dashboard-api/adapters/drivens/repo-querier-local-adapter.ts @@ -0,0 +1,14 @@ +import { userManagerProxy } from "../../../repository/app/composition-root"; +import { ExternalUser } from "../../../repository/app/schemas"; +import { User } from "../../app/schemas"; +import { ForRepoQuerying } from "../../ports/drivens"; + +export class RepoQuerierLocalAdapter implements ForRepoQuerying { + async getUser(email: string): Promise { + return await userManagerProxy.getUser(email) + } + + async createUser(user: User): Promise { + return await userManagerProxy.createUser(user) + } +} From 59d2b40cb389ffe3a43d0e8f43398dbbd646e6f6 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Sun, 3 Mar 2024 21:27:43 -0500 Subject: [PATCH 2/8] feat: implement RepoQueryLocal on localTRPCCompose --- src/services/dashboard-api/app/composition-root.ts | 7 ++++--- src/services/repository/app/composition-root.ts | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/dashboard-api/app/composition-root.ts b/src/services/dashboard-api/app/composition-root.ts index a99c50c..93a9ead 100644 --- a/src/services/dashboard-api/app/composition-root.ts +++ b/src/services/dashboard-api/app/composition-root.ts @@ -6,6 +6,7 @@ import { authTRPCAdapter, } from "../adapters/drivers"; import { initTRPC } from "@trpc/server"; +import { RepoQuerierLocalAdapter } from "../adapters/drivens"; const compositionMock = () => { // DRIVENS @@ -29,7 +30,7 @@ const compositionMock = () => { }; export const { authenticatorProxyAdapter } = compositionMock(); -/* +/* const registerMock = { name: "John", email: "jhon@gmail.com", @@ -42,12 +43,12 @@ authenticatorProxyAdapter.register(registerMock); export const localTRPCCompose = () => { // DRIVENS const controlAuthenticatorStub = new ControlAuthenticatorStub(); - const repoQuerierStub = new RepoQuerierStub(); + const repoQuerierLocal = new RepoQuerierLocalAdapter(); // APP const dashboardApiMock = new DashboardApi( controlAuthenticatorStub, - repoQuerierStub + repoQuerierLocal ); // TRPC INSTANCE diff --git a/src/services/repository/app/composition-root.ts b/src/services/repository/app/composition-root.ts index 9c6ad26..6206153 100644 --- a/src/services/repository/app/composition-root.ts +++ b/src/services/repository/app/composition-root.ts @@ -12,3 +12,5 @@ export const compositionMock = () => { userManagerProxy, }; }; + +export const { userManagerProxy } = compositionMock(); \ No newline at end of file From 72ae996e58469b6be8a366b86eba9384935f9247 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Mon, 4 Mar 2024 23:02:09 -0500 Subject: [PATCH 3/8] feat: add user permission managing new changes allows to create and get permissions --- .../repository/adapters/drivers/index.ts | 1 + .../adapters/drivers/user-manager-proxy.ts | 6 +- .../drivers/user-permission-manager-proxy.ts | 15 ++++ .../repository/app/composition-root.ts | 5 +- .../repository/app/repository.test.ts | 73 +++++++++++++++++++ src/services/repository/app/repository.ts | 51 ++++++++++++- src/services/repository/app/schemas/auth.ts | 11 +++ src/services/repository/app/schemas/index.ts | 3 +- .../ports/drivers/for-managing-permission.ts | 6 ++ .../ports/drivers/for-managing-user.ts | 3 +- 10 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 src/services/repository/adapters/drivers/user-permission-manager-proxy.ts create mode 100644 src/services/repository/app/schemas/auth.ts create mode 100644 src/services/repository/ports/drivers/for-managing-permission.ts diff --git a/src/services/repository/adapters/drivers/index.ts b/src/services/repository/adapters/drivers/index.ts index e7e16fd..5c46b06 100644 --- a/src/services/repository/adapters/drivers/index.ts +++ b/src/services/repository/adapters/drivers/index.ts @@ -1 +1,2 @@ export * from './user-manager-proxy'; +export * from './user-permission-manager-proxy'; \ No newline at end of file diff --git a/src/services/repository/adapters/drivers/user-manager-proxy.ts b/src/services/repository/adapters/drivers/user-manager-proxy.ts index 411b2ed..2c4919a 100644 --- a/src/services/repository/adapters/drivers/user-manager-proxy.ts +++ b/src/services/repository/adapters/drivers/user-manager-proxy.ts @@ -1,5 +1,5 @@ import { Repository } from "../../app/repository"; -import { ExternalUser, User } from "../../app/schemas"; +import { ExternalUser, Permissions, RepoUser, User, UserPermission } from "../../app/schemas"; import { ForManagingUser } from "../../ports/drivers"; export class UserManagerProxy implements ForManagingUser { @@ -12,4 +12,8 @@ export class UserManagerProxy implements ForManagingUser { async createUser(user: User): Promise { return this.repository.createUser(user); } + + async getInternalUser(email: string): Promise { + return this.repository.getInternalUser(email); + } } diff --git a/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts b/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts new file mode 100644 index 0000000..f96d95a --- /dev/null +++ b/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts @@ -0,0 +1,15 @@ +import { Repository } from "../../app/repository"; +import { Permissions, UserPermission } from "../../app/schemas"; +import { ForManagingPermission } from "../../ports/drivers/for-managing-permission"; + +export class UserPermissionManagerProxy implements ForManagingPermission { + constructor(private readonly repository: Repository) {} + + async getUserPermissions(userId: string): Promise { + return this.repository.getUserPermissions(userId) + } + + async createUserPermissions(userId: string, permissions: Permissions): Promise { + return this.repository.createUserPermissions(userId, permissions) + } +} diff --git a/src/services/repository/app/composition-root.ts b/src/services/repository/app/composition-root.ts index 6206153..750ceda 100644 --- a/src/services/repository/app/composition-root.ts +++ b/src/services/repository/app/composition-root.ts @@ -1,4 +1,5 @@ import { LoggerStubAdapter } from "../adapters/drivens"; +import { UserPermissionManagerProxy } from "../adapters/drivers"; import { UserManagerProxy } from "./../adapters/drivers/user-manager-proxy"; import { Repository } from "./repository"; @@ -7,10 +8,12 @@ export const compositionMock = () => { const repositoryMock = new Repository(monitorStub); const userManagerProxy = new UserManagerProxy(repositoryMock); + const userPermissionManagerProxy = new UserPermissionManagerProxy(repositoryMock); return { userManagerProxy, + userPermissionManagerProxy }; }; -export const { userManagerProxy } = compositionMock(); \ No newline at end of file +export const { userManagerProxy, userPermissionManagerProxy } = compositionMock(); \ No newline at end of file diff --git a/src/services/repository/app/repository.test.ts b/src/services/repository/app/repository.test.ts index 9574a5e..f3e8582 100644 --- a/src/services/repository/app/repository.test.ts +++ b/src/services/repository/app/repository.test.ts @@ -93,4 +93,77 @@ describe("Repository", () => { //THEN expect(result).toEqual(expectedResult); }); + + it.concurrent("should get internal user", async () => { + // GIVEN + const mockedEmail = "samuelcito@gmail.com"; + + const expectedResult = { + id: "1", + name: "Samuel", + email: "samuelcito@gmail.com", + password: 'password' + }; + + // WHEN + const result = await repositoryMock.getInternalUser(mockedEmail); + + // THEN + expect(result).toEqual(expectedResult); + }); + + it.concurrent("should create user permissions", async () => { + // GIVEN + const mockedUserId = "1"; + const mockedPermissions = { + admin: true, + user: true + } + + const expectedResult = { + id: "1", + userId: '1', + admin: true, + user: true + }; + + // WHEN + const result = await repositoryMock.createUserPermissions(mockedUserId, mockedPermissions); + + // THEN + expect(result).toEqual(expectedResult); + }); + + it.concurrent("should get user permissions", async () => { + // GIVEN + const mockedUserId = "1"; + + const expectedResult = { + id: "1", + userId: '1', + admin: true, + user: true + }; + + // WHEN + const result = await repositoryMock.getUserPermissions(mockedUserId); + + // THEN + expect(result).toEqual(expectedResult); + }); + + it.concurrent( + "should throw error when permissions does not exists for user", + async () => { + // GIVEN + const mockUserId = "2"; + + // WHEN + + // THEN + await expect( + repositoryMock.getUserPermissions(mockUserId) + ).rejects.toThrowError("Permissions not found"); + } + ); }); diff --git a/src/services/repository/app/repository.ts b/src/services/repository/app/repository.ts index 6be4ce5..e962bc5 100644 --- a/src/services/repository/app/repository.ts +++ b/src/services/repository/app/repository.ts @@ -1,9 +1,17 @@ import { ForMonitoring } from "../ports/drivens"; import { ForManagingUser } from "../ports/drivers"; -import { ExternalUser, RepoUser, User } from "./schemas"; +import { ForManagingPermission } from "../ports/drivers/for-managing-permission"; +import { + ExternalUser, + Permissions, + RepoUser, + User, + UserPermission, +} from "./schemas"; -export class Repository implements ForManagingUser { +export class Repository implements ForManagingUser, ForManagingPermission { private userList: RepoUser[] = []; + private userPermissionList: UserPermission[] = []; constructor(private readonly logger: ForMonitoring) {} async getUser(email: string): Promise { @@ -37,4 +45,43 @@ export class Repository implements ForManagingUser { email: newUser.email, }; } + + async getInternalUser(email: string): Promise { + const user = this.userList.find((user) => user.email === email); + if (!user) { + this.logger.log("GetInternalUser", "User not found"); + throw new Error("User not found"); + } + + return user; + } + + async getUserPermissions(userId: string): Promise { + const userPermission = this.userPermissionList.find( + (userPermission) => userPermission.userId === userId + ); + + if (!userPermission) { + this.logger.log("GetUserPermissions", "Permissions not found"); + throw new Error("Permissions not found"); + } + + return userPermission; + } + + async createUserPermissions( + userId: string, + permissions: Permissions + ): Promise { + const newUserPermission: UserPermission = { + id: String(this.userList.length + 1), + userId, + admin: permissions.admin, + user: permissions.user, + }; + + this.userPermissionList.push(newUserPermission); + + return newUserPermission; + } } diff --git a/src/services/repository/app/schemas/auth.ts b/src/services/repository/app/schemas/auth.ts new file mode 100644 index 0000000..87a912d --- /dev/null +++ b/src/services/repository/app/schemas/auth.ts @@ -0,0 +1,11 @@ +export interface UserPermission { + id: string + userId: string + admin: boolean + user: boolean +} + +export interface Permissions { + admin: boolean, + user: boolean +} \ No newline at end of file diff --git a/src/services/repository/app/schemas/index.ts b/src/services/repository/app/schemas/index.ts index e5abc85..fd7d4c3 100644 --- a/src/services/repository/app/schemas/index.ts +++ b/src/services/repository/app/schemas/index.ts @@ -1 +1,2 @@ -export * from './user'; +export * from './auth'; +export * from './user'; \ No newline at end of file diff --git a/src/services/repository/ports/drivers/for-managing-permission.ts b/src/services/repository/ports/drivers/for-managing-permission.ts new file mode 100644 index 0000000..865b5cd --- /dev/null +++ b/src/services/repository/ports/drivers/for-managing-permission.ts @@ -0,0 +1,6 @@ +import { Permissions, UserPermission } from "../../app/schemas"; + +export interface ForManagingPermission { + getUserPermissions(userId: string): Promise + createUserPermissions(userId: string, permissions: Permissions): Promise +} diff --git a/src/services/repository/ports/drivers/for-managing-user.ts b/src/services/repository/ports/drivers/for-managing-user.ts index d6e941f..77cb7df 100644 --- a/src/services/repository/ports/drivers/for-managing-user.ts +++ b/src/services/repository/ports/drivers/for-managing-user.ts @@ -1,6 +1,7 @@ -import { ExternalUser, User } from "../../app/schemas"; +import { ExternalUser, Permissions, RepoUser, User, UserPermission } from "../../app/schemas"; export interface ForManagingUser { getUser(email: string): Promise; createUser(user: User, password: string): Promise; + getInternalUser(email: string): Promise } From bf70f02a80910f708c3c7f363d00a79daae7b53b Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Mon, 4 Mar 2024 23:02:34 -0500 Subject: [PATCH 4/8] feat: add control plane and tests --- .../control-plane/adapters/drivens/index.ts | 3 ++ .../adapters/drivens/logger-stub.ts | 7 +++ .../repo-querier-local-adapter copy.ts | 21 +++++++++ .../drivens/repo-querier-stub-adapter.ts | 18 ++++++++ .../drivers/auth-manager-proxy-adapter.ts | 15 +++++++ .../control-plane/adapters/drivers/index.ts | 1 + .../control-plane/app/composition-root.ts | 15 +++++++ .../control-plane/app/control-plane.test.ts | 40 +++++++++++++++++ .../control-plane/app/control-plane.ts | 43 +++++++++++++++++++ .../control-plane/app/schemas/auth.ts | 16 +++++++ .../ports/drivens/for-logging.ts | 3 ++ .../ports/drivens/for-repo-querying.ts | 7 +++ .../control-plane/ports/drivens/index.ts | 1 + .../drivers/for-managing-authentication.ts | 6 +++ .../control-plane/ports/drivers/index.ts | 1 + 15 files changed, 197 insertions(+) create mode 100644 src/services/control-plane/adapters/drivens/index.ts create mode 100644 src/services/control-plane/adapters/drivens/logger-stub.ts create mode 100644 src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts create mode 100644 src/services/control-plane/adapters/drivens/repo-querier-stub-adapter.ts create mode 100644 src/services/control-plane/adapters/drivers/auth-manager-proxy-adapter.ts create mode 100644 src/services/control-plane/adapters/drivers/index.ts create mode 100644 src/services/control-plane/app/composition-root.ts create mode 100644 src/services/control-plane/app/control-plane.test.ts create mode 100644 src/services/control-plane/app/control-plane.ts create mode 100644 src/services/control-plane/app/schemas/auth.ts create mode 100644 src/services/control-plane/ports/drivens/for-logging.ts create mode 100644 src/services/control-plane/ports/drivens/for-repo-querying.ts create mode 100644 src/services/control-plane/ports/drivens/index.ts create mode 100644 src/services/control-plane/ports/drivers/for-managing-authentication.ts create mode 100644 src/services/control-plane/ports/drivers/index.ts diff --git a/src/services/control-plane/adapters/drivens/index.ts b/src/services/control-plane/adapters/drivens/index.ts new file mode 100644 index 0000000..313c5db --- /dev/null +++ b/src/services/control-plane/adapters/drivens/index.ts @@ -0,0 +1,3 @@ +export * from './logger-stub'; +export * from './repo-querier-local-adapter copy'; +export * from './repo-querier-stub-adapter'; \ No newline at end of file diff --git a/src/services/control-plane/adapters/drivens/logger-stub.ts b/src/services/control-plane/adapters/drivens/logger-stub.ts new file mode 100644 index 0000000..b1ead28 --- /dev/null +++ b/src/services/control-plane/adapters/drivens/logger-stub.ts @@ -0,0 +1,7 @@ +import { ForLogging } from "../../ports/drivens/for-logging"; + +export class LoggerStub implements ForLogging { + log(event: string, message: string): void { + console.log(event, message); + } +} diff --git a/src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts b/src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts new file mode 100644 index 0000000..cf2a444 --- /dev/null +++ b/src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts @@ -0,0 +1,21 @@ +import { userManagerProxy, userPermissionManagerProxy } from "../../../repository/app/composition-root"; +import { RepoUser } from "../../../repository/app/schemas"; +import { Permissions } from "../../app/schemas/auth"; +import { ForRepoQuerying } from "../../ports/drivens"; + +export class RepoQuerierStubAdapter implements ForRepoQuerying { + async getUser(email: string): Promise { + const user = userManagerProxy.getInternalUser(email) + + return user; + } + + async getPermissions(userId: string): Promise { + const userPermissions = await userPermissionManagerProxy.getUserPermissions(userId) + + return { + admin: userPermissions.admin, + user: userPermissions.user + } + } +} diff --git a/src/services/control-plane/adapters/drivens/repo-querier-stub-adapter.ts b/src/services/control-plane/adapters/drivens/repo-querier-stub-adapter.ts new file mode 100644 index 0000000..161c72f --- /dev/null +++ b/src/services/control-plane/adapters/drivens/repo-querier-stub-adapter.ts @@ -0,0 +1,18 @@ +import { RepoUser } from "../../../repository/app/schemas"; +import { UserPermissions } from "../../app/schemas/auth"; +import { ForRepoQuerying } from "../../ports/drivens"; + +export class RepoQuerierStubAdapter implements ForRepoQuerying { + async getUser(_email: string): Promise { + return { + id: "1", + name: "John Doe", + email: "john@gmail.com", + password: "123", + }; + } + + async getPermissions(_userId: string): Promise { + return Promise.resolve({ id: "1", userId: "1", admin: true, user: true }); + } +} diff --git a/src/services/control-plane/adapters/drivers/auth-manager-proxy-adapter.ts b/src/services/control-plane/adapters/drivers/auth-manager-proxy-adapter.ts new file mode 100644 index 0000000..02eab0f --- /dev/null +++ b/src/services/control-plane/adapters/drivers/auth-manager-proxy-adapter.ts @@ -0,0 +1,15 @@ +import { ControlPlane } from "../../app/control-plane"; +import { AuthDetails, Permissions } from "../../app/schemas/auth"; +import { ForManagingAuthentication } from "../../ports/drivers"; + +export class AuthManagerProxyAdapter implements ForManagingAuthentication { + constructor(private readonly controlPlane: ControlPlane) {} + + async getAuthDetails(email: string, password: string): Promise { + return this.controlPlane.getAuthDetails(email, password); + } + + async getPermissions(userId: string): Promise { + return this.controlPlane.getPermissions(userId); + } +} diff --git a/src/services/control-plane/adapters/drivers/index.ts b/src/services/control-plane/adapters/drivers/index.ts new file mode 100644 index 0000000..60db67d --- /dev/null +++ b/src/services/control-plane/adapters/drivers/index.ts @@ -0,0 +1 @@ +export * from './auth-manager-proxy-adapter'; \ No newline at end of file diff --git a/src/services/control-plane/app/composition-root.ts b/src/services/control-plane/app/composition-root.ts new file mode 100644 index 0000000..95286e3 --- /dev/null +++ b/src/services/control-plane/app/composition-root.ts @@ -0,0 +1,15 @@ +import { LoggerStub, RepoQuerierStubAdapter } from "../adapters/drivens"; +import { AuthManagerProxyAdapter } from "../adapters/drivers"; +import { ControlPlane } from "./control-plane"; + +const compositionMock = () => { + const repoQuerierstub = new RepoQuerierStubAdapter(); + const loggerStub = new LoggerStub(); + const controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + + const authManagerProxy = new AuthManagerProxyAdapter(controlPlane); + + return { authManagerProxy }; +}; + +export const { authManagerProxy } = compositionMock(); diff --git a/src/services/control-plane/app/control-plane.test.ts b/src/services/control-plane/app/control-plane.test.ts new file mode 100644 index 0000000..1df7bcf --- /dev/null +++ b/src/services/control-plane/app/control-plane.test.ts @@ -0,0 +1,40 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { LoggerStub, RepoQuerierStubAdapter } from "../adapters/drivens"; +import { ControlPlane } from "./control-plane"; + +describe("ControlPlane", () => { + const repoQuerierstub = new RepoQuerierStubAdapter(); + const loggerStub = new LoggerStub(); + let controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + + beforeEach(() => { + controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + }); + + it.concurrent("should get auth details", async () => { + // GIVEN + const mockEmail = "john@gmail.com"; + const mockPassword = "123"; + + const resultExpected = { token: "token", refreshToken: "refreshToken" }; + + // WHEN + const result = await controlPlane.getAuthDetails(mockEmail, mockPassword); + + // THEN + expect(result).toEqual(resultExpected); + }); + + it.concurrent("should get permissions", async () => { + // GIVEN + const mockUserId = "1"; + + const resultExpected = { admin: true, user: true }; + + // WHEN + const result = await controlPlane.getPermissions(mockUserId); + + // THEN + expect(result).toEqual(resultExpected); + }); +}); diff --git a/src/services/control-plane/app/control-plane.ts b/src/services/control-plane/app/control-plane.ts new file mode 100644 index 0000000..038051a --- /dev/null +++ b/src/services/control-plane/app/control-plane.ts @@ -0,0 +1,43 @@ +import { ForLogging } from "../ports/drivens/for-logging"; +import { ForRepoQuerying } from "../ports/drivens/for-repo-querying"; +import { ForManagingAuthentication } from "../ports/drivers"; +import { AuthDetails, Permissions } from "./schemas/auth"; + +export class ControlPlane implements ForManagingAuthentication { + constructor( + private readonly repoQuerier: ForRepoQuerying, + private readonly logger: ForLogging + ) {} + + async getAuthDetails(email: string, password: string): Promise { + const user = await this.repoQuerier.getUser(email); + + if (!user) { + this.logger.log("GetAuthDetails", "Wrong email"); + throw new Error("Wrong email"); + } + + if (password !== user.password) { + this.logger.log("GetAuthDetails", "Wrong password"); + throw new Error("Wrong password"); + } + + return { token: "token", refreshToken: "refreshToken" }; + } + + async getPermissions(userId: string): Promise { + const userPermissions = await this.repoQuerier.getPermissions(userId); + + if (!userPermissions) { + this.logger.log("GetPermissions", "Permissions not found"); + throw new Error("Permissions not found"); + } + + const permissions = { + admin: userPermissions.admin, + user: userPermissions.user, + }; + + return permissions; + } +} diff --git a/src/services/control-plane/app/schemas/auth.ts b/src/services/control-plane/app/schemas/auth.ts new file mode 100644 index 0000000..7533484 --- /dev/null +++ b/src/services/control-plane/app/schemas/auth.ts @@ -0,0 +1,16 @@ +export interface UserPermissions { + id: string; + userId: string; + admin: boolean; + user: boolean; +} + +export interface AuthDetails { + token: string; + refreshToken: string; +} + +export interface Permissions { + admin: boolean; + user: boolean; +} diff --git a/src/services/control-plane/ports/drivens/for-logging.ts b/src/services/control-plane/ports/drivens/for-logging.ts new file mode 100644 index 0000000..0f5e39b --- /dev/null +++ b/src/services/control-plane/ports/drivens/for-logging.ts @@ -0,0 +1,3 @@ +export interface ForLogging { + log(event: string, message: string): void; +} diff --git a/src/services/control-plane/ports/drivens/for-repo-querying.ts b/src/services/control-plane/ports/drivens/for-repo-querying.ts new file mode 100644 index 0000000..fd87409 --- /dev/null +++ b/src/services/control-plane/ports/drivens/for-repo-querying.ts @@ -0,0 +1,7 @@ +import { RepoUser } from "../../../repository/app/schemas"; +import { Permissions } from "../../app/schemas/auth"; + +export interface ForRepoQuerying { + getUser(email: string): Promise; + getPermissions(userId: string): Promise; +} diff --git a/src/services/control-plane/ports/drivens/index.ts b/src/services/control-plane/ports/drivens/index.ts new file mode 100644 index 0000000..3285e5a --- /dev/null +++ b/src/services/control-plane/ports/drivens/index.ts @@ -0,0 +1 @@ +export * from "./for-repo-querying"; diff --git a/src/services/control-plane/ports/drivers/for-managing-authentication.ts b/src/services/control-plane/ports/drivers/for-managing-authentication.ts new file mode 100644 index 0000000..d70c683 --- /dev/null +++ b/src/services/control-plane/ports/drivers/for-managing-authentication.ts @@ -0,0 +1,6 @@ +import { AuthDetails, Permissions } from "../../app/schemas/auth"; + +export interface ForManagingAuthentication { + getAuthDetails(email: string, password: string): Promise; + getPermissions(userId: string): Promise; +} diff --git a/src/services/control-plane/ports/drivers/index.ts b/src/services/control-plane/ports/drivers/index.ts new file mode 100644 index 0000000..6ab0d1d --- /dev/null +++ b/src/services/control-plane/ports/drivers/index.ts @@ -0,0 +1 @@ +export * from "./for-managing-authentication"; From 07af7a1fe121a44c95d2b5c5adc8113d4f957103 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Mon, 4 Mar 2024 23:00:11 -0500 Subject: [PATCH 5/8] feat(auth): update getPermissions method signature Modify the Auth interface contract to update the signature of the getPermissions method. Now it accepts only the userId parameter instead of (email, password) for more secure and efficient authentication handling. BREAKING CHANGE: The getPermissions method signature has been updated to accept only the userId parameter instead of (email, password). --- .../drivens/control-authenticator-stub-adapter.ts | 2 +- src/services/dashboard-api/app/dashboard-api.ts | 10 ++++------ .../ports/drivens/for-control-authenticating.ts | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/services/dashboard-api/adapters/drivens/control-authenticator-stub-adapter.ts b/src/services/dashboard-api/adapters/drivens/control-authenticator-stub-adapter.ts index 1bd80a4..5434ad2 100644 --- a/src/services/dashboard-api/adapters/drivens/control-authenticator-stub-adapter.ts +++ b/src/services/dashboard-api/adapters/drivens/control-authenticator-stub-adapter.ts @@ -16,7 +16,7 @@ export class ControlAuthenticatorStub implements ForControlAuthenticating { return Promise.resolve(authDetailsMock); } - getPermissions(_email: string, _password: string): Promise { + getPermissions(_userId: string): Promise { return Promise.resolve(permissionsMock); } } diff --git a/src/services/dashboard-api/app/dashboard-api.ts b/src/services/dashboard-api/app/dashboard-api.ts index 20de0bc..31ef6ec 100644 --- a/src/services/dashboard-api/app/dashboard-api.ts +++ b/src/services/dashboard-api/app/dashboard-api.ts @@ -14,11 +14,10 @@ export class DashboardApi implements ForAuthenticating { email, password ); - const permissions = await this.controlAuthenticator.getPermissions( - email, - password - ); const user = await this.repoQuerier.getUser(email); + + const permissions = await this.controlAuthenticator.getPermissions(user.id); + const result = { ...user, ...authDetails, @@ -37,8 +36,7 @@ export class DashboardApi implements ForAuthenticating { user.password ); const permissions = await this.controlAuthenticator.getPermissions( - user.email, - user.password + newUser.id ); const result = { diff --git a/src/services/dashboard-api/ports/drivens/for-control-authenticating.ts b/src/services/dashboard-api/ports/drivens/for-control-authenticating.ts index 64130c5..dc850c1 100644 --- a/src/services/dashboard-api/ports/drivens/for-control-authenticating.ts +++ b/src/services/dashboard-api/ports/drivens/for-control-authenticating.ts @@ -2,5 +2,5 @@ import { AuthDetails, Permissions } from "../../app/schemas"; export interface ForControlAuthenticating { getAuthDetails(email: string, password: string): Promise; - getPermissions(email: string, password: string): Promise; + getPermissions(userId: string): Promise; } From 50abc84443dcb28d28d2eb77cbd550a297a8d057 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Wed, 6 Mar 2024 11:05:12 -0500 Subject: [PATCH 6/8] fix: rename RepoQuerierLocalAdapter --- src/services/control-plane/adapters/drivens/index.ts | 2 +- ...rier-local-adapter copy.ts => repo-querier-local-adapter.ts} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/services/control-plane/adapters/drivens/{repo-querier-local-adapter copy.ts => repo-querier-local-adapter.ts} (90%) diff --git a/src/services/control-plane/adapters/drivens/index.ts b/src/services/control-plane/adapters/drivens/index.ts index 313c5db..08761c2 100644 --- a/src/services/control-plane/adapters/drivens/index.ts +++ b/src/services/control-plane/adapters/drivens/index.ts @@ -1,3 +1,3 @@ export * from './logger-stub'; -export * from './repo-querier-local-adapter copy'; +export * from './repo-querier-local-adapter'; export * from './repo-querier-stub-adapter'; \ No newline at end of file diff --git a/src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts b/src/services/control-plane/adapters/drivens/repo-querier-local-adapter.ts similarity index 90% rename from src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts rename to src/services/control-plane/adapters/drivens/repo-querier-local-adapter.ts index cf2a444..0dd9e82 100644 --- a/src/services/control-plane/adapters/drivens/repo-querier-local-adapter copy.ts +++ b/src/services/control-plane/adapters/drivens/repo-querier-local-adapter.ts @@ -3,7 +3,7 @@ import { RepoUser } from "../../../repository/app/schemas"; import { Permissions } from "../../app/schemas/auth"; import { ForRepoQuerying } from "../../ports/drivens"; -export class RepoQuerierStubAdapter implements ForRepoQuerying { +export class RepoQuerierLocalAdapter implements ForRepoQuerying { async getUser(email: string): Promise { const user = userManagerProxy.getInternalUser(email) From df311a464f2aeb3de4c70bec4e91bd4bbf15c5ca Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Wed, 6 Mar 2024 11:20:16 -0500 Subject: [PATCH 7/8] fix: remove unnecesary logger --- .../control-plane/adapters/drivens/index.ts | 1 - .../adapters/drivens/logger-stub.ts | 7 ------- .../control-plane/app/composition-root.ts | 5 ++--- .../control-plane/app/control-plane.test.ts | 7 +++---- src/services/control-plane/app/control-plane.ts | 17 ++++++----------- .../control-plane/ports/drivens/for-logging.ts | 3 --- 6 files changed, 11 insertions(+), 29 deletions(-) delete mode 100644 src/services/control-plane/adapters/drivens/logger-stub.ts delete mode 100644 src/services/control-plane/ports/drivens/for-logging.ts diff --git a/src/services/control-plane/adapters/drivens/index.ts b/src/services/control-plane/adapters/drivens/index.ts index 08761c2..6656bea 100644 --- a/src/services/control-plane/adapters/drivens/index.ts +++ b/src/services/control-plane/adapters/drivens/index.ts @@ -1,3 +1,2 @@ -export * from './logger-stub'; export * from './repo-querier-local-adapter'; export * from './repo-querier-stub-adapter'; \ No newline at end of file diff --git a/src/services/control-plane/adapters/drivens/logger-stub.ts b/src/services/control-plane/adapters/drivens/logger-stub.ts deleted file mode 100644 index b1ead28..0000000 --- a/src/services/control-plane/adapters/drivens/logger-stub.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ForLogging } from "../../ports/drivens/for-logging"; - -export class LoggerStub implements ForLogging { - log(event: string, message: string): void { - console.log(event, message); - } -} diff --git a/src/services/control-plane/app/composition-root.ts b/src/services/control-plane/app/composition-root.ts index 95286e3..19c9abb 100644 --- a/src/services/control-plane/app/composition-root.ts +++ b/src/services/control-plane/app/composition-root.ts @@ -1,11 +1,10 @@ -import { LoggerStub, RepoQuerierStubAdapter } from "../adapters/drivens"; +import { RepoQuerierStubAdapter } from "../adapters/drivens"; import { AuthManagerProxyAdapter } from "../adapters/drivers"; import { ControlPlane } from "./control-plane"; const compositionMock = () => { const repoQuerierstub = new RepoQuerierStubAdapter(); - const loggerStub = new LoggerStub(); - const controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + const controlPlane = new ControlPlane(repoQuerierstub); const authManagerProxy = new AuthManagerProxyAdapter(controlPlane); diff --git a/src/services/control-plane/app/control-plane.test.ts b/src/services/control-plane/app/control-plane.test.ts index 1df7bcf..680c1c4 100644 --- a/src/services/control-plane/app/control-plane.test.ts +++ b/src/services/control-plane/app/control-plane.test.ts @@ -1,14 +1,13 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { LoggerStub, RepoQuerierStubAdapter } from "../adapters/drivens"; +import { RepoQuerierStubAdapter } from "../adapters/drivens"; import { ControlPlane } from "./control-plane"; describe("ControlPlane", () => { const repoQuerierstub = new RepoQuerierStubAdapter(); - const loggerStub = new LoggerStub(); - let controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + let controlPlane = new ControlPlane(repoQuerierstub); beforeEach(() => { - controlPlane = new ControlPlane(repoQuerierstub, loggerStub); + controlPlane = new ControlPlane(repoQuerierstub); }); it.concurrent("should get auth details", async () => { diff --git a/src/services/control-plane/app/control-plane.ts b/src/services/control-plane/app/control-plane.ts index 038051a..11dd500 100644 --- a/src/services/control-plane/app/control-plane.ts +++ b/src/services/control-plane/app/control-plane.ts @@ -1,4 +1,4 @@ -import { ForLogging } from "../ports/drivens/for-logging"; +import { RepoUser } from "../../repository/app/schemas"; import { ForRepoQuerying } from "../ports/drivens/for-repo-querying"; import { ForManagingAuthentication } from "../ports/drivers"; import { AuthDetails, Permissions } from "./schemas/auth"; @@ -6,19 +6,19 @@ import { AuthDetails, Permissions } from "./schemas/auth"; export class ControlPlane implements ForManagingAuthentication { constructor( private readonly repoQuerier: ForRepoQuerying, - private readonly logger: ForLogging ) {} async getAuthDetails(email: string, password: string): Promise { - const user = await this.repoQuerier.getUser(email); + let user: RepoUser - if (!user) { - this.logger.log("GetAuthDetails", "Wrong email"); + try { + user = await this.repoQuerier.getUser(email); + } catch (error) { + // acá podría hacer una validación según el tipo de error throw new Error("Wrong email"); } if (password !== user.password) { - this.logger.log("GetAuthDetails", "Wrong password"); throw new Error("Wrong password"); } @@ -28,11 +28,6 @@ export class ControlPlane implements ForManagingAuthentication { async getPermissions(userId: string): Promise { const userPermissions = await this.repoQuerier.getPermissions(userId); - if (!userPermissions) { - this.logger.log("GetPermissions", "Permissions not found"); - throw new Error("Permissions not found"); - } - const permissions = { admin: userPermissions.admin, user: userPermissions.user, diff --git a/src/services/control-plane/ports/drivens/for-logging.ts b/src/services/control-plane/ports/drivens/for-logging.ts deleted file mode 100644 index 0f5e39b..0000000 --- a/src/services/control-plane/ports/drivens/for-logging.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ForLogging { - log(event: string, message: string): void; -} From 71c39ab760a8fe1ca5e914eeee05f3d899a3edd1 Mon Sep 17 00:00:00 2001 From: Mateo Gomez Date: Wed, 6 Mar 2024 12:30:49 -0500 Subject: [PATCH 8/8] fix: separate userPermissionRepository from repository --- .../drivers/user-permission-manager-proxy.ts | 3 +- .../repository/app/composition-root.ts | 5 +- .../repository/app/repository.test.ts | 55 ----------------- src/services/repository/app/repository.ts | 5 +- .../app/user-permission-repository.test.ts | 61 +++++++++++++++++++ .../app/user-permission-repository.ts | 40 ++++++++++++ 6 files changed, 109 insertions(+), 60 deletions(-) create mode 100644 src/services/repository/app/user-permission-repository.test.ts create mode 100644 src/services/repository/app/user-permission-repository.ts diff --git a/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts b/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts index f96d95a..61b9e9a 100644 --- a/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts +++ b/src/services/repository/adapters/drivers/user-permission-manager-proxy.ts @@ -1,9 +1,10 @@ import { Repository } from "../../app/repository"; import { Permissions, UserPermission } from "../../app/schemas"; +import { UserPermissionRepository } from "../../app/user-permission-repository"; import { ForManagingPermission } from "../../ports/drivers/for-managing-permission"; export class UserPermissionManagerProxy implements ForManagingPermission { - constructor(private readonly repository: Repository) {} + constructor(private readonly repository: UserPermissionRepository) {} async getUserPermissions(userId: string): Promise { return this.repository.getUserPermissions(userId) diff --git a/src/services/repository/app/composition-root.ts b/src/services/repository/app/composition-root.ts index 750ceda..1990d18 100644 --- a/src/services/repository/app/composition-root.ts +++ b/src/services/repository/app/composition-root.ts @@ -2,13 +2,16 @@ import { LoggerStubAdapter } from "../adapters/drivens"; import { UserPermissionManagerProxy } from "../adapters/drivers"; import { UserManagerProxy } from "./../adapters/drivers/user-manager-proxy"; import { Repository } from "./repository"; +import { UserPermissionRepository } from "./user-permission-repository"; export const compositionMock = () => { const monitorStub = new LoggerStubAdapter(); const repositoryMock = new Repository(monitorStub); const userManagerProxy = new UserManagerProxy(repositoryMock); - const userPermissionManagerProxy = new UserPermissionManagerProxy(repositoryMock); + + const userPermissionRepositoryMock = new UserPermissionRepository(monitorStub) + const userPermissionManagerProxy = new UserPermissionManagerProxy(userPermissionRepositoryMock); return { userManagerProxy, diff --git a/src/services/repository/app/repository.test.ts b/src/services/repository/app/repository.test.ts index f3e8582..765c20e 100644 --- a/src/services/repository/app/repository.test.ts +++ b/src/services/repository/app/repository.test.ts @@ -111,59 +111,4 @@ describe("Repository", () => { // THEN expect(result).toEqual(expectedResult); }); - - it.concurrent("should create user permissions", async () => { - // GIVEN - const mockedUserId = "1"; - const mockedPermissions = { - admin: true, - user: true - } - - const expectedResult = { - id: "1", - userId: '1', - admin: true, - user: true - }; - - // WHEN - const result = await repositoryMock.createUserPermissions(mockedUserId, mockedPermissions); - - // THEN - expect(result).toEqual(expectedResult); - }); - - it.concurrent("should get user permissions", async () => { - // GIVEN - const mockedUserId = "1"; - - const expectedResult = { - id: "1", - userId: '1', - admin: true, - user: true - }; - - // WHEN - const result = await repositoryMock.getUserPermissions(mockedUserId); - - // THEN - expect(result).toEqual(expectedResult); - }); - - it.concurrent( - "should throw error when permissions does not exists for user", - async () => { - // GIVEN - const mockUserId = "2"; - - // WHEN - - // THEN - await expect( - repositoryMock.getUserPermissions(mockUserId) - ).rejects.toThrowError("Permissions not found"); - } - ); }); diff --git a/src/services/repository/app/repository.ts b/src/services/repository/app/repository.ts index e962bc5..07f1e11 100644 --- a/src/services/repository/app/repository.ts +++ b/src/services/repository/app/repository.ts @@ -1,6 +1,5 @@ import { ForMonitoring } from "../ports/drivens"; import { ForManagingUser } from "../ports/drivers"; -import { ForManagingPermission } from "../ports/drivers/for-managing-permission"; import { ExternalUser, Permissions, @@ -9,7 +8,7 @@ import { UserPermission, } from "./schemas"; -export class Repository implements ForManagingUser, ForManagingPermission { +export class Repository implements ForManagingUser { private userList: RepoUser[] = []; private userPermissionList: UserPermission[] = []; constructor(private readonly logger: ForMonitoring) {} @@ -74,7 +73,7 @@ export class Repository implements ForManagingUser, ForManagingPermission { permissions: Permissions ): Promise { const newUserPermission: UserPermission = { - id: String(this.userList.length + 1), + id: crypto.randomUUID(), userId, admin: permissions.admin, user: permissions.user, diff --git a/src/services/repository/app/user-permission-repository.test.ts b/src/services/repository/app/user-permission-repository.test.ts new file mode 100644 index 0000000..8abdc30 --- /dev/null +++ b/src/services/repository/app/user-permission-repository.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from "vitest"; +import { LoggerStubAdapter } from "../adapters/drivens"; +import { Repository } from "./repository"; + +describe("Repository", () => { + const monitorStub = new LoggerStubAdapter(); + const repositoryMock = new Repository(monitorStub); + + it.concurrent("should create user permissions", async () => { + // GIVEN + const mockedUserId = "1"; + const mockedPermissions = { + admin: true, + user: true + } + + const expectedResult = { + userId: '1', + admin: true, + user: true + }; + + // WHEN + const result = await repositoryMock.createUserPermissions(mockedUserId, mockedPermissions); + + // THEN + expect(result).toEqual(expect.objectContaining(expectedResult)); + }); + + it.concurrent("should get user permissions", async () => { + // GIVEN + const mockedUserId = "1"; + + const expectedResult = { + userId: '1', + admin: true, + user: true + }; + + // WHEN + const result = await repositoryMock.getUserPermissions(mockedUserId); + + // THEN + expect(result).toEqual(expect.objectContaining(expectedResult)); + }); + + it.concurrent( + "should throw error when permissions does not exists for user", + async () => { + // GIVEN + const mockUserId = "2"; + + // WHEN + + // THEN + await expect( + repositoryMock.getUserPermissions(mockUserId) + ).rejects.toThrowError("Permissions not found"); + } + ); +}); diff --git a/src/services/repository/app/user-permission-repository.ts b/src/services/repository/app/user-permission-repository.ts new file mode 100644 index 0000000..d7c4bca --- /dev/null +++ b/src/services/repository/app/user-permission-repository.ts @@ -0,0 +1,40 @@ +import { ForMonitoring } from "../ports/drivens"; +import { ForManagingPermission } from "../ports/drivers/for-managing-permission"; +import { + Permissions, + UserPermission, +} from "./schemas"; + +export class UserPermissionRepository implements ForManagingPermission { + private userPermissionList: UserPermission[] = []; + constructor(private readonly logger: ForMonitoring) {} + + async getUserPermissions(userId: string): Promise { + const userPermission = this.userPermissionList.find( + (userPermission) => userPermission.userId === userId + ); + + if (!userPermission) { + this.logger.log("GetUserPermissions", "Permissions not found"); + throw new Error("Permissions not found"); + } + + return userPermission; + } + + async createUserPermissions( + userId: string, + permissions: Permissions + ): Promise { + const newUserPermission: UserPermission = { + id: crypto.randomUUID(), + userId, + admin: permissions.admin, + user: permissions.user, + }; + + this.userPermissionList.push(newUserPermission); + + return newUserPermission; + } +}