Skip to content

Commit

Permalink
fix(bulk-mileage): update bulk mileage (#16539)
Browse files Browse the repository at this point in the history
* fix: add xlsx

* fix: support xlsx

* feat: parse and display errors

* fix: coderabbit grilling session

* chore: prettier button

* chore: remove imports

* chore: more rabbit grilling

* chore: switch in csv-pasre

* fix: parse

* fix: handle 429

* chore: update message

* fix: use service error

* feat: fix reveiw errors

* fix: standard error handling

* fix: bunny comments

* feat: add search

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
thorkellmani and kodiakhq[bot] authored Oct 30, 2024
1 parent 3c56c86 commit 0947e07
Show file tree
Hide file tree
Showing 18 changed files with 598 additions and 249 deletions.
12 changes: 12 additions & 0 deletions libs/api/domains/vehicles/src/lib/dto/updateResponseError.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface UpdateResponseError {
type: string
title: string
status: number
Errors: Array<{
lookupNo: number
warnSever: string
errorMess: string
permno: string
warningSerialNumber: string
}>
}
3 changes: 3 additions & 0 deletions libs/api/domains/vehicles/src/lib/dto/vehiclesListInputV3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ export class VehiclesListInputV3 {

@Field()
page!: number

@Field({ nullable: true })
query?: string
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Field, ObjectType, ID } from '@nestjs/graphql'
import { Field, ObjectType, ID, Int } from '@nestjs/graphql'

@ObjectType()
export class VehiclesBulkMileageReadingResponse {
@Field(() => ID, {
nullable: true,
description:
'The GUID of the mileage registration post request. Used to fetch job status',
})
requestId!: string
requestId?: string

@Field(() => Int, { nullable: true })
errorCode?: number

@Field({ nullable: true })
errorMessage?: string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createUnionType } from '@nestjs/graphql'
import { VehicleMileageDetail } from '../getVehicleMileage.model'
import { VehiclesMileageUpdateError } from './vehicleMileageResponseError.model'

export const VehicleMileagePostResponse = createUnionType({
name: 'VehicleMileagePostResponse',
types: () => [VehicleMileageDetail, VehiclesMileageUpdateError] as const,
resolveType(value) {
if ('permno' in value && value.permno !== undefined) {
return VehicleMileageDetail
}

return VehiclesMileageUpdateError
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createUnionType } from '@nestjs/graphql'
import { VehicleMileagePutModel } from '../getVehicleMileage.model'
import { VehiclesMileageUpdateError } from './vehicleMileageResponseError.model'

export const VehicleMileagePutResponse = createUnionType({
name: 'VehicleMileagePutResponse',
types: () => [VehicleMileagePutModel, VehiclesMileageUpdateError] as const,
resolveType(value) {
if ('permno' in value && value.permno !== undefined) {
return VehicleMileagePutModel
}

return VehiclesMileageUpdateError
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Field, Int, ObjectType } from '@nestjs/graphql'
import GraphQLJSON from 'graphql-type-json'

@ObjectType()
export class VehiclesMileageUpdateError {
@Field()
message!: string

@Field(() => Int, { nullable: true })
code?: number

@Field(() => GraphQLJSON, { nullable: true })
error?: string
}
45 changes: 45 additions & 0 deletions libs/api/domains/vehicles/src/lib/resolvers/mileage.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
} from '@island.is/nest/feature-flags'
import { mileageDetailConstructor } from '../utils/helpers'
import { LOGGER_PROVIDER, type Logger } from '@island.is/logging'
import { VehicleMileagePostResponse } from '../models/v3/postVehicleMileageResponse.model'
import { VehiclesMileageUpdateError } from '../models/v3/vehicleMileageResponseError.model'
import { VehicleMileagePutResponse } from '../models/v3/putVehicleMileageResponse.model'

@UseGuards(IdsUserGuard, ScopesGuard, FeatureFlagGuard)
@FeatureFlag(Features.servicePortalVehicleMileagePageEnabled)
Expand Down Expand Up @@ -99,6 +102,48 @@ export class VehiclesMileageResolver {
return mileageDetailConstructor(res)
}

@Mutation(() => VehicleMileagePostResponse, {
name: 'vehicleMileagePostV2',
nullable: true,
})
@Audit()
async postVehicleMileageReadingV2(
@Args('input') input: PostVehicleMileageInput,
@CurrentUser() user: User,
) {
const res = await this.vehiclesService.postMileageReadingV2(user, {
...input,
mileage: Number(input.mileage ?? input.mileageNumber),
})

if (!res || res instanceof VehiclesMileageUpdateError) {
return res
}

return mileageDetailConstructor(res)
}

@Mutation(() => VehicleMileagePutResponse, {
name: 'vehicleMileagePutV2',
nullable: true,
})
@Audit()
async putVehicleMileageReadingV2(
@Args('input') input: PutVehicleMileageInput,
@CurrentUser() user: User,
) {
const res = await this.vehiclesService.putMileageReadingV2(user, {
...input,
mileage: Number(input.mileage ?? input.mileageNumber),
})

if (!res || res instanceof VehiclesMileageUpdateError) {
return res
}

return mileageDetailConstructor(res)
}

@ResolveField('canRegisterMileage', () => Boolean, {
nullable: true,
})
Expand Down
43 changes: 28 additions & 15 deletions libs/api/domains/vehicles/src/lib/services/bulkMileage.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Inject, Injectable } from '@nestjs/common'
import {
BulkMileageReadingRequestResultDto,
GetbulkmileagereadingrequeststatusGuidGetRequest,
MileageReadingApi,
} from '@island.is/clients/vehicles-mileage'
Expand All @@ -14,6 +13,7 @@ import { VehiclesBulkMileageReadingResponse } from '../models/v3/bulkMileage/bul
import { VehiclesBulkMileageRegistrationJobHistory } from '../models/v3/bulkMileage/bulkMileageRegistrationJobHistory.model'
import { VehiclesBulkMileageRegistrationRequestStatus } from '../models/v3/bulkMileage/bulkMileageRegistrationRequestStatus.model'
import { VehiclesBulkMileageRegistrationRequestOverview } from '../models/v3/bulkMileage/bulkMileageRegistrationRequestOverview.model'
import { FetchError } from '@island.is/clients/middlewares'

@Injectable()
export class BulkMileageService {
Expand All @@ -34,8 +34,10 @@ export class BulkMileageService {
return null
}

const res: BulkMileageReadingRequestResultDto =
await this.getMileageWithAuth(auth).requestbulkmileagereadingPost({
try {
const res = await this.getMileageWithAuth(
auth,
).requestbulkmileagereadingPost({
postBulkMileageReadingModel: {
originCode: input.originCode,
mileageData: input.mileageData.map((m) => ({
Expand All @@ -45,19 +47,30 @@ export class BulkMileageService {
},
})

if (!res.guid) {
this.logger.warn(
'Missing guid from bulk mileage reading registration response',
{
category: LOG_CATEGORY,
},
)
return null
}
if (!res.guid) {
this.logger.warn(
'Missing guid from bulk mileage reading registration response',
{
category: LOG_CATEGORY,
},
)
return null
}

return {
requestId: res.guid,
errorMessage: res.errorMessage ?? undefined,
return {
requestId: res.guid,
errorMessage: res.errorMessage ?? undefined,
}
} catch (e) {
const error: Error = e
if (error instanceof FetchError && error.status === 429) {
return {
requestId: undefined,
errorCode: 429,
errorMessage: error.statusText,
}
}
throw e
}
}

Expand Down
92 changes: 90 additions & 2 deletions libs/api/domains/vehicles/src/lib/services/vehicles.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
VehicleDtoListPagedResponse,
PersidnoLookupResultDto,
CurrentVehiclesWithMilageAndNextInspDtoListPagedResponse,
ApiResponse,
} from '@island.is/clients/vehicles'
import {
CanregistermileagePermnoGetRequest,
GetMileageReadingRequest,
MileageReadingApi,
MileageReadingDto,
PostMileageReadingModel,
PutMileageReadingModel,
RequiresmileageregistrationPermnoGetRequest,
RootPostRequest,
RootPutRequest,
Expand All @@ -33,17 +35,22 @@ import {
GetVehiclesForUserInput,
GetVehiclesListV2Input,
} from '../dto/getVehiclesForUserInput'
import { VehicleMileageOverview } from '../models/getVehicleMileage.model'
import {
VehicleMileageDetail,
VehicleMileageOverview,
} from '../models/getVehicleMileage.model'
import isSameDay from 'date-fns/isSameDay'
import { mileageDetailConstructor } from '../utils/helpers'
import { handle404 } from '@island.is/clients/middlewares'
import { FetchError, handle404 } from '@island.is/clients/middlewares'
import { VehicleSearchCustomDto } from '../vehicles.type'
import { operatorStatusMapper } from '../utils/operatorStatusMapper'
import { VehiclesListInputV3 } from '../dto/vehiclesListInputV3'
import { VehiclesCurrentListResponse } from '../models/v3/currentVehicleListResponse.model'
import { isDefined } from '@island.is/shared/utils'
import { GetVehicleMileageInput } from '../dto/getVehicleMileageInput'
import { MileageRegistrationHistory } from '../models/v3/mileageRegistrationHistory.model'
import { VehiclesMileageUpdateError } from '../models/v3/vehicleMileageResponseError.model'
import { UpdateResponseError } from '../dto/updateResponseError.dto'

const ORIGIN_CODE = 'ISLAND.IS'
const LOG_CATEGORY = 'vehicle-service'
Expand Down Expand Up @@ -111,6 +118,11 @@ export class VehiclesService {
showOwned: true,
page: input.page,
pageSize: input.pageSize,
permno: input.query
? input.query.length < 5
? `${input.query}*`
: `${input.query}`
: undefined,
})

if (
Expand Down Expand Up @@ -463,6 +475,82 @@ export class VehiclesService {
})
}

async postMileageReadingV2(
auth: User,
input: RootPostRequest['postMileageReadingModel'],
): Promise<PostMileageReadingModel | VehiclesMileageUpdateError | null> {
if (!input) return null

const isAllowed = await this.isAllowedMileageRegistration(
auth,
input.permno,
)
if (!isAllowed) {
this.logger.error(UNAUTHORIZED_OWNERSHIP_LOG, {
category: LOG_CATEGORY,
error: 'postMileageReading failed',
})
throw new ForbiddenException(UNAUTHORIZED_OWNERSHIP_LOG)
}

try {
const res = await this.getMileageWithAuth(auth).rootPostRaw({
postMileageReadingModel: input,
})

if (res.raw.status === 200) {
this.logger.info(
'Tried to post already existing mileage reading. Should use PUT',
)
return null
}

const value = await res.value()
return value
} catch (e) {
if (e instanceof FetchError && (e.status === 400 || e.status === 429)) {
const errorBody = e.body as UpdateResponseError
return {
code: e.status,
message: errorBody.Errors?.[0]?.errorMess || 'Unknown error',
}
} else throw e
}
}

async putMileageReadingV2(
auth: User,
input: RootPutRequest['putMileageReadingModel'],
): Promise<MileageReadingDto | VehiclesMileageUpdateError | null> {
if (!input) return null

const isAllowed = await this.isAllowedMileageRegistration(
auth,
input.permno,
)
if (!isAllowed) {
this.logger.error(UNAUTHORIZED_OWNERSHIP_LOG, {
category: LOG_CATEGORY,
error: 'putMileageReading failed',
})
throw new ForbiddenException(UNAUTHORIZED_OWNERSHIP_LOG)
}

try {
return this.getMileageWithAuth(auth).rootPut({
putMileageReadingModel: input,
})
} catch (e) {
if (e instanceof FetchError && (e.status === 400 || e.status === 429)) {
const errorBody = e.body as UpdateResponseError
return {
code: e.status,
message: errorBody.Errors?.[0]?.errorMess || 'Unknown error',
}
} else throw e
}
}

async canRegisterMileage(
auth: User,
input: CanregistermileagePermnoGetRequest,
Expand Down
17 changes: 15 additions & 2 deletions libs/service-portal/assets/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -874,14 +874,23 @@ export const vehicleMessage = defineMessages({
id: 'sp.vehicles:mileage-errors-input-too-low',
defaultMessage: 'Verður að vera hærri en síðasta staðfesta skráning',
},
mileageInputPositive: {
id: 'sp.vehicles:mileage-errors-min-value',
defaultMessage: 'Skráning þarf að vera að minnsta kosti 1 km',
},
mileageInputMinLength: {
id: 'sp.vehicles:mileage-errors-min-length',
defaultMessage: 'Skrá verður inn kílómetrastöðu til að vista',
defaultMessage: 'Skrá þarf einhverja kílómetrastöðu',
},
mileageSuccessFormTitle: {
id: 'sp.vehicles:mileage-success-form-title',
defaultMessage: 'Kílómetrastaða skráð',
},
mileageUploadTooManyRequests: {
id: 'sp.vehicles:mileage-error-too-many-request',
defaultMessage:
'Of margar upphleðslur á stuttum tíma. Vinsamlegast hinkraðu um stund.',
},
mileageSuccessFormText: {
id: 'sp.vehicles:mileage-success-form-text',
defaultMessage:
Expand Down Expand Up @@ -972,6 +981,10 @@ export const vehicleMessage = defineMessages({
id: 'sp.vehicles:upload-failed',
defaultMessage: 'Upphleðsla mistókst',
},
wrongFileType: {
id: 'sp.vehicles:wrong-file-type',
defaultMessage: 'Vitlaus skráartýpa. Skrá verður að vera .csv eða .xslx',
},
errorWhileProcessing: {
id: 'sp.vehicles:error-while-processing',
defaultMessage: 'Villa við að meðhöndla skjal. Villur: ',
Expand Down Expand Up @@ -1026,7 +1039,7 @@ export const vehicleMessage = defineMessages({
},
fileUploadAcceptedTypes: {
id: 'sp.vehicles:file-upload-accepted-types',
defaultMessage: 'Tekið er við skjölum með endingu; .csv',
defaultMessage: 'Tekið er við skjölum með endingu; .csv, .xlsx',
},
dataAboutJob: {
id: 'sp.vehicles:data-about-job',
Expand Down
Loading

0 comments on commit 0947e07

Please sign in to comment.