Skip to content

Commit

Permalink
Moved admin area to a more appropriate spot and created new tests for…
Browse files Browse the repository at this point in the history
… the new endpoints and services
  • Loading branch information
GrahamS-Quartech committed Mar 19, 2024
1 parent b8f3ae3 commit 34d35f9
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 52 deletions.
2 changes: 1 addition & 1 deletion express-api/src/controllers/admin/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as adminAccessRequests from './accessRequests/accessRequestsController';
import * as adminAdministrativeAreas from './administrativeAreas/administrativeAreasController';
import * as adminAdministrativeAreas from '../administrativeAreas/administrativeAreasController';
import * as adminRoles from './roles/rolesController';
import * as adminClaims from './claims/claimsController';
import * as adminUsers from './users/usersController';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Request, Response } from 'express';
import { NextFunction, Request, Response } from 'express';
import { stubResponse } from '@/utilities/stubResponse';
import { KeycloakUser } from '@bcgov/citz-imb-kc-express';
import { AdministrativeAreaFilterSchema, AdministrativeAreaPublicResponseSchema } from '@/services/administrativeAreas/administrativeAreaSchema';
import {
AdministrativeAreaFilterSchema,
AdministrativeAreaPublicResponseSchema,
} from '@/services/administrativeAreas/administrativeAreaSchema';
import administrativeAreasServices from '@/services/administrativeAreas/administrativeAreasServices';
import { Roles } from '@/constants/roles';

