diff --git a/apps/api/src/categories/categories.service.ts b/apps/api/src/categories/categories.service.ts index 4a1549c9..5cabbdbd 100644 --- a/apps/api/src/categories/categories.service.ts +++ b/apps/api/src/categories/categories.service.ts @@ -8,7 +8,7 @@ import { CategoriesQuery } from "./api/categories.types"; import { CategorySortFields, CategorySortField } from "./schemas/categoryQuery"; import { DatabasePg, Pagination } from "src/common"; import { UserRole, UserRoles } from "src/users/schemas/user-roles"; -import { getSortOptions } from "./helpers"; +import { getSortOptions } from "src/common/helpers/getSortOptions"; @Injectable() export class CategoriesService { @@ -42,7 +42,11 @@ export class CategoriesService { .select(selectedColumns) .from(categories) .where(filterCondition) - .orderBy(sortOrder(this.getColumnToSortBy(sortedField, isAdmin))); + .orderBy( + sortOrder( + this.getColumnToSortBy(sortedField as CategorySortField, isAdmin), + ), + ); const dynamicQuery = queryDB.$dynamic(); diff --git a/apps/api/src/categories/helpers/index.ts b/apps/api/src/categories/helpers/index.ts deleted file mode 100644 index 92d5ee2c..00000000 --- a/apps/api/src/categories/helpers/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { asc, desc } from "drizzle-orm"; -import { - CategorySortField, - SortCategoryFieldsOptions, -} from "../schemas/categoryQuery"; - -export const getSortOptions = (sort: SortCategoryFieldsOptions) => ({ - sortOrder: sort.startsWith("-") ? desc : asc, - sortedField: sort.replace(/^-/, "") as CategorySortField, -}); diff --git a/apps/api/src/common/helpers/getSortOptions.ts b/apps/api/src/common/helpers/getSortOptions.ts new file mode 100644 index 00000000..d5e58a4b --- /dev/null +++ b/apps/api/src/common/helpers/getSortOptions.ts @@ -0,0 +1,7 @@ +import { asc, desc } from "drizzle-orm"; +export function getSortOptions(sort: T) { + return { + sortOrder: sort.startsWith("-") ? desc : asc, + sortedField: sort.replace(/^-/, "") as K, + }; +} diff --git a/apps/api/src/courses/api/courses.controller.ts b/apps/api/src/courses/api/courses.controller.ts index 8a0450d8..2d088046 100644 --- a/apps/api/src/courses/api/courses.controller.ts +++ b/apps/api/src/courses/api/courses.controller.ts @@ -55,7 +55,7 @@ export class CoursesController { return new PaginatedResponse(data); } - @Get("getStudentCourses") + @Get("get-student-courses") @Validate({ response: paginatedResponse(allCoursesSchema), request: [ @@ -78,7 +78,7 @@ export class CoursesController { @Query("page") page: number, @Query("perPage") perPage: number, @Query("sort") sort: SortCourseFieldsOptions, - @CurrentUser() currentUser: { userId: string }, + @CurrentUser("userId") currentUserId: string, ): Promise> { const filters: CoursesFilterSchema = { title, @@ -93,7 +93,7 @@ export class CoursesController { const data = await this.coursesService.getCoursesForUser( query, - currentUser.userId, + currentUserId, ); return new PaginatedResponse(data); diff --git a/apps/api/src/courses/courses.service.ts b/apps/api/src/courses/courses.service.ts index 899797bf..595481b5 100644 --- a/apps/api/src/courses/courses.service.ts +++ b/apps/api/src/courses/courses.service.ts @@ -4,8 +4,11 @@ import { addPagination, DEFAULT_PAGE_SIZE } from "src/common/pagination"; import { CoursesQuery } from "./api/courses.types"; import { DatabasePg, Pagination } from "src/common"; import { AllCoursesResponse } from "./schemas/course.schema"; -import { CourseSortField, CourseSortFields } from "./schemas/courseQuery"; -import { getSortOptions } from "./helpers"; +import { + CoursesFilterSchema, + CourseSortField, + CourseSortFields, +} from "./schemas/courseQuery"; import { categories, courseLessons, @@ -13,6 +16,7 @@ import { studentCourses, users, } from "../storage/schema"; +import { getSortOptions } from "src/common/helpers/getSortOptions"; @Injectable() export class CoursesService { @@ -29,43 +33,11 @@ export class CoursesService { } = query; const { sortOrder, sortedField } = getSortOptions(sort); - - const selectedColumns = { - id: courses.id, - creationDate: courses.createdAt, - title: courses.title, - imageUrl: courses.imageUrl, - author: sql`CONCAT(${users.firstName} || ' ' || ${users.lastName})`, - category: categories.title, - courseLessonCount: sql`(SELECT COUNT(*) FROM ${courseLessons} WHERE ${courseLessons.courseId} = ${courses.id})::INTEGER`, - enrolledParticipantCount: count(studentCourses.courseId), - }; + const conditions = this.getFiltersConditions(filters); return this.db.transaction(async (tx) => { - const conditions = [eq(courses.state, "published")]; - - if (filters.title) { - conditions.push( - like(categories.title, `%${filters.title.toLowerCase()}%`), - ); - } - if (filters.category) { - conditions.push(like(categories.title, `%${filters.category}%`)); - } - if (filters.author) { - const authorNameConcat = `${users.firstName} || ' ' || ${users.lastName}`; - conditions.push(sql`${authorNameConcat} LIKE ${`%${filters.author}%`}`); - } - if (filters.creationDateRange) { - const [startDate, endDate] = filters.creationDateRange; - const start = new Date(startDate).toISOString(); - const end = new Date(endDate).toISOString(); - - conditions.push(between(courses.createdAt, start, end)); - } - const queryDB = tx - .select(selectedColumns) + .select(this.getSelectFiled()) .from(courses) .leftJoin(users, eq(courses.authorId, users.id)) .innerJoin(categories, eq(courses.categoryId, categories.id)) @@ -80,10 +52,14 @@ export class CoursesService { users.lastName, categories.title, ) - .orderBy(sortOrder(this.getColumnToSortBy(sortedField))); + .orderBy( + sortOrder(this.getColumnToSortBy(sortedField as CourseSortField)), + ); + const dynamicQuery = queryDB.$dynamic(); const paginatedQuery = addPagination(dynamicQuery, page, perPage); const data = await paginatedQuery; + const [totalItems] = await tx .select({ count: count() }) .from(courses) @@ -125,45 +101,12 @@ export class CoursesService { const { sortOrder, sortedField } = getSortOptions(sort); - const selectedColumns = { - id: courses.id, - creationDate: courses.createdAt, - title: courses.title, - imageUrl: courses.imageUrl, - author: sql`CONCAT(${users.firstName} || ' ' || ${users.lastName})`, - category: categories.title, - courseLessonCount: sql`(SELECT COUNT(*) FROM ${courseLessons} WHERE ${courseLessons.courseId} = ${courses.id})::INTEGER`, - enrolledParticipantCount: count(studentCourses.courseId), - }; - return this.db.transaction(async (tx) => { - const conditions = [ - eq(courses.state, "published"), - eq(studentCourses.studentId, userId), - ]; - - if (filters.title) { - conditions.push( - like(categories.title, `%${filters.title.toLowerCase()}%`), - ); - } - if (filters.category) { - conditions.push(like(categories.title, `%${filters.category}%`)); - } - if (filters.author) { - const authorNameConcat = `${users.firstName} || ' ' || ${users.lastName}`; - conditions.push(sql`${authorNameConcat} LIKE ${`%${filters.author}%`}`); - } - if (filters.creationDateRange) { - const [startDate, endDate] = filters.creationDateRange; - const start = new Date(startDate).toISOString(); - const end = new Date(endDate).toISOString(); - - conditions.push(between(courses.createdAt, start, end)); - } + const conditions = [eq(studentCourses.studentId, userId)]; + conditions.push(...this.getFiltersConditions(filters)); const queryDB = tx - .select(selectedColumns) + .select(this.getSelectFiled()) .from(studentCourses) .innerJoin(courses, eq(studentCourses.courseId, courses.id)) .innerJoin(categories, eq(courses.categoryId, categories.id)) @@ -178,7 +121,10 @@ export class CoursesService { users.lastName, categories.title, ) - .orderBy(sortOrder(this.getColumnToSortBy(sortedField))); + .orderBy( + sortOrder(this.getColumnToSortBy(sortedField as CourseSortField)), + ); + const dynamicQuery = queryDB.$dynamic(); const paginatedQuery = addPagination(dynamicQuery, page, perPage); const data = await paginatedQuery; @@ -210,6 +156,43 @@ export class CoursesService { }); } + private getSelectFiled() { + return { + id: courses.id, + creationDate: courses.createdAt, + title: courses.title, + imageUrl: courses.imageUrl, + author: sql`CONCAT(${users.firstName} || ' ' || ${users.lastName})`, + category: categories.title, + courseLessonCount: sql`(SELECT COUNT(*) FROM ${courseLessons} WHERE ${courseLessons.courseId} = ${courses.id})::INTEGER`, + enrolledParticipantCount: count(studentCourses.courseId), + }; + } + + private getFiltersConditions(filters: CoursesFilterSchema) { + const conditions = [eq(courses.state, "published")]; + if (filters.title) { + conditions.push( + like(categories.title, `%${filters.title.toLowerCase()}%`), + ); + } + if (filters.category) { + conditions.push(like(categories.title, `%${filters.category}%`)); + } + if (filters.author) { + const authorNameConcat = `${users.firstName} || ' ' || ${users.lastName}`; + conditions.push(sql`${authorNameConcat} LIKE ${`%${filters.author}%`}`); + } + if (filters.creationDateRange) { + const [startDate, endDate] = filters.creationDateRange; + const start = new Date(startDate).toISOString(); + const end = new Date(endDate).toISOString(); + + conditions.push(between(courses.createdAt, start, end)); + } + return conditions; + } + private getColumnToSortBy(sort: CourseSortField) { switch (sort) { case CourseSortFields.author: diff --git a/apps/api/src/courses/helpers/index.ts b/apps/api/src/courses/helpers/index.ts deleted file mode 100644 index d9e6ec25..00000000 --- a/apps/api/src/courses/helpers/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { asc, desc } from "drizzle-orm"; -import { - CourseSortField, - SortCourseFieldsOptions, -} from "src/courses/schemas/courseQuery"; - -export const getSortOptions = (sort: SortCourseFieldsOptions) => ({ - sortOrder: sort.startsWith("-") ? desc : asc, - sortedField: sort.replace(/^-/, "") as CourseSortField, -}); diff --git a/apps/api/src/swagger/api-schema.json b/apps/api/src/swagger/api-schema.json index 7898fafd..7791a4ae 100644 --- a/apps/api/src/swagger/api-schema.json +++ b/apps/api/src/swagger/api-schema.json @@ -705,7 +705,7 @@ } } }, - "/api/courses/getStudentCourses": { + "/api/courses/get-student-courses": { "get": { "operationId": "CoursesController_getStudentCourses", "parameters": [