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

feat: enable search with multiple types and statuses (MAPCO-4427, MAPCO-4430) #41

Merged
merged 6 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
58 changes: 58 additions & 0 deletions openapi3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/errorMessage'
/jobs/find:
post:
operationId: findJobsByCriteria
summary: gets jobs by criteria
tags:
- jobs
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/getJobsByCriteria'
responses:
'200':
description: Array of jobs
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/jobResponse'
/jobs/{jobId}:
parameters:
- $ref: '#/components/parameters/jobId'
Expand Down Expand Up @@ -668,6 +689,43 @@ components:
type: string
format: uuid
schemas:
getJobsByCriteria:
type: object
description: get jobs criteria
properties:
resourceId:
type: string
version:
type: string
isCleaned:
type: boolean
status:
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
type: array
items:
$ref: '#/components/schemas/status'
type:
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
type: array
items:
type: string
shouldReturnTasks:
type: boolean
default: false
fromDate:
type: string
format: date-time
tillDate:
type: string
format: date-time
productType:
type: string
internalId:
type: string
format: uuid
domain:
type: string
shouldReturnAvailableActions:
type: boolean
default: false
findTaskRequest:
type: object
description: task find model
Expand Down
39 changes: 39 additions & 0 deletions src/DAL/repositories/jobRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
IFindJobsRequest,
IUpdateJobRequest,
IJobsQuery,
IFindJobsByCriteriaBody,
} from '../../common/dataModels/jobs';
import { JobModelConvertor } from '../convertors/jobModelConverter';
import { OperationStatus } from '../../common/dataModels/enums';
Expand Down Expand Up @@ -69,6 +70,44 @@ export class JobRepository extends GeneralRepository<JobEntity> {
return models;
}

public async findJobsByCriteria(req: IFindJobsByCriteriaBody): Promise<FindJobsResponse> {
const filter: Record<string, unknown> = {
resourceId: req.resourceId,
version: req.version,
isCleaned: req.isCleaned,
productType: req.productType,
internalId: req.internalId,
domain: req.domain,
};

if (req.fromDate != undefined && req.tillDate != undefined) {
filter.updateTime = Between(req.fromDate, req.tillDate);
} else if (req.tillDate != undefined) {
filter.updateTime = LessThanOrEqual(req.tillDate);
} else if (req.fromDate != undefined) {
filter.updateTime = MoreThanOrEqual(req.fromDate);
}

for (const key of Object.keys(filter)) {
if (filter[key] == undefined) {
delete filter[key];
}
}
if (req.shouldReturnTasks !== false) {
filter.relations = ['tasks'];
}
const queryBuilder = this.createQueryBuilder().select('job').from(JobEntity, 'job').where(filter);
if ((req.type?.length ?? 0) > 0) {
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
queryBuilder.andWhere('job.type IN (:...types)', { types: req.type });
}
if ((req.status?.length ?? 0) > 0) {
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
queryBuilder.andWhere('job.status IN (:...statuses)', { statuses: req.status });
}
const entities = await queryBuilder.getMany();
const models = entities.map((entity) => this.jobConvertor.entityToModel(entity));
return models;
}

