-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from wri/feat/TM-1531-delayed-job-with-data
[TM-1531] delayed job with data
- Loading branch information
Showing
7 changed files
with
348 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,114 @@ | ||
import { Controller, Get, NotFoundException, Param, UnauthorizedException } from '@nestjs/common'; | ||
import { ApiException } from '@nanogiants/nestjs-swagger-api-exception-decorator'; | ||
import { ApiOperation } from '@nestjs/swagger'; | ||
import { JsonApiResponse } from '@terramatch-microservices/common/decorators'; | ||
import { | ||
buildJsonApi, | ||
JsonApiDocument, | ||
} from '@terramatch-microservices/common/util'; | ||
import { DelayedJobDto } from './dto/delayed-job.dto'; | ||
import { DelayedJob } from '@terramatch-microservices/database/entities'; | ||
Controller, | ||
Get, | ||
NotFoundException, | ||
Param, | ||
UnauthorizedException, | ||
Request, | ||
Patch, | ||
BadRequestException, | ||
Body, | ||
Logger | ||
} from "@nestjs/common"; | ||
import { ApiException } from "@nanogiants/nestjs-swagger-api-exception-decorator"; | ||
import { ApiOperation } from "@nestjs/swagger"; | ||
import { Op } from "sequelize"; | ||
import { JsonApiResponse } from "@terramatch-microservices/common/decorators"; | ||
import { buildJsonApi, JsonApiDocument } from "@terramatch-microservices/common/util"; | ||
import { DelayedJobDto } from "./dto/delayed-job.dto"; | ||
import { DelayedJob } from "@terramatch-microservices/database/entities"; | ||
import { DelayedJobBulkUpdateBodyDto } from "./dto/delayed-job-update.dto"; | ||
|
||
@Controller('jobs/v3/delayedJobs') | ||
@Controller("jobs/v3/delayedJobs") | ||
export class DelayedJobsController { | ||
@Get(':uuid') | ||
@Get() | ||
@ApiOperation({ | ||
operationId: 'delayedJobsFind', | ||
description: 'Get the current status and potentially payload or error from a delayed job.', | ||
operationId: "listDelayedJobs", | ||
description: "Retrieve a list of all delayed jobs." | ||
}) | ||
@JsonApiResponse({ data: { type: DelayedJobDto } }) | ||
@ApiException(() => UnauthorizedException, { | ||
description: 'Authentication failed.', | ||
description: "Authentication failed." | ||
}) | ||
async getRunningJobs(@Request() { authenticatedUserId }): Promise<JsonApiDocument> { | ||
const runningJobs = await DelayedJob.findAll({ | ||
where: { | ||
isAcknowledged: false, | ||
createdBy: authenticatedUserId | ||
}, | ||
order: [["createdAt", "DESC"]] | ||
}); | ||
|
||
const document = buildJsonApi(); | ||
runningJobs.forEach(job => { | ||
document.addData(job.uuid, new DelayedJobDto(job)); | ||
}); | ||
return document.serialize(); | ||
} | ||
|
||
@Get(":uuid") | ||
@ApiOperation({ | ||
operationId: "delayedJobsFind", | ||
description: "Get the current status and potentially payload or error from a delayed job." | ||
}) | ||
@JsonApiResponse({ data: { type: DelayedJobDto } }) | ||
@ApiException(() => UnauthorizedException, { | ||
description: "Authentication failed." | ||
}) | ||
@ApiException(() => NotFoundException, { | ||
description: 'Job with that UUID not found.' | ||
description: "Job with that UUID not found." | ||
}) | ||
async findOne(@Param('uuid') pathUUID: string): Promise<JsonApiDocument> { | ||
const job = await DelayedJob.findOne({ where: { uuid: pathUUID }}); | ||
// Note: Since jobs are very generic and we don't track which resources are related to a given | ||
// job, there is no effective way to make a policy for jobs until we expand the service to | ||
// include an owner ID on the job table. | ||
async findOne(@Param("uuid") pathUUID: string): Promise<JsonApiDocument> { | ||
const job = await DelayedJob.findOne({ where: { uuid: pathUUID } }); | ||
if (job == null) throw new NotFoundException(); | ||
|
||
// Note: Since jobs are very generic and we don't track which resources are related to a given | ||
// job, there is no effective way to make a policy for jobs until we expand the service to | ||
// include an owner ID on the job table. | ||
return buildJsonApi().addData(pathUUID, new DelayedJobDto(job)).document.serialize(); | ||
} | ||
|
||
@Patch("bulk-update") | ||
@ApiOperation({ | ||
operationId: "bulkUpdateJobs", | ||
summary: "Bulk update jobs to modify isAcknowledged for specified job IDs", | ||
description: `Accepts a JSON:API-compliant payload to bulk update jobs, allowing each job's isAcknowledged attribute to be set to true or false.` | ||
}) | ||
@JsonApiResponse({ data: { type: DelayedJobDto } }) | ||
@ApiException(() => UnauthorizedException, { description: "Authentication failed." }) | ||
@ApiException(() => BadRequestException, { description: "Invalid payload or IDs provided." }) | ||
@ApiException(() => NotFoundException, { | ||
description: "One or more jobs specified in the payload could not be found." | ||
}) | ||
async bulkUpdateJobs( | ||
@Body() bulkUpdateJobsDto: DelayedJobBulkUpdateBodyDto, | ||
@Request() { authenticatedUserId } | ||
): Promise<JsonApiDocument> { | ||
const jobUpdates = bulkUpdateJobsDto.data; | ||
const jobs = await DelayedJob.findAll({ | ||
where: { | ||
uuid: { [Op.in]: jobUpdates.map(({ uuid }) => uuid) }, | ||
createdBy: authenticatedUserId, | ||
status: { [Op.ne]: "pending" } | ||
} | ||
}); | ||
if (jobs.length !== jobUpdates.length) { | ||
throw new NotFoundException("Some jobs in the request could not be updated"); | ||
} | ||
|
||
const updatePromises = jobUpdates.map(async ({ uuid, attributes }) => { | ||
const job = jobs.find(job => job.uuid === uuid); | ||
job.isAcknowledged = attributes.isAcknowledged; | ||
await job.save(); | ||
return job; | ||
}); | ||
|
||
const updatedJobs = await Promise.all(updatePromises); | ||
|
||
return buildJsonApi() | ||
.addData(pathUUID, new DelayedJobDto(job)) | ||
.document.serialize(); | ||
const jsonApiBuilder = buildJsonApi(); | ||
updatedJobs.forEach(job => { | ||
jsonApiBuilder.addData(job.uuid, new DelayedJobDto(job)); | ||
}); | ||
return jsonApiBuilder.serialize(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsArray, IsBoolean, IsUUID, ValidateNested } from 'class-validator'; | ||
import { Type } from 'class-transformer'; | ||
|
||
export class DelayedJobAttributes { | ||
@IsBoolean() | ||
@ApiProperty({ description: 'Value to set for isAcknowledged', example: true }) | ||
isAcknowledged: boolean; | ||
} | ||
|
||
export class DelayedJobData { | ||
@ApiProperty({ enum: ['delayedJobs'], description: 'Type of the resource', example: 'delayedJobs' }) | ||
type: 'delayedJobs'; | ||
|
||
@IsUUID() | ||
@ApiProperty({ format: 'uuid', description: 'UUID of the job', example: 'uuid-1' }) | ||
uuid: string; | ||
|
||
@ValidateNested() | ||
@Type(() => DelayedJobAttributes) | ||
@ApiProperty({ description: 'Attributes to update for the job', type: DelayedJobAttributes }) | ||
attributes: DelayedJobAttributes; | ||
} | ||
|
||
export class DelayedJobBulkUpdateBodyDto { | ||
@IsArray() | ||
@ValidateNested({ each: true }) | ||
@Type(() => DelayedJobData) | ||
@ApiProperty({ description: 'List of jobs to update isAcknowledged', type: [DelayedJobData] }) | ||
data: DelayedJobData[]; | ||
} |
Oops, something went wrong.