Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/checkin form endpoints #166

Open
wants to merge 68 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
f71b453
Add DTO for checkin query keys
curtwl Jun 16, 2024
c810118
Add Swagger docs boilerplate
curtwl Jun 16, 2024
b314e62
Begin implementing service for getting checkin forms
curtwl Jun 16, 2024
3056408
Implement Prisma logic for getting checkin forms
curtwl Jun 16, 2024
41ac190
Make responses uniform from different DB tables
curtwl Jun 16, 2024
21ce397
Add CheckinFormResponse properties to sprint responses
curtwl Jun 16, 2024
923abad
Add test setup
curtwl Jun 16, 2024
fe548fa
Add test for GET by voyageNumber
curtwl Jun 16, 2024
6c8f73d
Add test for GET by teamId
curtwl Jun 16, 2024
5407959
Add test for GET by sprintNumber
curtwl Jun 16, 2024
dfc1f06
Add test for GET by userId
curtwl Jun 16, 2024
d0b34ce
Add test for invalid query params 400
curtwl Jun 16, 2024
630b478
Add test for 401 unauthorized
curtwl Jun 16, 2024
b06f571
Add test for 404 no checkin responses found
curtwl Jun 16, 2024
0679e81
Implement expected response body in tests
curtwl Jun 16, 2024
93d246e
Merge from dev
curtwl Jun 16, 2024
132bb14
Set ValidationPipe transform to true to use DTO in tests
curtwl Jun 16, 2024
9a971de
Update changelog
curtwl Jun 16, 2024
49bf975
Destructure query params in function args
curtwl Jun 16, 2024
e3a39d4
Refactor error handling
curtwl Jun 17, 2024
c8f8aeb
merge from dev
curtwl Jun 17, 2024
4a82df9
Add examples in form response definition
curtwl Jun 21, 2024
2911fb5
Change paths from /src/ to /../
curtwl Jun 22, 2024
ff2c839
merge from dev
curtwl Jul 7, 2024
b230b79
Make var name more clear
curtwl Jul 7, 2024
94308c2
Add logic to check if query contains sprintNumber
curtwl Jul 7, 2024
eb902d2
Implement logic to extract keys/vals in sprintNumber case
curtwl Jul 7, 2024
e873eaf
Fetch full responses from db
curtwl Jul 7, 2024
60e8301
Update error handling
curtwl Jul 7, 2024
84d3fd3
Update response definitions
curtwl Jul 7, 2024
8e46352
Add response shape defs to tests
curtwl Jul 7, 2024
52a38eb
Update test for voyageNumber case
curtwl Jul 7, 2024
c48e79f
Update test for teamId case
curtwl Jul 7, 2024
ab4fce3
Update test for sprintNumber case
curtwl Jul 7, 2024
34fcb5b
Update test for userId case
curtwl Jul 7, 2024
1bd23be
Add test for sprintNumber without voyageNumber
curtwl Jul 7, 2024
9b5f4d9
Add a type for clarity
curtwl Jul 7, 2024
d54a639
Remove console.log
curtwl Jul 7, 2024
cefeebf
Add global function to check existence of item in DB
curtwl Jul 24, 2024
88e4d2a
Simplify global function API
curtwl Jul 24, 2024
733049b
Fix bug
curtwl Jul 24, 2024
1e8046e
Add documentation for global function API
curtwl Jul 25, 2024
dc31748
Add buildQuery helper function
curtwl Jul 25, 2024
f4da458
Add executeQuery helper function
curtwl Jul 25, 2024
5cc237d
Refactor getCheckinFormResponse to use helper functions
curtwl Jul 25, 2024
4c40101
Add helper function to validate sprint queries
curtwl Jul 25, 2024
8991d7e
Move sprint validation logic to buildQuery
curtwl Jul 25, 2024
7749b9b
Remove sprint validation check
curtwl Jul 25, 2024
21df335
Simplify executeQuery
curtwl Jul 25, 2024
3c987c1
Update tests
curtwl Jul 25, 2024
e78a559
Update Swagger docs
curtwl Jul 25, 2024
ce71c4a
Update sprint responses docs
curtwl Jul 25, 2024
6f95710
Update sprint responses docs
curtwl Jul 25, 2024
c3eb90d
Fix bugs
curtwl Jul 25, 2024
8e3dda8
Fix expected responses in tests
curtwl Jul 25, 2024
b27db34
Fix bugs
curtwl Jul 25, 2024
6859c72
Fix error message
curtwl Jul 25, 2024
31966e6
merge from dev
curtwl Jul 25, 2024
8fb4040
Update a few comments etc
curtwl Jul 26, 2024
ab76449
Fix bug
curtwl Jul 27, 2024
3fff430
Revert a type change
curtwl Jul 27, 2024
1ead84a
Sort by teamId
curtwl Jul 28, 2024
26717b4
Merge branch 'dev' into feature/checkin-form-endpoints
cherylli Jul 29, 2024
7baeb6d
Enforce min/max for sprint number
curtwl Jul 29, 2024
19b64f4
Fix mistaken response type assignment
curtwl Jul 29, 2024
4307ce0
Merge branch 'feature/checkin-form-endpoints' of https://github.com/c…
curtwl Jul 29, 2024
6e441c9
remove sprintId min max in create-checkin-form.dto.ts
cherylli Jul 29, 2024
d8ae433
add min and isInt to sprintId in create-checkin-form.dto
cherylli Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Another example [here](https://co-pilot.dev/changelog)
- Add swagger access info, add forms authorization and e2e tests ([#160](https://github.com/chingu-x/chingu-dashboard-be/pull/160))
- Add voyages unit test, also had to update all files (seed, tests, services) to meet strict null rule due to prismaMock requirements ([#163](https://github.com/chingu-x/chingu-dashboard-be/pull/163))
- Add e2e tests for users controller ([#165](https://github.com/chingu-x/chingu-dashboard-be/pull/165))
- Add GET endpoint for check-in form responses ([#166](https://github.com/chingu-x/chingu-dashboard-be/pull/166))


### Changed

Expand Down
21 changes: 21 additions & 0 deletions src/sprints/dto/get-checkin-form-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IsOptional, IsUUID, IsInt } from "class-validator";
import { Type } from "class-transformer";

export class CheckinQueryDto {
@IsOptional()
@IsInt()
@Type(() => Number)
teamId?: number;

@IsOptional()
@IsInt()
@Type(() => Number)
sprintNumber?: number;

@IsOptional()
voyageNumber?: string;

@IsOptional()
@IsUUID()
userId?: string;
}
64 changes: 63 additions & 1 deletion src/sprints/sprints.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ import {
ValidationPipe,
HttpStatus,
Request,
Query,
} from "@nestjs/common";
import { SprintsService } from "./sprints.service";
import { UpdateTeamMeetingDto } from "./dto/update-team-meeting.dto";
import { CreateTeamMeetingDto } from "./dto/create-team-meeting.dto";
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from "@nestjs/swagger";
import {
ApiOperation,
ApiParam,
ApiQuery,
ApiResponse,
ApiTags,
} from "@nestjs/swagger";
import { CreateAgendaDto } from "./dto/create-agenda.dto";
import { UpdateAgendaDto } from "./dto/update-agenda.dto";
import { FormInputValidationPipe } from "../pipes/form-input-validation";
Expand All @@ -38,6 +45,9 @@ import { FormResponse, ResponseResponse } from "../forms/forms.response";
import { CreateCheckinFormDto } from "./dto/create-checkin-form.dto";
import { CustomRequest } from "../global/types/CustomRequest";
import { VoyageTeamMemberValidationPipe } from "../pipes/voyage-team-member-validation";
import { CheckinQueryDto } from "./dto/get-checkin-form-response";
import { Action } from "../ability/ability.factory/ability.factory";
import { CheckAbilities } from "../global/decorators/abilities.decorator";

@Controller()
@ApiTags("Voyage - Sprints")
Expand Down Expand Up @@ -542,4 +552,56 @@ export class SprintsController {
createCheckinFormResponse,
);
}

@ApiOperation({
summary:
"[Admin only]: gets checkin forms given a sprintNumber, voyageId, userId, or teamId",
description:
"Takes any 1 of 4 keys: sprintNumber, voyageId, userId, or teamId",
})
@ApiResponse({
status: HttpStatus.OK,
description: "successfully gets checkin form responses",
type: CheckinSubmissionResponse,
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: "no matching checkin form response found",
type: NotFoundErrorResponse,
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: "invalid key",
type: BadRequestErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: "User is not logged in or doesn't have admin access",
type: UnauthorizedErrorResponse,
})
@ApiQuery({
name: "teamId",
required: false,
description: "Example: 1",
})
@ApiQuery({
name: "sprintNumber",
required: false,
description: "Example: 1",
})
@ApiQuery({
name: "voyageNumber",
required: false,
description: "Example: 46",
})
@ApiQuery({
name: "userId",
required: false,
description: "Example: 6bd33861-04c0-4270-8e96-62d4fb587527",
})
@CheckAbilities({ action: Action.Manage, subject: "all" })
@Get("check-in")
getCheckinFormResponse(@Query() query: CheckinQueryDto) {
return this.sprintsService.getCheckinFormResponse(query);
}
}
29 changes: 29 additions & 0 deletions src/sprints/sprints.response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,32 @@ export class CheckinSubmissionResponse {
@ApiProperty({ example: "2023-11-30T06:47:11.694Z" })
createdAt: Date;
}

export class CheckinFormResponse {
curtwl marked this conversation as resolved.
Show resolved Hide resolved
@ApiProperty({ example: 1 })
id: number;

@ApiProperty({ example: 1 })
voyageTeamMemberId: number;

@ApiProperty({ example: 1 })
voyageTeamId: number;

@ApiProperty({ example: 1 })
sprintId: number;

@ApiProperty({ example: "Great job!" })
adminComments: string;

@ApiProperty({ example: true })
feedbackSent: boolean;

@ApiProperty({ example: 1 })
responseGroupId: number;

@ApiProperty({ example: "2023-11-30T06:47:11.694Z" })
createdAt: Date;

@ApiProperty({ example: "2023-11-30T06:47:11.694Z" })
updatedAt: Date;
}
109 changes: 109 additions & 0 deletions src/sprints/sprints.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CreateCheckinFormDto } from "./dto/create-checkin-form.dto";
import { GlobalService } from "../global/global.service";
import { FormTitles } from "../global/constants/formTitles";
import { CustomRequest } from "../global/types/CustomRequest";
import { CheckinQueryDto } from "./dto/get-checkin-form-response";

@Injectable()
export class SprintsService {
Expand Down Expand Up @@ -615,4 +616,112 @@ export class SprintsService {
}
}
}

async getCheckinFormResponse(query: CheckinQueryDto) {
// convert query's key/val pair into strings for error handling
const queryObjectStringified = Object.entries(query).filter(
([_, v]) => v,
);
// just grab the first key if there are multiple provided by accident
const [key, val] = queryObjectStringified[0] || [];

if (!key) {
throw new BadRequestException("No query provided");
}

let checkinFormResponse;

switch (key) {
case "teamId":
checkinFormResponse =
await this.prisma.voyageTeamMember.findMany({
where: {
voyageTeamId: val,
},
include: {
checkinForms: true,
},
});
break;
case "sprintNumber":
checkinFormResponse =
await this.prisma.formResponseCheckin.findMany({
where: {
sprintId: val,
},
include: {
voyageTeamMember: {
select: {
voyageTeamId: true,
},
},
},
orderBy: {
voyageTeamMember: {
voyageTeamId: "asc",
},
},
});
break;
case "voyageNumber":
checkinFormResponse = await this.prisma.sprint.findMany({
where: {
voyage: {
number: val,
},
},
include: {
checkinForms: {
include: {
voyageTeamMember: {
select: {
voyageTeamId: true,
},
},
},
},
},
});
break;
case "userId":
checkinFormResponse =
await this.prisma.voyageTeamMember.findMany({
where: {
userId: val,
},
include: {
checkinForms: {
include: {
voyageTeamMember: {
select: {
voyageTeamId: true,
},
},
},
},
},
});
break;
default:
throw new BadRequestException(
`Query ${key} did not match any keywords`,
);
}

// make responses uniform and get rid of empty arrays
checkinFormResponse = checkinFormResponse.flatMap(
(item) => item.checkinForms || [item],
);
checkinFormResponse = checkinFormResponse.filter(
(item) => Object.keys(item).length > 0,
);

if (checkinFormResponse.length < 1) {
throw new NotFoundException(
`Query ${key} with value ${val} did not match any check-in form responses`,
);
}

return checkinFormResponse;
}
}
Loading
Loading