Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
5caa36f
chore: setup new schema definitions for enums and jobs table
matzduniuk Jun 24, 2026
dd6dd6f
chore: add basic structure for new background jobs service, module an…
matzduniuk Jun 24, 2026
033f9ee
chore: add new migration file
matzduniuk Jun 24, 2026
4647fa4
chore: setup background jobs DTOs classes
matzduniuk Jun 24, 2026
a414a2d
chore: add new permission policies for new jobs service
matzduniuk Jun 24, 2026
279a13a
chore: implement the background jobs service
matzduniuk Jun 24, 2026
35d8fec
chore: implement the jobs controller
matzduniuk Jun 24, 2026
c13923e
fix: add missing await statements
matzduniuk Jun 24, 2026
2715a08
chore: update background jobs module to include new imports
matzduniuk Jun 24, 2026
c6862ab
chore: update prisma schema linting
matzduniuk Jun 24, 2026
e63f353
chore: add background job module to main App module
matzduniuk Jun 24, 2026
042f26d
fix: add missing await
matzduniuk Jun 24, 2026
11d74c5
fix: fix final bugs in the job service implementation
matzduniuk Jun 24, 2026
e1236de
chore: add background jobs service integration tests
matzduniuk Jun 24, 2026
e7f6f11
fix: fix import paths
matzduniuk Jun 24, 2026
a25bfc8
fix: fix endpoint param name typo
matzduniuk Jun 24, 2026
a8dc082
chore: add background jobs controller e2e tests
matzduniuk Jun 24, 2026
3928ed2
chore: update table index to include the status column
matzduniuk Jun 24, 2026
dc2cd4e
fix: add missing null return type
matzduniuk Jun 24, 2026
2e5de29
chore: remove unnecessary guard
matzduniuk Jun 24, 2026
14d4aed
fix: remove unnecessary prisma result check
matzduniuk Jun 24, 2026
0c225cb
fix: fix e2e test
matzduniuk Jun 24, 2026
f919f00
chore: simplify the background jib fetch
matzduniuk Jun 24, 2026
225ca62
fix: remove unnecessary import
matzduniuk Jun 24, 2026
4ccef64
fix: add missing DTO validation pipes
matzduniuk Jun 24, 2026
56905d4
fix: update failing test
matzduniuk Jun 24, 2026
7ab04bb
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jun 24, 2026
93191a5
chore: update backend swagger types
matzduniuk Jun 24, 2026
e4b62ae
Merge branch '6384/bulk-csv-background-jobs' of https://github.com/bl…
matzduniuk Jun 24, 2026
49922be
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jun 24, 2026
7b9e156
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 6, 2026
9b016b6
fix: update typo in schema mapped name
matzduniuk Jul 6, 2026
25be74a
fix: add missing default UUIDv4 generation
matzduniuk Jul 6, 2026
0045109
fix: add permission service checks to all background job service clas…
matzduniuk Jul 6, 2026
5993ab7
fix: optimize active job serach prisma query by limiting columns number
matzduniuk Jul 6, 2026
457a6ec
fix: add validation pipes to query and param based id strings
matzduniuk Jul 6, 2026
dfac35d
fix: update background jobs service integration tests
matzduniuk Jul 6, 2026
6821cde
fix: fix linting error
matzduniuk Jul 6, 2026
596918d
fix: update the e2e tests
matzduniuk Jul 6, 2026
091ec7d
Revert "fix: update the e2e tests"
matzduniuk Jul 6, 2026
f9ace09
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 6, 2026
a5d2b9e
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 9, 2026
491fa94
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 13, 2026
1a902bd
fix; update to relative paths
matzduniuk Jul 13, 2026
dcfe4d8
fix: move over from prisma findOrThrow method
matzduniuk Jul 13, 2026
70993df
chore: add new endpoint for getting the background jobs status
matzduniuk Jul 13, 2026
4d7fc55
fix: remove the passkey related tests from suite
matzduniuk Jul 13, 2026
19f3a40
chore: add new e2e tests for the new endpoint
matzduniuk Jul 13, 2026
645ff53
fix: remove unnecessary async in tests
matzduniuk Jul 13, 2026
cf222e7
fix: update failing tests
matzduniuk Jul 13, 2026
af0d659
fix: generate client
matzduniuk Jul 13, 2026
f2d7a94
fix: reorder controller endpoint handlers
matzduniuk Jul 13, 2026
7841bcd
fix: update the endpoint response to a successDTO
matzduniuk Jul 13, 2026
65d783b
fix: update e2e tests
matzduniuk Jul 13, 2026
c11183b
fix: update background jobs service tests
matzduniuk Jul 13, 2026
ec37121
fix: change global imports to relative
matzduniuk Jul 13, 2026
49c5939
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 13, 2026
6cc5c3a
fix: update new response body test validation method
matzduniuk Jul 13, 2026
02b69c0
Merge branch '6384/bulk-csv-background-jobs' of https://github.com/bl…
matzduniuk Jul 13, 2026
170fa69
chore: update the background jobs to have a reference to user accounts
matzduniuk Jul 14, 2026
d58b5df
chore: update endpoint to return an array of active jobs for listing
matzduniuk Jul 14, 2026
f648465
fix: fix failing service tests
matzduniuk Jul 14, 2026
e41398f
fix: update controller e2e tests
matzduniuk Jul 14, 2026
a59c77e
Merge branch 'main' into 6384/bulk-csv-background-jobs
matzduniuk Jul 16, 2026
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
25 changes: 25 additions & 0 deletions api/prisma/migrations/63_add_background_jobs_table/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- CreateEnum
CREATE TYPE "BackgroundJobStatusEnum" AS ENUM ('processing', 'completed', 'failed');

