From 4c5e862421a1d5f8d3544dff7c37a5e6ac260ef8 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Mon, 26 Aug 2024 13:42:12 -0700 Subject: [PATCH 1/6] get parcels and buildings conncurrently --- .../services/properties/propertiesServices.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/express-api/src/services/properties/propertiesServices.ts b/express-api/src/services/properties/propertiesServices.ts index cb53d70e1..4692257f3 100644 --- a/express-api/src/services/properties/propertiesServices.ts +++ b/express-api/src/services/properties/propertiesServices.ts @@ -802,8 +802,6 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { // Use IDs from filtered properties to get those properites with joins const parcelQueryOptions: FindManyOptions = { relations: { - CreatedBy: true, - UpdatedBy: true, Evaluations: true, Fiscals: true, }, @@ -813,20 +811,22 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { }; const buildingQueryOptions: FindManyOptions = { relations: { - CreatedBy: true, - UpdatedBy: true, Evaluations: true, Fiscals: true, }, where: { Id: In(buildingIds) }, }; + let properties: (Parcel | Building)[] = []; - properties = properties.concat( - await AppDataSource.getRepository(Parcel).find(parcelQueryOptions), - ); - properties = properties.concat( - await AppDataSource.getRepository(Building).find(buildingQueryOptions), - ); + const ongoingFinds = []; + ongoingFinds.push(AppDataSource.getRepository(Parcel).find(parcelQueryOptions)); + ongoingFinds.push(AppDataSource.getRepository(Building).find(buildingQueryOptions)); + + const resolvedFinds = await Promise.all(ongoingFinds); + resolvedFinds.forEach((propertyList) => { + properties = properties.concat(...propertyList); + }); + return properties; }; From fc3d742b925e2247a248bb2a9e50e8d89351cb8c Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Mon, 26 Aug 2024 14:07:57 -0700 Subject: [PATCH 2/6] error message when export fails --- react-app/src/components/table/DataTable.tsx | 34 +++++++++++++------- react-app/src/hooks/api/usePropertiesApi.ts | 4 +-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/react-app/src/components/table/DataTable.tsx b/react-app/src/components/table/DataTable.tsx index e7127a201..9a7315810 100644 --- a/react-app/src/components/table/DataTable.tsx +++ b/react-app/src/components/table/DataTable.tsx @@ -48,6 +48,7 @@ import { CommonFiltering } from '@/interfaces/ICommonFiltering'; import { useSearchParams } from 'react-router-dom'; import { Roles } from '@/constants/roles'; import { AuthContext } from '@/contexts/authContext'; +import { SnackBarContext } from '@/contexts/snackbarContext'; type RenderCellParams = GridRenderCellParams; @@ -207,6 +208,8 @@ export const FilterSearchDataGrid = (props: FilterSearchDataGridProps) => { const [dataSourceLoading, setDataSourceLoading] = useState(false); const tableApiRef = useGridApiRef(); // Ref to MUI DataGrid const previousController = useRef(); + const snackbar = useContext(SnackBarContext); + interface ITableModelCollection { pagination?: GridPaginationModel; sort?: GridSortModel; @@ -535,23 +538,30 @@ export const FilterSearchDataGrid = (props: FilterSearchDataGridProps) => { rows = props.excelDataSource ? await props.excelDataSource(sortFilterObj, signal) : await props.dataSource(sortFilterObj, signal); - if (props.customExcelMap) rows = props.customExcelMap(rows); } else { // Client-side tables rows = gridFilteredSortedRowEntriesSelector(tableApiRef).map((row) => row.model); + } + if (rows) { if (props.customExcelMap) rows = props.customExcelMap(rows); + // Convert back to MUI table model + rows = rows.map((r, i) => ({ + model: r, + id: i, + })); + downloadExcelFile({ + data: rows, + tableName: props.excelTitle, + filterName: selectValue, + includeDate: true, + }); + } else { + snackbar.setMessageState({ + style: snackbar.styles.warning, + text: 'Table failed to export.', + open: true, + }); } - // Convert back to MUI table model - rows = rows.map((r, i) => ({ - model: r, - id: i, - })); - downloadExcelFile({ - data: rows, - tableName: props.excelTitle, - filterName: selectValue, - includeDate: true, - }); setIsExporting(false); }} > diff --git a/react-app/src/hooks/api/usePropertiesApi.ts b/react-app/src/hooks/api/usePropertiesApi.ts index 106d016cc..ee4a8b9ca 100644 --- a/react-app/src/hooks/api/usePropertiesApi.ts +++ b/react-app/src/hooks/api/usePropertiesApi.ts @@ -127,8 +127,8 @@ const usePropertiesApi = (absoluteFetch: IFetch) => { }, { signal }, ); - if ((parsedBody as Record).error) { - return []; + if ((parsedBody as Record)?.error) { + return; } return parsedBody as (Parcel | Building)[]; }; From 7e048a14a2bca1955a9b10861a10fd69ab6c1ad1 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Tue, 27 Aug 2024 08:58:23 -0700 Subject: [PATCH 3/6] both solutions present --- .../services/properties/propertiesServices.ts | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/express-api/src/services/properties/propertiesServices.ts b/express-api/src/services/properties/propertiesServices.ts index 4692257f3..abdfcd7c1 100644 --- a/express-api/src/services/properties/propertiesServices.ts +++ b/express-api/src/services/properties/propertiesServices.ts @@ -817,6 +817,7 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { where: { Id: In(buildingIds) }, }; + const startTime = new Date() let properties: (Parcel | Building)[] = []; const ongoingFinds = []; ongoingFinds.push(AppDataSource.getRepository(Parcel).find(parcelQueryOptions)); @@ -826,8 +827,51 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { resolvedFinds.forEach((propertyList) => { properties = properties.concat(...propertyList); }); - + const endTime = new Date() + console.log('time', Math.abs(endTime.getTime() - startTime.getTime())) return properties; + + // Getting evals, fiscals, and filtering separately: 850-1050ms + // Getting as as part of joins, where clause: 1587-1672ms + + // const ongoingFinds = []; + // ongoingFinds.push(AppDataSource.getRepository(Parcel).find(parcelQueryOptions)); + // ongoingFinds.push(AppDataSource.getRepository(Building).find(buildingQueryOptions)); + // ongoingFinds.push(AppDataSource.getRepository(ParcelEvaluation).find({order: {Year: 'DESC'}})); + // ongoingFinds.push(AppDataSource.getRepository(ParcelFiscal).find({order: {FiscalYear: 'DESC'}})); + // ongoingFinds.push(AppDataSource.getRepository(BuildingEvaluation).find({order: {Year: 'DESC'}})); + // ongoingFinds.push(AppDataSource.getRepository(BuildingFiscal).find({order: {FiscalYear: 'DESC'}})); + + + // const resolvedFinds = await Promise.all(ongoingFinds); + // const parcelEvaluations = resolvedFinds.at(2) as ParcelEvaluation[]; + // const parcelFiscals = resolvedFinds.at(3) as ParcelFiscal[]; + // const buildingEvaluations = resolvedFinds.at(4) as BuildingEvaluation[]; + // const buildingFiscals = resolvedFinds.at(5) as BuildingFiscal[]; + // const parcels: Parcel[] = (resolvedFinds.at(0) as Parcel[]).filter((p: Parcel) => parcelIds.includes(p.Id)) + // .map((p: Parcel) => { + // const evaluation = parcelEvaluations.find(pe => pe.ParcelId === p.Id); + // const fiscal = parcelFiscals.find(pf => pf.ParcelId === p.Id); + // return ({ + // ...p, + // Evaluations: evaluation ? [evaluation] : undefined, + // Fiscals: fiscal ? [fiscal] : undefined, + // })}); + // const buildings: Building[] = (resolvedFinds.at(1) as Building[]).filter((b: Building) => buildingIds.includes(b.Id)) + // .map((b: Building) => { + // const evaluation = buildingEvaluations.find(be => be.BuildingId === b.Id); + // const fiscal = buildingFiscals.find(bf => bf.BuildingId === b.Id); + // return ({ + // ...b, + // Evaluations: evaluation ? [evaluation] : undefined, + // Fiscals: fiscal ? [fiscal] : undefined, + // })});; + + // const endTime = new Date() + // console.log('time', Math.abs(endTime.getTime() - startTime.getTime())) + + + // return [...parcels, ...buildings]; }; /** From 9684959a1313c0287331db012a58ee08c28ff855 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Tue, 27 Aug 2024 09:31:47 -0700 Subject: [PATCH 4/6] only keep separated implementation --- .../services/properties/propertiesServices.ts | 121 ++++++++---------- express-api/tests/testUtils/factories.ts | 22 ++-- .../buildings/buildingService.test.ts | 12 +- .../services/parcels/parcelsService.test.ts | 14 +- .../properties/propertyServices.test.ts | 39 ++++-- 5 files changed, 104 insertions(+), 104 deletions(-) diff --git a/express-api/src/services/properties/propertiesServices.ts b/express-api/src/services/properties/propertiesServices.ts index abdfcd7c1..649df0089 100644 --- a/express-api/src/services/properties/propertiesServices.ts +++ b/express-api/src/services/properties/propertiesServices.ts @@ -30,7 +30,7 @@ import { constructFindOptionFromQuerySingleSelect, } from '@/utilities/helperFunctions'; import userServices from '../users/usersServices'; -import { Brackets, FindManyOptions, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm'; +import { Brackets, FindOptionsWhere, ILike, In, QueryRunner } from 'typeorm'; import { SSOUser } from '@bcgov/citz-imb-sso-express'; import { PropertyType } from '@/constants/propertyType'; import { ProjectStatus } from '@/constants/projectStatus'; @@ -799,79 +799,62 @@ const getPropertiesForExport = async (filter: PropertyUnionFilter) => { const buildingIds = filteredProperties .filter((p) => p.PropertyTypeId === PropertyType.BUILDING) .map((b) => b.Id); - // Use IDs from filtered properties to get those properites with joins - const parcelQueryOptions: FindManyOptions = { - relations: { - Evaluations: true, - Fiscals: true, - }, - where: { - Id: In(parcelIds), - }, - }; - const buildingQueryOptions: FindManyOptions = { - relations: { - Evaluations: true, - Fiscals: true, - }, - where: { Id: In(buildingIds) }, - }; - const startTime = new Date() - let properties: (Parcel | Building)[] = []; + /** + * For some reason, getting the data in multiple calls and filtering here is faster than letting TypeORM do it. + * + * Getting evals, fiscals, and filtering separately: 850-1050ms + * Getting as as part of joins, WHERE clause with TypeORM: 1587-1672ms + */ + const ongoingFinds = []; - ongoingFinds.push(AppDataSource.getRepository(Parcel).find(parcelQueryOptions)); - ongoingFinds.push(AppDataSource.getRepository(Building).find(buildingQueryOptions)); + ongoingFinds.push(AppDataSource.getRepository(Parcel).find()); + ongoingFinds.push(AppDataSource.getRepository(Building).find()); + // Order these to guarantee the find operation later gets the most recent one. + ongoingFinds.push( + AppDataSource.getRepository(ParcelEvaluation).find({ order: { Year: 'DESC' } }), + ); + ongoingFinds.push( + AppDataSource.getRepository(ParcelFiscal).find({ order: { FiscalYear: 'DESC' } }), + ); + ongoingFinds.push( + AppDataSource.getRepository(BuildingEvaluation).find({ order: { Year: 'DESC' } }), + ); + ongoingFinds.push( + AppDataSource.getRepository(BuildingFiscal).find({ order: { FiscalYear: 'DESC' } }), + ); + // Wait for all database requests to resolve, then build the parcels and buildings lists + // Use IDs from filtered properties above to filter lists const resolvedFinds = await Promise.all(ongoingFinds); - resolvedFinds.forEach((propertyList) => { - properties = properties.concat(...propertyList); - }); - const endTime = new Date() - console.log('time', Math.abs(endTime.getTime() - startTime.getTime())) - return properties; + const parcelEvaluations = resolvedFinds.at(2) as ParcelEvaluation[]; + const parcelFiscals = resolvedFinds.at(3) as ParcelFiscal[]; + const buildingEvaluations = resolvedFinds.at(4) as BuildingEvaluation[]; + const buildingFiscals = resolvedFinds.at(5) as BuildingFiscal[]; + const parcels: Parcel[] = (resolvedFinds.at(0) as Parcel[]) + .filter((p: Parcel) => parcelIds.includes(p.Id)) + .map((p: Parcel) => { + const evaluation = parcelEvaluations.find((pe) => pe.ParcelId === p.Id); + const fiscal = parcelFiscals.find((pf) => pf.ParcelId === p.Id); + return { + ...p, + Evaluations: evaluation ? [evaluation] : undefined, + Fiscals: fiscal ? [fiscal] : undefined, + }; + }); + const buildings: Building[] = (resolvedFinds.at(1) as Building[]) + .filter((b: Building) => buildingIds.includes(b.Id)) + .map((b: Building) => { + const evaluation = buildingEvaluations.find((be) => be.BuildingId === b.Id); + const fiscal = buildingFiscals.find((bf) => bf.BuildingId === b.Id); + return { + ...b, + Evaluations: evaluation ? [evaluation] : undefined, + Fiscals: fiscal ? [fiscal] : undefined, + }; + }); - // Getting evals, fiscals, and filtering separately: 850-1050ms - // Getting as as part of joins, where clause: 1587-1672ms - - // const ongoingFinds = []; - // ongoingFinds.push(AppDataSource.getRepository(Parcel).find(parcelQueryOptions)); - // ongoingFinds.push(AppDataSource.getRepository(Building).find(buildingQueryOptions)); - // ongoingFinds.push(AppDataSource.getRepository(ParcelEvaluation).find({order: {Year: 'DESC'}})); - // ongoingFinds.push(AppDataSource.getRepository(ParcelFiscal).find({order: {FiscalYear: 'DESC'}})); - // ongoingFinds.push(AppDataSource.getRepository(BuildingEvaluation).find({order: {Year: 'DESC'}})); - // ongoingFinds.push(AppDataSource.getRepository(BuildingFiscal).find({order: {FiscalYear: 'DESC'}})); - - - // const resolvedFinds = await Promise.all(ongoingFinds); - // const parcelEvaluations = resolvedFinds.at(2) as ParcelEvaluation[]; - // const parcelFiscals = resolvedFinds.at(3) as ParcelFiscal[]; - // const buildingEvaluations = resolvedFinds.at(4) as BuildingEvaluation[]; - // const buildingFiscals = resolvedFinds.at(5) as BuildingFiscal[]; - // const parcels: Parcel[] = (resolvedFinds.at(0) as Parcel[]).filter((p: Parcel) => parcelIds.includes(p.Id)) - // .map((p: Parcel) => { - // const evaluation = parcelEvaluations.find(pe => pe.ParcelId === p.Id); - // const fiscal = parcelFiscals.find(pf => pf.ParcelId === p.Id); - // return ({ - // ...p, - // Evaluations: evaluation ? [evaluation] : undefined, - // Fiscals: fiscal ? [fiscal] : undefined, - // })}); - // const buildings: Building[] = (resolvedFinds.at(1) as Building[]).filter((b: Building) => buildingIds.includes(b.Id)) - // .map((b: Building) => { - // const evaluation = buildingEvaluations.find(be => be.BuildingId === b.Id); - // const fiscal = buildingFiscals.find(bf => bf.BuildingId === b.Id); - // return ({ - // ...b, - // Evaluations: evaluation ? [evaluation] : undefined, - // Fiscals: fiscal ? [fiscal] : undefined, - // })});; - - // const endTime = new Date() - // console.log('time', Math.abs(endTime.getTime() - startTime.getTime())) - - - // return [...parcels, ...buildings]; + return [...parcels, ...buildings]; }; /** diff --git a/express-api/tests/testUtils/factories.ts b/express-api/tests/testUtils/factories.ts index 1c050ab58..13e6483d4 100644 --- a/express-api/tests/testUtils/factories.ts +++ b/express-api/tests/testUtils/factories.ts @@ -217,7 +217,7 @@ export const produceSSO = (props?: Partial): SSOUser => { }; }; -export const produceParcel = (): Parcel => { +export const produceParcel = (props?: Partial): Parcel => { const id = faker.number.int({ max: 10 }); return { Id: faker.number.int({ max: 10 }), @@ -252,11 +252,12 @@ export const produceParcel = (): Parcel => { CreatedBy: undefined, UpdatedById: undefined, UpdatedBy: undefined, - Fiscals: produceParcelFiscal(id), - Evaluations: produceParcelEvaluation(id), + Fiscals: produceParcelFiscals(id), + Evaluations: produceParcelEvaluations(id), DeletedById: null, DeletedOn: null, DeletedBy: undefined, + ...props, }; }; @@ -284,7 +285,7 @@ export const produceEmail = (props: Partial): IEmail => { return email; }; -export const produceBuilding = (): Building => { +export const produceBuilding = (props?: Partial): Building => { const agencyId = faker.number.int(); const id = faker.number.int({ max: 10 }); return { @@ -328,17 +329,18 @@ export const produceBuilding = (): Building => { CreatedBy: undefined, UpdatedById: undefined, UpdatedBy: undefined, - Fiscals: produceBuildingFiscal(id), - Evaluations: produceBuildingEvaluation(id), + Fiscals: produceBuildingFiscals(id), + Evaluations: produceBuildingEvaluations(id), PID: undefined, PIN: undefined, DeletedById: null, DeletedOn: null, DeletedBy: undefined, + ...props, }; }; -export const produceBuildingEvaluation = ( +export const produceBuildingEvaluations = ( buildingId: number, props?: Partial, ): BuildingEvaluation[] => { @@ -355,7 +357,7 @@ export const produceBuildingEvaluation = ( return [evaluation]; }; -export const produceBuildingFiscal = ( +export const produceBuildingFiscals = ( buildingId: number, props?: Partial, ): BuildingFiscal[] => { @@ -370,7 +372,7 @@ export const produceBuildingFiscal = ( return [fiscal]; }; -export const produceParcelEvaluation = ( +export const produceParcelEvaluations = ( parcelId: number, props?: Partial, ): ParcelEvaluation[] => { @@ -388,7 +390,7 @@ export const produceParcelEvaluation = ( return [evaluation]; }; -export const produceParcelFiscal = ( +export const produceParcelFiscals = ( parcelId: number, props?: Partial, ): ParcelFiscal[] => { diff --git a/express-api/tests/unit/services/buildings/buildingService.test.ts b/express-api/tests/unit/services/buildings/buildingService.test.ts index 88b5ebe7b..8ae538e13 100644 --- a/express-api/tests/unit/services/buildings/buildingService.test.ts +++ b/express-api/tests/unit/services/buildings/buildingService.test.ts @@ -2,8 +2,8 @@ import { AppDataSource } from '@/appDataSource'; import { Building } from '@/typeorm/Entities/Building'; import { produceBuilding, - produceBuildingEvaluation, - produceBuildingFiscal, + produceBuildingEvaluations, + produceBuildingFiscals, produceSSO, produceUser, } from 'tests/testUtils/factories'; @@ -26,11 +26,11 @@ const _buildingSave = jest const _buildingFiscalFindOne = jest .spyOn(AppDataSource.getRepository(BuildingFiscal), 'findOne') - .mockImplementation(async () => produceBuildingFiscal(1)[0]); + .mockImplementation(async () => produceBuildingFiscals(1)[0]); const _buildingEvaluationFindOne = jest .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'findOne') - .mockImplementation(async () => produceBuildingEvaluation(1)[0]); + .mockImplementation(async () => produceBuildingEvaluations(1)[0]); const _buildingFindOne = jest .spyOn(buildingRepo, 'findOne') @@ -38,11 +38,11 @@ const _buildingFindOne = jest jest .spyOn(AppDataSource.getRepository(BuildingFiscal), 'find') - .mockImplementation(async () => produceBuildingFiscal(1)); + .mockImplementation(async () => produceBuildingFiscals(1)); jest .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'find') - .mockImplementation(async () => produceBuildingEvaluation(1)); + .mockImplementation(async () => produceBuildingEvaluations(1)); const _mockStartTransaction = jest.fn(async () => {}); const _mockRollbackTransaction = jest.fn(async () => {}); diff --git a/express-api/tests/unit/services/parcels/parcelsService.test.ts b/express-api/tests/unit/services/parcels/parcelsService.test.ts index fb3059c05..931da63a5 100644 --- a/express-api/tests/unit/services/parcels/parcelsService.test.ts +++ b/express-api/tests/unit/services/parcels/parcelsService.test.ts @@ -2,8 +2,8 @@ import { AppDataSource } from '@/appDataSource'; import { Parcel } from '@/typeorm/Entities/Parcel'; import { produceParcel, - produceParcelEvaluation, - produceParcelFiscal, + produceParcelEvaluations, + produceParcelFiscals, produceSSO, produceUser, } from 'tests/testUtils/factories'; @@ -28,7 +28,7 @@ const _parcelSave = jest const _parcelFindOne = jest.spyOn(parcelRepo, 'findOne').mockImplementation(async () => { const parcel = produceParcel(); const { Id } = parcel; - produceParcelFiscal(Id); + produceParcelFiscals(Id); return parcel; }); @@ -42,10 +42,10 @@ const _parcelFindOne = jest.spyOn(parcelRepo, 'findOne').mockImplementation(asyn const _parcelEvaluationFindOne = jest .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'findOne') - .mockImplementation(async () => produceParcelEvaluation(1)[0]); + .mockImplementation(async () => produceParcelEvaluations(1)[0]); const _parcelFiscalFindOne = jest .spyOn(AppDataSource.getRepository(ParcelFiscal), 'findOne') - .mockImplementation(async () => produceParcelFiscal(1)[0]); + .mockImplementation(async () => produceParcelFiscals(1)[0]); // const _parcelFindOne = jest // .spyOn(parcelRepo, 'findOne') @@ -60,10 +60,10 @@ jest.spyOn(userServices, 'getAgencies').mockImplementation(async () => []); jest .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'find') - .mockImplementation(async () => produceParcelEvaluation(1)); + .mockImplementation(async () => produceParcelEvaluations(1)); jest .spyOn(AppDataSource.getRepository(ParcelFiscal), 'find') - .mockImplementation(async () => produceParcelFiscal(1)); + .mockImplementation(async () => produceParcelFiscals(1)); const _mockStartTransaction = jest.fn(async () => {}); const _mockRollbackTransaction = jest.fn(async () => {}); diff --git a/express-api/tests/unit/services/properties/propertyServices.test.ts b/express-api/tests/unit/services/properties/propertyServices.test.ts index f65d9dcd9..0a2aa64d5 100644 --- a/express-api/tests/unit/services/properties/propertyServices.test.ts +++ b/express-api/tests/unit/services/properties/propertyServices.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { AppDataSource } from '@/appDataSource'; +import { PropertyType } from '@/constants/propertyType'; import { Roles } from '@/constants/roles'; import propertyServices, { getAgencyOrThrowIfMismatched, @@ -36,10 +37,10 @@ import { produceAdminArea, produceUser, produceImportResult, - produceParcelEvaluation, - produceParcelFiscal, - produceBuildingEvaluation, - produceBuildingFiscal, + produceParcelEvaluations, + produceParcelFiscals, + produceBuildingEvaluations, + produceBuildingFiscals, produceSSO, produceProjectStatus, produceProjectProperty, @@ -80,8 +81,8 @@ const _propertyUnionCreateQueryBuilder: any = { take: () => _propertyUnionCreateQueryBuilder, skip: () => _propertyUnionCreateQueryBuilder, orderBy: () => _propertyUnionCreateQueryBuilder, - getMany: () => [producePropertyUnion()], - getManyAndCount: () => [[producePropertyUnion()], 1], + getMany: () => [producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING })], + getManyAndCount: () => [[producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING })], 1], }; const _projectStatusCreateQueryBuilder: any = { @@ -148,12 +149,24 @@ jest .spyOn(AppDataSource.getRepository(PropertyUnion), 'createQueryBuilder') .mockImplementation(() => _propertyUnionCreateQueryBuilder); -jest +const parcelRepoSpy = jest .spyOn(AppDataSource.getRepository(Parcel), 'find') .mockImplementation(async () => [produceParcel()]); -jest + const buildingRepoSpy = jest .spyOn(AppDataSource.getRepository(Building), 'find') .mockImplementation(async () => [produceBuilding()]); +jest + .spyOn(AppDataSource.getRepository(ParcelEvaluation), 'find') + .mockImplementation(async () => produceParcelEvaluations(1)); +jest + .spyOn(AppDataSource.getRepository(ParcelFiscal), 'find') + .mockImplementation(async () => produceParcelFiscals(1)); +jest + .spyOn(AppDataSource.getRepository(BuildingEvaluation), 'find') + .mockImplementation(async () => produceBuildingEvaluations(1)); +jest + .spyOn(AppDataSource.getRepository(BuildingFiscal), 'find') + .mockImplementation(async () => produceBuildingFiscals(1)); jest .spyOn(AppDataSource.getRepository(Parcel), 'save') .mockImplementation(async () => produceParcel()); @@ -198,13 +211,13 @@ const _mockEntityManager = { } else if (entityClass === Building) { return produceBuilding(); } else if (entityClass === ParcelEvaluation) { - return produceParcelEvaluation(1, { Year: 2023 }); + return produceParcelEvaluations(1, { Year: 2023 }); } else if (entityClass === ParcelFiscal) { - return produceParcelFiscal(1, { FiscalYear: 2023 }); + return produceParcelFiscals(1, { FiscalYear: 2023 }); } else if (entityClass === BuildingEvaluation) { - return produceBuildingEvaluation(1, { Year: 2023 }); + return produceBuildingEvaluations(1, { Year: 2023 }); } else if (entityClass === BuildingFiscal) { - return produceBuildingFiscal(1, { FiscalYear: 2023 }); + return produceBuildingFiscals(1, { FiscalYear: 2023 }); } else { return []; } @@ -295,6 +308,8 @@ describe('UNIT - Property Services', () => { describe('getPropertiesforExport', () => { it('should get a list of properties based on the filter', async () => { + parcelRepoSpy.mockImplementationOnce(async () => [produceParcel({Id: 1})]) + buildingRepoSpy.mockImplementationOnce(async () => [produceBuilding({Id: 1})]) const result = await propertyServices.getPropertiesForExport({ pid: 'contains,123', pin: 'contains,456', From d7d6306dd175f06660d5a7348317e129eb422525 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Tue, 27 Aug 2024 09:32:10 -0700 Subject: [PATCH 5/6] linting fix --- .../properties/propertyServices.test.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/express-api/tests/unit/services/properties/propertyServices.test.ts b/express-api/tests/unit/services/properties/propertyServices.test.ts index 0a2aa64d5..f83b972e2 100644 --- a/express-api/tests/unit/services/properties/propertyServices.test.ts +++ b/express-api/tests/unit/services/properties/propertyServices.test.ts @@ -81,8 +81,17 @@ const _propertyUnionCreateQueryBuilder: any = { take: () => _propertyUnionCreateQueryBuilder, skip: () => _propertyUnionCreateQueryBuilder, orderBy: () => _propertyUnionCreateQueryBuilder, - getMany: () => [producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING })], - getManyAndCount: () => [[producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING })], 1], + getMany: () => [ + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING }), + ], + getManyAndCount: () => [ + [ + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.LAND }), + producePropertyUnion({ Id: 1, PropertyTypeId: PropertyType.BUILDING }), + ], + 1, + ], }; const _projectStatusCreateQueryBuilder: any = { @@ -152,7 +161,7 @@ jest const parcelRepoSpy = jest .spyOn(AppDataSource.getRepository(Parcel), 'find') .mockImplementation(async () => [produceParcel()]); - const buildingRepoSpy = jest +const buildingRepoSpy = jest .spyOn(AppDataSource.getRepository(Building), 'find') .mockImplementation(async () => [produceBuilding()]); jest @@ -308,8 +317,8 @@ describe('UNIT - Property Services', () => { describe('getPropertiesforExport', () => { it('should get a list of properties based on the filter', async () => { - parcelRepoSpy.mockImplementationOnce(async () => [produceParcel({Id: 1})]) - buildingRepoSpy.mockImplementationOnce(async () => [produceBuilding({Id: 1})]) + parcelRepoSpy.mockImplementationOnce(async () => [produceParcel({ Id: 1 })]); + buildingRepoSpy.mockImplementationOnce(async () => [produceBuilding({ Id: 1 })]); const result = await propertyServices.getPropertiesForExport({ pid: 'contains,123', pin: 'contains,456', From ac9cc3ea8e8c5c25423b2fea58087cde57a27477 Mon Sep 17 00:00:00 2001 From: dbarkowsky Date: Tue, 27 Aug 2024 09:54:09 -0700 Subject: [PATCH 6/6] format pid and add IsSensitive column to export --- react-app/src/components/property/PropertyTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/react-app/src/components/property/PropertyTable.tsx b/react-app/src/components/property/PropertyTable.tsx index 90e867927..5c5c269ff 100644 --- a/react-app/src/components/property/PropertyTable.tsx +++ b/react-app/src/components/property/PropertyTable.tsx @@ -251,8 +251,9 @@ const PropertyTable = (props: IPropertyTable) => { property.AdministrativeAreaId, )?.Name, Postal: property.Postal, - PID: property.PID, + PID: pidFormatter(property.PID), PIN: property.PIN, + 'Is Sensitive': property.IsSensitive, 'Assessed Value': property.Evaluations?.length ? property.Evaluations.sort( (