diff --git a/api/src/modules/eudr-alerts/eudr.controller.ts b/api/src/modules/eudr-alerts/eudr.controller.ts index 0e0236545..8c0cbfcf7 100644 --- a/api/src/modules/eudr-alerts/eudr.controller.ts +++ b/api/src/modules/eudr-alerts/eudr.controller.ts @@ -6,13 +6,11 @@ import { ValidationPipe, } from '@nestjs/common'; import { - ApiExtraModels, ApiForbiddenResponse, ApiOkResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse, - refs, } from '@nestjs/swagger'; import { ApiOkTreeResponse } from 'decorators/api-tree-response.decorator'; @@ -37,11 +35,10 @@ import { EudrService } from 'modules/eudr-alerts/eudr.service'; import { GetEUDRAlertsDto } from 'modules/eudr-alerts/dto/get-alerts.dto'; import { EUDRAlertDates } from 'modules/eudr-alerts/eudr.repositoty.interface'; import { GetEUDRFeaturesGeoJSONDto } from 'modules/geo-regions/dto/get-features-geojson.dto'; -import { Feature, FeatureCollection } from 'geojson'; import { GeoFeatureCollectionResponse, GeoFeatureResponse, -} from '../geo-regions/dto/geo-feature-response.dto'; +} from 'modules/geo-regions/dto/geo-feature-response.dto'; @ApiTags('EUDR') @Controller('/api/v1/eudr') @@ -180,7 +177,7 @@ export class EudrController { async getGeoFeatureList( @Query(ValidationPipe) dto: GetEUDRFeaturesGeoJSONDto, ): Promise { - return this.geoRegionsService.getGeoJson(dto); + return this.geoRegionsService.getGeoJson({ ...dto, eudr: true }); } @ApiOperation({ @@ -193,8 +190,10 @@ export class EudrController { async getGeoFeatureCollection( @Query(ValidationPipe) dto: GetEUDRFeaturesGeoJSONDto, ): Promise { - return this.geoRegionsService.getGeoJson( - Object.assign(dto, { collection: true }), - ); + return this.geoRegionsService.getGeoJson({ + ...dto, + eudr: true, + collection: true, + }); } } diff --git a/api/src/modules/eudr-alerts/eudr.module.ts b/api/src/modules/eudr-alerts/eudr.module.ts index 692323e5e..9c7692615 100644 --- a/api/src/modules/eudr-alerts/eudr.module.ts +++ b/api/src/modules/eudr-alerts/eudr.module.ts @@ -15,7 +15,10 @@ export const IEUDRAlertsRepositoryToken: symbol = Symbol( export const EUDRDataSetToken: symbol = Symbol('EUDRDataSet'); export const EUDRCredentialsToken: symbol = Symbol('EUDRCredentials'); -const { credentials, dataset } = AppConfig.get('eudr'); +const { credentials, dataset } = AppConfig.get<{ + credentials: string; + dataset: string; +}>('eudr'); // TODO: Use token injection and refer to the interface, right now I am having a dependencv issue @Module({ diff --git a/api/src/modules/geo-regions/dto/get-features-geojson.dto.ts b/api/src/modules/geo-regions/dto/get-features-geojson.dto.ts index 2bd593067..f96f48567 100644 --- a/api/src/modules/geo-regions/dto/get-features-geojson.dto.ts +++ b/api/src/modules/geo-regions/dto/get-features-geojson.dto.ts @@ -1,25 +1,20 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsOptional, IsUUID } from 'class-validator'; - -export class GetFeaturesGeoJsonDto { - @ApiPropertyOptional() - @IsOptional() - @IsUUID('4', { each: true }) - geoRegionIds!: string[]; +import { IsBoolean, IsOptional } from 'class-validator'; +import { + CommonEUDRFiltersDTO, + CommonFiltersDto, +} from 'utils/base.query-builder'; +export class GetFeaturesGeoJsonDto extends CommonFiltersDto { @IsOptional() @IsBoolean() @Type(() => Boolean) collection: boolean = false; - - isEUDRRequested(): boolean { - return 'eudr' in this; - } } -export class GetEUDRFeaturesGeoJSONDto extends GetFeaturesGeoJsonDto { +export class GetEUDRFeaturesGeoJSONDto extends CommonEUDRFiltersDTO { @IsOptional() @IsBoolean() - eudr: boolean = true; + @Type(() => Boolean) + collection: boolean = false; } diff --git a/api/src/modules/geo-regions/geo-features.service.ts b/api/src/modules/geo-regions/geo-features.service.ts index 231978169..08a2f8ace 100644 --- a/api/src/modules/geo-regions/geo-features.service.ts +++ b/api/src/modules/geo-regions/geo-features.service.ts @@ -6,10 +6,8 @@ import { GetEUDRFeaturesGeoJSONDto, GetFeaturesGeoJsonDto, } from 'modules/geo-regions/dto/get-features-geojson.dto'; -import { - LOCATION_TYPES, - SourcingLocation, -} from 'modules/sourcing-locations/sourcing-location.entity'; +import { SourcingLocation } from 'modules/sourcing-locations/sourcing-location.entity'; +import { BaseQueryBuilder } from 'utils/base.query-builder'; @Injectable() export class GeoFeaturesService extends Repository { @@ -25,20 +23,14 @@ export class GeoFeaturesService extends Repository { const queryBuilder: SelectQueryBuilder = this.createQueryBuilder('gr'); queryBuilder.innerJoin(SourcingLocation, 'sl', 'sl.geoRegionId = gr.id'); - if (dto.isEUDRRequested()) { - queryBuilder.where('sl.locationType = :locationType', { - locationType: LOCATION_TYPES.EUDR, - }); - } - if (dto.geoRegionIds) { - queryBuilder.andWhere('gr.id IN (:...geoRegionIds)', { - geoRegionIds: dto.geoRegionIds, - }); - } + + const filteredQueryBuilder: SelectQueryBuilder = + BaseQueryBuilder.addFilters(queryBuilder, dto); + if (dto?.collection) { - return this.selectAsFeatureCollection(queryBuilder); + return this.selectAsFeatureCollection(filteredQueryBuilder); } - return this.selectAsFeatures(queryBuilder); + return this.selectAsFeatures(filteredQueryBuilder); } private async selectAsFeatures( diff --git a/api/src/utils/base.query-builder.ts b/api/src/utils/base.query-builder.ts index b9325ddaf..6e8fb0ee4 100644 --- a/api/src/utils/base.query-builder.ts +++ b/api/src/utils/base.query-builder.ts @@ -47,6 +47,11 @@ export class BaseQueryBuilder { originIds: filters.originIds, }); } + if (filters.geoRegionIds) { + queryBuilder.andWhere('sl.geoRegionId IN (:...geoRegionIds)', { + geoRegionIds: filters.geoRegionIds, + }); + } if (filters.eudr) { queryBuilder.andWhere('sl.locationType = :eudr', { eudr: LOCATION_TYPES.EUDR, @@ -137,6 +142,8 @@ export class CommonFiltersDto { scenarioIds?: string[]; eudr?: boolean; + + geoRegionIds?: string[]; } export class CommonEUDRFiltersDTO extends OmitType(CommonFiltersDto, [ @@ -146,4 +153,9 @@ export class CommonEUDRFiltersDTO extends OmitType(CommonFiltersDto, [ 'businessUnitIds', ]) { eudr?: boolean; + + @ApiPropertyOptional({ name: 'geoRegionIds[]' }) + @IsOptional() + @IsUUID('4', { each: true }) + geoRegionIds?: string[]; } diff --git a/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts b/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts index 39d6ca42f..4c18421d6 100644 --- a/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts +++ b/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts @@ -1,5 +1,6 @@ import { EUDRTestManager } from './fixtures'; import { TestManager } from '../../utils/test-manager'; +import { Supplier } from '../../../src/modules/suppliers/supplier.entity'; describe('Admin Regions EUDR Filters (e2e)', () => { let testManager: EUDRTestManager; @@ -43,7 +44,7 @@ describe('Admin Regions EUDR Filters (e2e)', () => { ); await testManager.WhenIRequestEUDRAdminRegions({ 'materialIds[]': [eudrMaterials[0].id], - 'producerIds[]': eudrSuppliers.map((s) => s.id), + 'producerIds[]': eudrSuppliers.map((s: Supplier) => s.id), }); testManager.ThenIShouldOnlyReceiveCorrespondingAdminRegions([ eudrAdminRegions[0], diff --git a/api/test/e2e/eudr/fixtures.ts b/api/test/e2e/eudr/fixtures.ts index 671658e8d..81951deb1 100644 --- a/api/test/e2e/eudr/fixtures.ts +++ b/api/test/e2e/eudr/fixtures.ts @@ -54,6 +54,7 @@ export class EUDRTestManager extends TestManager { sourcingLocations[i].materialId = materials[i].id; await sourcingLocations[i].save(); } + return materials; }; AndAssociatedSuppliers = async ( sourcingLocations: SourcingLocation[], @@ -65,9 +66,10 @@ export class EUDRTestManager extends TestManager { } const limitLength = Math.min(suppliers.length, sourcingLocations.length); for (let i = 0; i < limitLength; i++) { - sourcingLocations[i].materialId = suppliers[i].id; + sourcingLocations[i].producerId = suppliers[i].id; await sourcingLocations[i].save(); } + return suppliers; }; GivenEUDRAdminRegions = async () => { const adminRegion = await createAdminRegion({ diff --git a/api/test/e2e/geo-regions/fixtures.ts b/api/test/e2e/geo-regions/fixtures.ts index 86539d2b8..f13b4820c 100644 --- a/api/test/e2e/geo-regions/fixtures.ts +++ b/api/test/e2e/geo-regions/fixtures.ts @@ -1,9 +1,19 @@ -import { createGeoRegion, createSourcingLocation } from '../../entity-mocks'; +import { + createAdminRegion, + createGeoRegion, + createMaterial, + createSourcingLocation, + createSupplier, +} from '../../entity-mocks'; import * as request from 'supertest'; import { GeoRegion } from '../../../src/modules/geo-regions/geo-region.entity'; -import { LOCATION_TYPES } from '../../../src/modules/sourcing-locations/sourcing-location.entity'; +import { + LOCATION_TYPES, + SourcingLocation, +} 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'; export class GeoRegionsTestManager extends TestManager { constructor(manager: TestManager) { @@ -38,13 +48,33 @@ export class GeoRegionsTestManager extends TestManager { const geoRegion2 = await createGeoRegion({ name: this.generateRandomName(), }); + + const adminRegion = await createAdminRegion({ name: 'EUDR AdminRegion' }); + const adminRegion2 = await createAdminRegion({ + name: 'EUDR AdminRegion 2', + }); + + const supplier = await createSupplier({ + name: 'EUDR Supplier', + }); + + const material = await createMaterial({ name: 'EUDR Material' }); + const material2 = await createMaterial({ name: 'EUDR Material 2' }); + + const supplier2 = await createSupplier({ name: 'EUDR Supplier 2' }); const sourcingLocation1 = await createSourcingLocation({ geoRegionId: geoRegion.id, locationType: LOCATION_TYPES.EUDR, + adminRegionId: adminRegion.id, + producerId: supplier.id, + materialId: material.id, }); const sourcingLocation2 = await createSourcingLocation({ geoRegionId: geoRegion2.id, locationType: LOCATION_TYPES.EUDR, + adminRegionId: adminRegion2.id, + producerId: supplier2.id, + materialId: material2.id, }); return { eudrGeoRegions: [geoRegion, geoRegion2], @@ -63,7 +93,10 @@ export class GeoRegionsTestManager extends TestManager { }; WhenIRequestEUDRGeoFeatureCollection = async (filters: { - 'geoRegionIds[]': string[]; + 'geoRegionIds[]'?: string[]; + 'producerIds[]'?: string[]; + 'materialIds[]'?: string[]; + 'originIds[]'?: string[]; }): Promise => { this.response = await request(this.testApp.getHttpServer()) .get('/api/v1/eudr/geo-features/collection') diff --git a/api/test/e2e/geo-regions/geo-features.spec.ts b/api/test/e2e/geo-regions/geo-features.spec.ts index e9ebe3e82..b7de75f96 100644 --- a/api/test/e2e/geo-regions/geo-features.spec.ts +++ b/api/test/e2e/geo-regions/geo-features.spec.ts @@ -41,7 +41,21 @@ describe('Admin Regions EUDR Filters (e2e)', () => { eudrGeoRegions[0], ]); }); - test('sould only get EUDR geo-features as a FeatureCollection', async () => { + 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, + ); + }); + test('sould only get EUDR geo-features as a FeatureCollection and filtered by geo regions', async () => { await testManager.GivenRegularSourcingLocationsWithGeoRegions(); const { eudrGeoRegions } = await testManager.GivenEUDRSourcingLocationsWithGeoRegions();