Skip to content

Commit d0d6250

Browse files
committed
add: project-defined-roles
1 parent 7f6ac40 commit d0d6250

File tree

16 files changed

+579
-89
lines changed

16 files changed

+579
-89
lines changed

apps/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"zod": "^3.24.1"
3838
},
3939
"devDependencies": {
40+
"@faker-js/faker": "^9.8.0",
4041
"@repo/eslint-config": "workspace:*",
4142
"@repo/typescript-config": "workspace:*",
4243
"@types/jsonwebtoken": "^9.0.8",

apps/api/src/modules/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import auth from "./auth";
55
import userRoutes from "./user";
66
import project from "./project";
77
import labels from "./labels";
8+
import statuses from "./statuses";
89

910
const getOptionsWithPrefix = (
1011
options: FastifyPluginOptions,
@@ -33,6 +34,10 @@ export default fastifyPlugin(
3334
labels,
3435
getOptionsWithPrefix(options, "/edge/projects/:projectId/labels")
3536
),
37+
fastify.register(
38+
statuses,
39+
getOptionsWithPrefix(options, "/edge/projects/:projectId/statuses")
40+
),
3641
]);
3742
}
3843
);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
2+
import { fastifyPlugin } from "fastify-plugin";
3+
import StatusRoute from "./status.route";
4+
5+
export default fastifyPlugin(
6+
async (fastify: FastifyInstance, options: FastifyPluginOptions) => {
7+
await fastify.register(StatusRoute, options);
8+
}
9+
);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { FastifyReply, FastifyRequest } from "fastify";
2+
import type StatusService from "./status.service";
3+
4+
export default class StatusController {
5+
constructor(private statusService: StatusService) {}
6+
7+
async createStatus(
8+
request: FastifyRequest<{
9+
Body: {
10+
name: string;
11+
color: string;
12+
description: string;
13+
order: number;
14+
};
15+
Params: { projectId: string };
16+
}>,
17+
reply: FastifyReply
18+
) {
19+
const status = await this.statusService.createStatus({
20+
...request.body,
21+
projectId: request.params.projectId,
22+
creatorId: request.jwtUser.id,
23+
});
24+
25+
return reply.status(201).send({ status });
26+
}
27+
28+
async getStatusById(
29+
request: FastifyRequest<{
30+
Params: { statusId: string };
31+
}>,
32+
reply: FastifyReply
33+
) {
34+
const status = await this.statusService.getStatusById(
35+
request.params.statusId
36+
);
37+
38+
if (!status) {
39+
return reply.status(404).send({
40+
statusCode: 404,
41+
error: "Not Found",
42+
message: "Status not found",
43+
});
44+
}
45+
46+
return reply.send({ status });
47+
}
48+
49+
async getStatusesByProjectId(
50+
request: FastifyRequest<{
51+
Params: { projectId: string };
52+
}>,
53+
reply: FastifyReply
54+
) {
55+
const statuses = await this.statusService.getStatusesByProjectId(
56+
request.params.projectId
57+
);
58+
59+
return reply.send({ statuses });
60+
}
61+
62+
async updateStatus(
63+
request: FastifyRequest<{
64+
Params: { statusId: string };
65+
Body: {
66+
name?: string;
67+
color?: string;
68+
description?: string;
69+
order?: number;
70+
};
71+
}>,
72+
reply: FastifyReply
73+
) {
74+
const status = await this.statusService.updateStatus(
75+
request.params.statusId,
76+
request.body
77+
);
78+
79+
return reply.send({ status });
80+
}
81+
82+
async deleteStatus(
83+
request: FastifyRequest<{
84+
Params: { statusId: string };
85+
}>,
86+
reply: FastifyReply
87+
) {
88+
const status = await this.statusService.deleteStatus(
89+
request.params.statusId
90+
);
91+
92+
return reply.send({ status });
93+
}
94+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import type { FastifyInstance } from "fastify";
2+
import StatusController from "./status.controller";
3+
import StatusService from "./status.service";
4+
import { statusSchemas, statusSchemaRef } from "./status.schema";
5+
6+
export default function StatusRoutes(fastify: FastifyInstance) {
7+
// Register schemas
8+
for (const schema of statusSchemas) {
9+
fastify.addSchema(schema);
10+
}
11+
12+
const statusController = new StatusController(new StatusService());
13+
14+
// Create a new status
15+
fastify.post(
16+
"/",
17+
{
18+
onRequest: [fastify.jwtAuth],
19+
schema: {
20+
tags: ["Task Statuses"],
21+
description: "Create a new task status",
22+
body: statusSchemaRef("createStatusSchema"),
23+
response: {
24+
201: statusSchemaRef("statusResponse"),
25+
400: statusSchemaRef("errorResponse"),
26+
},
27+
},
28+
},
29+
// @ts-expect-error - this is a bug in fastify-zod
30+
statusController.createStatus.bind(statusController)
31+
);
32+
33+
// Get all statuses of a project
34+
fastify.get(
35+
"/",
36+
{
37+
onRequest: [fastify.jwtAuth],
38+
schema: {
39+
tags: ["Task Statuses"],
40+
description: "Get all task statuses of a project",
41+
params: {
42+
type: "object",
43+
properties: {
44+
projectId: { type: "string" },
45+
},
46+
required: ["projectId"],
47+
},
48+
response: {
49+
200: statusSchemaRef("statusesResponse"),
50+
},
51+
},
52+
},
53+
// @ts-expect-error - this is a bug in fastify-zod
54+
statusController.getStatusesByProjectId.bind(statusController)
55+
);
56+
57+
// Update a status
58+
fastify.put(
59+
"/:statusId",
60+
{
61+
onRequest: [fastify.jwtAuth],
62+
schema: {
63+
tags: ["Task Statuses"],
64+
description: "Update a task status",
65+
body: statusSchemaRef("updateStatusSchema"),
66+
response: {
67+
200: statusSchemaRef("statusResponse"),
68+
400: statusSchemaRef("errorResponse"),
69+
},
70+
},
71+
},
72+
// @ts-expect-error - this is a bug in fastify-zod
73+
statusController.updateStatus.bind(statusController)
74+
);
75+
76+
// Delete a status
77+
fastify.delete(
78+
"/:statusId",
79+
{
80+
onRequest: [fastify.jwtAuth],
81+
schema: {
82+
tags: ["Task Statuses"],
83+
description: "Delete a task status",
84+
response: {
85+
200: statusSchemaRef("statusResponse"),
86+
400: statusSchemaRef("errorResponse"),
87+
},
88+
},
89+
},
90+
// @ts-expect-error - this is a bug in fastify-zod
91+
statusController.deleteStatus.bind(statusController)
92+
);
93+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { z } from "zod";
2+
import { buildJsonSchemas } from "fastify-zod";
3+
4+
const ProjectStatus = {
5+
id: z.string().uuid(),
6+
name: z.string(),
7+
color: z.string(),
8+
description: z.string(),
9+
projectId: z.string().uuid(),
10+
creatorId: z.string().uuid(),
11+
order: z.number().int(),
12+
};
13+
14+
const createStatusSchema = z.object({
15+
name: z.string().min(1).max(50),
16+
color: z.string(),
17+
description: z.string(),
18+
order: z.number().int().min(0),
19+
});
20+
21+
const updateStatusSchema = z.object({
22+
name: z.string().min(1).max(50).optional(),
23+
color: z.string().optional(),
24+
description: z.string().optional(),
25+
order: z.number().int().min(0).optional(),
26+
});
27+
28+
const statusResponseSchema = z.object({
29+
status: z.object(ProjectStatus),
30+
});
31+
32+
const statusesResponseSchema = z.object({
33+
statuses: z.array(z.object(ProjectStatus)),
34+
});
35+
36+
const errorResponseSchema = z.object({
37+
statusCode: z.number(),
38+
error: z.string(),
39+
message: z.string(),
40+
});
41+
42+
const statusSchemaWithId = z.object({
43+
id: z.string().uuid(),
44+
});
45+
46+
export const { schemas: statusSchemas, $ref: statusSchemaRef } =
47+
buildJsonSchemas(
48+
{
49+
createStatusSchema,
50+
updateStatusSchema,
51+
statusResponse: statusResponseSchema,
52+
statusesResponse: statusesResponseSchema,
53+
errorResponse: errorResponseSchema,
54+
statusSchemaWithId,
55+
} as const,
56+
{ $id: "StatusSchema" }
57+
);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { StatusDal, type DbNewStatus, type DbStatus } from "@taskcord/database";
2+
3+
export default class StatusService {
4+
public async createStatus(input: DbNewStatus) {
5+
return StatusDal.createStatus(input);
6+
}
7+
8+
public async getStatusById(id: string) {
9+
return StatusDal.getStatusById(id);
10+
}
11+
12+
public async getStatusesByProjectId(projectId: string): Promise<DbStatus[]> {
13+
return StatusDal.getStatusesByProjectId(projectId);
14+
}
15+
16+
public async updateStatus(
17+
id: string,
18+
data: Partial<DbNewStatus>
19+
): Promise<DbStatus | null> {
20+
return StatusDal.updateStatus(id, data);
21+
}
22+
23+
public async deleteStatus(id: string): Promise<DbStatus | null> {
24+
return StatusDal.deleteStatus(id);
25+
}
26+
}

apps/api/src/modules/user/user.controller.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,13 @@ export default class UserController {
4343

4444
return reply.send(discordServerList);
4545
}
46+
47+
public async createDummyUsers(
48+
request: FastifyRequest<{ Querystring: { userCount?: number } }>,
49+
reply: FastifyReply
50+
) {
51+
const userCount = request.query.userCount || 10;
52+
const createdUsers = await this.userService.createDummyUser(userCount);
53+
return reply.send(createdUsers);
54+
}
4655
}

apps/api/src/modules/user/user.route.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default function AuthRoute(fastify: FastifyInstance) {
2727
},
2828
userController.me.bind(userController)
2929
);
30+
3031
fastify.get(
3132
"/discord/guilds",
3233
{
@@ -42,4 +43,25 @@ export default function AuthRoute(fastify: FastifyInstance) {
4243
},
4344
userController.getUserDiscordServerList.bind(userController)
4445
);
46+
47+
if (["local", "staging"].includes(process.env.NODE_ENV)) {
48+
fastify.post(
49+
"/dummy",
50+
{
51+
onRequest: [fastify.jwtAuth],
52+
schema: {
53+
tags: ["User"],
54+
description: "Create dummy users",
55+
query: {
56+
type: "object",
57+
properties: {
58+
userCount: { type: "number" },
59+
},
60+
},
61+
},
62+
},
63+
// @ts-expect-error - this is a bug in fastify-zod
64+
userController.createDummyUsers.bind(userController)
65+
);
66+
}
4567
}

0 commit comments

Comments
 (0)