-- CreateTable
CREATE TABLE "background_job" (
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
"listing_id" UUID NOT NULL,
"requested_by_user_id" UUID NOT NULL,
Comment thread
matzduniuk marked this conversation as resolved.
"status" "BackgroundJobStatusEnum" NOT NULL,
"total_records" INTEGER,
"input_s3_key" TEXT NOT NULL,
"error_message" TEXT,
"error_row" INTEGER,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"completed_at" TIMESTAMP(3),

CONSTRAINT "background_job_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "background_job_listing_id_status_idx" ON "background_job"("listing_id", "status");

-- AddForeignKey
ALTER TABLE "background_job" ADD CONSTRAINT "background_job_requested_by_user_id_fkey" FOREIGN KEY ("requested_by_user_id") REFERENCES "user_accounts"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
221 changes: 123 additions & 98 deletions api/prisma/schema.prisma

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions api/src/controllers/background-jobs.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
Body,
Controller,
Get,
Param,
ParseUUIDPipe,
Post,
Query,
Request,
UseGuards,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import {
ApiExtraModels,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { Request as ExpressRequest } from 'express';
import { ApiKeyGuard } from '../guards/api-key.guard';
import { BackgroundJobsService } from '../services/background-jobs.service';
import { BackgroundJobCreate } from '../dtos/background-jobs/background-job-create.dto';
import { mapTo } from '../utilities/mapTo';
import { User } from '../dtos/users/user.dto';
import { BackgroundJob } from '../dtos/background-jobs/background-job.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options';
import { SuccessDTO } from '../dtos/shared/success.dto';

@Controller('jobs')
@ApiTags('jobs')
@ApiExtraModels(BackgroundJob, BackgroundJobCreate)
@PermissionTypeDecorator('jobs')
@UseGuards(ApiKeyGuard, JwtAuthGuard)
export class BackgroundJobsController {
constructor(private readonly backgroundJobsService: BackgroundJobsService) {}

@Post()
@ApiOperation({
summary: 'Creates a new background job record in the database',
operationId: 'createBackgroundJob',
})
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@UseGuards(ApiKeyGuard)
@ApiOkResponse({ type: BackgroundJob })
public async createBackgroundJob(
@Request() req: ExpressRequest,
@Body() dto: BackgroundJobCreate,
): Promise<BackgroundJob> {
return await this.backgroundJobsService.create(
dto,
mapTo(User, req['user']),
);
}
@Get('active')
@ApiOperation({
summary: 'Get info if any jobs are currently running',
operationId: 'activeJobStatus',
})
@ApiOkResponse({ type: SuccessDTO })
public async activeJobStatus(
@Request() req: ExpressRequest,
): Promise<SuccessDTO> {
return this.backgroundJobsService.findActiveJob(mapTo(User, req['user']));
}

@Get(':jobId')
@ApiOperation({
summary: 'Get a background job data by its ID',
operationId: 'getBackgroundJob',
})
@ApiOkResponse({ type: BackgroundJob })
public async getJobById(
@Request() req: ExpressRequest,
@Param('jobId', new ParseUUIDPipe({ version: '4' })) jobId: string,
): Promise<BackgroundJob> {
return await this.backgroundJobsService.getById(
jobId,
mapTo(User, req['user']),
);
}

@Get()
@ApiOperation({
summary: 'Get an active background job for a listing',
operationId: 'findActiveJobForListing',
})
@ApiOkResponse({ type: Array<BackgroundJob> })
public async getListingActiveJob(
@Request() req: ExpressRequest,
@Query('listingId', new ParseUUIDPipe({ version: '4' })) listingId: string,
): Promise<BackgroundJob[]> {
return await this.backgroundJobsService.findActiveForListing(
listingId,
mapTo(User, req['user']),
);
}
}
19 changes: 19 additions & 0 deletions api/src/dtos/background-jobs/background-job-create.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';
import { IsDefined, IsString, IsUUID } from 'class-validator';
import { ValidationsGroupsEnum } from '../../enums/shared/validation-groups-enum';

export class BackgroundJobCreate {
@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
@IsUUID(4, { groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty()
listingId: string;

@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty()
inputS3Key: string;
}
70 changes: 70 additions & 0 deletions api/src/dtos/background-jobs/background-job.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Expose, Type } from 'class-transformer';
import {
IsDefined,
IsDate,
IsEnum,
IsInt,
IsOptional,
IsString,
IsUUID,
} from 'class-validator';
import { BackgroundJobStatusEnum } from '@prisma/client';
import { ValidationsGroupsEnum } from '../../enums/shared/validation-groups-enum';
import { AbstractDTO } from '../shared/abstract.dto';

export class BackgroundJob extends AbstractDTO {
@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
@IsUUID(4, { groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty()
listingId: string;

@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
@IsUUID(4, { groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty()
requestedByUserId: string;

@Expose()
@IsEnum(BackgroundJobStatusEnum, { groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty({
enum: BackgroundJobStatusEnum,
enumName: 'BackgroundJobStatusEnum',
})
status: BackgroundJobStatusEnum;

@Expose()
@IsOptional({ groups: [ValidationsGroupsEnum.default] })
@IsInt({ groups: [ValidationsGroupsEnum.default] })
@ApiPropertyOptional()
totalRecords?: number;

@Expose()
@IsString({ groups: [ValidationsGroupsEnum.default] })
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@ApiProperty()
inputS3Key: string;

@Expose()
@IsOptional({ groups: [ValidationsGroupsEnum.default] })
@IsString({ groups: [ValidationsGroupsEnum.default] })
@ApiPropertyOptional()
errorMessage?: string;

@Expose()
@IsOptional({ groups: [ValidationsGroupsEnum.default] })
@IsInt({ groups: [ValidationsGroupsEnum.default] })
@ApiPropertyOptional()
errorRow?: number;

@Expose()
@IsOptional({ groups: [ValidationsGroupsEnum.default] })
@IsDate({ groups: [ValidationsGroupsEnum.default] })
@Type(() => Date)
@ApiPropertyOptional()
completedAt?: Date;
}
3 changes: 3 additions & 0 deletions api/src/modules/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { ThrottlerModule } from '@nestjs/throttler';
import { UnitRentTypeModule } from './unit-rent-type.module';
import { UnitTypeModule } from './unit-type.module';
import { UserModule } from './user.module';
import { BackgroundJobsModule } from './background-jobs.module';

@Module({
imports: [
Expand All @@ -42,6 +43,7 @@ import { UserModule } from './user.module';
UserModule,
PrismaModule,
AuthModule,
BackgroundJobsModule,
ApplicationFlaggedSetModule,
MapLayerModule,
ScriptRunnerModule,
Expand Down Expand Up @@ -84,6 +86,7 @@ import { UserModule } from './user.module';
UserModule,
PrismaModule,
AuthModule,
BackgroundJobsModule,
ApplicationFlaggedSetModule,
MapLayerModule,
ScriptRunnerModule,
Expand Down
14 changes: 14 additions & 0 deletions api/src/modules/background-jobs.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma.module';
import { S3Module } from './s3.module';
import { PermissionModule } from './permission.module';
import { BackgroundJobsController } from '../controllers/background-jobs.controller';
import { BackgroundJobsService } from '../services/background-jobs.service';

@Module({
imports: [PrismaModule, S3Module, PermissionModule],
providers: [BackgroundJobsService],
controllers: [BackgroundJobsController],
exports: [BackgroundJobsService],
})
export class BackgroundJobsModule {}
5 changes: 5 additions & 0 deletions api/src/permission-configs/permission_policy.csv
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ p, jurisdictionAdmin, unitRentType, true, read
p, limitedJurisdictionAdmin, unitRentType, true, read
p, partner, unitRentType, true, read

p, admin, jobs, true, .*
p, supportAdmin, jobs, true, .*
p, jurisdictionAdmin, jobs, true, .*
p, limitedJurisdictionAdmin, jobs, true, .*
p, partner, jobs, true, .*

p, admin, applicationMethod, true, .*
p, supportAdmin, applicationMethod, true, .*
Expand Down
Loading
Loading