Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugix/scenario location types #904

Merged
merged 1 commit into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/src/guards/roles.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export class RolesGuard implements CanActivate {
return true;
}
const { user } = context.switchToHttp().getRequest();
if (!user.roles.length) {
return false;
}
return requiredRoles.some((role: ROLES) =>
user.roles?.map((role: Role) => role.name).includes(role),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
IsUUID,
} from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { Transform, TransformFnParams, Type } from 'class-transformer';
import { LOCATION_TYPES } from 'modules/sourcing-locations/sourcing-location.entity';

export class GetAdminRegionTreeWithOptionsDto {
Expand Down Expand Up @@ -69,7 +69,9 @@ export class GetAdminRegionTreeWithOptionsDto {
@IsUUID('4')
scenarioId?: string;

@ApiPropertyOptional()
@ApiPropertyOptional({
description: 'Array of Scenario Ids to include in the admin region search',
})
@IsOptional()
@IsUUID('4', { each: true })
scenarioIds?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ export class GetBusinessUnitTreeWithOptionsDto {
@Type(() => String)
locationTypes?: LOCATION_TYPES[];

@ApiPropertyOptional()
@ApiPropertyOptional({
description: 'Array of Scenario Ids to include in the business unit search',
})
@IsOptional()
@IsUUID('4')
scenarioId?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ export class GetMaterialTreeWithOptionsDto {
@IsUUID('4')
scenarioId?: string;

@ApiPropertyOptional()
@ApiPropertyOptional({
description: 'Array of Scenario Ids to include in the material search',
})
@IsOptional()
@IsUUID('4', { each: true })
scenarioIds?: string[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { IsArray, IsEnum, IsOptional, IsUUID } from 'class-validator';
import {
IsArray,
IsBoolean,
IsEnum,
IsIn,
IsOptional,
IsString,
IsUUID,
} from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { LOCATION_TYPES } from 'modules/sourcing-locations/sourcing-location.entity';
import { Type } from 'class-transformer';
Expand Down Expand Up @@ -47,4 +55,32 @@ export class GetLocationTypesDto {
@ApiPropertyOptional()
@IsOptional()
scenarioId?: string;

@ApiPropertyOptional({
description: 'Array of Scenario Ids to include in the location type search',
})
@IsOptional()
@IsUUID('4', { each: true })
scenarioIds?: string[];

@ApiPropertyOptional({
description:
'Get all supported location types. Setting this to true overrides all other parameters',
})
@IsOptional()
@IsBoolean()
@Type(() => Boolean)
supported?: boolean;

@ApiPropertyOptional({
description: 'Sorting parameter to order the result. Defaults to ASC ',
enum: ['ASC', 'DESC'],
})
@Type(() => String)
@IsString()
@IsOptional()
@IsIn(['ASC', 'DESC'], {
message: `sort property must be either 'ASC' (Ascendant) or 'DESC' (Descendent)`,
})
sort?: 'ASC' | 'DESC';
}
48 changes: 26 additions & 22 deletions api/src/modules/sourcing-locations/sourcing-location.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,33 +59,37 @@ export class SourcingLocationRepository extends AppBaseRepository<SourcingLocati
});
}

if (locationTypesOptions.scenarioId) {
queryBuilder
.leftJoin(
ScenarioIntervention,
'scenarioIntervention',
'sl.scenarioInterventionId = scenarioIntervention.id',
)
.andWhere(
new Brackets((qb: WhereExpressionBuilder) => {
qb.where('sl.scenarioInterventionId is null').orWhere(
new Brackets((qbInterv: WhereExpressionBuilder) => {
qbInterv
.where('scenarioIntervention.scenarioId = :scenarioId', {
scenarioId: locationTypesOptions.scenarioId,
})
.andWhere(`scenarioIntervention.status = :status`, {
status: SCENARIO_INTERVENTION_STATUS.ACTIVE,
});
}),
);
}),
);
if (locationTypesOptions.scenarioIds) {
queryBuilder.leftJoin(
ScenarioIntervention,
'scenarioIntervention',
'sl.scenarioInterventionId = scenarioIntervention.id',
);
queryBuilder.andWhere(
new Brackets((qb: WhereExpressionBuilder) => {
qb.where('sl.scenarioInterventionId is null').orWhere(
new Brackets((qbInterv: WhereExpressionBuilder) => {
qbInterv
.where('scenarioIntervention.scenarioId IN (:...scenarioIds)', {
scenarioIds: locationTypesOptions.scenarioIds,
})
.andWhere(`scenarioIntervention.status = :status`, {
status: SCENARIO_INTERVENTION_STATUS.ACTIVE,
});
}),
);
}),
);
} else {
queryBuilder.andWhere('sl.scenarioInterventionId is null');
queryBuilder.andWhere('sl.interventionType is null');
}

queryBuilder.orderBy(
'sl.locationType',
locationTypesOptions.sort ?? 'DESC',
);

const locationTypes: { locationType: string }[] =
await queryBuilder.getRawMany();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { GetLocationTypesDto } from 'modules/sourcing-locations/dto/location-typ
import { RolesGuard } from 'guards/roles.guard';
import { RequiredRoles } from 'decorators/roles.decorator';
import { ROLES } from 'modules/authorization/roles/roles.enum';
import { SetScenarioIdsInterceptor } from 'modules/impact/set-scenario-ids.interceptor';

@Controller(`/api/v1/sourcing-locations`)
@ApiTags(sourcingLocationResource.className)
Expand Down Expand Up @@ -123,20 +124,33 @@ export class SourcingLocationsController {
);
}

@ApiOperation({ description: 'Gets available location types' })
@ApiOperation({
description:
'Gets available location types. Optionally returns all supported location types',
})
@ApiOkResponse({ type: LocationTypesDto })
@UseInterceptors(SetScenarioIdsInterceptor)
@Get('/location-types')
async getLocationTypes(
@Query(ValidationPipe) locationTypesOptions: GetLocationTypesDto,
): Promise<LocationTypesDto> {
return this.sourcingLocationsService.getLocationTypes(locationTypesOptions);
}

@ApiOperation({ description: 'Get location types supported by the platform' })
/**
* @deprecated: We will deprecate this endpoint in favour of /location-types supporting a supported param
* to return all supported location types
*/
@ApiOperation({
description: 'Get location types supported by the platform',
deprecated: true,
})
@ApiOkResponse({ type: LocationTypesDto })
@Get('/location-types/supported')
async getAllSupportedLocationTypes(): Promise<LocationTypesDto> {
return this.sourcingLocationsService.getAllSupportedLocationTypes();
return this.sourcingLocationsService.getAllSupportedLocationTypes({
sort: 'ASC',
});
}

@ApiOperation({ description: 'Find sourcing location by id' })
Expand Down
17 changes: 13 additions & 4 deletions api/src/modules/sourcing-locations/sourcing-locations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
forwardRef,
Inject,
Injectable,
Logger,
NotFoundException,
} from '@nestjs/common';
import {
Expand Down Expand Up @@ -193,6 +192,11 @@ export class SourcingLocationsService extends AppBaseService<
async getLocationTypes(
locationTypesOptions: GetLocationTypesDto,
): Promise<LocationTypesDto> {
if (locationTypesOptions.supported) {
return this.getAllSupportedLocationTypes({
sort: locationTypesOptions.sort ?? 'DESC',
});
}
if (locationTypesOptions.originIds) {
locationTypesOptions.originIds =
await this.adminRegionService.getAdminRegionDescendants(
Expand Down Expand Up @@ -232,7 +236,6 @@ export class SourcingLocationsService extends AppBaseService<

async extendFindAllQuery(
query: SelectQueryBuilder<SourcingLocation>,
fetchSpecification: Record<string, unknown>,
): Promise<SelectQueryBuilder<SourcingLocation>> {
query
.where(`${this.alias}.scenarioInterventionId IS NULL`)
Expand All @@ -244,10 +247,16 @@ export class SourcingLocationsService extends AppBaseService<
/**
* @description Returns a hardcoded list of all location types supported by the platform
*/
getAllSupportedLocationTypes(): any {
getAllSupportedLocationTypes(options: { sort?: 'ASC' | 'DESC' }): any {
const locationTypes: { locationType: string }[] = Object.values(
LOCATION_TYPES,
).map((locationType: string) => ({ locationType }));
)
.map((locationType: string) => ({ locationType }))
.sort((a: { locationType: string }, b: { locationType: string }) => {
const comparison: number =
a.locationType.toLowerCase() < b.locationType.toLowerCase() ? -1 : 1;
return options.sort === 'DESC' ? comparison * -1 : comparison;
});

return { data: locationTypeParser(locationTypes) };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ export class GetSupplierTreeWithOptions {
@Type(() => String)
locationTypes?: LOCATION_TYPES[];

@ApiPropertyOptional()
@ApiPropertyOptional({
description: 'Array of Scenario Ids to include in the supplier search',
})
@IsOptional()
@IsUUID('4')
scenarioId?: string;
Expand Down
14 changes: 11 additions & 3 deletions api/test/e2e/impact/impact-table/impact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1057,10 +1057,18 @@ describe('Impact Table and Charts test suite (e2e)', () => {
.expect(HttpStatus.OK);

const impactTable = response.body.data.impactTable[0];
expect(impactTable.rows).toEqualArrayUnordered(
groupByLocationTypeResponseData.rows,
// expect(impactTable.rows.sort()).toEqualArrayUnordered(
// groupByLocationTypeResponseData.rows,
// );
// expect(impactTable.yearSum).toEqualArrayUnordered(
// groupByLocationTypeResponseData.yearSum,
// );
expect(impactTable.rows).toEqual(
groupByLocationTypeResponseData.rows.sort((a: any, b: any) =>
a.name > b.name ? 1 : b.name > a.name ? -1 : 0,
),
);
expect(impactTable.yearSum).toEqualArrayUnordered(
expect(impactTable.yearSum).toEqual(
groupByLocationTypeResponseData.yearSum,
);
});
Expand Down
Loading