Skip to content

Commit

Permalink
Merge pull request #145 from amosproj/125-backend-module-filter-backu…
Browse files Browse the repository at this point in the history
…p-data-by-backup-tasks-ids

125 backend module filter backup data by backup tasks ids
  • Loading branch information
chrisklg authored Dec 8, 2024
2 parents eeed490 + b8a1351 commit 19a98a6
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 36 deletions.
51 changes: 39 additions & 12 deletions apps/backend/src/app/backupData/backupData.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ describe('BackupDataController (e2e)', () => {
});
});

it('/backupData (GET) with pagination should return paginated backup data entries', async () => {
it('/backupData/filter (POST) with pagination should return paginated backup data entries', async () => {
const response = await request(app.getHttpServer())
.get('/backupData?offset=0&limit=1')
.expect(200);
.post('/backupData/filter?offset=0&limit=1')
.expect(201);

expect(response.body).toEqual({
data: [
Expand All @@ -117,10 +117,10 @@ describe('BackupDataController (e2e)', () => {
});
});

it('/backupData (GET) with date range should return backup data entries within date range', async () => {
it('/backupData/filter (POST) with date range should return backup data entries within date range', async () => {
const response = await request(app.getHttpServer())
.get('/backupData?fromDate=2023-12-01&toDate=2023-12-31')
.expect(200);
.post('/backupData/filter?fromDate=2023-12-01&toDate=2023-12-31')
.expect(201);

expect(response.body).toEqual({
data: [
Expand All @@ -139,21 +139,21 @@ describe('BackupDataController (e2e)', () => {
where: { creationDate: expect.any(Object), type: BackupType.FULL },
});
});
it('/backupData (GET) with taskId should return backup data entries with the specified taskId', async () => {
it('/backupData/filter (POST) with taskId should return backup data entries with the specified taskId', async () => {
await request(app.getHttpServer())
.get('/backupData?taskId=task-123')
.expect(200);
.post('/backupData/filter?taskId=task-123')
.expect(201);

expect(mockBackupDataRepository.findAndCount).toBeCalledWith({
order: { creationDate: 'DESC' },
where: { taskId: { id: 'task-123' }, type: BackupType.FULL },
});
});

it('/backupData (GET) with taskName should return backup data entries with the specified taskName', async () => {
it('/backupData/filter (POST) with taskName should return backup data entries with the specified taskName', async () => {
await request(app.getHttpServer())
.get('/backupData?taskName=backup-task')
.expect(200);
.post('/backupData/filter?taskName=backup-task')
.expect(201);

expect(mockBackupDataRepository.findAndCount).toBeCalledWith({
order: { creationDate: 'DESC' },
Expand All @@ -163,4 +163,31 @@ describe('BackupDataController (e2e)', () => {
},
});
});

it('/backupData/filter (POST) with multiple taskIds should return backup data entries with the specified taskIds', async () => {
const response = await request(app.getHttpServer())
.post('/backupData/filter')
.send({ taskIds: ['task-1', 'task-2'] })
.expect(201);

expect(response.body).toEqual({
data: [
{
...mockBackupDataEntity,
creationDate: mockBackupDataEntity.creationDate.toISOString(),
},
],
paginationData: {
total: 1,
},
});

expect(mockBackupDataRepository.findAndCount).toBeCalledWith({
order: { creationDate: 'DESC' },
where: {
taskId: [{ id: 'task-1' }, { id: 'task-2' }],
type: BackupType.FULL,
},
});
});
});
14 changes: 11 additions & 3 deletions apps/backend/src/app/backupData/backupData.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Query,
} from '@nestjs/common';
import {
ApiBody,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
Expand All @@ -21,6 +22,7 @@ import { PaginationDto } from '../utils/pagination/PaginationDto';
import { PaginationOptionsDto } from '../utils/pagination/PaginationOptionsDto';
import { BackupDataFilterDto } from './dto/backupDataFilter.dto';
import { BackupDataOrderOptionsDto } from './dto/backupDataOrderOptions.dto';
import { BackupDataFilterByTaskIdsDto } from './dto/backupDataFilterByTaskIds.dto';

@ApiTags('Backup Data')
@Controller('backupData')
Expand All @@ -41,18 +43,24 @@ export class BackupDataController {
return entity;
}

@Get()
@Post('filter')
@ApiOperation({ summary: 'Returns all backupData Objects.' })
@ApiOkResponse()
@ApiBody({
type: BackupDataFilterByTaskIdsDto,
required: false,
})
async findAll(
@Query() paginationOptionsDto: PaginationOptionsDto,
@Query() backupDataFilterDto: BackupDataFilterDto,
@Query() backupDataOrderOptionsDto: BackupDataOrderOptionsDto
@Query() backupDataOrderOptionsDto: BackupDataOrderOptionsDto,
@Body() backupDataFilterByTaskIdsDto?: BackupDataFilterByTaskIdsDto
): Promise<PaginationDto<BackupDataDto>> {
return this.backupDataService.findAll(
paginationOptionsDto,
backupDataOrderOptionsDto,
backupDataFilterDto
backupDataFilterDto,
backupDataFilterByTaskIdsDto
);
}

Expand Down
10 changes: 10 additions & 0 deletions apps/backend/src/app/backupData/backupData.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ describe('BackupDataService', () => {
type: BackupType.FULL,
});
});

it('should create a where clause for multiple taskIds', () => {
const filterDto: BackupDataFilterDto = {};
const filterByTaskIdsDto = { taskIds: ['task-1', 'task-2'] };
const where = service.createWhereClause(filterDto, filterByTaskIdsDto);
expect(where).toEqual({
taskId: [{ id: 'task-1' }, { id: 'task-2' }],
type: BackupType.FULL,
});
});
});

describe('createOrderClause', () => {
Expand Down
22 changes: 19 additions & 3 deletions apps/backend/src/app/backupData/backupData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { BackupDataDto } from './dto/backupData.dto';
import { BackupDataFilterDto } from './dto/backupDataFilter.dto';
import { BackupDataOrderOptionsDto } from './dto/backupDataOrderOptions.dto';
import { BackupType } from './dto/backupType';
import { BackupDataFilterByTaskIdsDto } from './dto/backupDataFilterByTaskIds.dto';
import { TaskEntity } from '../tasks/entity/task.entity';

@Injectable()
export class BackupDataService extends PaginationService {
Expand All @@ -41,12 +43,13 @@ export class BackupDataService extends PaginationService {
async findAll(
paginationOptionsDto: PaginationOptionsDto,
backupDataOrderOptionsDto: BackupDataOrderOptionsDto,
backupDataFilterDto: BackupDataFilterDto
backupDataFilterDto: BackupDataFilterDto,
backupDataFilterByTaskIdsDto?: BackupDataFilterByTaskIdsDto
): Promise<PaginationDto<BackupDataDto>> {
return await this.paginate<BackupDataEntity>(
this.backupDataRepository,
this.createOrderClause(backupDataOrderOptionsDto),
this.createWhereClause(backupDataFilterDto),
this.createWhereClause(backupDataFilterDto, backupDataFilterByTaskIdsDto),
paginationOptionsDto
);
}
Expand Down Expand Up @@ -79,7 +82,10 @@ export class BackupDataService extends PaginationService {
* Create where clause.
* @param backupDataFilterDto
*/
createWhereClause(backupDataFilterDto: BackupDataFilterDto) {
createWhereClause(
backupDataFilterDto: BackupDataFilterDto,
backupDataFilterByTaskIdsDto?: BackupDataFilterByTaskIdsDto
) {
const where: FindOptionsWhere<BackupDataEntity> = {};

//ID search
Expand Down Expand Up @@ -144,6 +150,16 @@ export class BackupDataService extends PaginationService {
where.taskId = { id: backupDataFilterDto.taskId };
}

//Multiple Task ids from body search
if (backupDataFilterByTaskIdsDto?.taskIds) {
const taskIdFilter: FindOptionsWhere<TaskEntity>[] = [];
const taskIds = backupDataFilterByTaskIdsDto.taskIds;
for (const taskId of taskIds) {
taskIdFilter.push({ id: taskId });
}
where.taskId = taskIdFilter;
}

//Task name search
if (backupDataFilterDto.taskName) {
where.taskId = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional } from 'class-validator';

export class BackupDataFilterByTaskIdsDto {
@ApiProperty({
description: 'Array of task ids to be filtered by',
})
@IsOptional()
taskIds?: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { fail } from 'assert';
describe('BackupService', () => {
let service: BackupService;
let httpClientMock: {
get: ReturnType<typeof vi.fn>;
post: ReturnType<typeof vi.fn>;
};
const baseUrl = 'http://test-url';

beforeEach(() => {
httpClientMock = {
get: vi.fn(),
post: vi.fn(),
};

service = new BackupService(
Expand Down Expand Up @@ -46,16 +46,17 @@ describe('BackupService', () => {
},
};

httpClientMock.get.mockReturnValue(of(mockResponse));
httpClientMock.post.mockReturnValue(of(mockResponse));

service.getAllBackups(mockFilterParams).subscribe((response) => {
expect(httpClientMock.get).toHaveBeenCalledWith(
`${baseUrl}/backupData`,
expect(httpClientMock.post).toHaveBeenCalledWith(
`${baseUrl}/backupData/filter`,
null,
{
params: expect.any(HttpParams),
}
);
const passedParams = httpClientMock.get.mock.calls[0][1].params;
const passedParams = httpClientMock.post.mock.calls[0][2].params;
expect(passedParams.keys()).toEqual(['offset', 'limit']);
});
});
Expand All @@ -79,17 +80,18 @@ describe('BackupService', () => {
},
};

httpClientMock.get.mockReturnValue(of(mockResponse));
httpClientMock.post.mockReturnValue(of(mockResponse));

service.getAllBackups(mockFilterParams).subscribe((response) => {
expect(httpClientMock.get).toHaveBeenCalledWith(
`${baseUrl}/backupData`,
expect(httpClientMock.post).toHaveBeenCalledWith(
`${baseUrl}/backupData/filter`,
null,
{
params: expect.any(HttpParams),
}
);

const passedParams = httpClientMock.get.mock.calls[0][1].params;
const passedParams = httpClientMock.post.mock.calls[0][2].params;
expect(passedParams.get('limit')).toBe('10');
expect(passedParams.get('offset')).toBe('0');
expect(passedParams.get('orderBy')).toBe('createdAt');
Expand All @@ -114,14 +116,14 @@ describe('BackupService', () => {
},
};

httpClientMock.get.mockReturnValue(of(mockResponse));
httpClientMock.post.mockReturnValue(of(mockResponse));

const observable = service.getAllBackups(mockFilterParams);

observable.subscribe();
observable.subscribe();

expect(httpClientMock.get).toHaveBeenCalledTimes(1);
expect(httpClientMock.post).toHaveBeenCalledTimes(1);
});

it('should handle empty filter params', () => {
Expand All @@ -134,17 +136,18 @@ describe('BackupService', () => {
},
};

httpClientMock.get.mockReturnValue(of(mockResponse));
httpClientMock.post.mockReturnValue(of(mockResponse));

service.getAllBackups(mockFilterParams).subscribe((response) => {
expect(httpClientMock.get).toHaveBeenCalledWith(
`${baseUrl}/backupData`,
expect(httpClientMock.post).toHaveBeenCalledWith(
`${baseUrl}/backupData/filter`,
null,
{
params: expect.any(HttpParams),
}
);

const passedParams = httpClientMock.get.mock.calls[0][1].params;
const passedParams = httpClientMock.post.mock.calls[0][2].params;
expect(passedParams.keys().length).toBe(0);
});
});
Expand All @@ -157,7 +160,7 @@ describe('BackupService', () => {

const mockError = new Error('Network error');

httpClientMock.get.mockReturnValue(throwError(() => mockError));
httpClientMock.post.mockReturnValue(throwError(() => mockError));
service.getAllBackups(mockFilterParams).subscribe({
next: () => fail('expected an error, not backups'),
error: (error) => expect(error).toBe(mockError),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class BackupService {
const params = new HttpParams({ fromObject: cleanParams });

return this.http
.get<APIResponse<Backup>>(`${this.baseUrl}/backupData`, {
.post<APIResponse<Backup>>(`${this.baseUrl}/backupData/filter`, null, {
params: params,
})
.pipe(shareReplay(1));
Expand Down

0 comments on commit 19a98a6

Please sign in to comment.