Skip to content

Commit

Permalink
feat: add report locking patch admin api (#540)
Browse files Browse the repository at this point in the history
  • Loading branch information
goemen authored Jun 13, 2024
1 parent 7f95cff commit 22188ed
Show file tree
Hide file tree
Showing 9 changed files with 701 additions and 472 deletions.
7 changes: 7 additions & 0 deletions backend/db/migrations/V1.0.25__admin_report_audit_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SET search_path TO pay_transparency;

alter table pay_transparency_report add column idir_modified_username varchar(255) null;
alter table pay_transparency_report add column idir_modified_date timestamp null;

alter table report_history add column idir_modified_username varchar(255) null;
alter table report_history add column idir_modified_date timestamp null;
4 changes: 4 additions & 0 deletions backend/src/v1/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ model pay_transparency_report {
is_unlocked Boolean @default(true)
reporting_year Decimal @db.Decimal
report_unlock_date DateTime? @db.Timestamp(6)
idir_modified_username String? @db.VarChar(255)
idir_modified_date DateTime? @db.Timestamp(6)
pay_transparency_calculated_data pay_transparency_calculated_data[]
naics_code_pay_transparency_report_naics_codeTonaics_code naics_code @relation("pay_transparency_report_naics_codeTonaics_code", fields: [naics_code], references: [naics_code], onDelete: NoAction, onUpdate: NoAction, map: "pay_transparency_report_naics_code_fk")
employee_count_range employee_count_range @relation(fields: [employee_count_range_id], references: [employee_count_range_id], onDelete: NoAction, onUpdate: NoAction, map: "pay_transparency_report_employee_count_range_id_fk")
Expand Down Expand Up @@ -176,6 +178,8 @@ model report_history {
is_unlocked Boolean @default(true)
reporting_year Decimal @db.Decimal
report_unlock_date DateTime? @db.Timestamp(6)
idir_modified_username String? @db.VarChar(255)
idir_modified_date DateTime? @db.Timestamp(6)
calculated_data_history calculated_data_history[]
pay_transparency_company pay_transparency_company @relation(fields: [company_id], references: [company_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_company_id_fk")
employee_count_range employee_count_range @relation(fields: [employee_count_range_id], references: [employee_count_range_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_employee_count_range_id_fk")
Expand Down
115 changes: 93 additions & 22 deletions backend/src/v1/routes/admin-report-routes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import express, { Application } from 'express';
import router from './admin-report-routes';
import request from 'supertest';
import bodyParser from 'body-parser';

jest.mock('../services/utils-service', () => ({
utils: {
...jest.requireActual('../services/utils-service').utils,
getSessionUser: () => ({ idir_username: 'SOME_USR'}),
},
}));

const mockSearchReport = jest.fn();
jest.mock('../services/report-search-service', () => ({
reportSearchService: {
const mockChangeReportLockStatus = jest.fn();
jest.mock('../services/admin-report-service', () => ({
adminReportService: {
searchReport: (...args) => mockSearchReport(...args),
changeReportLockStatus: (...args) => mockChangeReportLockStatus(...args),
},
}));

Expand All @@ -14,32 +24,93 @@ describe('admin-report-routes', () => {
beforeEach(() => {
jest.clearAllMocks();
app = express();
app.use(bodyParser.json())
app.use(router);
});

it('should default offset=0 and limit=20', async () => {
await request(app).get('').expect(200);
expect(mockSearchReport).toHaveBeenCalledWith(
describe('GET /', () => {
it('should default offset=0 and limit=20', async () => {
await request(app).get('').expect(200);
expect(mockSearchReport).toHaveBeenCalledWith(
0,
20,
undefined,
undefined
)
});

it('should resolve query params', async () => {
await request(app).get('')
.query({offset: 1, limit: 50, filter: 'naics_code=11', sort: 'field=naics_code,direction=asc'}).expect(200);
expect(mockSearchReport).toHaveBeenCalledWith(
1,
50,
'field=naics_code,direction=asc',
'naics_code=11',
);
undefined,
);
});

it('should resolve query params', async () => {
await request(app)
.get('')
.query({
offset: 1,
limit: 50,
filter: 'naics_code=11',
sort: 'field=naics_code,direction=asc',
})
.expect(200);
expect(mockSearchReport).toHaveBeenCalledWith(
1,
50,
'field=naics_code,direction=asc',
'naics_code=11',
);
});
it('400 - if search reports fails', async () => {
mockSearchReport.mockRejectedValue({ error: true });
await request(app)
.get('')
.query({
offset: 1,
limit: 50,
filter: 'naics_code=11',
sort: 'field=naics_code,direction=asc',
})
.expect(400);
});
});
it('400 - if search reports fails', async () => {
mockSearchReport.mockRejectedValue({error: true})
await request(app).get('')
.query({offset: 1, limit: 50, filter: 'naics_code=11', sort: 'field=naics_code,direction=asc'}).expect(400);

describe('PATCH /:id', () => {
describe('400', () => {
it('invalid body', () => {
return request(app).patch('/4492feff-99d7-4b2b-8896-12a59a75d4e3')
.send({invalid: false})
.expect(400)
.expect(({body}) => {
expect(body.error).toBe('Missing field "is_unlocked" in the data');
});
});
it('fail to change report lock status', () => {
mockChangeReportLockStatus.mockRejectedValue({ error: 'Error happened' });
return request(app).patch('/4492feff-99d7-4b2b-8896-12a59a75d4e3')
.send({is_unlocked: false})
.expect(400)
.expect(({body}) => {
expect(body.error).toBe('Error happened');
});
});
});
it('404 - report is not found', () => {
mockChangeReportLockStatus.mockRejectedValue({ code: 'P2025' });
return request(app).patch('/4492feff-99d7-4b2b-8896-12a59a75d4e3')
.send({is_unlocked: false})
.expect(404)
.expect(({body}) => {
expect(body.error).toBe('Report not found')
});
});
it('200 - report lock status changed', () => {
mockChangeReportLockStatus.mockResolvedValue({
report_id: '4492feff-99d7-4b2b-8896-12a59a75d4e3',
});
return request(app).patch('/4492feff-99d7-4b2b-8896-12a59a75d4e3')
.send({is_unlocked: false})
.expect(200)
.expect(({body}) => {
expect(body).toEqual({
report_id: '4492feff-99d7-4b2b-8896-12a59a75d4e3',
});
});
});
});
});
41 changes: 39 additions & 2 deletions backend/src/v1/routes/admin-report-routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import express, { Request, Response } from 'express';
import { logger } from '../../logger';
import { utils } from '../services/utils-service';
import { reportSearchService } from '../services/report-search-service';
import { adminReportService } from '../services/admin-report-service';
import has from 'lodash/has';

const router = express.Router();
/**
Expand All @@ -17,7 +18,7 @@ router.get(
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0;
const limit = req.query.limit ? parseInt(req.query.limit as string) : 20;
try {
const paginatedReportsResponse = await reportSearchService.searchReport(
const paginatedReportsResponse = await adminReportService.searchReport(
offset,
limit,
req.query.sort as string,
Expand All @@ -31,4 +32,40 @@ router.get(
}),
);

/**
* PATCH - /admin-api/v1/reports/:id
* Update report is_unlocked status
* Example: body {is_unlocked: true/false}
*/
router.patch(
'/:id',
utils.asyncHandler(async (req, res: Response) => {
logger.info('Update report is_unlocked status called');
logger.silly(req.body);
try {
const { body, params } = req;
const user = utils.getSessionUser(req);
if (!has(body, 'is_unlocked')) {
return res
.status(400)
.json({ error: 'Missing field "is_unlocked" in the data' });
}

const report = await adminReportService.changeReportLockStatus(
params?.id,
user?.idir_username,
body.is_unlocked,
);

return res.status(200).json(report);
} catch (error) {
logger.error(error);
if (error.code === 'P2025') {
return res.status(404).json({ error: 'Report not found' });
}
res.status(400).json(error);
}
}),
);

export default router;
Loading

0 comments on commit 22188ed

Please sign in to comment.