Skip to content

Commit

Permalink
Add metadata to geo features
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Mar 7, 2024
1 parent 145b15d commit 2b41f81
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 57 deletions.
25 changes: 23 additions & 2 deletions api/src/modules/geo-regions/geo-features.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from 'modules/geo-regions/dto/get-features-geojson.dto';
import { SourcingLocation } from 'modules/sourcing-locations/sourcing-location.entity';
import { BaseQueryBuilder } from 'utils/base.query-builder';
import { Supplier } from '../suppliers/supplier.entity';

@Injectable()
export class GeoFeaturesService extends Repository<GeoRegion> {
Expand All @@ -23,6 +24,7 @@ export class GeoFeaturesService extends Repository<GeoRegion> {
const queryBuilder: SelectQueryBuilder<GeoRegion> =
this.createQueryBuilder('gr');
queryBuilder.innerJoin(SourcingLocation, 'sl', 'sl.geoRegionId = gr.id');
queryBuilder.innerJoin(Supplier, 's', 's.id = sl."producerId"');

const filteredQueryBuilder: SelectQueryBuilder<GeoRegion> =
BaseQueryBuilder.addFilters(queryBuilder, dto);
Expand All @@ -41,7 +43,7 @@ export class GeoFeaturesService extends Repository<GeoRegion> {
json_build_object(
'type', 'Feature',
'geometry', ST_AsGeoJSON(gr.theGeom)::json,
'properties', json_build_object('id', gr.id)
'properties', json_build_object(${this.injectMetaDataQuery()})
)`,
'geojson',
);
Expand All @@ -63,7 +65,7 @@ export class GeoFeaturesService extends Repository<GeoRegion> {
json_build_object(
'type', 'Feature',
'geometry', ST_AsGeoJSON(gr.theGeom)::json,
'properties', json_build_object('id', gr.id)
'properties', json_build_object(${this.injectMetaDataQuery()})
)
)
)`,
Expand All @@ -76,4 +78,23 @@ export class GeoFeaturesService extends Repository<GeoRegion> {
}
return result;
}

/**
* @description: The Baseline Volume is the purchase volume of a supplier for a specific year (2020) which is the cut-off date for the EUDR
* This is very specific to EUDR and not a dynamic thing, so for now we will be hardcoding it
*/
private injectMetaDataQuery(): string {
const baselineVolumeYear: number = 2020;
return `
'id', gr.id,
'supplierName', s.name,
'plotName', gr.name,
'baselineVolume', (
SELECT SUM(sr.tonnage)
FROM sourcing_records sr
WHERE sr."sourcingLocationId" = sl.id
AND sr.year = ${baselineVolumeYear}
)
`;
}
}
7 changes: 5 additions & 2 deletions api/test/e2e/eudr/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ export class EUDRTestManager extends TestManager {
'producerIds[]'?: string[];
'materialIds[]'?: string[];
}) => {
return this.GET({ url: `${this.url}/admin-regions`, query: filters });
return this.getRequest({
url: `${this.url}/admin-regions`,
query: filters,
});
};

ThenIShouldOnlyReceiveCorrespondingAdminRegions = (
Expand Down Expand Up @@ -145,7 +148,7 @@ export class EUDRTestManager extends TestManager {
'producerIds[]'?: string[];
'materialIds[]'?: string[];
}) => {
return this.GET({ url: `${this.url}/geo-regions`, query: filters });
return this.getRequest({ url: `${this.url}/geo-regions`, query: filters });
};

ThenIShouldOnlyReceiveCorrespondingGeoRegions = (
Expand Down
68 changes: 58 additions & 10 deletions api/test/e2e/geo-regions/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createGeoRegion,
createMaterial,
createSourcingLocation,
createSourcingRecord,
createSupplier,
} from '../../entity-mocks';
import * as request from 'supertest';
Expand All @@ -13,7 +14,8 @@ import {
} from '../../../src/modules/sourcing-locations/sourcing-location.entity';
import { TestManager } from '../../utils/test-manager';
import { Feature } from 'geojson';
import { SUPPLIER_TYPES } from 'modules/suppliers/supplier.entity';
import { SourcingRecord } from '../../../src/modules/sourcing-records/sourcing-record.entity';
import { Supplier } from '../../../src/modules/suppliers/supplier.entity';

