Skip to content

Commit

Permalink
feat: GEO-698 Save errors in db (#551)
Browse files Browse the repository at this point in the history
  • Loading branch information
jer3k committed Jun 24, 2024
1 parent 28207e8 commit b768e39
Show file tree
Hide file tree
Showing 22 changed files with 818 additions and 30 deletions.
1 change: 1 addition & 0 deletions .github/workflows/.deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ jobs:
--set-string global.secrets.bceidWsOnlineServiceId="${{ secrets.BCEID_WS_ONLINE_SERVICE_ID }}" \
--set-string global.secrets.externalConsumerApiKey="${{ secrets.EXTERNAL_CONSUMER_API_KEY }}" \
--set-string global.secrets.externalConsumerDeleteReportsApiKey="${{ secrets.EXTERNAL_CONSUMER_DELETE_REPORTS_API_KEY }}" \
--set-string global.secrets.externalConsumerErrorReportsApiKey="${{ secrets.EXTERNAL_CONSUMER_ERROR_REPORTS_API_KEY }}" \
--set-string global.secrets.chesTokenURL="${{ secrets.CHES_TOKEN_ENDPOINT }}" \
--set-string global.secrets.chesClientID="${{ secrets.CHES_CLIENT_ID }}" \
--set-string global.secrets.chesClientSecret="${{ secrets.CHES_CLIENT_SECRET }}" \
Expand Down
6 changes: 5 additions & 1 deletion backend-external/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ config.defaults({
morganFormat: 'dev',
apiKey: process.env.EXTERNAL_CONSUMER_API_KEY || 'api-key',
deleteReportsApiKey:
process.env.EXTERNAL_CONSUMER_DELETE_REPORTS_API_KEY || 'api-delete-reports-key',
process.env.EXTERNAL_CONSUMER_DELETE_REPORTS_API_KEY ||
'api-delete-reports-key',
errorReportsApiKey:
process.env.EXTERNAL_CONSUMER_ERROR_REPORTS_API_KEY ||
'api-error-reports-key',
port: process.env.PORT || 3002,
rateLimit: {
enabled: process.env.IS_RATE_LIMIT_ENABLED || false, // Disable if rate limiting is not required
Expand Down
69 changes: 68 additions & 1 deletion backend-external/src/v1/routes/pay-transparency-routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import router from './pay-transparency-routes';

const mockGetPayTransparencyData = jest.fn();
const mockDeleteReports = jest.fn();
const mockGetReportErrors = jest.fn();
jest.mock('../services/pay-transparency-service', () => ({
payTransparencyService: {
getPayTransparencyData: (...args) => mockGetPayTransparencyData(...args),
deleteReports: (...args) => mockDeleteReports(...args),
getReportErrors: (...args) => mockGetReportErrors(...args),
},
}));

Expand Down Expand Up @@ -60,7 +62,7 @@ describe('pay-transparency-routes', () => {
describe('should default to max page size 50', () => {
it('when specified page size if greater than 50', () => {
mockGetPayTransparencyData.mockImplementation((...args) => {
expect(args[3]).toBe(50)
expect(args[3]).toBe(50);
return {
status: 200,
data: [{ id: 1 }],
Expand Down Expand Up @@ -116,4 +118,69 @@ describe('pay-transparency-routes', () => {
.expect(404);
});
});

describe('GET /errors', () => {
it('should return data if user does not send query params', async () => {
mockGetReportErrors.mockReturnValue({
status: 200,
data: [{ id: 1 }],
});
await request(app)
.get('/errors')
.set('x-api-key', 'api-error-reports-key')
.expect(200)
.expect(({ body }) => {
expect(body).toHaveLength(1);
});
expect(mockGetReportErrors).toHaveBeenCalledWith(
undefined,
undefined,
undefined,
undefined,
);
});
it('should forward all parameters to the function', async () => {
mockGetReportErrors.mockReturnValue({
status: 200,
data: [{ id: 1 }],
});
await request(app)
.get('/errors')
.set('x-api-key', 'api-error-reports-key')
.query({
startDate: 'start',
endDate: 'end',
page: '1',
pageSize: '50',
})
.expect(200)
.expect(({ body }) => {
expect(body).toHaveLength(1);
});
expect(mockGetReportErrors).toHaveBeenCalledWith(
'start',
'end',
'1',
'50',
);
});

it('should return 400 if the function has an error', () => {
mockGetReportErrors.mockReturnValue({
data: { message: 'Failed to get data', error: true },
});
return request(app)
.get('/errors')
.set('x-api-key', 'api-error-reports-key')
.expect(400);
});

it('should fail if request fails', () => {
mockGetReportErrors.mockRejectedValue({ message: 'Error happened' });
return request(app)
.get('/errors')
.set('x-api-key', 'api-error-reports-key')
.expect(500);
});
});
});
150 changes: 146 additions & 4 deletions backend-external/src/v1/routes/pay-transparency-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,70 @@ const validateApiKey =
* type: array
* items:
* $ref: "#/components/schemas/Report"
*
* Error:
* type: object
* properties:
* user_error_id:
* type:string
* user_id:
* type:string
* company_id:
* type:string
* error:
* type:string
* create_date:
* type:string
* format:date-time
* pay_transparency_company:
* type: array
* items:
* $ref: "#/components/schemas/Company"
*
* Company:
* type: object
* properties:
* company_id:
* type: string
* company_name:
* type: string
* province:
* type: string
* city:
* type: string
* bceid_business_guid:
* type: string
* country:
* type: string
* postal_code:
* type: string
* postal_address_line1:
* type: string
* postal_address_line2:
* type: string
* create_date:
* type:string
* format:date-time
* update_date:
* type:string
* format:date-time
*
* PaginatedErrors:
* type: object
* properties:
* totalRecords:
* type: number
* description: total number of records in the database
* page:
* type: number
* description: Current page offset
* pageSize:
* type: number
* description: Number of reports retrieved per request
* records:
* type: array
* items:
* $ref: "#/components/schemas/Error"
*/

/**
Expand All @@ -130,7 +194,7 @@ const validateApiKey =
* type: integer
* minimum: 0
* required: false
* description: The page offset number to retrive reports - optional
* description: The page offset number to retrieve reports - optional
* - in: query
* name: pageSize
* schema:
Expand Down Expand Up @@ -166,9 +230,7 @@ const validateApiKey =
router.get(
'/',
validateApiKey(config.get('server:apiKey')),
query('startDate')
.isISO8601()
.withMessage('Invalid start date format'),
query('startDate').isISO8601().withMessage('Invalid start date format'),
query('endDate').isISO8601().withMessage('Invalid end date format'),
utils.asyncHandler(async (req: Request, res: Response) => {
try {
Expand Down Expand Up @@ -254,4 +316,84 @@ router.delete(
},
);

/**
* @swagger
* tags:
* name: Errors
* /reports/errors:
* get:
* summary: Get errors of submitted reports created within a period of time (date range defaults to the last 30 full UTC days, which does not including today)
* tags: [Errors]
* security:
* - ApiKeyAuth: []
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* minimum: 0
* required: false
* description: The page offset number to retrieve errors - optional
* - in: query
* name: pageSize
* schema:
* type: integer
* minimum: 1
* maximum: 1000
* required: false
* description: The number of records per page (max 1000, default 1000) - optional
* - in: query
* name: startDate
* type: date
* required: false
* description: "Start date (in UTC or ISO-8601) to retrieve records (format: YYYY-MM-dd HH:mm, default -31 days ) - optional"
* - in: query
* name: endDate
* type: string
* required: false
* description: "End date (in UTC or ISO-8601) to retrieve records (format: YYYY-MM-dd HH:mm, default now) - optional"
*
*
* responses:
* 200:
* description: A paginated list of errors
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/PaginatedErrors"
*/
router.get(
'/errors',
validateApiKey(config.get('server:errorReportsApiKey')),
utils.asyncHandler(
async (
req: Request<
null,
null,
null,
{ startDate: string; endDate: string; page: string; pageSize: string }
>,
res: Response,
) => {
try {
const { status, data } = await payTransparencyService.getReportErrors(
req.query.startDate,
req.query.endDate,
req.query.page,
req.query.pageSize,
);

if (data.error) {
return res.status(400).json({ error: data.message });
}
res.status(status).json(data);
} catch (e) {
res.status(500).json({ error: e.message });
}
},
),
);

export default router;
36 changes: 29 additions & 7 deletions backend-external/src/v1/services/pay-transparency-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe('pay-transparency-service', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('getPayTransparencyData', () => {
it('should forward to request to the backend api', async () => {
mockGet.mockReturnValue({});
Expand All @@ -25,14 +26,17 @@ describe('pay-transparency-service', () => {
0,
1000,
);
expect(mockGet).toHaveBeenCalledWith('/external-consumer-api/v1/reports', {
params: {
startDate: 'start',
endDate: 'end',
offset: 0,
limit: 1000,
expect(mockGet).toHaveBeenCalledWith(
'/external-consumer-api/v1/reports',
{
params: {
startDate: 'start',
endDate: 'end',
offset: 0,
limit: 1000,
},
},
});
);
});
});

Expand All @@ -54,4 +58,22 @@ describe('pay-transparency-service', () => {
);
});
});

describe('getReportErrors', () => {
it('should forward to request to the backend api', async () => {
mockGet.mockReturnValue({});
await payTransparencyService.getReportErrors('start', 'end', '0', '1000');
expect(mockGet).toHaveBeenCalledWith(
'/external-consumer-api/v1/reports/errors',
{
params: {
startDate: 'start',
endDate: 'end',
page: '0',
limit: '1000',
},
},
);
});
});
});
21 changes: 21 additions & 0 deletions backend-external/src/v1/services/pay-transparency-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const payTransparencyService = {
.get('/external-consumer-api/v1/reports', axiosConfig);
return { status, data };
},

async deleteReports(req: Request) {
const axiosConfig: AxiosRequestConfig = {
params: req.query,
Expand All @@ -36,4 +37,24 @@ export const payTransparencyService = {
}>('/external-consumer-api/v1/reports', axiosConfig);
return { status, data };
},

async getReportErrors(
startDate: string,
endDate: string,
page: string,
limit: string,
) {
const axiosConfig = {
params: {
startDate,
endDate,
page,
limit,
},
};
const { status, data } = await utils
.backendAxios()
.get('/external-consumer-api/v1/reports/errors', axiosConfig);
return { status, data };
},
};
17 changes: 17 additions & 0 deletions backend/db/migrations/V1.0.26__error_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SET search_path TO pay_transparency;

create table if not exists user_error
(
user_error_id uuid default gen_random_uuid() not null,
user_id uuid not null,
company_id uuid not null,
error varchar(255) not null,
create_date timestamp default current_timestamp not null,
constraint user_error_id_pk primary key (user_error_id),
constraint error_pt_user_id_fk foreign key (user_id) references pay_transparency_user (user_id),
constraint error_pt_company_id_fk foreign key (company_id) references pay_transparency_company (company_id)

);

CREATE INDEX "user_error_create_date_idx" ON "user_error"("create_date");

Loading

0 comments on commit b768e39

Please sign in to comment.