Skip to content

Commit

Permalink
merge with release-jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
despadam committed Oct 15, 2024
2 parents ad44254 + 53d8124 commit 80b6698
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 50 deletions.
28 changes: 28 additions & 0 deletions src/jobs/interfaces/job-filters.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
interface IDateRange {
begin: string;
end: string;
}

interface IJobFieldObject {
$regex: string;
$options: string;
}

interface IJobIdsFieldObject {
$in: string[];
}

export interface IJobFields {
mode?: Record<string, unknown>;
text?: string;
createdAt?: IDateRange;
id?: IJobFieldObject;
_id?: IJobIdsFieldObject;
type?: IJobFieldObject;
statusCode?: IJobFieldObject;
jobParams?: IJobFieldObject;
jobResultObject?: IJobFieldObject;
ownerUser?: IJobFieldObject;
ownerGroup?: string[];
accessGroups?: string[];
}
83 changes: 42 additions & 41 deletions src/jobs/jobs.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
} from "src/common/utils";
import { JobAction } from "./config/jobconfig";
import { JobType, DatasetState, JobParams } from "./types/job-types.enum";

Check failure on line 54 in src/jobs/jobs.controller.ts

View workflow job for this annotation

GitHub Actions / eslint

Delete `·`
import { IJobFields } from "./interfaces/job-filters.interface";

@ApiBearerAuth()
@ApiTags("jobs")
Expand Down Expand Up @@ -583,8 +584,6 @@ export class JobsController {

async performJobStatusUpdateAction(jobInstance: JobClass): Promise<void> {
const jobConfig = this.getJobTypeConfiguration(jobInstance.type);

// TODO - what shall we do when configVersion does not match?
if (jobConfig.configVersion !== jobInstance.configVersion) {
Logger.log(
`
Expand All @@ -594,7 +593,6 @@ export class JobsController {
"JobStatusUpdate",
);
}

for (const action of jobConfig.statusUpdate.actions) {
await this.performJobAction(jobInstance, action);
}
Expand Down Expand Up @@ -695,7 +693,6 @@ export class JobsController {
if (!canUpdateStatus) {
throw new ForbiddenException("Unauthorized to update this job.");
}

// Update job in database
const updatedJob = await this.jobsService.statusUpdate(
id,
Expand Down Expand Up @@ -769,10 +766,10 @@ export class JobsController {
const jobInstance = await this.generateJobInstanceForPermissions(
jobsFound[i],
);
const canCreate =
const canRead =
ability.can(Action.JobReadAny, JobClass) ||
ability.can(Action.JobReadAccess, jobInstance);
if (canCreate) {
if (canRead) {
jobsAccessible.push(jobsFound[i]);
}
}
Expand All @@ -799,11 +796,8 @@ export class JobsController {
@Get("/fullfacet")
@ApiOperation({
summary: "It returns a list of job facets matching the filter provided.",
description: `
This endpoint was added for completeness reasons,
so that the frontend can work with the new backend version.
For now, it always returns an empty array.
`,
description:
"It returns a list of job facets matching the filter provided.",
})
@ApiQuery({
name: "fields",
Expand All @@ -827,36 +821,44 @@ export class JobsController {
async fullFacet(
@Req() request: Request,
@Query() filters: { fields?: string; facets?: string },
): Promise<JobClass[]> {
): Promise<Record<string, unknown>[]> {
try {
// const parsedFilters: IFacets<FilterQuery<JobDocument>> = {
// fields: JSON.parse(filters.fields ?? "{}" as string),
// facets: JSON.parse(filters.facets ?? "[]" as string),
// };
// const jobsFound = await this.jobsService.fullfacet(parsedFilters);
const jobsAccessible: JobClass[] = [];
const fields: IJobFields = JSON.parse(filters.fields ?? ("{}" as string));
const queryFilters: IFilters<JobDocument, FilterQuery<JobDocument>> = {
fields: fields,
limits: JSON.parse("{}" as string),
};
const jobsFound = await this.jobsService.fullquery(queryFilters);
const jobIdsAccessible: string[] = [];

// for each job run a casl JobReadOwner on a jobInstance
// for (const i in jobsFound) {
// const jobConfiguration = this.getJobTypeConfiguration(
// jobsFound[i].type,
// );
// const ability = this.caslAbilityFactory.jobsInstanceAccess(
// request.user as JWTUser,
// jobConfiguration,
// );
// // check if the user can get this job
// const jobInstance = await this.generateJobInstanceForPermissions(
// jobsFound[i],
// );
// const canCreate =
// ability.can(Action.JobReadAny, JobClass) ||
// ability.can(Action.JobReadAccess, jobInstance);
// if (canCreate) {
// jobsAccessible.push(jobsFound[i]);
// }
// }
return jobsAccessible;
if (jobsFound != null) {
for (const i in jobsFound) {
const jobConfiguration = this.getJobTypeConfiguration(
jobsFound[i].type,
);
const ability = this.caslAbilityFactory.jobsInstanceAccess(
request.user as JWTUser,
jobConfiguration,
);
// check if the user can get this job
const jobInstance = await this.generateJobInstanceForPermissions(
jobsFound[i],
);
const canRead =
ability.can(Action.JobReadAny, JobClass) ||
ability.can(Action.JobReadAccess, jobInstance);
if (canRead) {
jobIdsAccessible.push(jobsFound[i]._id);
}
}
}
fields._id = { $in: jobIdsAccessible };
const facetFilters: IFacets<IJobFields> = {
fields: fields,
facets: JSON.parse(filters.facets ?? ("[]" as string)),
};
return await this.jobsService.fullfacet(facetFilters);
} catch (e) {
throw new HttpException(
{
Expand Down Expand Up @@ -974,10 +976,10 @@ export class JobsController {
const jobInstance = await this.generateJobInstanceForPermissions(
jobsFound[i],
);
const canCreate =
const canRead =
ability.can(Action.JobReadAny, JobClass) ||
ability.can(Action.JobReadAccess, jobInstance);
if (canCreate) {
if (canRead) {
jobsAccessible.push(jobsFound[i]);
}
}
Expand Down Expand Up @@ -1024,7 +1026,6 @@ export class JobsController {
HttpStatus.BAD_REQUEST,
);
}

Logger.log(`Deleting job with id ${id}!`);
return this.jobsService.remove({ _id: id });
}
Expand Down
7 changes: 4 additions & 3 deletions src/jobs/jobs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { CreateJobDto } from "./dto/create-job.dto";
import { StatusUpdateJobDto } from "./dto/status-update-job.dto";
import { JobClass, JobDocument } from "./schemas/job.schema";
import { IJobFields } from "./interfaces/job-filters.interface";

@Injectable({ scope: Scope.REQUEST })
export class JobsService {
Expand Down Expand Up @@ -76,14 +77,14 @@ export class JobsService {
}

async fullfacet(
filters: IFacets<FilterQuery<JobDocument>>,
): Promise<JobClass[]> {
filters: IFacets<IJobFields>,
): Promise<Record<string, unknown>[]> {
const fields = filters.fields ?? {};
const facets = filters.facets ?? [];

const pipeline: PipelineStage[] = createFullfacetPipeline<
JobDocument,
FilterQuery<JobDocument>
IJobFields
>(this.jobModel, "id", fields, facets);

return await this.jobModel.aggregate(pipeline).exec();
Expand Down
144 changes: 138 additions & 6 deletions test/Jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3877,7 +3877,7 @@ describe("1100: Jobs: Test New Job Model", () => {
});
});

it("2000: Fullquery jobs as a user from ADMIN_GROUPS that were created by User5.1", async () => {
it("2000: Fullquery jobs as a user from ADMIN_GROUPS that were created by User5.1, limited by 5", async () => {
const queryFields = { createdBy: "user5.1" };
const queryLimits = { limit: 5 };
return request(appUrl)
Expand Down Expand Up @@ -4004,26 +4004,158 @@ describe("1100: Jobs: Test New Job Model", () => {
.expect("Content-Type", /json/);
});

it("3000: Fullfacet jobs as a user from ADMIN_GROUPS, which should always return an empty array", async () => {
it("3010: Fullfacet jobs as unauthenticated user, which should be forbidden", async () => {
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.set("Accept", "application/json")
.expect(TestData.AccessForbiddenStatusCode)
.expect("Content-Type", /json/);
});

it("3020: Fullfacet jobs as a user from ADMIN_GROUPS that were created by admin", async () => {
const query = { createdBy: "admin" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenAdmin}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").to.have.lengthOf(0);
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 36 }] });
});
});

it("3010: Fullfacet jobs as unauthenticated user, which should be forbidden", async () => {
it("3030: Fullfacet jobs as a user from ADMIN_GROUPS that were created by User1", async () => {
const query = { createdBy: "user1" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.expect(TestData.AccessForbiddenStatusCode)
.expect("Content-Type", /json/);
.set({ Authorization: `Bearer ${accessTokenAdmin}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 11 }] });
});
});

