From 26990b920c66acff103810913d7bac9278bab3a8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 19 Dec 2023 17:05:35 +0100 Subject: [PATCH] fix(FeatureAmountsExporter): Workaround for exporting Feature Amounts when a feature has been previously deleted --- ...mounts-per-planning-unit.piece-exporter.ts | 19 +++++---- ...r-planning-unit.piece-exporter.e2e-spec.ts | 40 +++++++++++++++---- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/api/apps/geoprocessing/src/export/pieces-exporters/project-feature-amounts-per-planning-unit.piece-exporter.ts b/api/apps/geoprocessing/src/export/pieces-exporters/project-feature-amounts-per-planning-unit.piece-exporter.ts index 114044fd5c..7ffbe13639 100644 --- a/api/apps/geoprocessing/src/export/pieces-exporters/project-feature-amounts-per-planning-unit.piece-exporter.ts +++ b/api/apps/geoprocessing/src/export/pieces-exporters/project-feature-amounts-per-planning-unit.piece-exporter.ts @@ -107,16 +107,20 @@ export class ProjectFeatureAmountsPerPlanningUnitPieceExporter featureAmountPerPlanningUnit: FeatureAmountPerProjectPlanningUnit[], projectId: string, ) { - const featureIds = featureAmountPerPlanningUnit.map( - ({ featureId }) => featureId, + const featureIds = new Set( + featureAmountPerPlanningUnit.map(({ featureId }) => featureId), ); - - const featuresById = await this.getFeaturesById(featureIds); + const featuresById = await this.getFeaturesById([...featureIds]); const projectPusById = await this.getProjectPusById(projectId); - return featureAmountPerPlanningUnit.map( - ({ featureId, amount, projectPuId }) => { + // @TODO: Because the feature_amounts_per_planning_unit table is not accounted for in the clean up task, it can happen + // that when exporting after having deleted a feature, in the mapping below the feature info would not be found and + // cause an error. Because it's not clear whether deleting the feature_amount_per_pu records of the dangling feature + // in the clean up task has any implications, to avoid this error, the missing features are discarded. + return featureAmountPerPlanningUnit + .filter(({ featureId }) => featuresById[featureId]) + .map(({ featureId, amount, projectPuId }) => { const feature = featuresById[featureId]; return { amount, @@ -124,8 +128,7 @@ export class ProjectFeatureAmountsPerPlanningUnitPieceExporter featureName: feature.featureName, isCustom: feature.isCustom, }; - }, - ); + }); } private async getFeaturesById(featureIds: string[]) { diff --git a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-feature-amounts-per-planning-unit.piece-exporter.e2e-spec.ts b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-feature-amounts-per-planning-unit.piece-exporter.e2e-spec.ts index 6307b0f21f..5360de749a 100644 --- a/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-feature-amounts-per-planning-unit.piece-exporter.e2e-spec.ts +++ b/api/apps/geoprocessing/test/integration/cloning/piece-exporters/project-feature-amounts-per-planning-unit.piece-exporter.e2e-spec.ts @@ -45,11 +45,11 @@ let fixtures: FixtureType; describe(ProjectFeatureAmountsPerPlanningUnitPieceExporter, () => { beforeEach(async () => { fixtures = await getFixtures(); - }, 10_000); + }, 100000); afterEach(async () => { await fixtures?.cleanUp(); - }); + }, 100000); it("saves an empty file when project doesn't have neither derived features and puvspr calculations", async () => { const input = @@ -69,6 +69,7 @@ describe(ProjectFeatureAmountsPerPlanningUnitPieceExporter, () => { await fixtures.GivenProjectExist(); const { customFeature } = await fixtures.GivenACustomAndAPlatformFeatureForProject(); + await fixtures.GivenProjectHasPlanningUnits(); await fixtures.GivenProjectHasFeatureAmountsPerPlanningUnit( customFeature.id, ); @@ -85,6 +86,7 @@ describe(ProjectFeatureAmountsPerPlanningUnitPieceExporter, () => { await fixtures.GivenProjectExist(); const { platformFeature } = await fixtures.GivenACustomAndAPlatformFeatureForProject(); + await fixtures.GivenProjectHasPlanningUnits(); await fixtures.GivenProjectHasFeatureAmountsPerPlanningUnit( platformFeature.id, ); @@ -128,6 +130,24 @@ describe(ProjectFeatureAmountsPerPlanningUnitPieceExporter, () => { customFeature, ); }); + + it('saves successfully discarding feature amount with no corresponding feature found on the api', async () => { + const input = + fixtures.GivenAProjectFeatureAmountsPerPlanningUnitExportJob(); + await fixtures.GivenProjectExist(); + const { customFeature } = + await fixtures.GivenACustomAndAPlatformFeatureForProject(); + await fixtures.GivenProjectHasPlanningUnits(); + await fixtures.GivenProjectHasFeatureAmountsPerPlanningUnit( + customFeature.id, + ); + await fixtures.GivenProjectHasFeatureAmountsPerPlanningUnit(v4()); + await fixtures + .WhenPieceExporterIsInvoked(input) + .ThenFeatureAmountsPerPlanningUnitFileHasFeatureAmountsPerPlanningUnitForFeature( + customFeature, + ); + }); }); const getFixtures = async () => { @@ -251,21 +271,25 @@ const getFixtures = async () => { return derivedFeature; }, GivenNoDerivedFeaturesForProject: () => {}, - GivenProjectHasFeatureAmountsPerPlanningUnit: async (featureId: string) => { + GivenProjectHasPlanningUnits: async () => { projectPus = await GivenProjectPus( geoEntityManager, projectId, amountOfPuvsrCalculationsPerFeature, ); + }, + GivenProjectHasFeatureAmountsPerPlanningUnit: async (featureId: string) => { const featureAmountsPerPlanningUnit = Array( amountOfPuvsrCalculationsPerFeature, ) .fill(0) - .map((_, index) => ({ - amount: expectedAmountPerPu, - featureId, - projectPuId: projectPus[index].id, - })); + .map((_, index) => { + return { + amount: expectedAmountPerPu, + featureId, + projectPuId: projectPus[index].id, + }; + }); return featureAmountsPerPlanningUnitRepo.saveAmountPerPlanningUnitAndFeature( projectId, featureAmountsPerPlanningUnit,