diff --git a/api/src/modules/admin-regions/admin-regions.controller.ts b/api/src/modules/admin-regions/admin-regions.controller.ts index 031331bdca..b44cffd831 100644 --- a/api/src/modules/admin-regions/admin-regions.controller.ts +++ b/api/src/modules/admin-regions/admin-regions.controller.ts @@ -100,29 +100,6 @@ export class AdminRegionsController { return this.adminRegionsService.serialize(results); } - @ApiOperation({ - description: - 'Find all EUDR admin regions and return them in a tree format. Data in the "children" will recursively extend for the full depth of the tree', - }) - @ApiOkTreeResponse({ - treeNodeType: AdminRegion, - }) - @ApiUnauthorizedResponse() - @ApiForbiddenResponse() - @UseInterceptors(SetScenarioIdsInterceptor) - @Get('/trees/eudr') - async getTreesForEudr( - @Query(ValidationPipe) - adminRegionTreeOptions: GetEUDRAdminRegions, - ): Promise { - const results: AdminRegion[] = await this.adminRegionsService.getTrees({ - ...adminRegionTreeOptions, - withSourcingLocations: true, - eudr: true, - }); - return this.adminRegionsService.serialize(results); - } - @ApiOperation({ description: 'Find all admin regions given a country and return data in a tree format. Data in the "children" will recursively extend for the full depth of the tree', diff --git a/api/src/modules/eudr/eudr.controller.ts b/api/src/modules/eudr/eudr.controller.ts index 602cbb3302..b0393ec1a6 100644 --- a/api/src/modules/eudr/eudr.controller.ts +++ b/api/src/modules/eudr/eudr.controller.ts @@ -1,10 +1,138 @@ -import { Controller, Get } from '@nestjs/common'; +import { + Controller, + Get, + Query, + UseInterceptors, + ValidationPipe, +} from '@nestjs/common'; import { Public } from 'decorators/public.decorator'; import { CartoConnector } from 'modules/eudr/carto/carto.connector'; +import { + ApiForbiddenResponse, + ApiOkResponse, + ApiOperation, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { ApiOkTreeResponse } from '../../decorators/api-tree-response.decorator'; +import { Supplier } from '../suppliers/supplier.entity'; +import { SetScenarioIdsInterceptor } from '../impact/set-scenario-ids.interceptor'; +import { GetSupplierEUDR } from '../suppliers/dto/get-supplier-by-type.dto'; +import { SuppliersService } from '../suppliers/suppliers.service'; +import { MaterialsService } from 'modules/materials/materials.service'; +import { GeoRegionsService } from 'modules/geo-regions/geo-regions.service'; +import { AdminRegionsService } from 'modules/admin-regions/admin-regions.service'; +import { Material } from '../materials/material.entity'; +import { GetEUDRMaterials } from '../materials/dto/get-material-tree-with-options.dto'; +import { AdminRegion } from '../admin-regions/admin-region.entity'; +import { GetEUDRAdminRegions } from '../admin-regions/dto/get-admin-region-tree-with-options.dto'; +import { GeoRegion, geoRegionResource } from '../geo-regions/geo-region.entity'; +import { JSONAPIQueryParams } from '../../decorators/json-api-parameters.decorator'; +import { GetEUDRGeoRegions } from '../geo-regions/dto/get-geo-region.dto'; @Controller('/api/v1/eudr') export class EudrController { - constructor(private readonly carto: CartoConnector) {} + constructor( + private readonly carto: CartoConnector, + private readonly suppliersService: SuppliersService, + private readonly materialsService: MaterialsService, + private readonly geoRegionsService: GeoRegionsService, + private readonly adminRegionsService: AdminRegionsService, + ) {} + + @ApiOperation({ + description: + 'Find all EUDR suppliers and return them in a flat format. Data in the "children" will recursively extend for the full depth of the tree', + }) + @ApiOkTreeResponse({ + treeNodeType: Supplier, + }) + @ApiUnauthorizedResponse() + @ApiForbiddenResponse() + @UseInterceptors(SetScenarioIdsInterceptor) + @Get('/suppliers') + async getSuppliers( + @Query(ValidationPipe) dto: GetSupplierEUDR, + ): Promise { + const results: Supplier[] = await this.suppliersService.getSupplierByType({ + ...dto, + eudr: true, + }); + return this.suppliersService.serialize(results); + } + + @ApiOperation({ + description: + 'Find all EUDR materials and return them in a tree format. Data in the "children" will recursively extend for the full depth of the tree', + }) + @ApiOkTreeResponse({ + treeNodeType: Material, + }) + @ApiUnauthorizedResponse() + @ApiForbiddenResponse() + @Get('/eudr') + async getMaterialsTree( + @Query(ValidationPipe) materialTreeOptions: GetEUDRMaterials, + ): Promise { + const results: Material[] = await this.materialsService.getTrees({ + ...materialTreeOptions, + withSourcingLocations: true, + eudr: true, + }); + return this.materialsService.serialize(results); + } + + @ApiOperation({ + description: + 'Find all EUDR admin regions and return them in a tree format. Data in the "children" will recursively extend for the full depth of the tree', + }) + @ApiOkTreeResponse({ + treeNodeType: AdminRegion, + }) + @ApiUnauthorizedResponse() + @ApiForbiddenResponse() + @UseInterceptors(SetScenarioIdsInterceptor) + @Get('/admin-regions') + async getTreesForEudr( + @Query(ValidationPipe) + adminRegionTreeOptions: GetEUDRAdminRegions, + ): Promise { + const results: AdminRegion[] = await this.adminRegionsService.getTrees({ + ...adminRegionTreeOptions, + withSourcingLocations: true, + eudr: true, + }); + return this.adminRegionsService.serialize(results); + } + + @ApiOperation({ + description: 'Find all EUDR geo regions', + }) + @ApiOkResponse({ + type: GeoRegion, + isArray: true, + }) + @ApiUnauthorizedResponse() + @ApiForbiddenResponse() + @JSONAPIQueryParams({ + availableFilters: geoRegionResource.columnsAllowedAsFilter.map( + (columnName: string) => ({ + name: columnName, + }), + ), + }) + @Get('/geo-regions') + async findAllEudr( + @Query(ValidationPipe) + dto: GetEUDRGeoRegions, + ): Promise { + const results: GeoRegion[] = + await this.geoRegionsService.getGeoRegionsFromSourcingLocations({ + ...dto, + withSourcingLocations: true, + eudr: true, + }); + return this.geoRegionsService.serialize(results); + } @Public() @Get('test') diff --git a/api/src/modules/eudr/eudr.module.ts b/api/src/modules/eudr/eudr.module.ts index 179ff1f3d2..02ca18b8b5 100644 --- a/api/src/modules/eudr/eudr.module.ts +++ b/api/src/modules/eudr/eudr.module.ts @@ -4,9 +4,12 @@ import { EudrService } from 'modules/eudr/eudr.service'; import { EudrController } from 'modules/eudr/eudr.controller'; import { CartodbRepository } from 'modules/eudr/carto/cartodb.repository'; import { CartoConnector } from './carto/carto.connector'; +import { MaterialsModule } from 'modules/materials/materials.module'; +import { SuppliersModule } from 'modules/suppliers/suppliers.module'; +import { GeoRegionsModule } from 'modules/geo-regions/geo-regions.module'; @Module({ - imports: [HttpModule], + imports: [HttpModule, MaterialsModule, SuppliersModule, GeoRegionsModule], providers: [EudrService, CartodbRepository, CartoConnector], controllers: [EudrController], }) diff --git a/api/src/modules/geo-regions/geo-regions.controller.ts b/api/src/modules/geo-regions/geo-regions.controller.ts index 38194b9492..1ab8d4102c 100644 --- a/api/src/modules/geo-regions/geo-regions.controller.ts +++ b/api/src/modules/geo-regions/geo-regions.controller.ts @@ -73,36 +73,6 @@ export class GeoRegionsController { return this.geoRegionsService.serialize(results.data, results.metadata); } - @ApiOperation({ - description: 'Find all EUDR geo regions', - }) - @ApiOkResponse({ - type: GeoRegion, - isArray: true, - }) - @ApiUnauthorizedResponse() - @ApiForbiddenResponse() - @JSONAPIQueryParams({ - availableFilters: geoRegionResource.columnsAllowedAsFilter.map( - (columnName: string) => ({ - name: columnName, - }), - ), - }) - @Get('/eudr') - async findAllEudr( - @Query(ValidationPipe) - dto: GetEUDRGeoRegions, - ): Promise { - const results: GeoRegion[] = - await this.geoRegionsService.getGeoRegionsFromSourcingLocations({ - ...dto, - withSourcingLocations: true, - eudr: true, - }); - return this.geoRegionsService.serialize(results); - } - @ApiOperation({ description: 'Find geo region by id' }) @ApiOkResponse({ type: GeoRegion }) @ApiNotFoundResponse({ description: 'Geo region not found' }) diff --git a/api/src/modules/materials/materials.controller.ts b/api/src/modules/materials/materials.controller.ts index 8ddf3247ae..3f2a83addb 100644 --- a/api/src/modules/materials/materials.controller.ts +++ b/api/src/modules/materials/materials.controller.ts @@ -102,27 +102,6 @@ export class MaterialsController { return this.materialsService.serialize(results); } - @ApiOperation({ - description: - 'Find all EUDR materials and return them in a tree format. Data in the "children" will recursively extend for the full depth of the tree', - }) - @ApiOkTreeResponse({ - treeNodeType: Material, - }) - @ApiUnauthorizedResponse() - @ApiForbiddenResponse() - @Get('/trees/eudr') - async getTreesForEudr( - @Query(ValidationPipe) materialTreeOptions: GetEUDRMaterials, - ): Promise { - const results: Material[] = await this.materialsService.getTrees({ - ...materialTreeOptions, - withSourcingLocations: true, - eudr: true, - }); - return this.materialsService.serialize(results); - } - @ApiOperation({ description: 'Find material by id' }) @ApiNotFoundResponse({ description: 'Material not found' }) @ApiOkResponse({ type: Material }) diff --git a/api/src/modules/suppliers/suppliers.controller.ts b/api/src/modules/suppliers/suppliers.controller.ts index 6e7912631f..a1b4e39e55 100644 --- a/api/src/modules/suppliers/suppliers.controller.ts +++ b/api/src/modules/suppliers/suppliers.controller.ts @@ -30,11 +30,7 @@ import { FetchSpecification, ProcessFetchSpecification, } from 'nestjs-base-service'; -import { - Supplier, - SUPPLIER_TYPES, - supplierResource, -} from 'modules/suppliers/supplier.entity'; +import { Supplier, supplierResource } from 'modules/suppliers/supplier.entity'; import { CreateSupplierDto } from 'modules/suppliers/dto/create.supplier.dto'; import { UpdateSupplierDto } from 'modules/suppliers/dto/update.supplier.dto'; import { ApiOkTreeResponse } from 'decorators/api-tree-response.decorator'; @@ -101,27 +97,6 @@ export class SuppliersController { return this.suppliersService.serialize(results); } - @ApiOperation({ - description: - 'Find all EUDR suppliers and return them in a flat format. Data in the "children" will recursively extend for the full depth of the tree', - }) - @ApiOkTreeResponse({ - treeNodeType: Supplier, - }) - @ApiUnauthorizedResponse() - @ApiForbiddenResponse() - @UseInterceptors(SetScenarioIdsInterceptor) - @Get('/eudr') - async getTreesForEudr( - @Query(ValidationPipe) dto: GetSupplierEUDR, - ): Promise { - const results: Supplier[] = await this.suppliersService.getSupplierByType({ - ...dto, - eudr: true, - }); - return this.suppliersService.serialize(results); - } - @ApiOperation({ description: 'Find all suppliers by type', }) diff --git a/api/test/e2e/admin-regions/admin-regions-eudr-smart-filters.spec.ts b/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts similarity index 91% rename from api/test/e2e/admin-regions/admin-regions-eudr-smart-filters.spec.ts rename to api/test/e2e/eudr/eudr-admin-region-filters.spec.ts index 1c405a93d2..17e2a74d45 100644 --- a/api/test/e2e/admin-regions/admin-regions-eudr-smart-filters.spec.ts +++ b/api/test/e2e/eudr/eudr-admin-region-filters.spec.ts @@ -1,10 +1,10 @@ -import { AdminRegionTestManager } from './fixtures'; +import { EUDRTestManager } from './fixtures'; describe('Admin Regions EUDR Filters (e2e)', () => { - let testManager: AdminRegionTestManager; + let testManager: EUDRTestManager; beforeAll(async () => { - testManager = await AdminRegionTestManager.load(); + testManager = await EUDRTestManager.load(); }); beforeEach(async () => { diff --git a/api/test/e2e/eudr/eudr-geo-region-filters.spec.ts b/api/test/e2e/eudr/eudr-geo-region-filters.spec.ts new file mode 100644 index 0000000000..844ab04964 --- /dev/null +++ b/api/test/e2e/eudr/eudr-geo-region-filters.spec.ts @@ -0,0 +1,28 @@ +import { EUDRTestManager } from './fixtures'; + +describe('GeoRegions Filters (e2e)', () => { + let testManager: EUDRTestManager; + + beforeAll(async () => { + testManager = await EUDRTestManager.load(); + }); + beforeEach(async () => { + await testManager.refreshState(); + }); + + afterEach(async () => { + await testManager.clearDatabase(); + }); + + afterAll(async () => { + await testManager.close(); + }); + describe('EUDR Geo Regions Filters', () => { + it('should only get geo-regions that are part of EUDR data', async () => { + await testManager.GivenGeoRegionsOfSourcingLocations(); + const { eudrGeoRegions } = await testManager.GivenEUDRGeoRegions(); + const response = await testManager.WhenIRequestEUDRGeoRegions(); + testManager.ThenIShouldOnlyReceiveCorrespondingGeoRegions(eudrGeoRegions); + }); + }); +}); diff --git a/api/test/e2e/admin-regions/fixtures.ts b/api/test/e2e/eudr/fixtures.ts similarity index 62% rename from api/test/e2e/admin-regions/fixtures.ts rename to api/test/e2e/eudr/fixtures.ts index 45c5542b58..b0d1f98ac0 100644 --- a/api/test/e2e/admin-regions/fixtures.ts +++ b/api/test/e2e/eudr/fixtures.ts @@ -7,16 +7,17 @@ import { AdminRegion } from 'modules/admin-regions/admin-region.entity'; import { AndAssociatedMaterials } from '../../common-steps/and-associated-materials'; import { AndAssociatedSuppliers } from '../../common-steps/and-associated-suppliers'; import { TestManager } from '../../utils/test-manager'; +import { GeoRegion } from '../../../src/modules/geo-regions/geo-region.entity'; -export class AdminRegionTestManager extends TestManager { - url = '/api/v1/admin-regions/'; +export class EUDRTestManager extends TestManager { + url = '/api/v1/eudr/'; constructor(manager: TestManager) { super(manager.testApp, manager.jwtToken, manager.dataSource); } static async load() { - return new AdminRegionTestManager(await this.createManager()); + return new EUDRTestManager(await this.createManager()); } GivenAdminRegionsOfSourcingLocations = async () => { @@ -77,7 +78,7 @@ export class AdminRegionTestManager extends TestManager { 'producerIds[]'?: string[]; 'materialIds[]'?: string[]; }) => { - return this.GET({ url: `${this.url}trees/eudr`, query: filters }); + return this.GET({ url: `${this.url}/admin-regions`, query: filters }); }; ThenIShouldOnlyReceiveCorrespondingAdminRegions = ( @@ -94,4 +95,56 @@ export class AdminRegionTestManager extends TestManager { ).toBeDefined(); } }; + + GivenGeoRegionsOfSourcingLocations = async () => { + const [geoRegion, geoRegion2] = await this.createGeoRegions([ + 'Regular GeoRegion', + 'Regular GeoRegion 2', + ]); + await createSourcingLocation({ geoRegionId: geoRegion.id }); + await createSourcingLocation({ geoRegionId: geoRegion2.id }); + return { + geoRegions: [geoRegion, geoRegion2], + }; + }; + + GivenEUDRGeoRegions = async () => { + const [geoRegion, geoRegion2] = await this.createGeoRegions([ + 'EUDR GeoRegion', + 'EUDR GeoRegion 2', + ]); + await createSourcingLocation({ + geoRegionId: geoRegion.id, + locationType: LOCATION_TYPES.EUDR, + }); + await createSourcingLocation({ + geoRegionId: geoRegion2.id, + locationType: LOCATION_TYPES.EUDR, + }); + return { + eudrGeoRegions: [geoRegion, geoRegion2], + }; + }; + + WhenIRequestEUDRGeoRegions = async (filters?: { + 'producerIds[]'?: string[]; + 'materialIds[]'?: string[]; + }) => { + return this.GET({ url: `${this.url}/geo-regions`, query: filters }); + }; + + ThenIShouldOnlyReceiveCorrespondingGeoRegions = ( + eudrGeoRegions: GeoRegion[], + ) => { + expect(this.response!.status).toBe(200); + expect(this.response!.body.data.length).toBe(eudrGeoRegions.length); + for (const geoRegion of eudrGeoRegions) { + expect( + this.response!.body.data.find( + (adminRegionResponse: AdminRegion) => + adminRegionResponse.id === geoRegion.id, + ), + ).toBeDefined(); + } + }; } diff --git a/api/test/utils/test-manager.ts b/api/test/utils/test-manager.ts index 3c6bd70b3b..d6190f14ad 100644 --- a/api/test/utils/test-manager.ts +++ b/api/test/utils/test-manager.ts @@ -87,7 +87,7 @@ export class TestManager { return createdSuppliers; } - async createGeoRegion(names?: string[]) { + async createGeoRegions(names?: string[]) { const namesToCreate = names || [randomName()]; const createdGeoRegions: GeoRegion[] = []; for (let i = 0; i < namesToCreate.length; i++) { diff --git a/api/test/utils/userAuth.ts b/api/test/utils/userAuth.ts index 0707d9a21b..f568c05900 100644 --- a/api/test/utils/userAuth.ts +++ b/api/test/utils/userAuth.ts @@ -37,6 +37,7 @@ export async function setupTestUser( isActive: true, isDeleted: false, roles: [role], + ...restOfExtraData, }); }