Skip to content

Commit 264d393

Browse files
committed
add curriculums basics backend routes (crud) with tests
1 parent f63456f commit 264d393

23 files changed

Lines changed: 892 additions & 8 deletions

@apps/backend/src/app/app.router.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { type AuthModule, type UserModule } from "@libs/users-backend";
1+
import { type AuthModule, type UserModule, type CurriculumModule } from "@libs/users-backend";
22
import type { FastifyInstanceType } from "./app.js";
33
import { statusRoute } from "./status.route.js";
44

55
interface AppRouterOptions {
66
authModule: AuthModule;
77
userModule: UserModule;
8+
curriculumModule: CurriculumModule;
89
}
910

1011
export async function appRouter(
1112
fastify: FastifyInstanceType,
12-
{ authModule, userModule }: AppRouterOptions,
13+
{ authModule, userModule, curriculumModule }: AppRouterOptions,
1314
) {
1415
await fastify.register(
1516
async function (fastify) {
@@ -25,6 +26,7 @@ export async function appRouter(
2526
});
2627

2728
await userModule.setupRoutes(fastify);
29+
await curriculumModule.setupRoutes(fastify);
2830
},
2931
{
3032
prefix: "api/v1",

@apps/backend/src/app/app.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import packageJson from "../../package.json" with { type: "json" };
1919
import { appRouter } from "./app.router.js";
2020
import type { ApplicationContext } from "./application.context.js";
2121
import { logger } from "./logger.js";
22-
import { UserModule, AuthModule } from "@libs/users-backend";
22+
import { UserModule, AuthModule, CurriculumModule } from "@libs/users-backend";
2323

2424
export type FastifyInstanceType = FastifyInstance<
2525
RawServerDefault,
@@ -162,6 +162,12 @@ export class App {
162162
jwtSecret: this.context.configuration.JWT_SECRET,
163163
},
164164
}),
165+
curriculumModule: CurriculumModule.init({
166+
em: this.context.orm.em.fork(),
167+
configuration: {
168+
jwtSecret: this.context.configuration.JWT_SECRET,
169+
},
170+
}),
165171
});
166172
}
167173

@apps/backend/src/app/database.connection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { defineConfig, MikroORM } from "@mikro-orm/postgresql";
22
import type { AppConfiguration } from "../configuration.js";
3-
import { RefreshTokenEntity, UserEntity } from "@libs/users-backend";
3+
import { RefreshTokenEntity, UserEntity, CurriculumEntity } from "@libs/users-backend";
44

55
export function databaseConfig(config: Pick<AppConfiguration, "DATABASE_URI">) {
66
return defineConfig({
77
seeder: {
88
pathTs: "./src/seeders",
99
},
1010
clientUrl: config.DATABASE_URI,
11-
entities: [UserEntity, RefreshTokenEntity],
11+
entities: [UserEntity, RefreshTokenEntity, CurriculumEntity],
1212
});
1313
}
1414

@libs/users-backend/src/context.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ export interface AuthLibraryContext {
1414
jwtSecret: string;
1515
};
1616
}
17+
18+
export interface CurriculumLibraryContext {
19+
em: EntityManager;
20+
configuration: {
21+
jwtSecret: string;
22+
};
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineEntity, p, type InferEntity } from "@mikro-orm/core";
2+
3+
export const CurriculumEntity = defineEntity({
4+
name: "Curriculum",
5+
properties: {
6+
id: p.string().primary(),
7+
userId: p.string().index(),
8+
title: p.string(),
9+
updatedAt: p.datetime(),
10+
},
11+
});
12+
13+
export type CurriculumEntityType = InferEntity<typeof CurriculumEntity>;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineEntity, p, type InferEntity } from "@mikro-orm/core";
2+
3+
export const ProposedSectionEntity = defineEntity({
4+
name: "ProposedSection",
5+
properties: {
6+
id: p.string().primary(),
7+
curriculumId: p.string().index(),
8+
title: p.string(),
9+
position: p.integer(),
10+
},
11+
});
12+
13+
export type ProposedSectionEntityType = InferEntity<typeof ProposedSectionEntity>;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineEntity, p, type InferEntity } from "@mikro-orm/core";
2+
3+
export const SectionEntity = defineEntity({
4+
name: "Section",
5+
properties: {
6+
id: p.string().primary(),
7+
curriculumId: p.string().index(),
8+
title: p.string(),
9+
position: p.integer(),
10+
},
11+
});
12+
13+
export type SectionEntityType = InferEntity<typeof SectionEntity>;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineEntity, p, type InferEntity } from "@mikro-orm/core";
2+
3+
export const SectionItemEntity = defineEntity({
4+
name: "SectionItem",
5+
properties: {
6+
id: p.string().primary(),
7+
sectionId: p.string().index(),
8+
position: p.integer(),
9+
jsonData: p.json(),
10+
},
11+
});
12+
13+
export type SectionItemEntityType = InferEntity<typeof SectionItemEntity>;

