diff --git a/apps/api/src/environment/controller/environment.controller.ts b/apps/api/src/environment/controller/environment.controller.ts index f0f09f52..31d5be19 100644 --- a/apps/api/src/environment/controller/environment.controller.ts +++ b/apps/api/src/environment/controller/environment.controller.ts @@ -57,7 +57,7 @@ export class EnvironmentController { async getEnvironmentsOfProject( @CurrentUser() user: User, @Param('projectId') projectId: string, - @Query('page') page: number = 1, + @Query('page') page: number = 0, @Query('limit') limit: number = 10, @Query('sort') sort: string = 'name', @Query('order') order: string = 'asc', diff --git a/apps/api/src/environment/environment.e2e.spec.ts b/apps/api/src/environment/environment.e2e.spec.ts index 8c01527a..af8504d5 100644 --- a/apps/api/src/environment/environment.e2e.spec.ts +++ b/apps/api/src/environment/environment.e2e.spec.ts @@ -27,6 +27,7 @@ import { EventService } from '../event/service/event.service' import { EnvironmentService } from './service/environment.service' import { UserModule } from '../user/user.module' import { UserService } from '../user/service/user.service' +import { QueryTransformPipe } from '../common/query.transform.pipe' describe('Environment Controller Tests', () => { let app: NestFastifyApplication @@ -64,6 +65,8 @@ describe('Environment Controller Tests', () => { environmentService = moduleRef.get(EnvironmentService) userService = moduleRef.get(UserService) + app.useGlobalPipes(new QueryTransformPipe()) + await app.init() await app.getHttpAdapter().getInstance().ready() }) @@ -399,13 +402,27 @@ describe('Environment Controller Tests', () => { it('should be able to fetch all environments of a project', async () => { const response = await app.inject({ method: 'GET', - url: `/environment/all/${project1.id}`, + url: `/environment/all/${project1.id}?page=0&limit=10`, headers: { 'x-e2e-user-email': user1.email } }) expect(response.statusCode).toBe(200) + //check metadata + const metadata = response.json().metadata + expect(metadata.totalCount).toEqual(2) + expect(metadata.links.self).toBe( + `/environment/all/${project1.id}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(metadata.links.first).toBe( + `/environment/all/${project1.id}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(metadata.links.previous).toBeNull() + expect(metadata.links.next).toBeNull() + expect(metadata.links.last).toBe( + `/environment/all/${project1.id}?page=0&limit=10&sort=name&order=asc&search=` + ) }) it('should not be able to fetch all environments of a project that does not exist', async () => { diff --git a/apps/api/src/environment/service/environment.service.ts b/apps/api/src/environment/service/environment.service.ts index 4ae10629..199ab3f9 100644 --- a/apps/api/src/environment/service/environment.service.ts +++ b/apps/api/src/environment/service/environment.service.ts @@ -17,6 +17,7 @@ import { UpdateEnvironment } from '../dto/update.environment/update.environment' import { PrismaService } from '../../prisma/prisma.service' import createEvent from '../../common/create-event' import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { paginate } from '../../common/paginate' @Injectable() export class EnvironmentService { @@ -171,8 +172,8 @@ export class EnvironmentService { prisma: this.prisma }) - // Get the environments - return await this.prisma.environment.findMany({ + // Get the environments for the required page + const items = await this.prisma.environment.findMany({ where: { projectId, name: { @@ -200,6 +201,24 @@ export class EnvironmentService { [sort]: order } }) + // Calculate metadata for pagination + const totalCount = await this.prisma.environment.count({ + where: { + projectId, + name: { + contains: search + } + } + }) + const metadata = paginate(totalCount, `/environment/all/${projectId}`, { + page, + limit, + sort, + order, + search + }) + + return { items, metadata } } async deleteEnvironment(user: User, environmentId: Environment['id']) { diff --git a/apps/cli/src/types/index.types.d.ts b/apps/cli/src/types/index.types.d.ts index 5ec40ef6..7fdb06ce 100644 --- a/apps/cli/src/types/index.types.d.ts +++ b/apps/cli/src/types/index.types.d.ts @@ -12,4 +12,21 @@ export interface ProfileConfig { } } +export interface Page { + items: T[] + metadata: { + page: number + perPage: number + pageCount: number + totalCount: number + links: { + self: string + first: string + previous: string | null + next: string | null + last: string + } + } +} + export type PrivateKeyConfig = Record diff --git a/packages/api-client/src/types/environment.types.d.ts b/packages/api-client/src/types/environment.types.d.ts index 14b44839..ed140704 100644 --- a/packages/api-client/src/types/environment.types.d.ts +++ b/packages/api-client/src/types/environment.types.d.ts @@ -1,3 +1,5 @@ +import { Page } from '../../../../apps/cli/src/types/index.types' + export interface CreateEnvironmentRequest { name: string description?: string @@ -53,8 +55,8 @@ export interface GetAllEnvironmentsOfProjectRequest { search?: string } -export interface GetAllEnvironmentsOfProjectResponse { - items: { +export interface GetAllEnvironmentsOfProjectResponse + extends Page<{ id: string name: string description: string | null @@ -66,8 +68,7 @@ export interface GetAllEnvironmentsOfProjectResponse { email: string profilePictureUrl: string | null } - }[] -} + }> {} export interface DeleteEnvironmentRequest { id: string diff --git a/packages/api-client/tests/environment.spec.ts b/packages/api-client/tests/environment.spec.ts index 4b0e68b6..1e4fcc12 100644 --- a/packages/api-client/tests/environment.spec.ts +++ b/packages/api-client/tests/environment.spec.ts @@ -69,15 +69,32 @@ describe('Get Environments Tests', () => { const environments = await EnvironmentController.getAllEnvironmentsOfProject( { - projectId + + projectId, + page: 0, + limit: 10 + }, { 'x-e2e-user-email': email } ) + expect(environments.items).toHaveLength(2) + expect(environments.items[0].name).toBe('Default') - expect(environments).toHaveLength(2) - expect(environments[0].name).toBe('Default') + //check metadata + expect(environments.metadata.totalCount).toEqual(2) + expect(environments.metadata.links.self).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(environments.metadata.links.first).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(environments.metadata.links.previous).toBeNull() + expect(environments.metadata.links.next).toBeNull() + expect(environments.metadata.links.last).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) }) it('should be able to fetch environment by ID', async () => { @@ -179,6 +196,18 @@ describe('Get Environments Tests', () => { } ) - expect(environments).toHaveLength(2) + expect(environments.items).toHaveLength(2) + expect(environments.metadata.totalCount).toEqual(2) + expect(environments.metadata.links.self).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(environments.metadata.links.first).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(environments.metadata.links.previous).toBeNull() + expect(environments.metadata.links.next).toBeNull() + expect(environments.metadata.links.last).toBe( + `/environment/all/${projectId}?page=0&limit=10&sort=name&order=asc&search=` + ) }) })