From a26e54652cf85139e608d571c8135ffb6ea50e8a Mon Sep 17 00:00:00 2001 From: PreciousIfeaka Date: Sun, 21 Jul 2024 23:02:57 +0100 Subject: [PATCH 1/3] [feat] Create organisation resource --- src/controllers/createorgController.ts | 32 ++++++++++++++++++ src/index.ts | 3 +- src/models/organization.ts | 47 ++++++++++++++++++++++++-- src/models/user-organisation.ts | 28 +++++++++++++++ src/models/user.ts | 9 ++--- src/routes/createOrg.ts | 8 +++++ src/services/createOrg.services.ts | 38 +++++++++++++++++++++ src/types/index.d.ts | 15 ++++++++ 8 files changed, 172 insertions(+), 8 deletions(-) create mode 100644 src/controllers/createorgController.ts create mode 100644 src/models/user-organisation.ts create mode 100644 src/routes/createOrg.ts create mode 100644 src/services/createOrg.services.ts diff --git a/src/controllers/createorgController.ts b/src/controllers/createorgController.ts new file mode 100644 index 00000000..b2e52dd9 --- /dev/null +++ b/src/controllers/createorgController.ts @@ -0,0 +1,32 @@ +import { NextFunction, Request, Response } from "express"; +import { HttpError } from "../middleware"; +import * as jwt from "jsonwebtoken" +import config from "../config"; +import { OrganisationService } from "../services/createOrg.services"; + +export const createOrganisation = async (req: Request, res: Response, next: NextFunction) => { + try { + const authHeader = req.headers["authorization"]; + const token = authHeader && authHeader.split(' ')[1]; + if (!token) { + throw new HttpError(401, "Unauthorized"); + } + const decoded: any = jwt.verify(token, config.TOKEN_SECRET); + const userId = decoded.userId; + const payload = req.body; + + const organisationService = new OrganisationService(); + const newOrganisation = await organisationService.createOrganisation(payload, userId); + + const respObj = { + status: "success", + message: "organisation created successfully", + data: newOrganisation, + status_code: 201 + } + + return res.status(201).json(respObj); + } catch (error) { + return next(error); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 4177914a..40480208 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { seed } from "./seeder"; import { orgRouter } from "./routes/organisation"; import swaggerUi from "swagger-ui-express"; import swaggerSpec from "./swaggerConfig"; +import { organisationRoute } from "./routes/createOrg"; dotenv.config(); @@ -37,7 +38,7 @@ server.use(express.json()); server.get("/", (req: Request, res: Response) => { res.send("Hello world"); }); -server.use("/api/v1", userRouter, orgRouter); +server.use("/api/v1", userRouter, orgRouter, organisationRoute); server.use("/api/v1/auth", authRoute); server.use("/api/v1", testimonialRoute); server.use("/api/v1/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); diff --git a/src/models/organization.ts b/src/models/organization.ts index c0360084..abfa119b 100644 --- a/src/models/organization.ts +++ b/src/models/organization.ts @@ -1,5 +1,7 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from "typeorm"; -import { User } from "./user"; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToMany, BeforeInsert, UpdateDateColumn } from "typeorm"; +import { User } from "."; +import {v4 as uuidv4} from "uuid" +import { UserOrganization } from "./user-organisation"; import ExtendedBaseEntity from "./extended-base-entity"; @Entity() @@ -7,12 +9,51 @@ export class Organization extends ExtendedBaseEntity { @PrimaryGeneratedColumn("uuid") id: string; + @Column({ unique: true }) + slug: string; + @Column() name: string; - @Column() + @Column({ nullable: true }) + email: string; + + @Column({ nullable: true }) + industry: string; + + @Column({ nullable: true }) + type: string; + + @Column({ nullable: true }) + country: string; + + @Column({ nullable: true }) + address: string; + + @Column({ nullable: true }) + state: string; + + @Column('text', { nullable: true }) description: string; + @UpdateDateColumn() + created_at: Date; + + @UpdateDateColumn() + updated_at: Date; + + @Column('uuid') + owner_id: string; + + @OneToMany(() => UserOrganization, userOrganization => userOrganization.organization) + userOrganizations: UserOrganization[]; + @ManyToMany(() => User, (user) => user.organizations) users: User[]; + + @BeforeInsert() + generateSlug() { + this.slug = uuidv4(); + } } + diff --git a/src/models/user-organisation.ts b/src/models/user-organisation.ts new file mode 100644 index 00000000..a3f5ecb4 --- /dev/null +++ b/src/models/user-organisation.ts @@ -0,0 +1,28 @@ +import { Entity, ManyToOne, Column, PrimaryColumn, JoinColumn } from 'typeorm'; +import { User } from "./user"; +import { Organization } from "./organization"; +import { UserRole } from '../enums/userRoles'; +import ExtendedBaseEntity from './extended-base-entity'; + +@Entity() +export class UserOrganization extends ExtendedBaseEntity { + @PrimaryColumn() + userId: string; + + @PrimaryColumn() + organizationId: string; + + @ManyToOne(() => User, user => user.userOrganizations) + @JoinColumn({ name: 'userId' }) + user: User; + + @ManyToOne(() => Organization, organization => organization.userOrganizations) + @JoinColumn({ name: 'organizationId' }) + organization: Organization; + + @Column({ + type: "enum", + enum: UserRole, + }) + role: UserRole; +} \ No newline at end of file diff --git a/src/models/user.ts b/src/models/user.ts index 74a159a0..8003e454 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -12,6 +12,7 @@ import { UpdateDateColumn, } from "typeorm"; import { Profile, Product, Organization, Sms, Blog } from "."; +import { UserOrganization } from "./user-organisation"; import { IsEmail } from "class-validator"; import ExtendedBaseEntity from "./extended-base-entity"; import { getIsInvalidMessage } from "../utils"; @@ -62,7 +63,10 @@ export class User extends ExtendedBaseEntity { @OneToMany(() => Blog, (blog) => blog.author) blogs: Blog[]; - @OneToMany(() => Sms, (sms) => sms.sender) + @OneToMany(() => UserOrganization, userOrganization => userOrganization.user) + userOrganizations: UserOrganization[]; + + @OneToMany(() => Sms, (sms) => sms.sender, { cascade: true }) sms: Sms[]; @ManyToMany(() => Organization, (organization) => organization.users, { @@ -71,9 +75,6 @@ export class User extends ExtendedBaseEntity { @JoinTable() organizations: Organization[]; - @OneToMany(() => Sms, (sms) => sms.sender, { cascade: true }) - sms: Sms[]; - @CreateDateColumn() createdAt: Date; diff --git a/src/routes/createOrg.ts b/src/routes/createOrg.ts new file mode 100644 index 00000000..dd0a9d14 --- /dev/null +++ b/src/routes/createOrg.ts @@ -0,0 +1,8 @@ +import { Router } from "express"; +import { createOrganisation } from "../controllers/createorgController" + +const organisationRoute = Router(); + +organisationRoute.post("/organisations", createOrganisation); + +export { organisationRoute } \ No newline at end of file diff --git a/src/services/createOrg.services.ts b/src/services/createOrg.services.ts new file mode 100644 index 00000000..fc0f753c --- /dev/null +++ b/src/services/createOrg.services.ts @@ -0,0 +1,38 @@ +import { AppDataSource } from "../data-source"; +import { Organization, User } from "../models"; +import { UserRole } from "../enums/userRoles"; +import { ICreateOrganisation, IOrganisationService } from "../types"; +import { HttpError } from "../middleware"; +import { UserOrganization } from "../models/user-organisation"; + +export class OrganisationService implements IOrganisationService { + public async createOrganisation(payload: ICreateOrganisation, userId: string): Promise<{ + newOrganisation: Partial; + }> { + try { + const organisation = new Organization(); + organisation.owner_id = userId; + Object.assign(organisation, payload); + + const newOrganisation = await AppDataSource.manager.save(organisation); + + const userOrganization = new UserOrganization(); + userOrganization.userId = userId; + userOrganization.organizationId = newOrganisation.id; + userOrganization.role = UserRole.ADMIN; + + await AppDataSource.manager.save(userOrganization); + + const user = await AppDataSource + .getRepository(User) + .findOne({ where: { id: userId }, relations: ["userOrganizations", "userOrganizations.organization"] }); + + user.userOrganizations.push(userOrganization); + await AppDataSource.getRepository(User).save(user); + + return { newOrganisation }; + } catch (error) { + throw new HttpError(error.status || 500, error.message || error); + } + } +} \ No newline at end of file diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 67d1bb76..5e9490f5 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -32,6 +32,21 @@ export interface IAuthService { verifyEmail(token: string, otp: number): Promise<{ message: string }>; } +export interface ICreateOrganisation { + name: string; + description: string; + email: string; + industry: string; + type: string; + country: string; + address: string; + state: string; +} + +export interface IOrganisationService { + createOrganisation(payload: ICreateOrganisation, userId: string): Promise; +} + declare module "express-serve-static-core" { interface Request { user?: User; From 2542a5c140208ac01d57da16d32762522623d0a0 Mon Sep 17 00:00:00 2001 From: PreciousIfeaka Date: Mon, 22 Jul 2024 10:03:40 +0100 Subject: [PATCH 2/3] [FEAT]: create organisation --- src/services/createOrg.services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/createOrg.services.ts b/src/services/createOrg.services.ts index fc0f753c..db483770 100644 --- a/src/services/createOrg.services.ts +++ b/src/services/createOrg.services.ts @@ -1,4 +1,4 @@ -import { AppDataSource } from "../data-source"; +import AppDataSource from "../data-source"; import { Organization, User } from "../models"; import { UserRole } from "../enums/userRoles"; import { ICreateOrganisation, IOrganisationService } from "../types"; From ab3fc59076f4642ee9afdb14a51d3f2bef2c30ef Mon Sep 17 00:00:00 2001 From: PreciousIfeaka Date: Mon, 22 Jul 2024 10:44:56 +0100 Subject: [PATCH 3/3] [FEAT]: Create Organisation resource --- src/controllers/createorgController.ts | 12 ++---------- src/routes/createOrg.ts | 3 ++- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/controllers/createorgController.ts b/src/controllers/createorgController.ts index b2e52dd9..82683b1c 100644 --- a/src/controllers/createorgController.ts +++ b/src/controllers/createorgController.ts @@ -1,19 +1,11 @@ import { NextFunction, Request, Response } from "express"; -import { HttpError } from "../middleware"; -import * as jwt from "jsonwebtoken" -import config from "../config"; import { OrganisationService } from "../services/createOrg.services"; export const createOrganisation = async (req: Request, res: Response, next: NextFunction) => { try { - const authHeader = req.headers["authorization"]; - const token = authHeader && authHeader.split(' ')[1]; - if (!token) { - throw new HttpError(401, "Unauthorized"); - } - const decoded: any = jwt.verify(token, config.TOKEN_SECRET); - const userId = decoded.userId; const payload = req.body; + const user = req.user; + const userId = user.id; const organisationService = new OrganisationService(); const newOrganisation = await organisationService.createOrganisation(payload, userId); diff --git a/src/routes/createOrg.ts b/src/routes/createOrg.ts index dd0a9d14..abc921c7 100644 --- a/src/routes/createOrg.ts +++ b/src/routes/createOrg.ts @@ -1,8 +1,9 @@ import { Router } from "express"; +import { authMiddleware } from "../middleware"; import { createOrganisation } from "../controllers/createorgController" const organisationRoute = Router(); -organisationRoute.post("/organisations", createOrganisation); +organisationRoute.post("/organisations", authMiddleware, createOrganisation); export { organisationRoute } \ No newline at end of file