Skip to content

Commit

Permalink
Merge pull request #1421 from sbliven/validateaction
Browse files Browse the repository at this point in the history
feat(jobs): ValidateAction
  • Loading branch information
sbliven authored Oct 24, 2024
2 parents f1d6dc5 + 761bac5 commit 1b44fe0
Show file tree
Hide file tree
Showing 20 changed files with 965 additions and 507 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Thank you for your interest in contributing to our project!
1. **Running the unit tests:** `npm run test`
2. **Running the e2e(api) tests:**

- First of all run `npm run prepare:local` to prepare the local environment for starting
- First of all run `npm run prepare:local` to prepare the local docker environment for starting
- After that run `npm run test:api` which will start the backend locally and run both `jest` and `mocha` e2e(api) tests.
- [Optional] If you want to run only the mocha tests you will need to start the backend locally with `npm run start` and then use `npm run test:api:mocha`
- [Optional] If you want to run only the jest tests you can use `npm run test:api:jest`
Expand Down
60 changes: 60 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"dotenv": "^16.0.3",
"express-session": "^1.17.3",
"handlebars": "^4.7.7",
"jsonpath-plus": "^9.0.0",
"lodash": "^4.17.21",
"luxon": "^3.2.1",
"mathjs": "^13.0.0",
Expand Down Expand Up @@ -87,6 +88,7 @@
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/jest": "^27.0.2",
"@types/jsonpath-plus": "^5.0.5",
"@types/lodash": "^4.14.180",
"@types/luxon": "^3.1.0",
"@types/mocha": "^10.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const convertToSI = (
.toJSON();
return { valueSI: Number(quantity.value), unitSI: quantity.unit };
} catch (error) {
console.error(error);
Logger.warn(`Error converting unit to SI: {error}`);
return { valueSI: inputValue, unitSI: inputUnit };
}
};
Expand Down
3 changes: 3 additions & 0 deletions src/config/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as fs from "fs";
import { merge } from "lodash";
import localconfiguration from "./localconfiguration";
import { boolean } from "mathjs";
import { ValidateAction } from "src/jobs/actions/validateaction";

