Skip to content

Commit

Permalink
Merge branch 'main' into PIMS-1801-Replace-Joins-with-Lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarkowsky committed Jun 20, 2024
2 parents 6044af6 + 500b730 commit c7b6fce
Show file tree
Hide file tree
Showing 38 changed files with 923 additions and 290 deletions.
4 changes: 4 additions & 0 deletions express-api/src/controllers/users/usersSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ export const UserFilteringSchema = z.object({
firstName: z.string().optional(),
email: z.string().optional(),
agencyId: z.number().optional() || z.array(z.number().int().nonnegative()).optional(),
agency: z.string().optional(),
role: z.string().optional(),
position: z.string().optional(),
id: z.string().uuid().optional(),
guid: z.string().uuid().optional(),
status: z.string().optional(),
sortOrder: z.string().optional(),
sortKey: z.string().optional(),
});

export type UserFiltering = z.infer<typeof UserFilteringSchema> & { id?: UUID }; //Kinda hacky, but the type expected in typeorm is more strict than what zod infers here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { z } from 'zod';
export const AdministrativeAreaFilterSchema = z.object({
page: z.coerce.number().optional(),
quantity: z.coerce.number().optional(),
sort: z.string().optional(),
sortKey: z.string().optional(),
sortOrder: z.string().optional(),
sortRelation: z.string().optional(),
name: z.string().optional(),
provinceId: z.string().optional(),
regionalDistrictId: z.number().int().optional(),
regionalDistrict: z.string().optional(),
isDisabled: z.string().optional(),
});

export const AdministrativeAreaPublicResponseSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import { AppDataSource } from '@/appDataSource';
import { AdministrativeArea } from '@/typeorm/Entities/AdministrativeArea';
import { AdministrativeAreaFilter } from './administrativeAreaSchema';
import { DeepPartial, FindOptionsOrder } from 'typeorm';
import { DeepPartial, FindOptionsOrder, FindOptionsOrderValue } from 'typeorm';
import { ErrorWithCode } from '@/utilities/customErrors/ErrorWithCode';
import { constructFindOptionFromQuery } from '@/utilities/helperFunctions';

const sortKeyMapping = (
sortKey: string,
sortDirection: FindOptionsOrderValue,
): FindOptionsOrder<AdministrativeArea> => {
switch (sortKey) {
case 'RegionalDistrict':
return { RegionalDistrict: { Name: sortDirection } };
default:
return { [sortKey]: sortDirection };
}
};

const collectFindOptions = (filter: AdministrativeAreaFilter) => {
const options = [];
if (filter.name) options.push(constructFindOptionFromQuery('Name', filter.name));
if (filter.regionalDistrict)
options.push({
RegionalDistrict: constructFindOptionFromQuery('Name', filter.regionalDistrict),
});
if (filter.isDisabled)
options.push(constructFindOptionFromQuery('IsDisabled', filter.isDisabled));
return options;
};

const getAdministrativeAreas = (filter: AdministrativeAreaFilter) => {
return AppDataSource.getRepository(AdministrativeArea).find({
where: {
Name: filter.name,
ProvinceId: filter.provinceId,
RegionalDistrictId: filter.regionalDistrictId,
},
where: collectFindOptions(filter),
take: filter.quantity,
skip: (filter.quantity ?? 0) * (filter.page ?? 0),
order: filter.sort as FindOptionsOrder<AdministrativeArea>,
order: sortKeyMapping(filter.sortKey, filter.sortOrder as FindOptionsOrderValue),
});
};