public async getJobByJobParameters(parameters: JobParameters): Promise<FindJobsResponse> {
this.appLogger.info({ parameters }, 'Getting jobs by jobs parameters');
try {
Expand Down
15 changes: 15 additions & 0 deletions src/common/dataModels/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ export interface IFindJobsRequest {
domain?: string;
}

export interface IFindJobsByCriteriaBody {
resourceId?: string;
version?: string;
isCleaned?: boolean;
status?: [OperationStatus];
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
type?: [string];
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
shouldReturnTasks?: boolean;
shouldReturnAvailableActions?: boolean;
productType?: string;
fromDate?: string;
tillDate?: string;
internalId?: string;
domain?: string;
}

export interface ICreateJobBody {
resourceId: string;
version: string;
Expand Down
11 changes: 11 additions & 0 deletions src/jobs/controllers/jobController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
FindJobsResponse,
ICreateJobBody,
ICreateJobResponse,
IFindJobsByCriteriaBody,
IFindJobsRequest,
IGetJobResponse,
IIsResettableResponse,
Expand All @@ -22,6 +23,7 @@ import { JobParameters } from '../../DAL/repositories/jobRepository';

type CreateResourceHandler = RequestHandler<undefined, ICreateJobResponse, ICreateJobBody>;
type FindResourceHandler = RequestHandler<undefined, FindJobsResponse, undefined, IFindJobsRequest>;
type FindResourceByCriteriaHandler = RequestHandler<undefined, FindJobsResponse, IFindJobsByCriteriaBody>;
type GetJobsByJobsParametersHandler = RequestHandler<undefined, FindJobsResponse, undefined, JobParameters>;
type GetResourceHandler = RequestHandler<IJobsParams, IGetJobResponse, undefined, IJobsQuery>;
type DeleteResourceHandler = RequestHandler<IJobsParams, DefaultResponse>;
Expand Down Expand Up @@ -50,6 +52,15 @@ export class JobController {
}
};

public findResourceByCriteria: FindResourceByCriteriaHandler = async (req, res, next) => {
try {
const jobsRes = await this.manager.findJobsByCriteria(req.body);
return res.status(httpStatus.OK).json(jobsRes);
} catch (err) {
return next(err);
}
};

public getJobByJobsParameters: GetJobsByJobsParametersHandler = async (req, res, next) => {
try {
const jobsRes = await this.manager.getJobsByJobsParameters(req.query);
Expand Down
19 changes: 19 additions & 0 deletions src/jobs/models/jobManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
IIsResettableResponse,
IResetJobRequest,
IAvailableActions,
IFindJobsByCriteriaBody,
} from '../../common/dataModels/jobs';
import { JobParameters, JobRepository } from '../../DAL/repositories/jobRepository';
import { TransactionActions } from '../../DAL/repositories/transactionActions';
Expand Down Expand Up @@ -51,6 +52,24 @@ export class JobManager {
return res;
}

@withSpanAsyncV4
public async findJobsByCriteria(req: IFindJobsByCriteriaBody): Promise<FindJobsResponse> {
const repo = await this.getRepository();
let res = await repo.findJobsByCriteria(req);

if (req.shouldReturnAvailableActions === true) {
if (res.length !== 0) {
res = await Promise.all(
res.map(async (job) => ({
...job,
availableActions: await this.getAvailableActions(job),
}))
);
}
}
return res;
}

@withSpanAsyncV4
public async getJobsByJobsParameters(req: JobParameters): Promise<FindJobsResponse> {
const repo = await this.getRepository();
Expand Down
1 change: 1 addition & 0 deletions src/jobs/routes/jobRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const jobRouterFactory: FactoryFunction<Router> = (dependencyContainer) => {
const tasksController = dependencyContainer.resolve(TaskController);

router.get('/', jobsController.findResource);
router.post('/find', jobsController.findResourceByCriteria);
router.post('/', jobsController.createResource);
router.get('/parameters', jobsController.getJobByJobsParameters);
router.get('/:jobId', jobsController.getResource);
Expand Down
1 change: 1 addition & 0 deletions tests/configurations/unit/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'!**/controllers/**',
'!**/routes/**',
'!<rootDir>/src/*',
'!<rootDir>/src/DAL/*',
],
coverageDirectory: '<rootDir>/coverage',
reporters: [
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/jobs/helpers/jobsRequestSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class JobsRequestSender {
return supertest.agent(this.app).post(`/jobs`).set('Content-Type', 'application/json').send(body);
}

public async findJobs(body: Record<string, unknown>): Promise<supertest.Response> {
return supertest.agent(this.app).post(`/jobs/find`).set('Content-Type', 'application/json').send(body);
}

public async deleteResource(id: string): Promise<supertest.Response> {
return supertest.agent(this.app).delete(`/jobs/${id}`).set('Content-Type', 'application/json');
}
Expand Down
61 changes: 61 additions & 0 deletions tests/integration/jobs/jobs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,67 @@ describe('job', function () {
});
});

describe('findJobsByCriteria', () => {
it('should get all jobs and return 200', async function () {
const filter = {
isCleaned: true,
resourceId: '1',
status: ['Pending'],
type: ['2'],
version: '3',
};
const jobModel = createJobDataForFind();
const jobEntity = jobModelToEntity(jobModel);
const select = jobRepositoryMocks.queryBuilder.select;
const from = jobRepositoryMocks.queryBuilder.from;
const where = jobRepositoryMocks.queryBuilder.where;
const andWhere = jobRepositoryMocks.queryBuilder.andWhere;
const getMany = jobRepositoryMocks.queryBuilder.getMany;
getMany.mockResolvedValue([jobEntity]);

const response = await requestSender.findJobs(filter);

expect(response.status).toBe(httpStatusCodes.OK);
expect(select).toHaveBeenCalledTimes(1);
expect(from).toHaveBeenCalledTimes(1);
expect(where).toHaveBeenCalledTimes(1);
expect(andWhere).toHaveBeenCalledTimes(2);
expect(getMany).toHaveBeenCalledTimes(1);

const jobs = response.body as unknown;
expect(jobs).toEqual([jobModel]);
expect(response).toSatisfyApiSpec();
});

it('should no jobs and return 200', async function () {
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
const filter = {
isCleaned: true,
resourceId: '1',
status: ['Pending'],
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
type: ['2'],
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
version: '3',
};

const select = jobRepositoryMocks.queryBuilder.select;
const from = jobRepositoryMocks.queryBuilder.from;
const where = jobRepositoryMocks.queryBuilder.where;
const andWhere = jobRepositoryMocks.queryBuilder.andWhere;
const getMany = jobRepositoryMocks.queryBuilder.getMany;
getMany.mockResolvedValue([]);

const response = await requestSender.findJobs(filter);

expect(response.status).toBe(httpStatusCodes.OK);
CL-SHLOMIKONCHA marked this conversation as resolved.
Show resolved Hide resolved
expect(select).toHaveBeenCalledTimes(1);
expect(from).toHaveBeenCalledTimes(1);
expect(where).toHaveBeenCalledTimes(1);
expect(andWhere).toHaveBeenCalledTimes(2);
expect(getMany).toHaveBeenCalledTimes(1);

expect(response).toSatisfyApiSpec();
});
});

describe('getJob', () => {
it('should get specific job and return 200', async function () {
const jobModel = createJobDataForGetJob();
Expand Down
Loading