Expand All @@ -11,26 +14,29 @@ import { Roles } from '@/constants/roles';
* @param {Response} res Outgoing response
* @returns {Response} A 200 status with a list of administrative areas.
*/
export const getAdministrativeAreas = async (req: Request, res: Response) => {
export const getAdministrativeAreas = async (req: Request, res: Response, next: NextFunction) => {
/**
* #swagger.tags = ['Administrative Areas - Admin']
* #swagger.description = 'Returns a paged list of administrative areas from the datasource.'
* #swagger.security = [{
"bearerAuth": []
}]
*/

const kcUser = req.user as KeycloakUser;
const filter = AdministrativeAreaFilterSchema.safeParse(req.query);
if (filter.success) {
const adminAreas = await administrativeAreasServices.getAdministrativeAreas(filter.data);
if (!kcUser.client_roles || !kcUser.client_roles.includes(Roles.ADMIN)) {
const trimmed = AdministrativeAreaPublicResponseSchema.array().parse(adminAreas);
return res.status(200).send(trimmed);
try {
const kcUser = req.user as KeycloakUser;
const filter = AdministrativeAreaFilterSchema.safeParse(req.query);
if (filter.success) {
const adminAreas = await administrativeAreasServices.getAdministrativeAreas(filter.data);
if (!kcUser.client_roles || !kcUser.client_roles.includes(Roles.ADMIN)) {
const trimmed = AdministrativeAreaPublicResponseSchema.array().parse(adminAreas);
return res.status(200).send(trimmed);
}
return res.status(200).send(adminAreas);
} else {
return res.status(400).send('Could not parse filter.');
}
return res.status(200).send(adminAreas);
} else {
return res.status(400).send('Could not parse filter.');
} catch (e) {
next(e);
}
};

Expand Down
84 changes: 67 additions & 17 deletions express-api/src/controllers/lookup/lookupController.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { AppDataSource } from '@/appDataSource';
import { PropertyClassification } from '@/typeorm/Entities/PropertyClassification';
import { stubResponse } from '@/utilities/stubResponse';
import { Request, Response } from 'express';
import { NextFunction, Request, Response } from 'express';
import {
BuildingConstructionPublicResponseSchema,
ClassificationPublicResponseSchema,
PredominateUsePublicResponseSchema,
} from './lookupSchema';
import { BuildingPredominateUse } from '@/typeorm/Entities/BuildingPredominateUse';
import { BuildingConstructionType } from '@/typeorm/Entities/BuildingConstructionType';

// TODO: What controllers here could just be replaced by existing GET requests?

Expand Down Expand Up @@ -54,33 +56,81 @@ export const lookupRoles = async (req: Request, res: Response) => {
* @param {Response} res Outgoing response
* @returns {Response} A 200 status and a list of property classifications.
*/
export const lookupPropertyClassifications = async (req: Request, res: Response) => {
export const lookupPropertyClassifications = async (
req: Request,
res: Response,
next: NextFunction,
) => {
/**
* #swagger.tags = ['Lookup']
* #swagger.description = 'Get all property classification entries.'
* #swagger.security = [{
"bearerAuth": []
}]
*/
try {
const classifications = await AppDataSource.getRepository(PropertyClassification).find();
const filtered = classifications.filter((c) => !c.IsDisabled);
const parsed = ClassificationPublicResponseSchema.array().safeParse(filtered);
if (parsed.success) {
return res.status(200).send(parsed.data);
} else {
return res.status(400).send('Something went wrong.');
}
} catch (e) {
next(e);
}
};

const classifications = await AppDataSource.getRepository(PropertyClassification).find();
const filtered = classifications.filter((c) => !c.IsDisabled);
const parsed = ClassificationPublicResponseSchema.array().safeParse(filtered);
if (parsed.success) {
return res.status(200).send(parsed.data);
} else {
return res.status(400).send('Something went wrong.');
export const lookupBuildingPredominateUse = async (
req: Request,
res: Response,
next: NextFunction,
) => {
/**
* #swagger.tags = ['Lookup']
* #swagger.description = 'Get all predomanite uses entries.'
* #swagger.security = [{
"bearerAuth": []
}]
*/
try {
const uses = await AppDataSource.getRepository(BuildingPredominateUse).find();
const filtered = uses.filter((u) => !u.IsDisabled);
const parsed = PredominateUsePublicResponseSchema.array().safeParse(filtered);
if (parsed.success) {
return res.status(200).send(parsed.data);
} else {
return res.status(400).send('Something went wrong.');
}
} catch (e) {
next(e);
}
};

export const lookupBuildingPredominateUse = async (req: Request, res: Response) => {
const uses = await AppDataSource.getRepository(BuildingPredominateUse).find();
const filtered = uses.filter((u) => !u.IsDisabled);
const parsed = PredominateUsePublicResponseSchema.array().safeParse(filtered);
if (parsed.success) {
return res.status(200).send(parsed.data);
} else {
return res.status(400).send('Something went wrong.');
export const lookupBuildingConstructionType = async (
req: Request,
res: Response,
next: NextFunction,
) => {
/**
* #swagger.tags = ['Lookup']
* #swagger.description = 'Get all building construction type entries.'
* #swagger.security = [{
"bearerAuth": []
}]
*/
try {
const uses = await AppDataSource.getRepository(BuildingConstructionType).find();
const filtered = uses.filter((u) => !u.IsDisabled);
const parsed = BuildingConstructionPublicResponseSchema.array().safeParse(filtered);
if (parsed.success) {
return res.status(200).send(parsed.data);
} else {
return res.status(400).send('Something went wrong.');
}
} catch (e) {
next(e);
}
};

Expand Down
6 changes: 6 additions & 0 deletions express-api/src/controllers/lookup/lookupSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ export const PredominateUsePublicResponseSchema = z.object({
Id: z.number(),
SortOrder: z.number(),
});

export const BuildingConstructionPublicResponseSchema = z.object({
Name: z.string(),
Id: z.number(),
SortOrder: z.number(),
});
2 changes: 1 addition & 1 deletion express-api/src/routes/administrativeAreasRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getAdministrativeAreaById,
updateAdministrativeAreaById,
deleteAdministrativeAreaById,
} from '@/controllers/admin/administrativeAreas/administrativeAreasController';
} from '@/controllers/administrativeAreas/administrativeAreasController';
import { protectedRoute } from '@bcgov/citz-imb-kc-express';
import express from 'express';

Expand Down
4 changes: 3 additions & 1 deletion express-api/src/routes/lookupRouter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import controllers from '@/controllers';
import { lookupBuildingPredominateUse } from '@/controllers/lookup/lookupController';
import express from 'express';

const router = express.Router();
Expand All @@ -11,12 +10,15 @@ const {
lookupProjectTierLevels,
lookupPropertyClassifications,
lookupRoles,
lookupBuildingConstructionType,
lookupBuildingPredominateUse,
} = controllers;

router.route('/agencies').get(lookupAgencies);
router.route('/roles').get(lookupRoles);
router.route('/property/classifications').get(lookupPropertyClassifications);
router.route('/property/predominateUses').get(lookupBuildingPredominateUse);
router.route('/property/constructionTypes').get(lookupBuildingConstructionType);
router.route('/project/tier/levels').get(lookupProjectTierLevels);
router.route('/project/risks').get(lookupProjectRisks);
router.route('/all').get(lookupAll);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import supertest from 'supertest';
import { faker } from '@faker-js/faker';
import app from '@/express';
import { IAdministrativeArea } from '@/controllers/admin/administrativeAreas/IAdministrativeArea';
import { IAdministrativeArea } from '@/controllers/administrativeAreas/IAdministrativeArea';
import { UUID } from 'crypto';

const request = supertest(app);
Expand Down
82 changes: 81 additions & 1 deletion express-api/tests/testUtils/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
import { Agency } from '@/typeorm/Entities/Agency';
import { User, UserStatus } from '@/typeorm/Entities/User';
import { faker } from '@faker-js/faker';
import { UUID } from 'crypto';
import { UUID, randomUUID } from 'crypto';
import { Request, Response } from 'express';
import { Role as RolesEntity } from '@/typeorm/Entities/Role';
import { KeycloakUser } from '@bcgov/citz-imb-kc-express';
import { Parcel } from '@/typeorm/Entities/Parcel';
import { EmailBody, IChesStatusResponse, IEmail } from '@/services/ches/chesServices';
import { AdministrativeArea } from '@/typeorm/Entities/AdministrativeArea';
import { PropertyClassification } from '@/typeorm/Entities/PropertyClassification';
import { BuildingPredominateUse } from '@/typeorm/Entities/BuildingPredominateUse';

export class MockRes {
statusValue: any;
Expand Down Expand Up @@ -231,3 +234,80 @@ export const produceEmail = (props: Partial<IEmail>): IEmail => {
};
return email;
};

export const produceAdminArea = (props: Partial<AdministrativeArea>): AdministrativeArea => {
const adminArea: AdministrativeArea = {
Id: faker.number.int(),
Name: faker.location.city(),
IsDisabled: false,
SortOrder: 0,
RegionalDistrictId: 0,
RegionalDistrict: undefined,
ProvinceId: 'BC',
Province: undefined,
CreatedById: randomUUID(),
CreatedBy: undefined,
CreatedOn: new Date(),
UpdatedById: randomUUID(),
UpdatedOn: new Date(),
UpdatedBy: undefined,
...props,
};
return adminArea;
};

export const produceClassification = (
props: Partial<PropertyClassification>,
): PropertyClassification => {
const classification: PropertyClassification = {
Id: faker.number.int(),
Name: faker.lorem.word(),
IsDisabled: false,
SortOrder: 0,
IsVisible: false,
CreatedById: randomUUID(),
CreatedBy: undefined,
CreatedOn: new Date(),
UpdatedById: randomUUID(),
UpdatedBy: undefined,
UpdatedOn: new Date(),
...props,
};
return classification;
};

export const producePredominateUse = (
props: Partial<BuildingPredominateUse>,
): BuildingPredominateUse => {
const predominateUse: BuildingPredominateUse = {
Id: faker.number.int(),
Name: faker.lorem.word(),
IsDisabled: false,
SortOrder: 0,
CreatedById: randomUUID(),
CreatedBy: undefined,
CreatedOn: new Date(),
UpdatedById: randomUUID(),
UpdatedBy: undefined,
UpdatedOn: new Date(),
...props,
};
return predominateUse;
};

export const produceConstructionType = (props: Partial<BuildingPredominateUse>) => {
const constructionType: BuildingPredominateUse = {
Id: faker.number.int(),
Name: faker.lorem.word(),
IsDisabled: false,
SortOrder: 0,
CreatedById: randomUUID(),
CreatedBy: undefined,
CreatedOn: new Date(),
UpdatedById: randomUUID(),
UpdatedBy: undefined,
UpdatedOn: new Date(),
...props,
};
return constructionType;
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { Request, Response } from 'express';
import controllers from '@/controllers';
import { MockReq, MockRes, getRequestHandlerMocks } from '../../../../testUtils/factories';
import {
MockReq,
MockRes,
getRequestHandlerMocks,
produceAdminArea,
} from '../../../testUtils/factories';
import { Roles } from '@/constants/roles';
import { IAdministrativeArea } from '@/controllers/admin/administrativeAreas/IAdministrativeArea';
import { IAdministrativeArea } from '@/controllers/administrativeAreas/IAdministrativeArea';
import { faker } from '@faker-js/faker';
import { UUID } from 'crypto';

Expand Down Expand Up @@ -32,6 +37,12 @@ const mockAdministrativeArea: IAdministrativeArea = {
regionalDistrict: 'CPRD',
};

const _getAdminAreas = jest.fn().mockImplementation(() => [produceAdminArea({})]);
const _next = jest.fn();
jest.mock('@/services/administrativeAreas/administrativeAreasServices', () => ({
getAdministrativeAreas: () => _getAdminAreas(),
}));

describe('UNIT - Administrative Areas Admin', () => {
beforeEach(() => {
const { mockReq, mockRes } = getRequestHandlerMocks();
Expand All @@ -40,17 +51,29 @@ describe('UNIT - Administrative Areas Admin', () => {
mockResponse = mockRes;
});
describe('Controller getAdministrativeAreas', () => {
// TODO: remove stub test when controller is complete
it('should return the stub response of 501', async () => {
await getAdministrativeAreas(mockRequest, mockResponse);
expect(mockResponse.statusValue).toBe(501);
});

// TODO: enable other tests when controller is complete
xit('should return status 200 and a list of administrative areas', async () => {
await getAdministrativeAreas(mockRequest, mockResponse);
it('should return status 200 and a list of administrative areas', async () => {
await getAdministrativeAreas(mockRequest, mockResponse, _next);
expect(mockResponse.statusValue).toBe(200);
});
it('should return status 200 and a list of administrative areas, lacks metadata', async () => {
mockRequest.setUser({ client_roles: [] });
await getAdministrativeAreas(mockRequest, mockResponse, _next);
expect(mockResponse.statusValue).toBe(200);
expect(mockResponse.sendValue.CreatedOn).toBeUndefined();
});
it('should return status 400 when parse fails', async () => {
mockRequest.query = { name: ['a'] };
await getAdministrativeAreas(mockRequest, mockResponse, _next);
expect(mockResponse.statusValue).toBe(400);
});
it('should return status 400 when parse fails', async () => {
_getAdminAreas.mockImplementationOnce(() => {
throw Error();
});
await getAdministrativeAreas(mockRequest, mockResponse, _next);
expect(_next).toHaveBeenCalled();
});
});

describe('Controller addAdministrativeArea', () => {
Expand Down
Loading

0 comments on commit 34d35f9

Please sign in to comment.