Expand Down
12 changes: 9 additions & 3 deletions express-api/src/services/agencies/agencySchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ export const AgencyCreationSchema = z.object({
export const AgencyFilterSchema = z.object({
name: z.string().optional(),
parentId: z.coerce.number().int().optional(),
isDisabled: z.coerce.boolean().optional(),
sortOrder: z.coerce.number().optional(),
parent: z.string().optional(),
isDisabled: z.string().optional(),
sortOrder: z.string().optional(),
page: z.coerce.number().optional(),
quantity: z.coerce.number().optional(),
sort: z.string().optional(),
sortKey: z.string().optional(),
id: z.coerce.number().optional(),
status: z.string().optional(),
email: z.string().optional(),
updatedOn: z.string().optional(),
createdOn: z.string().optional(),
code: z.string().optional(),
});

export const AgencyPublicResponseSchema = z.object({
Expand Down
37 changes: 29 additions & 8 deletions express-api/src/services/agencies/agencyServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,49 @@ import { AppDataSource } from '@/appDataSource';
import { Agency } from '@/typeorm/Entities/Agency';
import { ErrorWithCode } from '@/utilities/customErrors/ErrorWithCode';
import { AgencyFilter } from './agencySchema';
import { FindOptionsOrder } from 'typeorm';
import { constructFindOptionFromQuery } from '@/utilities/helperFunctions';
import { FindOptionsOrderValue, FindOptionsOrder } from 'typeorm';

const agencyRepo = AppDataSource.getRepository(Agency);

const collectFindOptions = (filter: AgencyFilter) => {
const options = [];
if (filter.name) options.push(constructFindOptionFromQuery('Name', filter.name));
if (filter.parent) options.push({ Parent: constructFindOptionFromQuery('Name', filter.parent) });
if (filter.isDisabled)
options.push(constructFindOptionFromQuery('IsDisabled', filter.isDisabled));
if (filter.code) options.push(constructFindOptionFromQuery('Code', filter.code));
if (filter.email) options.push(constructFindOptionFromQuery('Email', filter.email));
if (filter.createdOn) options.push(constructFindOptionFromQuery('CreatedOn', filter.createdOn));
if (filter.updatedOn) options.push(constructFindOptionFromQuery('UpdatedOn', filter.updatedOn));
return options;
};

const sortKeyMapping = (
sortKey: string,
sortDirection: FindOptionsOrderValue,
): FindOptionsOrder<Agency> => {
switch (sortKey) {
case 'Parent':
return { Parent: { Name: sortDirection } };
default:
return { [sortKey]: sortDirection };
}
};

/**
* @description Gets and returns a list of all agencies.
* @returns { Agency[] } A list of all agencies in the database
*/
export const getAgencies = async (filter: AgencyFilter, includeRelations: boolean = false) => {
const allAgencies = await agencyRepo.find({
where: {
Name: filter.name,
IsDisabled: filter.isDisabled,
SortOrder: filter.sortOrder,
Id: filter.id,
},
where: collectFindOptions(filter),
relations: {
Parent: includeRelations,
},
take: filter.quantity,
skip: (filter.quantity ?? 0) * (filter.page ?? 0),
order: filter.sort as FindOptionsOrder<Agency>,
order: sortKeyMapping(filter.sortKey, filter.sortOrder as FindOptionsOrderValue),
});
return allAgencies;
};
Expand Down
6 changes: 5 additions & 1 deletion express-api/src/services/projects/projectSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ export const ProjectFilterSchema = z.object({
projectNumber: z.string().optional(),
name: z.string().optional(),
statusId: z.coerce.number().nonnegative().optional(),
status: z.string().optional(),
agencyId: z.union([z.number().optional(), z.array(z.number().int().nonnegative()).optional()]),
agency: z.string().optional(),
page: z.coerce.number().optional(),
updatedOn: z.string().optional(),
quantity: z.coerce.number().optional(),
sort: z.string().optional(),
sortKey: z.string().optional(),
sortOrder: z.string().optional(),
});

export type ProjectFilter = z.infer<typeof ProjectFilterSchema>;
42 changes: 33 additions & 9 deletions express-api/src/services/projects/projectsServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
DeepPartial,
FindManyOptions,
FindOptionsOrder,
FindOptionsOrderValue,
In,
InsertResult,
QueryRunner,
Expand All @@ -28,6 +29,7 @@ import { ProjectRisk } from '@/constants/projectRisk';
import notificationServices from '../notifications/notificationServices';
import { SSOUser } from '@bcgov/citz-imb-sso-express';
import userServices from '../users/usersServices';
import { constructFindOptionFromQuery } from '@/utilities/helperFunctions';
import { ProjectTimestamp } from '@/typeorm/Entities/ProjectTimestamp';
import { ProjectMonetary } from '@/typeorm/Entities/ProjectMonetary';
import { NotificationQueue } from '@/typeorm/Entities/NotificationQueue';
Expand Down Expand Up @@ -778,6 +780,34 @@ const deleteProjectById = async (id: number, username: string) => {
}
};

const sortKeyMapping = (
sortKey: string,
sortDirection: FindOptionsOrderValue,
): FindOptionsOrder<Project> => {
switch (sortKey) {
case 'Status':
return { Status: { Name: sortDirection } };
case 'Agency':
return { Agency: { Name: sortDirection } };
case 'UpdatedBy':
return { UpdatedBy: { LastName: sortDirection } };
default:
return { [sortKey]: sortDirection };
}
};

const collectFindOptions = (filter: ProjectFilter) => {
const options = [];
if (filter.name) options.push(constructFindOptionFromQuery('Name', filter.name));
if (filter.agency) options.push({ Agency: constructFindOptionFromQuery('Name', filter.agency) });
if (filter.status) options.push({ Status: constructFindOptionFromQuery('Name', filter.status) });
if (filter.projectNumber) {
options.push(constructFindOptionFromQuery('ProjectNumber', filter.projectNumber));
}
if (filter.updatedOn) options.push(constructFindOptionFromQuery('UpdatedOn', filter.updatedOn));
return options;
};

const getProjects = async (filter: ProjectFilter, includeRelations: boolean = false) => {
const queryOptions: FindManyOptions<Project> = {
relations: {
Expand All @@ -799,16 +829,10 @@ const getProjects = async (filter: ProjectFilter, includeRelations: boolean = fa
},
UpdatedBy: { Id: true, FirstName: true, LastName: true },
},
where: {
StatusId: filter.statusId,
AgencyId: filter.agencyId
? In(typeof filter.agencyId === 'number' ? [filter.agencyId] : filter.agencyId)
: undefined,
ProjectNumber: filter.projectNumber,
},
where: collectFindOptions(filter),
take: filter.quantity,
skip: (filter.page ?? 0) * (filter.quantity ?? 0),
order: filter.sort as FindOptionsOrder<Project>,
order: sortKeyMapping(filter.sortKey, filter.sortOrder as FindOptionsOrderValue),
};

const projects = await projectRepo.find(queryOptions);
Expand Down Expand Up @@ -873,7 +897,7 @@ const getProjectsForExport = async (filter: ProjectFilter, includeRelations: boo
},
take: filter.quantity,
skip: (filter.page ?? 0) * (filter.quantity ?? 0),
order: filter.sort as FindOptionsOrder<Project>,
order: sortKeyMapping(filter.sortKey, filter.sortOrder as FindOptionsOrderValue),
};
const projects = await projectRepo.find(queryOptions);

Expand Down
14 changes: 13 additions & 1 deletion express-api/src/services/users/usersServices.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { User, UserStatus } from '@/typeorm/Entities/User';
import { AppDataSource } from '@/appDataSource';
import { SSOBCeIDUser, SSOIdirUser, SSOUser } from '@bcgov/citz-imb-sso-express';
import { DeepPartial, In } from 'typeorm';
import { DeepPartial, FindOptionsOrderValue, In } from 'typeorm';
import { Agency } from '@/typeorm/Entities/Agency';
import { randomUUID, UUID } from 'crypto';
import KeycloakService from '@/services/keycloak/keycloakService';
Expand Down Expand Up @@ -210,6 +210,17 @@ const getAdministrators = async (agencyIds: string[]) => {
return admins;
};

const sortKeyMapping = (sortKey: string, sortDirection: FindOptionsOrderValue) => {
switch (sortKey) {
case 'Agency':
return { Agency: { Name: sortDirection } };
case 'Role':
return { Role: { Name: sortDirection } };
default:
return { [sortKey]: sortDirection };
}
};

const getUsers = async (filter: UserFiltering) => {
const users = await AppDataSource.getRepository(User).find({
relations: {
Expand All @@ -233,6 +244,7 @@ const getUsers = async (filter: UserFiltering) => {
},
take: filter.quantity,
skip: (filter.page ?? 0) * (filter.quantity ?? 0),
order: sortKeyMapping(filter.sortKey, filter.sortOrder as FindOptionsOrderValue),
});
return users;
};
Expand Down
Loading

0 comments on commit c7b6fce

Please sign in to comment.