export class GeoRegionsTestManager extends TestManager {
constructor(manager: TestManager) {
Expand Down Expand Up @@ -76,6 +78,18 @@ export class GeoRegionsTestManager extends TestManager {
producerId: supplier2.id,
materialId: material2.id,
});
for (const year of [2018, 2019, 2020, 2021, 2022, 2023]) {
await createSourcingRecord({
sourcingLocationId: sourcingLocation1.id,
year,
tonnage: 100 * year,
});
await createSourcingRecord({
sourcingLocationId: sourcingLocation2.id,
year,
tonnage: 100 * year,
});
}
return {
eudrGeoRegions: [geoRegion, geoRegion2],
eudrSourcingLocations: [sourcingLocation1, sourcingLocation2],
Expand All @@ -86,22 +100,22 @@ export class GeoRegionsTestManager extends TestManager {
'geoRegionIds[]': string[];
collection?: boolean;
}): Promise<void> => {
this.response = await request(this.testApp.getHttpServer())
.get('/api/v1/eudr/geo-features')
.query(filters)
.set('Authorization', `Bearer ${this.jwtToken}`);
this.response = await this.getRequest({
url: '/api/v1/eudr/geo-features',
query: filters,
});
};

WhenIRequestEUDRGeoFeatureCollection = async (filters: {
WhenIRequestEUDRGeoFeatureCollection = async (filters?: {
'geoRegionIds[]'?: string[];
'producerIds[]'?: string[];
'materialIds[]'?: string[];
'originIds[]'?: string[];
}): Promise<void> => {
this.response = await request(this.testApp.getHttpServer())
.get('/api/v1/eudr/geo-features/collection')
.query(filters)
.set('Authorization', `Bearer ${this.jwtToken}`);
this.response = await this.getRequest({
url: '/api/v1/eudr/geo-features/collection',
query: filters,
});
};

ThenIShouldOnlyRecieveCorrespondingGeoFeatures = (
Expand All @@ -127,4 +141,38 @@ export class GeoRegionsTestManager extends TestManager {
}
}
};

ThenTheGeoFeaturesShouldHaveCorrectMetadata = async (
sourceLocations: SourcingLocation[],
) => {
for (const feature of this.response!.body.geojson.features) {
const sourcingLocation = sourceLocations.find(
(r: any) => r.geoRegionId === feature.properties.id,
);
const expectedMetadataContentForEachFeature = await this.dataSource
.createQueryBuilder()
.from(SourcingRecord, 'sr')
.select('sr.tonnage', 'baselineVolume')
.addSelect('gr.id', 'id')
.addSelect('gr.name', 'plotName')
.addSelect('s.name', 'supplierName')
.innerJoin(SourcingLocation, 'sl', 'sr."sourcingLocationId" = sl.id')
.innerJoin(Supplier, 's', 'sl."producerId" = s.id')
.innerJoin(GeoRegion, 'gr', 'sl."geoRegionId" = gr.id')
.where('sr."sourcingLocationId" = :sourcingLocationId', {
sourcingLocationId: sourcingLocation!.id,
})
.andWhere('sr.year = :year', { year: 2020 })
.getRawOne();
expect(sourcingLocation).toBeDefined();
expect(feature.properties).toEqual({
id: expectedMetadataContentForEachFeature.id,
supplierName: expectedMetadataContentForEachFeature.supplierName,
plotName: expectedMetadataContentForEachFeature.plotName,
baselineVolume: parseInt(
expectedMetadataContentForEachFeature.baselineVolume,
),
});
}
};
}
97 changes: 55 additions & 42 deletions api/test/e2e/geo-regions/geo-features.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GeoRegionsTestManager } from './fixtures';
import { TestManager } from '../../utils/test-manager';

describe('Admin Regions EUDR Filters (e2e)', () => {
describe('Geo Features tests (e2e)', () => {
let testManager: GeoRegionsTestManager;

beforeAll(async () => {
Expand All @@ -20,51 +20,64 @@ describe('Admin Regions EUDR Filters (e2e)', () => {
await testManager.close();
});

test('should only get geo-features that are part of EUDR data', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatures({
'geoRegionIds[]': eudrGeoRegions.map((r) => r.id),
describe('EUDR Geo Features', () => {
test('should only get geo-features that are part of EUDR data', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatures({
'geoRegionIds[]': eudrGeoRegions.map((r) => r.id),
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(
eudrGeoRegions,
);
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(eudrGeoRegions);
});

test('should only get geo-features that are part of EUDR data and are filtered by geo region id', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatures({
'geoRegionIds[]': [eudrGeoRegions[0].id],
test('should only get geo-features that are part of EUDR data and are filtered by geo region id', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatures({
'geoRegionIds[]': [eudrGeoRegions[0].id],
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures([
eudrGeoRegions[0],
]);
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures([
eudrGeoRegions[0],
]);
});
test('should only get EUDR geo-features filtered by materials, suppliers and admin regions', async () => {
const {} = await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrSourcingLocations, eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatureCollection({
'materialIds[]': [eudrSourcingLocations[0].materialId],
'producerIds[]': [eudrSourcingLocations[0].producerId as string],
'originIds[]': [eudrSourcingLocations[0].adminRegionId],
test('should only get EUDR geo-features filtered by materials, suppliers and admin regions', async () => {
const {} =
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrSourcingLocations, eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatureCollection({
'materialIds[]': [eudrSourcingLocations[0].materialId],
'producerIds[]': [eudrSourcingLocations[0].producerId as string],
'originIds[]': [eudrSourcingLocations[0].adminRegionId],
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(
[eudrGeoRegions[0]],
true,
);
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(
[eudrGeoRegions[0]],
true,
);
});
test('sould only get EUDR geo-features as a FeatureCollection and filtered by geo regions', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatureCollection({
'geoRegionIds[]': eudrGeoRegions.map((r) => r.id),
test('sould only get EUDR geo-features as a FeatureCollection and filtered by geo regions', async () => {
await testManager.GivenRegularSourcingLocationsWithGeoRegions();
const { eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatureCollection({
'geoRegionIds[]': eudrGeoRegions.map((r) => r.id),
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(
eudrGeoRegions,
true,
);
});
test('each feature should include the corresponding metadata', async () => {
const { eudrSourcingLocations, eudrGeoRegions } =
await testManager.GivenEUDRSourcingLocationsWithGeoRegions();
await testManager.WhenIRequestEUDRGeoFeatureCollection();
await testManager.ThenTheGeoFeaturesShouldHaveCorrectMetadata(
eudrSourcingLocations,
);
});
testManager.ThenIShouldOnlyRecieveCorrespondingGeoFeatures(
eudrGeoRegions,
true,
);
});
});
2 changes: 1 addition & 1 deletion api/test/utils/test-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class TestManager {
await clearTestDataFromDatabase(this.dataSource);
}

async GET(options: { url: string; query?: object | string }) {
async getRequest(options: { url: string; query?: object | string }) {
this.response = await request(this.testApp.getHttpServer())
.get(options.url)
.query(options?.query || '')
Expand Down

0 comments on commit 2b41f81

Please sign in to comment.