it("3040: Fullfacet jobs as a user from ADMIN_GROUPS that were created by User5.1", async () => {
const queryFields = { createdBy: "user5.1" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(queryFields)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenAdmin}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 10 }] });
});
});

it("3050: Fullfacet jobs as a user from ADMIN_GROUPS that were created by User5.2", async () => {
const query = { createdBy: "user5.2" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenAdmin}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 1 }] });
});
});

it("3060: Fullfacet jobs as a user from ADMIN_GROUPS that were created by anonymous user", async () => {
const query = { createdBy: "anonymous" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenAdmin}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 2 }] });
});
});

it("3070: Fullfacet jobs as a user from CREATE_JOB_GROUPS that were created by admin", async () => {
const query = { createdBy: "admin" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenUser1}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 14 }] });
});
});

it("3080: Fullfacet jobs as a user from CREATE_JOB_GROUPS that were created by User1", async () => {
const query = { createdBy: "user1" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenUser1}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 11 }] });
});
});

it("3090: Fullfacet jobs as a normal user", async () => {
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenUser51}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 10 }] });
});
});

it("3100: Fullfacet jobs as a normal user (user5.1) that were created by admin", async () => {
const query = { createdBy: "admin" };
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.query("fields=" + encodeURIComponent(JSON.stringify(query)))
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenUser51}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [] });
});
});

it("3110: Fullfacet jobs as another normal user (user5.2)", async () => {
return request(appUrl)
.get(`/api/v3/Jobs/fullfacet`)
.send({})
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenUser52}` })
.expect(TestData.SuccessfulGetStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.be.an("array").that.deep.contains({ all: [{ totalSets: 2 }] });
});
});
});

0 comments on commit 80b6698

Please sign in to comment.