From 0eb6d785882e4e1fe4f2ceca7166a83658182b81 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Thu, 5 Sep 2024 11:07:45 -0700 Subject: [PATCH 01/22] added teamId foreign key to tech categories --- .../migration.sql | 11 +++++++++++ prisma/schema/teamStack.prisma | 2 ++ prisma/schema/voyage.prisma | 1 + prisma/seed/data/tech-stack-categories.ts | 6 ++++++ 4 files changed, 20 insertions(+) create mode 100644 prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql diff --git a/prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql b/prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql new file mode 100644 index 00000000..a22fa3e4 --- /dev/null +++ b/prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `voyageTeamId` to the `TechStackCategory` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamId" INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamId_fkey" FOREIGN KEY ("voyageTeamId") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index a4a5df26..095c3c39 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -4,6 +4,8 @@ model TechStackCategory { id Int @id @default(autoincrement()) name String @unique description String + voyageTeam VoyageTeam @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) + voyageTeamId Int createdAt DateTime @default(now()) @db.Timestamptz() updatedAt DateTime @updatedAt diff --git a/prisma/schema/voyage.prisma b/prisma/schema/voyage.prisma index 5f61b053..c260f2df 100644 --- a/prisma/schema/voyage.prisma +++ b/prisma/schema/voyage.prisma @@ -91,6 +91,7 @@ model VoyageTeam { voyageTeamMembers VoyageTeamMember[] teamTechStackItems TeamTechStackItem[] + TechStackCategories TechStackCategory[] teamMeetings TeamMeeting[] FormResponseVoyageProject FormResponseVoyageProject? } diff --git a/prisma/seed/data/tech-stack-categories.ts b/prisma/seed/data/tech-stack-categories.ts index 0646db60..d29952e1 100644 --- a/prisma/seed/data/tech-stack-categories.ts +++ b/prisma/seed/data/tech-stack-categories.ts @@ -2,25 +2,31 @@ export default [ { name: "Frontend", description: "Frontend Stuff", + voyageTeamId: 1, }, { name: "CSS Library", description: "CSS Library", + voyageTeamId: 1, }, { name: "Backend", description: "Backend Stuff", + voyageTeamId: 1, }, { name: "Project Management", description: "project management Stuff", + voyageTeamId: 1, }, { name: "Cloud Provider", description: "cloud stuff", + voyageTeamId: 1, }, { name: "Hosting", description: "Hosting stuff", + voyageTeamId: 1, }, ]; From ff3184a5088bf570bf1150550306ed06d3ce3ac9 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 7 Sep 2024 19:21:34 -0700 Subject: [PATCH 02/22] seed tech categories after teams, before team memebers --- .../migration.sql | 0 prisma/schema/teamStack.prisma | 2 +- prisma/schema/voyage.prisma | 2 +- prisma/seed/data/tech-stack-categories.ts | 2 +- prisma/seed/seed.ts | 3 ++- prisma/seed/tables.ts | 3 +-- prisma/seed/voyage-teams.ts | 8 ++++++++ 7 files changed, 14 insertions(+), 6 deletions(-) rename prisma/migrations/{20240905153957_tech_category_add_teamid => 20240906130941_techstack_category_teamid}/migration.sql (100%) diff --git a/prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql b/prisma/migrations/20240906130941_techstack_category_teamid/migration.sql similarity index 100% rename from prisma/migrations/20240905153957_tech_category_add_teamid/migration.sql rename to prisma/migrations/20240906130941_techstack_category_teamid/migration.sql diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index 095c3c39..4350bc0e 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -5,7 +5,7 @@ model TechStackCategory { name String @unique description String voyageTeam VoyageTeam @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) - voyageTeamId Int + voyageTeamId Int createdAt DateTime @default(now()) @db.Timestamptz() updatedAt DateTime @updatedAt diff --git a/prisma/schema/voyage.prisma b/prisma/schema/voyage.prisma index c260f2df..a1a937ad 100644 --- a/prisma/schema/voyage.prisma +++ b/prisma/schema/voyage.prisma @@ -91,7 +91,7 @@ model VoyageTeam { voyageTeamMembers VoyageTeamMember[] teamTechStackItems TeamTechStackItem[] - TechStackCategories TechStackCategory[] + TechStackCategory TechStackCategory[] teamMeetings TeamMeeting[] FormResponseVoyageProject FormResponseVoyageProject? } diff --git a/prisma/seed/data/tech-stack-categories.ts b/prisma/seed/data/tech-stack-categories.ts index d29952e1..7e43ea2d 100644 --- a/prisma/seed/data/tech-stack-categories.ts +++ b/prisma/seed/data/tech-stack-categories.ts @@ -1,4 +1,4 @@ -export default [ +export const techStackCategoriesData = [ { name: "Frontend", description: "Frontend Stuff", diff --git a/prisma/seed/seed.ts b/prisma/seed/seed.ts index bffa7892..e51dc305 100644 --- a/prisma/seed/seed.ts +++ b/prisma/seed/seed.ts @@ -3,6 +3,7 @@ import { populateVoyages } from "./voyage"; import { populateUsers } from "./users"; import { populateSprints } from "./sprints"; import { populateVoyageTeams } from "./voyage-teams"; +//import { populateTechStackCategories } from "./data/tech-stack-categories"; import { populateTeamResourcesAndProjectIdeas } from "./resources-project-ideas"; import { populateFormsAndResponses } from "./forms"; import { populateMeetings } from "./meetings"; @@ -34,7 +35,7 @@ export const deleteAllTables = async () => { export const seed = async () => { await deleteAllTables(); - await populateTables(); // tables with no relations + await populateTables(); await populateVoyages(); await populateUsers(); await populateSprints(); diff --git a/prisma/seed/tables.ts b/prisma/seed/tables.ts index 30301a6e..1e2aefd0 100644 --- a/prisma/seed/tables.ts +++ b/prisma/seed/tables.ts @@ -2,7 +2,7 @@ import Genders from "./data/genders"; import Tiers from "./data/tiers"; import VoyageRoles from "./data/voyage-roles"; import VoyageStatus from "./data/voyage-status"; -import TechStackCategories from "./data/tech-stack-categories"; +//import TechStackCategories from "./data/tech-stack-categories"; import FeatureCategories from "./data/feature-categories"; @@ -27,7 +27,6 @@ export const populateTables = async () => { await populateTable("role", Roles); await populateTable("voyageRole", VoyageRoles); await populateTable("voyageStatus", VoyageStatus); - await populateTable("techStackCategory", TechStackCategories); await populateTable("featureCategory", FeatureCategories); await populateTable("formType", FormTypes); await populateTable("inputType", InputTypes); diff --git a/prisma/seed/voyage-teams.ts b/prisma/seed/voyage-teams.ts index c9f971a7..bc34c673 100644 --- a/prisma/seed/voyage-teams.ts +++ b/prisma/seed/voyage-teams.ts @@ -1,4 +1,5 @@ import { prisma } from "./prisma-client"; +import { techStackCategoriesData } from "./data/tech-stack-categories"; export const populateVoyageTeams = async () => { const users = await prisma.user.findMany({}); @@ -1050,6 +1051,13 @@ export const populateVoyageTeams = async () => { }, }); + //Add Tech Stack Categories + //await populateTechStackCategories(); + for (const category of techStackCategoriesData) { + await prisma["TechStackCategory"].create({ data: category }); + } + console.log("TechStackCategories populated"); + const voyageTeamMembers = await prisma.voyageTeamMember.findMany({}); /* ============== Add tech stack items, etc to teams ================== */ From d3d44030ea6d441f85e09778d0980e304c3bea9c Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Mon, 9 Sep 2024 16:56:21 -0700 Subject: [PATCH 03/22] added tech ctaegory create, patch, delete routes to controller --- src/auth/auth.controller.spec.ts | 1 - src/techs/techs.controller.ts | 150 +++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/src/auth/auth.controller.spec.ts b/src/auth/auth.controller.spec.ts index f5cb5746..c0e6d0a1 100644 --- a/src/auth/auth.controller.spec.ts +++ b/src/auth/auth.controller.spec.ts @@ -1,7 +1,6 @@ import { Test, TestingModule } from "@nestjs/testing"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; - describe("AuthController", () => { let controller: AuthController; diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index ef5d8d41..d3eed5df 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -227,6 +227,156 @@ export class TechsController { ) { return this.techsService.deleteTeamTech(req, teamTechItemId); } + //****new */ + @ApiOperation({ + summary: + "[Permission: own_team] Adds a new tech (category to the team.", + description: "Requires login", + }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: "Successfully added a new tech stack category", + type: TechItemResponse, //new dto + }) + @ApiResponse({ + status: HttpStatus.CONFLICT, + description: "Tech stack category already exist for the team", + type: ConflictErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: "Invalid TeamId or userId", + type: BadRequestErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: "unauthorized access - user is not logged in", + type: UnauthorizedErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.FORBIDDEN, + description: "forbidden - user does not have the required permission", + type: ForbiddenErrorResponse, + }) + @ApiParam({ + name: "teamId", + description: "voyage team Id", + type: "Integer", + required: true, + example: 2, + }) + //@CheckAbilities({ action: Action.Create, subject: "TeamTechStackItem" }) + @Post("teams/:teamId/techCategory") + addNewTeamTechCategory( + @Request() req: CustomRequest, + @Param("teamId", ParseIntPipe) teamId: number, + @Body(ValidationPipe, VoyageTeamMemberValidationPipe) + createTeamTechDto: CreateTeamTechDto, + ) { + return this.techsService.addNewTeamTech(req, teamId, createTeamTechDto); + } + + @ApiOperation({ + summary: + "[Permission: own_team] Updates an existing tech stack category in the team", + description: "Requires login", + }) + @ApiResponse({ + status: HttpStatus.OK, + description: "Successfully updated a tech stack category", + type: TechItemUpdateResponse, + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: "unauthorized access - user is not logged in", + type: UnauthorizedErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.FORBIDDEN, + description: "forbidden - user does not have the required permission", + type: ForbiddenErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: "Bad Request - tech stack category couldn't be updated", + type: BadRequestErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.CONFLICT, + description: "Tech stack category already exists for the team", + type: ConflictErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: "Invalid tech stack category id", + type: NotFoundErrorResponse, + }) + @ApiParam({ + name: "teamTechItemId", + description: "team tech stack category Id", + type: "Integer", + required: true, + example: 1, + }) + //@CheckAbilities({ action: Action.Update, subject: "TeamTechStackItem" }) + @Patch("techs/:teamTechCategoryId") + updateTeamTechCategory( + @Request() req: CustomRequest, + @Param("teamTechItemId", ParseIntPipe) teamTechItemId: number, + @Body(ValidationPipe) updateTeamTechDto: UpdateTeamTechDto, + ) { + return this.techsService.updateExistingTeamTech( + req, + updateTeamTechDto, + teamTechItemId, + ); + } + + @ApiOperation({ + summary: + "[Permission: own_team] Delete a tech stack category of a team", + description: "Requires login", + }) + @ApiResponse({ + status: HttpStatus.OK, + description: "Tech stack category was successfully deleted", + type: TechItemDeleteResponse, + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: "unauthorized access - user is not logged in", + type: UnauthorizedErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.FORBIDDEN, + description: "forbidden - user does not have the required permission", + type: ForbiddenErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: "Bad Request - tech stack category couldn't be deleted", + type: BadRequestErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: "Invalid tech stack category id", + type: NotFoundErrorResponse, + }) + @ApiParam({ + name: "teamTechItemId", + description: "team tech stack item Id", + type: "Integer", + required: true, + example: 1, + }) + //@CheckAbilities({ action: Action.Delete, subject: "TeamTechStackItem" }) + @Delete("techs/:teamTechCategoryId") + deleteTeamTechCategory( + @Request() req: CustomRequest, + @Param("teamTechItemId", ParseIntPipe) teamTechItemId: number, + ) { + return this.techsService.deleteTeamTech(req, teamTechItemId); + } @ApiOperation({ summary: From 2c042c5302065492c1e041ecb1856a8b0f6cd256 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Thu, 19 Sep 2024 11:06:57 -0700 Subject: [PATCH 04/22] Added update and delete routes for techStackCategory --- .../dto/create-techstack-category.dto.ts | 24 ++++ src/techs/dto/update-tech.dto.ts | 2 +- .../dto/update-techstack-category.dto.ts | 24 ++++ src/techs/techs.controller.ts | 86 +++++++-------- src/techs/techs.response.ts | 20 ++++ src/techs/techs.service.ts | 104 ++++++++++++++++++ 6 files changed, 211 insertions(+), 49 deletions(-) create mode 100644 src/techs/dto/create-techstack-category.dto.ts create mode 100644 src/techs/dto/update-techstack-category.dto.ts diff --git a/src/techs/dto/create-techstack-category.dto.ts b/src/techs/dto/create-techstack-category.dto.ts new file mode 100644 index 00000000..c5f6e545 --- /dev/null +++ b/src/techs/dto/create-techstack-category.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsInt, IsNotEmpty, IsString } from "class-validator"; + +export class CreateTechStackCategoryDto { + @IsString() + @IsNotEmpty() + @ApiProperty({ example: "CDN" }) + name: string; + + @IsString() + @IsNotEmpty() + @ApiProperty({ example: "Host for static resources" }) + description: string; + + @IsInt() + @IsNotEmpty() + @ApiProperty({ example: 1 }) + voyageTeamId: number; + + @IsInt() + @IsNotEmpty() + @ApiProperty({ example: 4 }) + voyageTeamMemberId: number; +} diff --git a/src/techs/dto/update-tech.dto.ts b/src/techs/dto/update-tech.dto.ts index 5c37df2a..33eea54e 100644 --- a/src/techs/dto/update-tech.dto.ts +++ b/src/techs/dto/update-tech.dto.ts @@ -4,6 +4,6 @@ import { IsNotEmpty, IsString } from "class-validator"; export class UpdateTeamTechDto { @IsString() @IsNotEmpty() - @ApiProperty({ example: "Typescipt" }) + @ApiProperty({ example: "Typescript" }) techName: string; } diff --git a/src/techs/dto/update-techstack-category.dto.ts b/src/techs/dto/update-techstack-category.dto.ts new file mode 100644 index 00000000..2c70efd2 --- /dev/null +++ b/src/techs/dto/update-techstack-category.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsInt, IsNotEmpty, IsString } from "class-validator"; + +export class UpdateTechStackCategoryDto { + @IsString() + @IsNotEmpty() + @ApiProperty({ example: "ORM" }) + newName: string; + + @IsString() + @IsNotEmpty() + @ApiProperty({ example: "DB interface" }) + description: string; + + @IsInt() + @IsNotEmpty() + @ApiProperty({ example: 1 }) + categoryId: number; + + @IsInt() + @IsNotEmpty() + @ApiProperty({ example: 1 }) + voyageTeamId: number; +} diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index d3eed5df..57c6b4c1 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -15,11 +15,14 @@ import { TechsService } from "./techs.service"; import { ApiOperation, ApiParam, ApiResponse, ApiTags } from "@nestjs/swagger"; import { CreateTeamTechDto } from "./dto/create-tech.dto"; import { UpdateTechSelectionsDto } from "./dto/update-tech-selections.dto"; +import { CreateTechStackCategoryDto } from "./dto/create-techstack-category.dto"; +import { UpdateTechStackCategoryDto } from "./dto/update-techstack-category.dto"; import { TeamTechResponse, TechItemResponse, TechItemDeleteResponse, TechItemUpdateResponse, + TechCategoryResponse, } from "./techs.response"; import { BadRequestErrorResponse, @@ -230,50 +233,45 @@ export class TechsController { //****new */ @ApiOperation({ summary: - "[Permission: own_team] Adds a new tech (category to the team.", + "[Permission: own_team] Adds a new tech stack category to the team.", description: "Requires login", }) @ApiResponse({ status: HttpStatus.CREATED, description: "Successfully added a new tech stack category", - type: TechItemResponse, //new dto + type: TechCategoryResponse, //todo: new response }) @ApiResponse({ status: HttpStatus.CONFLICT, description: "Tech stack category already exist for the team", type: ConflictErrorResponse, }) + @ApiResponse({ + status: HttpStatus.FORBIDDEN, + description: "Forbidden - user is not a member of this team", + type: ForbiddenErrorResponse, + }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, - description: "Invalid TeamId or userId", + description: "Invalid userId", type: BadRequestErrorResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, - description: "unauthorized access - user is not logged in", + description: "Unauthorized access - user is not logged in", type: UnauthorizedErrorResponse, }) - @ApiResponse({ - status: HttpStatus.FORBIDDEN, - description: "forbidden - user does not have the required permission", - type: ForbiddenErrorResponse, - }) - @ApiParam({ - name: "teamId", - description: "voyage team Id", - type: "Integer", - required: true, - example: 2, - }) //@CheckAbilities({ action: Action.Create, subject: "TeamTechStackItem" }) - @Post("teams/:teamId/techCategory") - addNewTeamTechCategory( + @Post("teams/:teamId/techStackCategory") + addNewTechStackCategory( @Request() req: CustomRequest, - @Param("teamId", ParseIntPipe) teamId: number, @Body(ValidationPipe, VoyageTeamMemberValidationPipe) - createTeamTechDto: CreateTeamTechDto, + createTechStackCategoryDto: CreateTechStackCategoryDto, ) { - return this.techsService.addNewTeamTech(req, teamId, createTeamTechDto); + return this.techsService.addNewTechStackCategory( + req, + createTechStackCategoryDto, + ); } @ApiOperation({ @@ -311,30 +309,24 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - @ApiParam({ - name: "teamTechItemId", - description: "team tech stack category Id", - type: "Integer", - required: true, - example: 1, - }) //@CheckAbilities({ action: Action.Update, subject: "TeamTechStackItem" }) - @Patch("techs/:teamTechCategoryId") - updateTeamTechCategory( + @Patch("teams/:teamId/techStackCategory/:techStackCategoryId") + updateTechStackCategory( @Request() req: CustomRequest, - @Param("teamTechItemId", ParseIntPipe) teamTechItemId: number, - @Body(ValidationPipe) updateTeamTechDto: UpdateTeamTechDto, + @Param("teamId", ParseIntPipe) teamId: number, + @Param("techStackCategoryId", ParseIntPipe) techStackCategoryId: number, + @Body(ValidationPipe) + updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { - return this.techsService.updateExistingTeamTech( + return this.techsService.updateTechStackCategory( req, - updateTeamTechDto, - teamTechItemId, + teamId, + updateTechStackCategoryDto, ); } @ApiOperation({ - summary: - "[Permission: own_team] Delete a tech stack category of a team", + summary: "[Permission: own_team] Delete a team's tech stack category", description: "Requires login", }) @ApiResponse({ @@ -362,20 +354,18 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - @ApiParam({ - name: "teamTechItemId", - description: "team tech stack item Id", - type: "Integer", - required: true, - example: 1, - }) //@CheckAbilities({ action: Action.Delete, subject: "TeamTechStackItem" }) - @Delete("techs/:teamTechCategoryId") - deleteTeamTechCategory( + @Delete("teams/:teamId/techStackCategory/:techStackCategoryId") + deleteTechStackCategory( @Request() req: CustomRequest, - @Param("teamTechItemId", ParseIntPipe) teamTechItemId: number, + @Param("teamId", ParseIntPipe) teamId: number, + @Param("techStackCategoryId", ParseIntPipe) techStackCategoryId: number, ) { - return this.techsService.deleteTeamTech(req, teamTechItemId); + return this.techsService.deleteTechStackCategory( + req, + teamId, + techStackCategoryId, + ); } @ApiOperation({ diff --git a/src/techs/techs.response.ts b/src/techs/techs.response.ts index 8b0ffe59..979f077b 100644 --- a/src/techs/techs.response.ts +++ b/src/techs/techs.response.ts @@ -96,3 +96,23 @@ export class TechItemDeleteResponse { @ApiProperty({ example: 200 }) statusCode: number; } + +export class TechCategoryResponse { + @ApiProperty({ example: 10 }) + id: number; + + @ApiProperty({ example: "CDN" }) + name: string; + + @ApiProperty({ example: "Static storage" }) + description: string; + + @ApiProperty({ example: 1 }) + voyageTeamId: number; + + @ApiProperty({ example: "2023-12-01T13:55:00.611Z" }) + createdAt: Date; + + @ApiProperty({ example: "2023-12-01T13:55:00.611Z" }) + updatedAt: Date; +} diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index 229b498c..6a29cd67 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -8,6 +8,8 @@ import { } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service"; import { CreateTeamTechDto } from "./dto/create-tech.dto"; +import { CreateTechStackCategoryDto } from "./dto/create-techstack-category.dto"; +import { UpdateTechStackCategoryDto } from "./dto/update-techstack-category.dto"; import { UpdateTechSelectionsDto } from "./dto/update-tech-selections.dto"; import { UpdateTeamTechDto } from "./dto/update-tech.dto"; import { CustomRequest } from "../global/types/CustomRequest"; @@ -318,6 +320,108 @@ export class TechsService { } } + async addNewTechStackCategory( + req: CustomRequest, + createTechStackCategoryDto: CreateTechStackCategoryDto, + ) { + const teamId = createTechStackCategoryDto.voyageTeamId; + + //check if category name with teamid aready exists + const categoryAlreadyExists = + await this.prisma.techStackCategory.findFirst({ + where: { + voyageTeamId: createTechStackCategoryDto.voyageTeamId, + name: createTechStackCategoryDto.name, + }, + }); + if (categoryAlreadyExists) { + throw new ConflictException( + `${createTechStackCategoryDto.name} already exists in team ${teamId}'s tech stack.`, + ); + } + + try { + const categoryData = { + name: createTechStackCategoryDto.name, + description: createTechStackCategoryDto.description, + voyageTeamId: createTechStackCategoryDto.voyageTeamId, + }; + const newTeamTechCategory = + await this.prisma.techStackCategory.create({ + data: { ...categoryData }, + }); + return newTeamTechCategory; + } catch (e) { + throw e; + } + } + //new + async updateTechStackCategory( + req: CustomRequest, + teamId: number, + updateTechStackCategoryDto: UpdateTechStackCategoryDto, + ) { + //check for valid teamId + await this.validateTeamId(teamId); + + manageOwnVoyageTeamWithIdParam(req.user, teamId); + + //check if category name with teamid aready exists + const newCategoryAlreadyExists = + await this.prisma.techStackCategory.findFirst({ + where: { + voyageTeamId: updateTechStackCategoryDto.voyageTeamId, + name: updateTechStackCategoryDto.newName, + }, + }); + if (newCategoryAlreadyExists) { + throw new ConflictException( + `${updateTechStackCategoryDto.newName} already exists in team ${teamId}'s tech stack.`, + ); + } + + try { + // const categoryData = { name: updateTechStackCategoryDto.newName, + // description: updateTechStackCategoryDto.description, + // }; + const newTechStackCategory = + await this.prisma.techStackCategory.update({ + where: { + id: updateTechStackCategoryDto.categoryId, + }, + data: { + name: updateTechStackCategoryDto.newName, + description: updateTechStackCategoryDto.description, + // ...categoryData + }, + }); + return newTechStackCategory; + } catch (e) { + throw e; + } + } + + async deleteTechStackCategory( + req: CustomRequest, + teamId: number, + techStackCategoryId: number, + ) { + try { + //manageOwnVoyageTeamWithIdParam(req.user, teamTechItem.voyageTeamId); + + const deletedCategory = await this.prisma.techStackCategory.delete({ + where: { id: techStackCategoryId }, + }); + + return { + message: "The tech stack item is deleted", + statusCode: 200, + }; + } catch (e) { + throw e; + } + } + async addExistingTechVote(req: CustomRequest, teamTechItemId: number) { // check if team tech item exists const teamTechItem = await this.prisma.teamTechStackItem.findUnique({ From b9b4a9799200e967445ac00bf807e0a3b4cd0bbc Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Fri, 20 Sep 2024 17:17:40 -0700 Subject: [PATCH 05/22] dropped unique constraint from category names, also populated categories for all seed teams --- .../migration.sql | 2 ++ prisma/schema/teamStack.prisma | 2 +- prisma/seed/voyage-teams.ts | 23 ++++++------ src/techs/techs.controller.ts | 17 +++------ src/techs/techs.service.ts | 35 +++++++++++++------ 5 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 prisma/migrations/20240920234730_category_names_not_unique/migration.sql diff --git a/prisma/migrations/20240920234730_category_names_not_unique/migration.sql b/prisma/migrations/20240920234730_category_names_not_unique/migration.sql new file mode 100644 index 00000000..f33681c6 --- /dev/null +++ b/prisma/migrations/20240920234730_category_names_not_unique/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "TechStackCategory_name_key"; diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index 4350bc0e..d434e447 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -2,7 +2,7 @@ model TechStackCategory { id Int @id @default(autoincrement()) - name String @unique + name String description String voyageTeam VoyageTeam @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) voyageTeamId Int diff --git a/prisma/seed/voyage-teams.ts b/prisma/seed/voyage-teams.ts index bc34c673..79b3c999 100644 --- a/prisma/seed/voyage-teams.ts +++ b/prisma/seed/voyage-teams.ts @@ -1053,8 +1053,11 @@ export const populateVoyageTeams = async () => { //Add Tech Stack Categories //await populateTechStackCategories(); - for (const category of techStackCategoriesData) { - await prisma["TechStackCategory"].create({ data: category }); + for (let teamId = 1; teamId <= 11; teamId += 1) { + for (const category of techStackCategoriesData) { + category.voyageTeamId = teamId; + await prisma["TechStackCategory"].create({ data: category }); + } } console.log("TechStackCategories populated"); @@ -1074,7 +1077,7 @@ export const populateVoyageTeams = async () => { name: "Javascript", category: { connect: { - name: "Frontend", + id: 7, }, }, teamTechStackItemVotes: { @@ -1108,7 +1111,7 @@ export const populateVoyageTeams = async () => { name: "React", category: { connect: { - name: "Frontend", + id: 7, }, }, teamTechStackItemVotes: { @@ -1142,7 +1145,7 @@ export const populateVoyageTeams = async () => { name: "Tailwind", category: { connect: { - name: "CSS Library", + id: 8, }, }, teamTechStackItemVotes: { @@ -1176,7 +1179,7 @@ export const populateVoyageTeams = async () => { name: "Node", category: { connect: { - name: "Backend", + id: 9, }, }, teamTechStackItemVotes: { @@ -1210,7 +1213,7 @@ export const populateVoyageTeams = async () => { name: "Jira", category: { connect: { - name: "Project Management", + id: 10, }, }, teamTechStackItemVotes: { @@ -1244,7 +1247,7 @@ export const populateVoyageTeams = async () => { name: "Azure", category: { connect: { - name: "Cloud Provider", + id: 11, }, }, teamTechStackItemVotes: { @@ -1278,7 +1281,7 @@ export const populateVoyageTeams = async () => { name: "Netlify", category: { connect: { - name: "Hosting", + id: 12, }, }, teamTechStackItemVotes: { @@ -1312,7 +1315,7 @@ export const populateVoyageTeams = async () => { name: "Java", category: { connect: { - name: "Backend", + id: 9, }, }, teamTechStackItemVotes: { diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index 57c6b4c1..cc0ab94d 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -230,7 +230,7 @@ export class TechsController { ) { return this.techsService.deleteTeamTech(req, teamTechItemId); } - //****new */ + @ApiOperation({ summary: "[Permission: own_team] Adds a new tech stack category to the team.", @@ -239,18 +239,13 @@ export class TechsController { @ApiResponse({ status: HttpStatus.CREATED, description: "Successfully added a new tech stack category", - type: TechCategoryResponse, //todo: new response + type: TechCategoryResponse, }) @ApiResponse({ status: HttpStatus.CONFLICT, description: "Tech stack category already exist for the team", type: ConflictErrorResponse, }) - @ApiResponse({ - status: HttpStatus.FORBIDDEN, - description: "Forbidden - user is not a member of this team", - type: ForbiddenErrorResponse, - }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: "Invalid userId", @@ -265,6 +260,7 @@ export class TechsController { @Post("teams/:teamId/techStackCategory") addNewTechStackCategory( @Request() req: CustomRequest, + @Param("teamId", ParseIntPipe) teamTId: number, @Body(ValidationPipe, VoyageTeamMemberValidationPipe) createTechStackCategoryDto: CreateTechStackCategoryDto, ) { @@ -282,7 +278,7 @@ export class TechsController { @ApiResponse({ status: HttpStatus.OK, description: "Successfully updated a tech stack category", - type: TechItemUpdateResponse, + type: TechCategoryResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, @@ -310,17 +306,14 @@ export class TechsController { type: NotFoundErrorResponse, }) //@CheckAbilities({ action: Action.Update, subject: "TeamTechStackItem" }) - @Patch("teams/:teamId/techStackCategory/:techStackCategoryId") + @Patch("teams/techStackCategory") updateTechStackCategory( @Request() req: CustomRequest, - @Param("teamId", ParseIntPipe) teamId: number, - @Param("techStackCategoryId", ParseIntPipe) techStackCategoryId: number, @Body(ValidationPipe) updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { return this.techsService.updateTechStackCategory( req, - teamId, updateTechStackCategoryDto, ); } diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index 6a29cd67..eaae0e18 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -326,6 +326,8 @@ export class TechsService { ) { const teamId = createTechStackCategoryDto.voyageTeamId; + manageOwnVoyageTeamWithIdParam(req.user, teamId); + //check if category name with teamid aready exists const categoryAlreadyExists = await this.prisma.techStackCategory.findFirst({ @@ -355,16 +357,18 @@ export class TechsService { throw e; } } - //new + async updateTechStackCategory( req: CustomRequest, - teamId: number, updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { - //check for valid teamId - await this.validateTeamId(teamId); + const teamId = updateTechStackCategoryDto.voyageTeamId; manageOwnVoyageTeamWithIdParam(req.user, teamId); + await this.teamOwnsCategory( + teamId, + updateTechStackCategoryDto.categoryId, + ); //check if category name with teamid aready exists const newCategoryAlreadyExists = @@ -381,9 +385,6 @@ export class TechsService { } try { - // const categoryData = { name: updateTechStackCategoryDto.newName, - // description: updateTechStackCategoryDto.description, - // }; const newTechStackCategory = await this.prisma.techStackCategory.update({ where: { @@ -392,7 +393,6 @@ export class TechsService { data: { name: updateTechStackCategoryDto.newName, description: updateTechStackCategoryDto.description, - // ...categoryData }, }); return newTechStackCategory; @@ -406,9 +406,10 @@ export class TechsService { teamId: number, techStackCategoryId: number, ) { - try { - //manageOwnVoyageTeamWithIdParam(req.user, teamTechItem.voyageTeamId); + manageOwnVoyageTeamWithIdParam(req.user, teamId); + await this.teamOwnsCategory(teamId, techStackCategoryId); + try { const deletedCategory = await this.prisma.techStackCategory.delete({ where: { id: techStackCategoryId }, }); @@ -533,4 +534,18 @@ export class TechsService { throw e; } } + + private async teamOwnsCategory(teamId: number, categoryId: number) { + const match = await this.prisma.techStackCategory.findFirst({ + where: { + id: categoryId, + voyageTeamId: teamId, + }, + }); + if (!match) { + throw new BadRequestException( + `Category ${categoryId} does not belong to team ${teamId}`, + ); + } + } } From 8c3ffc93980a0013df78b15262c33bc802ac42f6 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 21 Sep 2024 16:23:38 -0700 Subject: [PATCH 06/22] updated get teams/{teamId}/techs response, corrected 'isSelected' location in swagger response --- prisma/seed/voyage-teams.ts | 1 - src/techs/techs.response.ts | 6 +++--- src/techs/techs.service.ts | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/prisma/seed/voyage-teams.ts b/prisma/seed/voyage-teams.ts index bf0fdbe8..1bbbf8e3 100644 --- a/prisma/seed/voyage-teams.ts +++ b/prisma/seed/voyage-teams.ts @@ -1052,7 +1052,6 @@ export const populateVoyageTeams = async () => { }); //Add Tech Stack Categories - //await populateTechStackCategories(); for (let teamId = 1; teamId <= 11; teamId += 1) { for (const category of techStackCategoriesData) { category.voyageTeamId = teamId; diff --git a/src/techs/techs.response.ts b/src/techs/techs.response.ts index 979f077b..de7b3020 100644 --- a/src/techs/techs.response.ts +++ b/src/techs/techs.response.ts @@ -34,6 +34,9 @@ class TeamTechStackItem { @ApiProperty({ example: "Javascript" }) name: string; + @ApiProperty({ example: false }) + isSelected: boolean; + @ApiProperty({ isArray: true }) teamTechStackItemVotes: TeamTechStackItemVote; } @@ -48,9 +51,6 @@ export class TeamTechResponse { @ApiProperty({ example: "Frontend Stuff" }) description: string; - @ApiProperty({ example: false }) - isSelected: boolean; - @ApiProperty({ isArray: true }) teamTechStackItems: TeamTechStackItem; } diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index eaae0e18..c3bb59cb 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -38,14 +38,14 @@ export class TechsService { manageOwnVoyageTeamWithIdParam(req.user, teamId); return this.prisma.techStackCategory.findMany({ + where: { + voyageTeamId: teamId, + }, select: { id: true, name: true, description: true, teamTechStackItems: { - where: { - voyageTeamId: teamId, - }, select: { id: true, name: true, From 6bae8a0db4120ac31e4f476597d4595457ec6b88 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 21 Sep 2024 16:40:26 -0700 Subject: [PATCH 07/22] misc revisions --- src/techs/techs.controller.ts | 6 ++---- src/techs/techs.response.ts | 8 ++++++++ src/techs/techs.service.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index cc0ab94d..897e367e 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -23,6 +23,7 @@ import { TechItemDeleteResponse, TechItemUpdateResponse, TechCategoryResponse, + TechCategoryDeleteResponse, } from "./techs.response"; import { BadRequestErrorResponse, @@ -256,7 +257,6 @@ export class TechsController { description: "Unauthorized access - user is not logged in", type: UnauthorizedErrorResponse, }) - //@CheckAbilities({ action: Action.Create, subject: "TeamTechStackItem" }) @Post("teams/:teamId/techStackCategory") addNewTechStackCategory( @Request() req: CustomRequest, @@ -305,7 +305,6 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - //@CheckAbilities({ action: Action.Update, subject: "TeamTechStackItem" }) @Patch("teams/techStackCategory") updateTechStackCategory( @Request() req: CustomRequest, @@ -325,7 +324,7 @@ export class TechsController { @ApiResponse({ status: HttpStatus.OK, description: "Tech stack category was successfully deleted", - type: TechItemDeleteResponse, + type: TechCategoryDeleteResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, @@ -347,7 +346,6 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - //@CheckAbilities({ action: Action.Delete, subject: "TeamTechStackItem" }) @Delete("teams/:teamId/techStackCategory/:techStackCategoryId") deleteTechStackCategory( @Request() req: CustomRequest, diff --git a/src/techs/techs.response.ts b/src/techs/techs.response.ts index de7b3020..8d4f34ae 100644 --- a/src/techs/techs.response.ts +++ b/src/techs/techs.response.ts @@ -116,3 +116,11 @@ export class TechCategoryResponse { @ApiProperty({ example: "2023-12-01T13:55:00.611Z" }) updatedAt: Date; } + +export class TechCategoryDeleteResponse { + @ApiProperty({ example: "The tech stack category was deleted" }) + message: string; + + @ApiProperty({ example: 200 }) + statusCode: number; +} diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index c3bb59cb..3d21862b 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -415,7 +415,7 @@ export class TechsService { }); return { - message: "The tech stack item is deleted", + message: "The tech stack icategory is deleted", statusCode: 200, }; } catch (e) { From ce7043b63e0f8448e5b55cb2e3f2f8f2bd43d139 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 21 Sep 2024 16:50:16 -0700 Subject: [PATCH 08/22] updated changelog --- CHANGELOG.md | 2 ++ src/techs/techs.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e081b7bc..fb6cf98d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ Another example [here](https://co-pilot.dev/changelog) - Add morgan middleware for request logging with custom logger ([#200](https://github.com/chingu-x/chingu-dashboard-be/pull/200)) - Add CASL permissions for Team Sprint endpoint ([#193](https://github.com/chingu-x/chingu-dashboard-be/pull/193)) - Add units tests for the teams resource controller & services([#201](https://github.com/chingu-x/chingu-dashboard-be/pull/201)) +- Added routes for teams to create own tech stack categories([#208](https://github.com/chingu-x/chingu-dashboard-be/pull/208)) + ### Changed - updated changelog ([#195](https://github.com/chingu-x/chingu-dashboard-be/pull/195)) diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index 3d21862b..fcc43c1c 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -410,12 +410,12 @@ export class TechsService { await this.teamOwnsCategory(teamId, techStackCategoryId); try { - const deletedCategory = await this.prisma.techStackCategory.delete({ + await this.prisma.techStackCategory.delete({ where: { id: techStackCategoryId }, }); return { - message: "The tech stack icategory is deleted", + message: "The tech stack category is deleted", statusCode: 200, }; } catch (e) { From 42489a70d86f2dc71e2520dd1e19c4997daaee99 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Sat, 21 Sep 2024 16:56:20 -0700 Subject: [PATCH 09/22] Update seed.ts - removed comment --- prisma/seed/seed.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/prisma/seed/seed.ts b/prisma/seed/seed.ts index e51dc305..27a24bcf 100644 --- a/prisma/seed/seed.ts +++ b/prisma/seed/seed.ts @@ -3,7 +3,6 @@ import { populateVoyages } from "./voyage"; import { populateUsers } from "./users"; import { populateSprints } from "./sprints"; import { populateVoyageTeams } from "./voyage-teams"; -//import { populateTechStackCategories } from "./data/tech-stack-categories"; import { populateTeamResourcesAndProjectIdeas } from "./resources-project-ideas"; import { populateFormsAndResponses } from "./forms"; import { populateMeetings } from "./meetings"; From 524d8c94a05ad2e6af4cd93ba14787de0efb58f2 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Sat, 21 Sep 2024 16:57:07 -0700 Subject: [PATCH 10/22] Update tables.ts - removed comment --- prisma/seed/tables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/prisma/seed/tables.ts b/prisma/seed/tables.ts index 1e2aefd0..c5a76413 100644 --- a/prisma/seed/tables.ts +++ b/prisma/seed/tables.ts @@ -2,7 +2,6 @@ import Genders from "./data/genders"; import Tiers from "./data/tiers"; import VoyageRoles from "./data/voyage-roles"; import VoyageStatus from "./data/voyage-status"; -//import TechStackCategories from "./data/tech-stack-categories"; import FeatureCategories from "./data/feature-categories"; From 906f41a8e7112d5b85c191cf230dcb4ebaa2fe02 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Sat, 21 Sep 2024 16:58:04 -0700 Subject: [PATCH 11/22] Update seed.ts - comment --- prisma/seed/seed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/seed/seed.ts b/prisma/seed/seed.ts index 27a24bcf..eecc1860 100644 --- a/prisma/seed/seed.ts +++ b/prisma/seed/seed.ts @@ -34,7 +34,7 @@ export const deleteAllTables = async () => { export const seed = async () => { await deleteAllTables(); - await populateTables(); + await populateTables(); //tables with no relations await populateVoyages(); await populateUsers(); await populateSprints(); From 50fe8e1a32855f04a64c3614c3fd33fcf2d202dd Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 21 Sep 2024 19:56:11 -0700 Subject: [PATCH 12/22] changed voyageTeamId to optional to pass railway check --- prisma/migrations/20240922025043_/migration.sql | 2 ++ prisma/schema/teamStack.prisma | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20240922025043_/migration.sql diff --git a/prisma/migrations/20240922025043_/migration.sql b/prisma/migrations/20240922025043_/migration.sql new file mode 100644 index 00000000..6cd58e4b --- /dev/null +++ b/prisma/migrations/20240922025043_/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "TechStackCategory" ALTER COLUMN "voyageTeamId" DROP NOT NULL; diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index d434e447..b98cb2a8 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -4,8 +4,8 @@ model TechStackCategory { id Int @id @default(autoincrement()) name String description String - voyageTeam VoyageTeam @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) - voyageTeamId Int + voyageTeam VoyageTeam? @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) + voyageTeamId Int? createdAt DateTime @default(now()) @db.Timestamptz() updatedAt DateTime @updatedAt From 78d33416d6ac1d0e4298c1ac87991de66db13162 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Mon, 23 Sep 2024 19:19:50 -0700 Subject: [PATCH 13/22] Update prisma/schema/voyage.prisma Co-authored-by: Cheryl M --- prisma/schema/voyage.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema/voyage.prisma b/prisma/schema/voyage.prisma index a1a937ad..c731f069 100644 --- a/prisma/schema/voyage.prisma +++ b/prisma/schema/voyage.prisma @@ -91,7 +91,7 @@ model VoyageTeam { voyageTeamMembers VoyageTeamMember[] teamTechStackItems TeamTechStackItem[] - TechStackCategory TechStackCategory[] + techStackCategory TechStackCategory[] teamMeetings TeamMeeting[] FormResponseVoyageProject FormResponseVoyageProject? } From 83e852d371fb2537677e4cce7d0447ac2aba3cdc Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 28 Sep 2024 18:33:17 -0700 Subject: [PATCH 14/22] combined migrations, removed voyageTeamId from GET route body, removed voyageTeamId and voyageTeamMemberId from create categroy dto --- .../migration.sql | 2 -- prisma/migrations/20240922025043_/migration.sql | 2 -- .../migration.sql | 9 +++------ src/techs/dto/create-techstack-category.dto.ts | 10 ---------- src/techs/dto/update-techstack-category.dto.ts | 5 ----- src/techs/techs.controller.ts | 9 ++++++--- src/techs/techs.service.ts | 15 ++++++--------- 7 files changed, 15 insertions(+), 37 deletions(-) delete mode 100644 prisma/migrations/20240920234730_category_names_not_unique/migration.sql delete mode 100644 prisma/migrations/20240922025043_/migration.sql rename prisma/migrations/{20240906130941_techstack_category_teamid => 20240926232539_update_tech_stack_catgeory}/migration.sql (59%) diff --git a/prisma/migrations/20240920234730_category_names_not_unique/migration.sql b/prisma/migrations/20240920234730_category_names_not_unique/migration.sql deleted file mode 100644 index f33681c6..00000000 --- a/prisma/migrations/20240920234730_category_names_not_unique/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- DropIndex -DROP INDEX "TechStackCategory_name_key"; diff --git a/prisma/migrations/20240922025043_/migration.sql b/prisma/migrations/20240922025043_/migration.sql deleted file mode 100644 index 6cd58e4b..00000000 --- a/prisma/migrations/20240922025043_/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "TechStackCategory" ALTER COLUMN "voyageTeamId" DROP NOT NULL; diff --git a/prisma/migrations/20240906130941_techstack_category_teamid/migration.sql b/prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql similarity index 59% rename from prisma/migrations/20240906130941_techstack_category_teamid/migration.sql rename to prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql index a22fa3e4..c84fac3a 100644 --- a/prisma/migrations/20240906130941_techstack_category_teamid/migration.sql +++ b/prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql @@ -1,11 +1,8 @@ -/* - Warnings: +-- DropIndex +DROP INDEX "TechStackCategory_name_key"; - - Added the required column `voyageTeamId` to the `TechStackCategory` table without a default value. This is not possible if the table is not empty. - -*/ -- AlterTable -ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamId" INTEGER NOT NULL; +ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamId" INTEGER; -- AddForeignKey ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamId_fkey" FOREIGN KEY ("voyageTeamId") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/techs/dto/create-techstack-category.dto.ts b/src/techs/dto/create-techstack-category.dto.ts index c5f6e545..8ad68938 100644 --- a/src/techs/dto/create-techstack-category.dto.ts +++ b/src/techs/dto/create-techstack-category.dto.ts @@ -11,14 +11,4 @@ export class CreateTechStackCategoryDto { @IsNotEmpty() @ApiProperty({ example: "Host for static resources" }) description: string; - - @IsInt() - @IsNotEmpty() - @ApiProperty({ example: 1 }) - voyageTeamId: number; - - @IsInt() - @IsNotEmpty() - @ApiProperty({ example: 4 }) - voyageTeamMemberId: number; } diff --git a/src/techs/dto/update-techstack-category.dto.ts b/src/techs/dto/update-techstack-category.dto.ts index 2c70efd2..63cc65b2 100644 --- a/src/techs/dto/update-techstack-category.dto.ts +++ b/src/techs/dto/update-techstack-category.dto.ts @@ -12,11 +12,6 @@ export class UpdateTechStackCategoryDto { @ApiProperty({ example: "DB interface" }) description: string; - @IsInt() - @IsNotEmpty() - @ApiProperty({ example: 1 }) - categoryId: number; - @IsInt() @IsNotEmpty() @ApiProperty({ example: 1 }) diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index 897e367e..a278e4d2 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -260,12 +260,13 @@ export class TechsController { @Post("teams/:teamId/techStackCategory") addNewTechStackCategory( @Request() req: CustomRequest, - @Param("teamId", ParseIntPipe) teamTId: number, - @Body(ValidationPipe, VoyageTeamMemberValidationPipe) + @Param("teamId", ParseIntPipe) teamId: number, + @Body(ValidationPipe) createTechStackCategoryDto: CreateTechStackCategoryDto, ) { return this.techsService.addNewTechStackCategory( req, + teamId, createTechStackCategoryDto, ); } @@ -305,14 +306,16 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - @Patch("teams/techStackCategory") + @Patch("teams/techStackCategory/:techStackCategoryId") updateTechStackCategory( @Request() req: CustomRequest, + @Param("techStackCategoryId", ParseIntPipe) techStackCategoryId: number, @Body(ValidationPipe) updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { return this.techsService.updateTechStackCategory( req, + techStackCategoryId, updateTechStackCategoryDto, ); } diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index fcc43c1c..bddf0b50 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -322,17 +322,16 @@ export class TechsService { async addNewTechStackCategory( req: CustomRequest, + teamId: number, createTechStackCategoryDto: CreateTechStackCategoryDto, ) { - const teamId = createTechStackCategoryDto.voyageTeamId; - manageOwnVoyageTeamWithIdParam(req.user, teamId); //check if category name with teamid aready exists const categoryAlreadyExists = await this.prisma.techStackCategory.findFirst({ where: { - voyageTeamId: createTechStackCategoryDto.voyageTeamId, + voyageTeamId: teamId, name: createTechStackCategoryDto.name, }, }); @@ -346,7 +345,7 @@ export class TechsService { const categoryData = { name: createTechStackCategoryDto.name, description: createTechStackCategoryDto.description, - voyageTeamId: createTechStackCategoryDto.voyageTeamId, + voyageTeamId: teamId, }; const newTeamTechCategory = await this.prisma.techStackCategory.create({ @@ -360,15 +359,13 @@ export class TechsService { async updateTechStackCategory( req: CustomRequest, + techStackCategoryId: number, updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { const teamId = updateTechStackCategoryDto.voyageTeamId; manageOwnVoyageTeamWithIdParam(req.user, teamId); - await this.teamOwnsCategory( - teamId, - updateTechStackCategoryDto.categoryId, - ); + await this.teamOwnsCategory(teamId, techStackCategoryId); //check if category name with teamid aready exists const newCategoryAlreadyExists = @@ -388,7 +385,7 @@ export class TechsService { const newTechStackCategory = await this.prisma.techStackCategory.update({ where: { - id: updateTechStackCategoryDto.categoryId, + id: techStackCategoryId, }, data: { name: updateTechStackCategoryDto.newName, From 115a92b5d436e34d2bf1de341ff5dd55f5b7d55f Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sat, 28 Sep 2024 18:37:43 -0700 Subject: [PATCH 15/22] fixed linter error --- src/techs/dto/create-techstack-category.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/techs/dto/create-techstack-category.dto.ts b/src/techs/dto/create-techstack-category.dto.ts index 8ad68938..ae582aeb 100644 --- a/src/techs/dto/create-techstack-category.dto.ts +++ b/src/techs/dto/create-techstack-category.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsInt, IsNotEmpty, IsString } from "class-validator"; +import { IsNotEmpty, IsString } from "class-validator"; export class CreateTechStackCategoryDto { @IsString() From ec1fc813f6dd565f702877d6ceaac1004e2e9488 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Wed, 16 Oct 2024 21:23:17 -0700 Subject: [PATCH 16/22] updated URIs, updated DTOs, added case-insensitive category name query --- src/global/types/CustomRequest.ts | 2 +- src/techs/techs.controller.ts | 4 +-- src/techs/techs.service.ts | 57 ++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/global/types/CustomRequest.ts b/src/global/types/CustomRequest.ts index 6aba02ef..f1b0aa80 100644 --- a/src/global/types/CustomRequest.ts +++ b/src/global/types/CustomRequest.ts @@ -1,6 +1,6 @@ import { Request } from "express"; -type VoyageTeam = { +export type VoyageTeam = { teamId: number; memberId: number; }; diff --git a/src/techs/techs.controller.ts b/src/techs/techs.controller.ts index a278e4d2..0bc46c8c 100644 --- a/src/techs/techs.controller.ts +++ b/src/techs/techs.controller.ts @@ -349,15 +349,13 @@ export class TechsController { description: "Invalid tech stack category id", type: NotFoundErrorResponse, }) - @Delete("teams/:teamId/techStackCategory/:techStackCategoryId") + @Delete("teams/techStackCategory/:techStackCategoryId") deleteTechStackCategory( @Request() req: CustomRequest, - @Param("teamId", ParseIntPipe) teamId: number, @Param("techStackCategoryId", ParseIntPipe) techStackCategoryId: number, ) { return this.techsService.deleteTechStackCategory( req, - teamId, techStackCategoryId, ); } diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index bddf0b50..1a2eb4da 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -7,12 +7,13 @@ import { NotFoundException, } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service"; +import { Prisma } from "@prisma/client"; import { CreateTeamTechDto } from "./dto/create-tech.dto"; import { CreateTechStackCategoryDto } from "./dto/create-techstack-category.dto"; import { UpdateTechStackCategoryDto } from "./dto/update-techstack-category.dto"; import { UpdateTechSelectionsDto } from "./dto/update-tech-selections.dto"; import { UpdateTeamTechDto } from "./dto/update-tech.dto"; -import { CustomRequest } from "../global/types/CustomRequest"; +import { CustomRequest, VoyageTeam } from "../global/types/CustomRequest"; import { manageOwnVoyageTeamWithIdParam } from "../ability/conditions/voyage-teams.ability"; const MAX_SELECTION_COUNT = 3; @@ -332,12 +333,15 @@ export class TechsService { await this.prisma.techStackCategory.findFirst({ where: { voyageTeamId: teamId, - name: createTechStackCategoryDto.name, + name: { + equals: createTechStackCategoryDto.name, + mode: "insensitive", + }, }, }); if (categoryAlreadyExists) { throw new ConflictException( - `${createTechStackCategoryDto.name} already exists in team ${teamId}'s tech stack.`, + `${createTechStackCategoryDto.name}, or a case-insensitive match, already exists in team ${teamId}'s tech stack.`, ); } @@ -362,22 +366,30 @@ export class TechsService { techStackCategoryId: number, updateTechStackCategoryDto: UpdateTechStackCategoryDto, ) { - const teamId = updateTechStackCategoryDto.voyageTeamId; - - manageOwnVoyageTeamWithIdParam(req.user, teamId); - await this.teamOwnsCategory(teamId, techStackCategoryId); + const permission = await this.userCanChangeCategory( + techStackCategoryId, + req.user.voyageTeams, + ); + if (!permission) { + throw new ForbiddenException( + `This user cannot change category ${techStackCategoryId}`, + ); + } //check if category name with teamid aready exists const newCategoryAlreadyExists = await this.prisma.techStackCategory.findFirst({ where: { voyageTeamId: updateTechStackCategoryDto.voyageTeamId, - name: updateTechStackCategoryDto.newName, + name: { + equals: updateTechStackCategoryDto.newName, + mode: "insensitive", + }, }, }); if (newCategoryAlreadyExists) { throw new ConflictException( - `${updateTechStackCategoryDto.newName} already exists in team ${teamId}'s tech stack.`, + `${updateTechStackCategoryDto.newName}, or a case-insensitive match, already exists in team's tech stack.`, ); } @@ -400,11 +412,17 @@ export class TechsService { async deleteTechStackCategory( req: CustomRequest, - teamId: number, techStackCategoryId: number, ) { - manageOwnVoyageTeamWithIdParam(req.user, teamId); - await this.teamOwnsCategory(teamId, techStackCategoryId); + const permission = await this.userCanChangeCategory( + techStackCategoryId, + req.user.voyageTeams, + ); + if (!permission) { + throw new ForbiddenException( + `This user cannot change category ${techStackCategoryId}`, + ); + } try { await this.prisma.techStackCategory.delete({ @@ -532,17 +550,16 @@ export class TechsService { } } - private async teamOwnsCategory(teamId: number, categoryId: number) { - const match = await this.prisma.techStackCategory.findFirst({ + private async userCanChangeCategory( + categoryId: number, + voyageTeams: VoyageTeam[], + ) { + const match = await this.prisma.techStackCategory.findUnique({ where: { id: categoryId, - voyageTeamId: teamId, }, }); - if (!match) { - throw new BadRequestException( - `Category ${categoryId} does not belong to team ${teamId}`, - ); - } + + return voyageTeams.some((team) => team.teamId === match?.voyageTeamId); } } From f52bd59a9e1dcbdde1b950a786e4222152da0f09 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Wed, 16 Oct 2024 21:26:45 -0700 Subject: [PATCH 17/22] Update techs.service.ts --- src/techs/techs.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index 1a2eb4da..20b0d58e 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -7,7 +7,6 @@ import { NotFoundException, } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service"; -import { Prisma } from "@prisma/client"; import { CreateTeamTechDto } from "./dto/create-tech.dto"; import { CreateTechStackCategoryDto } from "./dto/create-techstack-category.dto"; import { UpdateTechStackCategoryDto } from "./dto/update-techstack-category.dto"; From 7b28961f111e81f8463cd2d90f402ebc9bda5197 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Thu, 17 Oct 2024 15:55:00 -0700 Subject: [PATCH 18/22] changed category name to citext type , added unique name AND teamId constraint --- .../migration.sql | 11 +++++ prisma/schema/teamStack.prisma | 4 +- .../dto/update-techstack-category.dto.ts | 5 --- src/techs/techs.service.ts | 44 +++++-------------- 4 files changed, 24 insertions(+), 40 deletions(-) create mode 100644 prisma/migrations/20241017222548_category_name_citext_unique/migration.sql diff --git a/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql b/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql new file mode 100644 index 00000000..5d0e6cf3 --- /dev/null +++ b/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[name,voyageTeamId]` on the table `TechStackCategory` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "TechStackCategory" ALTER COLUMN "name" SET DATA TYPE CITEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "TechStackCategory_name_voyageTeamId_key" ON "TechStackCategory"("name", "voyageTeamId"); diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index b98cb2a8..16626844 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -2,7 +2,7 @@ model TechStackCategory { id Int @id @default(autoincrement()) - name String + name String @db.Citext description String voyageTeam VoyageTeam? @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) voyageTeamId Int? @@ -11,6 +11,8 @@ model TechStackCategory { updatedAt DateTime @updatedAt teamTechStackItems TeamTechStackItem[] + + @@unique(fields: [name, voyageTeamId], name: "teamCategoryUniqueKey") } model TeamTechStackItem { diff --git a/src/techs/dto/update-techstack-category.dto.ts b/src/techs/dto/update-techstack-category.dto.ts index 63cc65b2..e93825db 100644 --- a/src/techs/dto/update-techstack-category.dto.ts +++ b/src/techs/dto/update-techstack-category.dto.ts @@ -11,9 +11,4 @@ export class UpdateTechStackCategoryDto { @IsNotEmpty() @ApiProperty({ example: "DB interface" }) description: string; - - @IsInt() - @IsNotEmpty() - @ApiProperty({ example: 1 }) - voyageTeamId: number; } diff --git a/src/techs/techs.service.ts b/src/techs/techs.service.ts index 20b0d58e..3dbdb61c 100644 --- a/src/techs/techs.service.ts +++ b/src/techs/techs.service.ts @@ -327,23 +327,6 @@ export class TechsService { ) { manageOwnVoyageTeamWithIdParam(req.user, teamId); - //check if category name with teamid aready exists - const categoryAlreadyExists = - await this.prisma.techStackCategory.findFirst({ - where: { - voyageTeamId: teamId, - name: { - equals: createTechStackCategoryDto.name, - mode: "insensitive", - }, - }, - }); - if (categoryAlreadyExists) { - throw new ConflictException( - `${createTechStackCategoryDto.name}, or a case-insensitive match, already exists in team ${teamId}'s tech stack.`, - ); - } - try { const categoryData = { name: createTechStackCategoryDto.name, @@ -356,6 +339,11 @@ export class TechsService { }); return newTeamTechCategory; } catch (e) { + if (e.code === "P2002") { + throw new ConflictException( + `Category ${createTechStackCategoryDto.name} already exists for this team.`, + ); + } throw e; } } @@ -375,23 +363,6 @@ export class TechsService { ); } - //check if category name with teamid aready exists - const newCategoryAlreadyExists = - await this.prisma.techStackCategory.findFirst({ - where: { - voyageTeamId: updateTechStackCategoryDto.voyageTeamId, - name: { - equals: updateTechStackCategoryDto.newName, - mode: "insensitive", - }, - }, - }); - if (newCategoryAlreadyExists) { - throw new ConflictException( - `${updateTechStackCategoryDto.newName}, or a case-insensitive match, already exists in team's tech stack.`, - ); - } - try { const newTechStackCategory = await this.prisma.techStackCategory.update({ @@ -405,6 +376,11 @@ export class TechsService { }); return newTechStackCategory; } catch (e) { + if (e.code === "P2002") { + throw new ConflictException( + `Category ${updateTechStackCategoryDto.newName} already exists for this team.`, + ); + } throw e; } } From bdc670036fd724fbfda4871c1e0a4bb650b6fbb0 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Thu, 17 Oct 2024 16:06:46 -0700 Subject: [PATCH 19/22] removed isInt import from update category dto --- src/techs/dto/update-techstack-category.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/techs/dto/update-techstack-category.dto.ts b/src/techs/dto/update-techstack-category.dto.ts index e93825db..cd29bab2 100644 --- a/src/techs/dto/update-techstack-category.dto.ts +++ b/src/techs/dto/update-techstack-category.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsInt, IsNotEmpty, IsString } from "class-validator"; +import { IsNotEmpty, IsString } from "class-validator"; export class UpdateTechStackCategoryDto { @IsString() From 04af186997925c6514f53a0a9c48dc25868be56f Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Sun, 20 Oct 2024 10:50:27 -0700 Subject: [PATCH 20/22] added voyageteamIdNew column to category schema --- .../migration.sql | 5 +++++ prisma/schema/teamStack.prisma | 7 +++++-- prisma/schema/voyage.prisma | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20241020174120_category_new_teamid_column/migration.sql diff --git a/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql b/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql new file mode 100644 index 00000000..313bb59a --- /dev/null +++ b/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamIdNew" INTEGER; + +-- AddForeignKey +ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamIdNew_fkey" FOREIGN KEY ("voyageTeamIdNew") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index 16626844..34845496 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -4,8 +4,11 @@ model TechStackCategory { id Int @id @default(autoincrement()) name String @db.Citext description String - voyageTeam VoyageTeam? @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) - voyageTeamId Int? + voyageTeam VoyageTeam? @relation("Team", fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) + voyageTeamId Int? + + voyageTeamNew VoyageTeam? @relation("NewTeam", fields: [voyageTeamIdNew], references: [id], onUpdate: Cascade, onDelete: Cascade) + voyageTeamIdNew Int? createdAt DateTime @default(now()) @db.Timestamptz() updatedAt DateTime @updatedAt diff --git a/prisma/schema/voyage.prisma b/prisma/schema/voyage.prisma index c731f069..a0e1f58e 100644 --- a/prisma/schema/voyage.prisma +++ b/prisma/schema/voyage.prisma @@ -91,7 +91,10 @@ model VoyageTeam { voyageTeamMembers VoyageTeamMember[] teamTechStackItems TeamTechStackItem[] - techStackCategory TechStackCategory[] + techStackCategory TechStackCategory[] @relation("Team") + techStackCategoryNew TechStackCategory[] @relation("NewTeam") + + teamMeetings TeamMeeting[] FormResponseVoyageProject FormResponseVoyageProject? } From 49af84a806c2b6b5c3f1269433e4b71636c48a01 Mon Sep 17 00:00:00 2001 From: Josh Hinman Date: Wed, 23 Oct 2024 12:15:05 -0700 Subject: [PATCH 21/22] revised categories schema , migrations --- .../migration.sql | 8 -------- .../migration.sql | 11 ----------- .../migration.sql | 5 ----- .../migration.sql | 18 ++++++++++++++++++ prisma/schema/teamStack.prisma | 9 +++------ prisma/schema/voyage.prisma | 5 +---- 6 files changed, 22 insertions(+), 34 deletions(-) delete mode 100644 prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql delete mode 100644 prisma/migrations/20241017222548_category_name_citext_unique/migration.sql delete mode 100644 prisma/migrations/20241020174120_category_new_teamid_column/migration.sql create mode 100644 prisma/migrations/20241023191037_update_techstack_categories_schema/migration.sql diff --git a/prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql b/prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql deleted file mode 100644 index c84fac3a..00000000 --- a/prisma/migrations/20240926232539_update_tech_stack_catgeory/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- DropIndex -DROP INDEX "TechStackCategory_name_key"; - --- AlterTable -ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamId" INTEGER; - --- AddForeignKey -ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamId_fkey" FOREIGN KEY ("voyageTeamId") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql b/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql deleted file mode 100644 index 5d0e6cf3..00000000 --- a/prisma/migrations/20241017222548_category_name_citext_unique/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[name,voyageTeamId]` on the table `TechStackCategory` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "TechStackCategory" ALTER COLUMN "name" SET DATA TYPE CITEXT; - --- CreateIndex -CREATE UNIQUE INDEX "TechStackCategory_name_voyageTeamId_key" ON "TechStackCategory"("name", "voyageTeamId"); diff --git a/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql b/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql deleted file mode 100644 index 313bb59a..00000000 --- a/prisma/migrations/20241020174120_category_new_teamid_column/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamIdNew" INTEGER; - --- AddForeignKey -ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamIdNew_fkey" FOREIGN KEY ("voyageTeamIdNew") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241023191037_update_techstack_categories_schema/migration.sql b/prisma/migrations/20241023191037_update_techstack_categories_schema/migration.sql new file mode 100644 index 00000000..58c35b59 --- /dev/null +++ b/prisma/migrations/20241023191037_update_techstack_categories_schema/migration.sql @@ -0,0 +1,18 @@ +/* + Warnings: + + - A unique constraint covering the columns `[name,voyageTeamId]` on the table `TechStackCategory` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "TechStackCategory_name_key"; + +-- AlterTable +ALTER TABLE "TechStackCategory" ADD COLUMN "voyageTeamId" INTEGER, +ALTER COLUMN "name" SET DATA TYPE CITEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "TechStackCategory_name_voyageTeamId_key" ON "TechStackCategory"("name", "voyageTeamId"); + +-- AddForeignKey +ALTER TABLE "TechStackCategory" ADD CONSTRAINT "TechStackCategory_voyageTeamId_fkey" FOREIGN KEY ("voyageTeamId") REFERENCES "VoyageTeam"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema/teamStack.prisma b/prisma/schema/teamStack.prisma index 34845496..c6fb677c 100644 --- a/prisma/schema/teamStack.prisma +++ b/prisma/schema/teamStack.prisma @@ -1,15 +1,12 @@ // === Voyage - Tech stack === model TechStackCategory { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) name String @db.Citext - description String - voyageTeam VoyageTeam? @relation("Team", fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) + description String + voyageTeam VoyageTeam? @relation(fields: [voyageTeamId], references: [id], onUpdate: Cascade, onDelete: Cascade) voyageTeamId Int? - voyageTeamNew VoyageTeam? @relation("NewTeam", fields: [voyageTeamIdNew], references: [id], onUpdate: Cascade, onDelete: Cascade) - voyageTeamIdNew Int? - createdAt DateTime @default(now()) @db.Timestamptz() updatedAt DateTime @updatedAt diff --git a/prisma/schema/voyage.prisma b/prisma/schema/voyage.prisma index a0e1f58e..c731f069 100644 --- a/prisma/schema/voyage.prisma +++ b/prisma/schema/voyage.prisma @@ -91,10 +91,7 @@ model VoyageTeam { voyageTeamMembers VoyageTeamMember[] teamTechStackItems TeamTechStackItem[] - techStackCategory TechStackCategory[] @relation("Team") - techStackCategoryNew TechStackCategory[] @relation("NewTeam") - - + techStackCategory TechStackCategory[] teamMeetings TeamMeeting[] FormResponseVoyageProject FormResponseVoyageProject? } From 6d027c82fbcf383b93ecc9bd21ee60b2a2dab622 Mon Sep 17 00:00:00 2001 From: JoshuaHinman Date: Wed, 23 Oct 2024 12:20:44 -0700 Subject: [PATCH 22/22] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69818820..52e83981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ## [Unreleased] ### Added +- Added routes for teams to create own tech stack categories([#208](https://github.com/chingu-x/chingu-dashboard-be/pull/208)) ### Changed @@ -60,7 +61,6 @@ - Add CASL permissions for Team Sprint endpoint ([#193](https://github.com/chingu-x/chingu-dashboard-be/pull/193)) - Add units tests for the teams resource controller & services([#201](https://github.com/chingu-x/chingu-dashboard-be/pull/201)) - Add github workflow for PR reminders ([#202](https://github.com/chingu-x/chingu-dashboard-be/pull/202)) -- Added routes for teams to create own tech stack categories([#208](https://github.com/chingu-x/chingu-dashboard-be/pull/208)) ### Changed