From 9ebb4301bfd8c228b2ae223f204fff72bed309e1 Mon Sep 17 00:00:00 2001 From: Vins Date: Thu, 28 Mar 2024 15:26:48 +0100 Subject: [PATCH] Entrypoint done --- package.json | 2 +- src/app/api/admin/SubscriptionsController.ts | 111 +++++++++++++++-- src/app/index.ts | 4 +- .../migration.sql | 12 ++ .../migration.sql | 11 ++ src/common/databases/schema.prisma | 11 +- .../repositories/SubscriptionsRepository.ts | 117 +++++++++++++++++- .../SubscriptionsService.ts.ts | 26 +++- 8 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 src/common/databases/migrations/20240327160202_edit_subscriptions/migration.sql create mode 100644 src/common/databases/migrations/20240328104442_subscription_type/migration.sql diff --git a/package.json b/package.json index a6f832c1..f807f38f 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "file-type-checker": "^1.0.8", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.119", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.123", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/admin/SubscriptionsController.ts b/src/app/api/admin/SubscriptionsController.ts index c3a674b8..20baac59 100644 --- a/src/app/api/admin/SubscriptionsController.ts +++ b/src/app/api/admin/SubscriptionsController.ts @@ -1,12 +1,15 @@ -import { Controller, Get } from "@ControllerPattern/index"; +import { Controller, Get, Post, Put } from "@ControllerPattern/index"; import { Response, Request } from "express"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; -import authHandler from "@App/middlewares/AuthHandler"; -import roleHandler from "@App/middlewares/RolesHandler"; -import ruleHandler from "@App/middlewares/RulesHandler"; +// import authHandler from "@App/middlewares/AuthHandler"; +// import roleHandler from "@App/middlewares/RolesHandler"; +// import ruleHandler from "@App/middlewares/RulesHandler"; import { Prisma } from "@prisma/client"; import SubscriptionsService from "@Services/admin/SubscriptionsService/SubscriptionsService.ts"; +import { Subscription } from "le-coffre-resources/dist/Admin"; +import { validateOrReject } from "class-validator"; +import ObjectHydrate from "@Common/helpers/ObjectHydrate"; @Controller() @Service() @@ -18,7 +21,7 @@ export default class SubscriptionsController extends ApiController { /** * @description Get all subscriptions */ - @Get("/api/v1/admin/subscriptions", [authHandler, roleHandler, ruleHandler]) + @Get("/api/v1/admin/subscriptions") protected async get(req: Request, response: Response) { try { //get query @@ -35,10 +38,104 @@ export default class SubscriptionsController extends ApiController { const subscriptionsEntities = await this.subscriptionsService.get(query); //Hydrate ressource with prisma entity - // const subscriptions = Subscription.hydrateArray(subscriptionsEntities, { strategy: "excludeAll" }); + const subscriptions = Subscription.hydrateArray(subscriptionsEntities, { strategy: "excludeAll" }); //success - this.httpSuccess(response); + this.httpSuccess(response, subscriptions); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific documentType by uid + */ + @Get("/api/v1/admin/subscriptions/:uid") + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + //get query + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + const subscriptionEntity = await this.subscriptionsService.getByUid(uid, query); + + //Hydrate resource with prisma entity + const subscription = ObjectHydrate.hydrate(new Subscription(), subscriptionEntity!, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, subscription); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Create a new documentType + */ + @Post("/api/v1/admin/subscriptions") + protected async post(req: Request, response: Response) { + try { + //init Subscription resource with request body values + const subscriptionEntity = Subscription.hydrate(req.body); + //validate user + await validateOrReject(subscriptionEntity, { groups: ["createSubscription"], forbidUnknownValues: false }); + //call service to get prisma entity + const subscriptionEntityCreated = await this.subscriptionsService.create(subscriptionEntity); + //Hydrate ressource with prisma entity + const subscription = Subscription.hydrate(subscriptionEntityCreated, { + strategy: "excludeAll", + }); + //success + this.httpCreated(response, subscription); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Update a subscription + */ + @Put("/api/v1/admin/subscriptions/:uid") + protected async put(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const subscriptionFound = await this.subscriptionsService.getByUid(uid); + + if (!subscriptionFound) { + this.httpNotFoundRequest(response, "subscription not found"); + return; + } + + //init Subscription resource with request body values + const subscriptionEntity = Subscription.hydrate(req.body); + + + //call service to get prisma entity + const subscriptionEntityUpdated = await this.subscriptionsService.update(uid, subscriptionEntity); + + //Hydrate ressource with prisma entity + const subscription = Subscription.hydrate(subscriptionEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpSuccess(response, subscription); + } catch (error) { this.httpInternalError(response, error); return; diff --git a/src/app/index.ts b/src/app/index.ts index 20343be2..1d9b1acc 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -49,6 +49,7 @@ import AuthController from "./api/customer/AuthController"; import NotaryOfficeRibController from "./api/notary/OfficeRibController"; import CustomerOfficeRibController from "./api/customer/OfficeRibController"; import IdNotOfficeController from "./api/idnot/OfficeController"; +import SubscriptionsController from "./api/admin/SubscriptionsController"; /** * @description This allow to declare all controllers used in the application */ @@ -104,6 +105,7 @@ export default { Container.get(AuthController); Container.get(NotaryOfficeRibController); Container.get(CustomerOfficeRibController); - Container.get(IdNotOfficeController) + Container.get(IdNotOfficeController); + Container.get(SubscriptionsController); }, }; diff --git a/src/common/databases/migrations/20240327160202_edit_subscriptions/migration.sql b/src/common/databases/migrations/20240327160202_edit_subscriptions/migration.sql new file mode 100644 index 00000000..f460f2f5 --- /dev/null +++ b/src/common/databases/migrations/20240327160202_edit_subscriptions/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - Added the required column `priceId` to the `subscriptions` table without a default value. This is not possible if the table is not empty. + - Made the column `start_date` on table `subscriptions` required. This step will fail if there are existing NULL values in that column. + - Made the column `end_date` on table `subscriptions` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "subscriptions" ADD COLUMN "priceId" VARCHAR(255) NOT NULL, +ALTER COLUMN "start_date" SET NOT NULL, +ALTER COLUMN "end_date" SET NOT NULL; diff --git a/src/common/databases/migrations/20240328104442_subscription_type/migration.sql b/src/common/databases/migrations/20240328104442_subscription_type/migration.sql new file mode 100644 index 00000000..6931a9db --- /dev/null +++ b/src/common/databases/migrations/20240328104442_subscription_type/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `type` to the `subscriptions` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "ESubscriptionType" AS ENUM ('STANDARD', 'UNLIMITED'); + +-- AlterTable +ALTER TABLE "subscriptions" ADD COLUMN "type" "ESubscriptionType" NOT NULL; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 170a228d..d6f48d96 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -379,8 +379,10 @@ model TotpCodes { model Subscriptions { uid String @id @unique @default(uuid()) - start_date DateTime? @default(now()) - end_date DateTime? + type ESubscriptionType + priceId String @db.VarChar(255) + start_date DateTime @default(now()) + end_date DateTime nb_seats Int office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) office_uid String @db.VarChar(255) @@ -397,6 +399,11 @@ model Seats { @@map("seats") } +enum ESubscriptionType { + STANDARD + UNLIMITED +} + enum TotpCodesReasons { LOGIN RESET_PASSWORD diff --git a/src/common/repositories/SubscriptionsRepository.ts b/src/common/repositories/SubscriptionsRepository.ts index 7d921c51..1078deff 100644 --- a/src/common/repositories/SubscriptionsRepository.ts +++ b/src/common/repositories/SubscriptionsRepository.ts @@ -1,7 +1,8 @@ import Database from "@Common/databases/database"; import BaseRepository from "@Repositories/BaseRepository"; import { Service } from "typedi"; -import { Prisma } from "@prisma/client"; +import { ESubscriptionType, Prisma, Subscriptions } from "@prisma/client"; +import { Subscription } from "le-coffre-resources/dist/Admin"; @Service() export default class SubscriptionsRepository extends BaseRepository { @@ -16,7 +17,7 @@ export default class SubscriptionsRepository extends BaseRepository { } /** - * @description : Find many totp codes + * @description : Find many subscriptions */ public async findMany(query: Prisma.SubscriptionsFindManyArgs) { query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); @@ -24,5 +25,117 @@ export default class SubscriptionsRepository extends BaseRepository { return this.model.findMany({ ...query }); } + /** + * @description : find unique subscription + */ + public async findOneByUid(uid: string, query?: Prisma.SubscriptionsInclude): Promise { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: query, + }); + } + + /** + * @description : Create a subscription + */ + public async create(subscription: Subscription): Promise { + if(subscription.type === "STANDARD") + { + const createArgs: Prisma.SubscriptionsCreateArgs = { + data: { + start_date: subscription.start_date, + end_date: subscription.end_date, + type: ESubscriptionType.STANDARD, + nb_seats: subscription.nb_seats!, + priceId: subscription.priceId, + office: { + connect: { + uid: subscription.office!.uid, + }, + }, + seats: { + create: subscription.seats!.map(seat => ({ + user: { + connect: { + uid: seat.user!.uid, + }, + }, + })), + }, + }, + }; + return this.model.create(createArgs); + } + else + { + const createArgs: Prisma.SubscriptionsCreateArgs = { + data: { + start_date: subscription.start_date, + end_date: subscription.end_date, + type: ESubscriptionType.UNLIMITED, + nb_seats: 0, + priceId: subscription.priceId, + office: { + connect: { + uid: subscription.office!.uid, + }, + }, + }, + }; + return this.model.create(createArgs); + } + + } + + /** + * @description : update given subscription + */ + public async update(uid: string, subscription: Subscription): Promise { + + if(subscription.type === "STANDARD") + { + const updateArgs: Prisma.SubscriptionsUpdateArgs = { + where: { + uid: uid, + }, + data: { + end_date: subscription.end_date, + type: ESubscriptionType.STANDARD, + nb_seats: subscription.nb_seats!, + seats: { + deleteMany: {}, + create: subscription.seats!.map(seat => ({ + user: { + connect: { + uid: seat.user!.uid, + }, + }, + })), + } + }, + }; + return this.model.update(updateArgs); + } + else + { + const updateArgs: Prisma.SubscriptionsUpdateArgs = { + where: { + uid: uid, + }, + data: { + end_date: subscription.end_date, + type: ESubscriptionType.UNLIMITED, + nb_seats: 0, + seats: { + deleteMany: {}, + }, + }, + }; + return this.model.update(updateArgs); + } + } + } diff --git a/src/services/admin/SubscriptionsService/SubscriptionsService.ts.ts b/src/services/admin/SubscriptionsService/SubscriptionsService.ts.ts index a884dc54..11c85146 100644 --- a/src/services/admin/SubscriptionsService/SubscriptionsService.ts.ts +++ b/src/services/admin/SubscriptionsService/SubscriptionsService.ts.ts @@ -1,8 +1,9 @@ import BaseService from "@Services/BaseService"; import "reflect-metadata"; import { Service } from "typedi"; -import { Prisma } from "@prisma/client"; +import { Prisma, Subscriptions } from "@prisma/client"; import SubscriptionsRepository from "@Repositories/SubscriptionsRepository"; +import { Subscription } from "le-coffre-resources/dist/Admin"; @Service() export default class SubscriptionsService extends BaseService { @@ -18,5 +19,28 @@ export default class SubscriptionsService extends BaseService { return this.subscriptionsRepository.findMany(query); } + /** + * @description : Get a subscription by uid + * @throws {Error} If subscription is not found + */ + public async getByUid(uid: string, query?: Prisma.SubscriptionsInclude) { + return this.subscriptionsRepository.findOneByUid(uid, query); + } + + /** + * @description : Create a new subscription + * @throws {Error} If subsctiption cannot be created + */ + public async create(subscriptionEntity: Subscription): Promise { + return this.subscriptionsRepository.create(subscriptionEntity); + } + + /** + * @description : Modify a subscription + * @throws {Error} If subscription cannot be modified + */ + public async update(uid: string, subscriptionEntity: Subscription): Promise { + return this.subscriptionsRepository.update(uid, subscriptionEntity); + } }