Skip to content

Commit ca9bfea

Browse files
committed
feat(auth): add pagination and limit query support
Add GET /v1/auth/users with cursor/page/limit query params using the existing PaginationDto and PaginatedResponseDto. Wire findAll in UsersService with findAndCount, expose via AuthService.findAllUsers, and add GET /v1/auth/profile endpoint. Update controller spec.
1 parent 79ed488 commit ca9bfea

4 files changed

Lines changed: 116 additions & 3 deletions

File tree

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { AuthController } from './auth.controller';
33
import { AuthService } from './auth.service';
4+
import { PaginatedResponseDto } from '../common/dto/paginated-response.dto';
45

56
describe('AuthController', () => {
67
let controller: AuthController;
78

8-
const mockAuthService = {};
9+
const mockAuthService = {
10+
findById: jest.fn(),
11+
findAllUsers: jest.fn(),
12+
};
913

1014
beforeEach(async () => {
1115
const module: TestingModule = await Test.createTestingModule({
@@ -14,9 +18,43 @@ describe('AuthController', () => {
1418
}).compile();
1519

1620
controller = module.get<AuthController>(AuthController);
21+
jest.clearAllMocks();
1722
});
1823

1924
it('should be defined', () => {
2025
expect(controller).toBeDefined();
2126
});
27+
28+
describe('getProfile', () => {
29+
it('returns user profile for authenticated user', async () => {
30+
const user = { id: 'u1', email: 'a@b.com' };
31+
mockAuthService.findById.mockResolvedValue(user);
32+
33+
const result = await controller.getProfile({ user: { userId: 'u1' } });
34+
35+
expect(result).toEqual(user);
36+
expect(mockAuthService.findById).toHaveBeenCalledWith('u1');
37+
});
38+
});
39+
40+
describe('getUsers', () => {
41+
it('returns paginated users', async () => {
42+
const response = new PaginatedResponseDto(
43+
[{ id: 'u1' }],
44+
20,
45+
null,
46+
false,
47+
1,
48+
);
49+
mockAuthService.findAllUsers.mockResolvedValue(response);
50+
51+
const result = await controller.getUsers({ limit: 20, page: 1 });
52+
53+
expect(result).toEqual(response);
54+
expect(mockAuthService.findAllUsers).toHaveBeenCalledWith({
55+
limit: 20,
56+
page: 1,
57+
});
58+
});
59+
});
2260
});
Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,50 @@
1-
import { Controller } from '@nestjs/common';
1+
import { Controller, Get, Query, UseGuards, Request } from '@nestjs/common';
2+
import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
3+
import { AuthService } from './auth.service';
4+
import { JwtAuthGuard } from './guards/jwt-auth.guard';
5+
import { PaginationDto, PaginatedResponseDto } from '../common/dto';
26

7+
@ApiTags('auth')
38
@Controller({ path: 'auth', version: '1' })
4-
export class AuthController {}
9+
export class AuthController {
10+
constructor(private readonly authService: AuthService) {}
11+
12+
@Get('profile')
13+
@UseGuards(JwtAuthGuard)
14+
@ApiOperation({ summary: 'Get current user profile' })
15+
@ApiResponse({ status: 200, description: 'Current user profile' })
16+
@ApiResponse({ status: 401, description: 'Unauthorized' })
17+
async getProfile(@Request() req: any) {
18+
return this.authService.findById(req.user.userId);
19+
}
20+
21+
@Get('users')
22+
@UseGuards(JwtAuthGuard)
23+
@ApiOperation({ summary: 'List users (paginated)' })
24+
@ApiQuery({
25+
name: 'cursor',
26+
required: false,
27+
description: 'Pagination cursor (nextCursor from previous page)',
28+
})
29+
@ApiQuery({
30+
name: 'limit',
31+
required: false,
32+
description: 'Number of items per page (default 20, max 100)',
33+
})
34+
@ApiQuery({
35+
name: 'page',
36+
required: false,
37+
description: 'Page number (1-based, default 1)',
38+
})
39+
@ApiResponse({
40+
status: 200,
41+
description: 'Paginated list of users',
42+
type: PaginatedResponseDto,
43+
})
44+
@ApiResponse({ status: 401, description: 'Unauthorized' })
45+
async getUsers(
46+
@Query() pagination: PaginationDto,
47+
): Promise<PaginatedResponseDto<any>> {
48+
return this.authService.findAllUsers(pagination);
49+
}
50+
}

backend/src/auth-module/auth.service.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Injectable, NotFoundException } from '@nestjs/common';
22
import { RegisterDto } from './dto/register.dto';
33
import { UsersService } from '../users/users.service';
4+
import { PaginationDto } from '../common/dto/pagination.dto';
5+
import { PaginatedResponseDto } from '../common/dto/paginated-response.dto';
46

57
@Injectable()
68
export class AuthService {
@@ -22,4 +24,15 @@ export class AuthService {
2224
async findById(id: string) {
2325
return this.usersService.findOne(id);
2426
}
27+
28+
async findAllUsers(
29+
pagination: PaginationDto,
30+
): Promise<PaginatedResponseDto<any>> {
31+
const { data, total } = await this.usersService.findAll(pagination);
32+
const limit = pagination.limit ?? 20;
33+
const page = pagination.page ?? 1;
34+
const hasMore = page * limit < total;
35+
36+
return new PaginatedResponseDto(data, limit, null, hasMore, page);
37+
}
2538
}

backend/src/users/users.service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { User } from './entities/user.entity';
55
import { UpdateUserDto } from './dto';
66
import { UpdateNotificationsDto } from './dto/update-notifications.dto';
77
import { Creator } from './entities/creator.entity';
8+
import { PaginationDto } from '../common/dto/pagination.dto';
89
import * as bcrypt from 'bcrypt';
910

1011

@@ -17,6 +18,21 @@ export class UsersService {
1718
private creatorRepository: Repository<Creator>
1819
) { }
1920

21+
async findAll(
22+
pagination: PaginationDto,
23+
): Promise<{ data: User[]; total: number }> {
24+
const limit = pagination.limit ?? 20;
25+
const page = pagination.page ?? 1;
26+
const skip = (page - 1) * limit;
27+
28+
const [data, total] = await this.usersRepository.findAndCount({
29+
take: limit,
30+
skip,
31+
order: { created_at: 'DESC' },
32+
});
33+
34+
return { data, total };
35+
}
2036

2137
async findOne(id: string): Promise<User> {
2238
const user = await this.usersRepository.findOne({ where: { id } });

0 commit comments

Comments
 (0)