const configuration = () => {
const accessGroupsStaticValues =
Expand Down Expand Up @@ -230,10 +231,12 @@ export function registerDefaultActions() {
registerCreateAction(EmailJobAction);
registerCreateAction(URLAction);
registerCreateAction(RabbitMQJobAction);
registerCreateAction(ValidateAction);
// Status Update
registerStatusUpdateAction(LogJobAction);
registerStatusUpdateAction(EmailJobAction);
registerStatusUpdateAction(RabbitMQJobAction);
registerStatusUpdateAction(ValidateAction);
}

export type OidcConfig = ReturnType<typeof configuration>["oidc"];
Expand Down
5 changes: 2 additions & 3 deletions src/jobs/actions/emailaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { readFileSync } from "fs";
import { compile, TemplateDelegate } from "handlebars";
import { createTransport, Transporter } from "nodemailer";
import { Logger, NotFoundException } from "@nestjs/common";
import { JobAction } from "../config/jobconfig";
import { JobAction, JobDto } from "../config/jobconfig";
import { JobClass } from "../schemas/job.schema";
import configuration from "src/config/configuration";

// Handlebar options for JobClass templates
const jobTemplateOptions = {
Expand Down Expand Up @@ -40,7 +39,7 @@ type Auth = {
/**
* Send an email following a job
*/
export class EmailJobAction<T> implements JobAction<T> {
export class EmailJobAction<T extends JobDto> implements JobAction<T> {
public static readonly actionType = "email";

private mailService: Transporter;
Expand Down
8 changes: 6 additions & 2 deletions src/jobs/actions/logaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
*
*/
import { Logger } from "@nestjs/common";
import { JobAction } from "../config/jobconfig";
import { JobAction, JobDto } from "../config/jobconfig";
import { JobClass } from "../schemas/job.schema";

export class LogJobAction<T> implements JobAction<T> {
export class LogJobAction<T extends JobDto> implements JobAction<T> {
public static readonly actionType = "log";

getActionType(): string {
return LogJobAction.actionType;
}

async validate(dto: T) {
Logger.log("Validating job dto: " + JSON.stringify(dto), "LogJobAction");
}

async performJob(job: JobClass) {
Logger.log("Performing job: " + JSON.stringify(job), "LogJobAction");
}
Expand Down
4 changes: 2 additions & 2 deletions src/jobs/actions/rabbitmqaction.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Logger, NotFoundException } from "@nestjs/common";
import amqp, { Connection } from "amqplib/callback_api";
import { JobAction } from "../config/jobconfig";
import { JobAction, JobDto } from "../config/jobconfig";
import { JobClass } from "../schemas/job.schema";

/**
* Publish a message in a RabbitMQ queue
*/
export class RabbitMQJobAction<T> implements JobAction<T> {
export class RabbitMQJobAction<T extends JobDto> implements JobAction<T> {
public static readonly actionType = "rabbitmq";
private connection;
private binding;
Expand Down
17 changes: 4 additions & 13 deletions src/jobs/actions/urlaction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Logger, NotFoundException, HttpException } from "@nestjs/common";
import { JobAction } from "../config/jobconfig";
import { JobAction, JobDto } from "../config/jobconfig";
import { JobClass } from "../schemas/job.schema";
import * as Handlebars from "handlebars";

Expand Down Expand Up @@ -37,7 +37,7 @@ function isStringRecord(obj: any): obj is Record<string, string> {
/**
* Respond to Job events by making an HTTP call.
*/
export class URLAction<T> implements JobAction<T> {
export class URLAction<T extends JobDto> implements JobAction<T> {
public static readonly actionType = "url";

private urlTemplate: Handlebars.TemplateDelegate<JobClass>;
Expand All @@ -54,7 +54,7 @@ export class URLAction<T> implements JobAction<T> {

async performJob(job: JobClass) {
const url = encodeURI(this.urlTemplate(job, jobTemplateOptions));
Logger.log(`Requesting ${url}`, "UrlJobAction");
Logger.log(`Requesting ${url}`, "URLAction");

const response = await fetch(url, {
method: this.method,
Expand All @@ -71,11 +71,7 @@ export class URLAction<T> implements JobAction<T> {
: undefined,
});

Logger.log(
`Request for ${url} returned ${response.status}`,
"UrlJobAction",
);

Logger.log(`Request for ${url} returned ${response.status}`, "URLAction");
if (!response.ok) {
throw new HttpException(
{
Expand All @@ -102,11 +98,6 @@ export class URLAction<T> implements JobAction<T> {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(data: Record<string, any>) {
Logger.log(
"Initializing UrlJobAction. Params: " + JSON.stringify(data),
"UrlJobAction",
);

if (!data["url"]) {
throw new NotFoundException("Param 'url' is undefined in url action");
}
Expand Down
134 changes: 134 additions & 0 deletions src/jobs/actions/validateaction.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { ValidateAction } from "./validateaction";
import { CreateJobDto } from "../dto/create-job.dto";

const createJobBase = {
type: "validate",
ownerUser: "owner",
ownerGroup: "group",
contactEmail: "[email protected]",
};

describe("ValiateAction", () => {
const config = {
actionType: "validate",
request: {
"jobParams.stringVal": { type: "string" },
"jobParams.requiredArray[*]": { type: "string" },
"jobParams.numberVal": { type: "number" },
jobParams: { required: ["nonNull"] },
},
};
const action = new ValidateAction<CreateJobDto>(config);
it("should be configured successfully", async () => {
expect(action).toBeDefined();
});

it("should pass if required params are present", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: 1,
nonNull: "value1",
requiredArray: ["ok"],
},
};

await expect(action.validate(dto)).resolves.toBeUndefined();
});

it("should fail if nonNull is missing", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: 1,
//nonNull: "value1",
requiredArray: ["ok"],
},
};

await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Invalid value for 'jobParams'",
);
});

it("should fail if string type is wrong", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: 0xdeadbeef, // wrong type
numberVal: 1,
nonNull: "value1",
requiredArray: ["ok"],
},
};

await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Invalid value for 'jobParams.stringVal",
);
});

it("should fail if number type is wrong", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: "error",
nonNull: "value1",
requiredArray: ["ok"],
},
};

await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Invalid value for 'jobParams.numberVal'",
);
});

it("should fail if requiredArray is ommitted", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: 1,
nonNull: "value1",
//requiredArray: ["ok"],
},
};

await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Requires 'jobParams.requiredArray[*]'",
);
});

it("should fail if requiredArray is empty", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: 1,
nonNull: "value1",
requiredArray: [],
},
};
await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Requires 'jobParams.requiredArray[*]'",
);
});

it("should fail if requiredArray has the wrong type", async () => {
const dto: CreateJobDto = {
...createJobBase,
jobParams: {
stringVal: "ok",
numberVal: "error",
nonNull: "value1",
requiredArray: [0xdeadbeef],
},
};

await expect(action.validate(dto)).rejects.toThrow(
"Invalid request. Invalid value for 'jobParams.requiredArray[*]'",
);
});
});
Loading

0 comments on commit 1b44fe0

Please sign in to comment.