From 3f0549bd4431cb0549a3d82a80cfb0d80c7c6630 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 21 Jan 2025 11:37:28 +0100 Subject: [PATCH] api & permission services and commandHandler --- src/command/user/api/apiCommand.ts | 15 +++ src/command/user/api/apiCommandHandler.ts | 69 +++++++++++ src/command/user/api/apiPermissionCommand.ts | 16 +++ .../user/api/apiPermissionCommandHandler.ts | 114 ++++++++++++++++++ src/service/user/apiPermissionService.ts | 24 ++++ src/service/user/apiService.ts | 83 +++++++++++++ 6 files changed, 321 insertions(+) create mode 100644 src/command/user/api/apiCommand.ts create mode 100644 src/command/user/api/apiCommandHandler.ts create mode 100644 src/command/user/api/apiPermissionCommand.ts create mode 100644 src/command/user/api/apiPermissionCommandHandler.ts create mode 100644 src/service/user/apiPermissionService.ts create mode 100644 src/service/user/apiService.ts diff --git a/src/command/user/api/apiCommand.ts b/src/command/user/api/apiCommand.ts new file mode 100644 index 0000000..b2c669f --- /dev/null +++ b/src/command/user/api/apiCommand.ts @@ -0,0 +1,15 @@ +export interface CreateApiCommand { + title: string; + token: string; + expiry?: Date; +} + +export interface UpdateApiCommand { + id: number; + title: string; + expiry?: Date; +} + +export interface DeleteApiCommand { + id: number; +} diff --git a/src/command/user/api/apiCommandHandler.ts b/src/command/user/api/apiCommandHandler.ts new file mode 100644 index 0000000..180ff32 --- /dev/null +++ b/src/command/user/api/apiCommandHandler.ts @@ -0,0 +1,69 @@ +import { dataSource } from "../../../data-source"; +import { api } from "../../../entity/user/api"; +import InternalException from "../../../exceptions/internalException"; +import { CreateApiCommand, DeleteApiCommand, UpdateApiCommand } from "./apiCommand"; + +export default abstract class ApiCommandHandler { + /** + * @description create api + * @param {CreateApiCommand} createApi + * @returns {Promise} + */ + static async create(createApi: CreateApiCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(api) + .values({ + token: createApi.token, + title: createApi.title, + expiry: createApi.expiry, + }) + .execute() + .then((result) => { + return result.identifiers[0].token; + }) + .catch((err) => { + throw new InternalException("Failed creating api", err); + }); + } + + /** + * @description update api + * @param {UpdateApiCommand} updateApi + * @returns {Promise} + */ + static async update(updateApi: UpdateApiCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(api) + .set({ + title: updateApi.title, + expiry: updateApi.expiry, + }) + .where("id = :id", { id: updateApi.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed updating api", err); + }); + } + + /** + * @description delete api + * @param {DeleteApiCommand} deleteApi + * @returns {Promise} + */ + static async delete(deleteApi: DeleteApiCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(api) + .where("id = :id", { id: deleteApi.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed deleting api", err); + }); + } +} diff --git a/src/command/user/api/apiPermissionCommand.ts b/src/command/user/api/apiPermissionCommand.ts new file mode 100644 index 0000000..2b61b65 --- /dev/null +++ b/src/command/user/api/apiPermissionCommand.ts @@ -0,0 +1,16 @@ +import { PermissionString } from "../../../type/permissionTypes"; + +export interface CreateApiPermissionCommand { + permission: PermissionString; + apiId: number; +} + +export interface DeleteApiPermissionCommand { + permission: PermissionString; + apiId: number; +} + +export interface UpdateApiPermissionsCommand { + apiId: number; + permissions: Array; +} diff --git a/src/command/user/api/apiPermissionCommandHandler.ts b/src/command/user/api/apiPermissionCommandHandler.ts new file mode 100644 index 0000000..f6fdb64 --- /dev/null +++ b/src/command/user/api/apiPermissionCommandHandler.ts @@ -0,0 +1,114 @@ +import { DeleteResult, EntityManager, InsertResult } from "typeorm"; +import { dataSource } from "../../../data-source"; +import { apiPermission } from "../../../entity/user/api_permission"; +import InternalException from "../../../exceptions/internalException"; +import ApiService from "../../../service/user/apiService"; +import { + CreateApiPermissionCommand, + DeleteApiPermissionCommand, + UpdateApiPermissionsCommand, +} from "./apiPermissionCommand"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import ApiPermissionService from "../../../service/user/apiPermissionService"; +import { PermissionString } from "../../../type/permissionTypes"; + +export default abstract class ApiPermissionCommandHandler { + /** + * @description update api permissions + * @param {UpdateApiPermissionsCommand} updateApiPermissions + * @returns {Promise} + */ + static async updatePermissions(updateApiPermissions: UpdateApiPermissionsCommand): Promise { + let currentPermissions = (await ApiPermissionService.getByApi(updateApiPermissions.apiId)).map((r) => r.permission); + return await dataSource.manager + .transaction(async (manager) => { + let newPermissions = PermissionHelper.getWhatToAdd(currentPermissions, updateApiPermissions.permissions); + let removePermissions = PermissionHelper.getWhatToRemove(currentPermissions, updateApiPermissions.permissions); + if (newPermissions.length != 0) { + await this.updatePermissionsAdd(manager, updateApiPermissions.apiId, newPermissions); + } + if (removePermissions.length != 0) { + await this.updatePermissionsRemove(manager, updateApiPermissions.apiId, removePermissions); + } + }) + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed saving api permissions", err); + }); + } + + private static async updatePermissionsAdd( + manager: EntityManager, + apiId: number, + permissions: Array + ): Promise { + return await manager + .createQueryBuilder() + .insert() + .into(apiPermission) + .values( + permissions.map((p) => ({ + permission: p, + apiId: apiId, + })) + ) + .orIgnore() + .execute(); + } + + private static async updatePermissionsRemove( + manager: EntityManager, + apiId: number, + permissions: Array + ): Promise { + return await manager + .createQueryBuilder() + .delete() + .from(apiPermission) + .where("apiId = :id", { id: apiId }) + .andWhere("permission IN (:...permission)", { permission: permissions }) + .execute(); + } + + /** + * @description grant permission to user + * @param {CreateApiPermissionCommand} createPermission + * @returns {Promise} + */ + static async create(createPermission: CreateApiPermissionCommand): Promise { + return await dataSource + .createQueryBuilder() + .insert() + .into(apiPermission) + .values({ + permission: createPermission.permission, + apiId: createPermission.apiId, + }) + .execute() + .then((result) => { + return result.identifiers[0].id; + }) + .catch((err) => { + throw new InternalException("Failed saving api permission", err); + }); + } + + /** + * @description remove permission from api + * @param {DeleteApiPermissionCommand} deletePermission + * @returns {Promise} + */ + static async delete(deletePermission: DeleteApiPermissionCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(apiPermission) + .where("apiId = :id", { id: deletePermission.apiId }) + .andWhere("permission = :permission", { permission: deletePermission.permission }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("failed api permission removal", err); + }); + } +} diff --git a/src/service/user/apiPermissionService.ts b/src/service/user/apiPermissionService.ts new file mode 100644 index 0000000..13f0ed2 --- /dev/null +++ b/src/service/user/apiPermissionService.ts @@ -0,0 +1,24 @@ +import { dataSource } from "../../data-source"; +import { apiPermission } from "../../entity/user/api_permission"; +import InternalException from "../../exceptions/internalException"; + +export default abstract class ApiPermissionService { + /** + * @description get permission by api + * @param apiId number + * @returns {Promise>} + */ + static async getByApi(apiId: number): Promise> { + return await dataSource + .getRepository(apiPermission) + .createQueryBuilder("api_permission") + .where("api_permission.apiId = :apiId", { apiId: apiId }) + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("api permissions not found by api", err); + }); + } +} diff --git a/src/service/user/apiService.ts b/src/service/user/apiService.ts new file mode 100644 index 0000000..b44b3ca --- /dev/null +++ b/src/service/user/apiService.ts @@ -0,0 +1,83 @@ +import { dataSource } from "../../data-source"; +import { api } from "../../entity/user/api"; +import InternalException from "../../exceptions/internalException"; + +export default abstract class ApiService { + /** + * @description get apis + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(api) + .createQueryBuilder("api") + .leftJoinAndSelect("api.permissions", "permissions") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("apis not found", err); + }); + } + + /** + * @description get api by id + * @param id number + * @returns {Promise} + */ + static async getById(id: number): Promise { + return await dataSource + .getRepository(api) + .createQueryBuilder("api") + .leftJoinAndSelect("api.permissions", "permissions") + .where("api.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("api not found by id", err); + }); + } + + /** + * @description get api by token + * @param token string + * @returns {Promise} + */ + static async getByToken(token: string): Promise { + return await dataSource + .getRepository(api) + .createQueryBuilder("api") + .leftJoinAndSelect("api.permissions", "permissions") + .where("api.token = :token", { token: token }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("api not found by token", err); + }); + } + + /** + * @description get api by id + * @param id number + * @returns {Promise} + */ + static async getTokenById(id: number): Promise { + return await dataSource + .getRepository(api) + .createQueryBuilder("api") + .select("token") + .where("api.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("api token not found by id", err); + }); + } +}