@libs/users-backend/src/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { RefreshTokenEntity } from "#src/entities/refresh-token.entity.js";
22
import { UserEntity } from "#src/entities/user.entity.js";
3+
import { CurriculumEntity } from "#src/entities/curriculum.entity.js";
34

45
export * from "#src/entities/user.entity.js";
56
export * from "#src/entities/refresh-token.entity.js";
7+
export * from "#src/entities/curriculum.entity.js";
8+
69
export * from "#src/routes/create.route.js";
710
export * from "#src/routes/delete.route.js";
811
export * from "#src/routes/get.route.js";
@@ -13,11 +16,19 @@ export * from "#src/routes/profile.route.js";
1316
export * from "#src/routes/refresh.route.js";
1417
export * from "#src/routes/update.route.js";
1518
export * from "#src/serializers/user.serializer.js";
19+
1620
export * from "#src/utils/token.utils.js";
1721
export * from "#src/utils/token-cleanup.utils.js";
1822
export * from "#src/init.js";
1923
export * from "#src/utils/jwt.utils.js";
2024
export * from "#src/utils/auth.utils.js";
2125
export * from "#src/middlewares/jwt-auth.middleware.ts";
2226

23-
export const entities = [UserEntity, RefreshTokenEntity];
27+
export * from "#src/routes/curriculums/get.route.js";
28+
export * from "#src/routes/curriculums/list.route.js";
29+
export * from "#src/routes/curriculums/create.route.js";
30+
export * from "#src/routes/curriculums/update.route.js";
31+
export * from "#src/routes/curriculums/delete.route.js";
32+
export * from "#src/serializers/curriculum.serializer.js";
33+
34+
export const entities = [UserEntity, RefreshTokenEntity, CurriculumEntity];

@libs/users-backend/src/init.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import type {
55
RawRequestDefaultExpression,
66
RawServerDefault,
77
} from "fastify";
8-
import type { AuthLibraryContext, UserLibraryContext } from "./context.js";
8+
import type {
9+
AuthLibraryContext,
10+
UserLibraryContext,
11+
CurriculumLibraryContext,
12+
} from "./context.js";
913
import { type ZodTypeProvider } from "fastify-type-provider-zod";
1014
import { LoginRoute } from "#src/routes/login.route.js";
1115
import { RefreshRoute } from "#src/routes/refresh.route.js";
@@ -20,6 +24,13 @@ import { UserEntity } from "./entities/user.entity.js";
2024
import { type ModuleInterface, type Route } from "@libs/backend-shared";
2125
import { handleJsonApiErrors } from "@libs/backend-shared";
2226
import { createJwtAuthMiddleware } from "./index.ts";
27+
import { CurriculumEntity } from "./entities/curriculum.entity.ts";
28+
29+
import { GetCurriculumRoute } from "./routes/curriculums/get.route.ts";
30+
import { ListCurriculumRoute } from "./routes/curriculums/list.route.ts";
31+
import { CreateCurriculumRoute } from "./routes/curriculums/create.route.ts";
32+
import { UpdateCurriculumRoute } from "./routes/curriculums/update.route.ts";
33+
import { DeleteCurriculumRoute } from "./routes/curriculums/delete.route.ts";
2334

2435
export type FastifyInstanceTypeForModule = FastifyInstance<
2536
RawServerDefault,
@@ -107,3 +118,43 @@ export class UserModule implements ModuleInterface<FastifyInstanceTypeForModule>
107118
);
108119
}
109120
}
121+
122+
export class CurriculumModule implements ModuleInterface<FastifyInstanceTypeForModule> {
123+
private constructor(private context: CurriculumLibraryContext) {}
124+
125+
public static init(context: CurriculumLibraryContext): CurriculumModule {
126+
return new CurriculumModule(context);
127+
}
128+
129+
public async setupRoutes(fastify: FastifyInstanceTypeForModule): Promise<void> {
130+
const repository = this.context.em.getRepository(CurriculumEntity);
131+
132+
await fastify.register(
133+
async (f) => {
134+
const curriculumRoutes: Route<FastifyInstanceTypeForModule>[] = [
135+
new GetCurriculumRoute(repository),
136+
new ListCurriculumRoute(repository),
137+
new CreateCurriculumRoute(repository),
138+
new UpdateCurriculumRoute(repository),
139+
new DeleteCurriculumRoute(repository),
140+
];
141+
142+
f.setErrorHandler((error, request, reply) => {
143+
handleJsonApiErrors(error, request, reply);
144+
});
145+
146+
const jwtAuthMiddleware = createJwtAuthMiddleware(
147+
this.context.em,
148+
this.context.configuration.jwtSecret,
149+
);
150+
151+
f.addHook("preValidation", jwtAuthMiddleware);
152+
153+
for (const route of curriculumRoutes) {
154+
route.routeDefinition(f);
155+
}
156+
},
157+
{ prefix: "/curriculums" },
158+
);
159+
}
160+
}

0 commit comments

Comments
 (0)