diff --git a/packages/grid_client/src/helpers/validator.ts b/packages/grid_client/src/helpers/validator.ts
index 15bb9c4a75..2d6c5cb809 100644
--- a/packages/grid_client/src/helpers/validator.ts
+++ b/packages/grid_client/src/helpers/validator.ts
@@ -31,4 +31,151 @@ function validateHexSeed(seed: string, length: number): boolean {
return true;
}
-export { validateObject, validateInput, validateHexSeed };
+interface ValidationOptions {
+ props?: boolean | string | string[];
+ methods?: boolean | string | string[];
+}
+
+/*
+ * */
+
+/**
+ * @description
+ * This `ValidateMembers` is a config method which returns back a *classDecrator*
+ * Allows to configure which setter/methods should trigger validation for that specific class
+ *
+ * Example As follow
+ * @example
+ * ```typescript
+ * import { isLength, isInt } from "class-validator";
+ *
+ * // @ValidateMembers({ props: false, methods: true }) // - disable validation on set props
+ * // @ValidateMembers({ props: true, methods: false }) // - disable validation on call methods
+ * // @ValidateMembers({ props: 'name', methods: false }) // - validate only on setting 'name' prop
+ * // And so on...
+ * @ValidateMembers() // = @ValidateMembers({ props: true, methods: true })
+ * class User {
+ * @isLength(2) name: string
+ * @isInt() age: number
+ *
+ * greeting() {
+ * // Some logic
+ * }
+ * }
+ * ```
+ *
+ *
+ *
+ * @param options { ValidationOptions | undefined }
+ * @returns { ClassDecorator }
+ */
+function ValidateMembers(options?: ValidationOptions): ClassDecorator {
+ const _options = _normalizeValidationOptions(options);
+ return (target: any): any => {
+ const methods = _getMethods(target, _options);
+ for (const method of methods) {
+ const fn = target.prototype[method];
+ target.prototype[method] = function (...args: any[]): any {
+ const errors = validateSync(this);
+ if (errors.length) {
+ throw errors;
+ }
+ return fn.apply(this, args);
+ };
+ }
+
+ return class extends target {
+ constructor(...args: any[]) {
+ super(...args);
+
+ const props = _getProps(this, _options);
+ for (const prop of props) {
+ let _value = this[prop];
+
+ Object.defineProperty(this, prop, {
+ configurable: false,
+ enumerable: true,
+ get: () => _value,
+ set(value) {
+ _value = value;
+ const errors = validateSync(this);
+ for (const error of errors) {
+ if (error.property === prop) {
+ throw error;
+ }
+ }
+ },
+ });
+ }
+ }
+ };
+ };
+}
+
+function _normalizeValidationOptions(options?: ValidationOptions): Required {
+ return {
+ props: options?.props ?? true,
+ methods: options?.methods ?? true,
+ };
+}
+
+function _getProps(ctor: any, options: Required): string[] {
+ /* This env variable should be used while testing to prevent throw error while setting values */
+ if (process.env.SKIP_PROPS_VALIDATION) {
+ return [];
+ }
+
+ if (options.props === true) {
+ return Object.getOwnPropertyNames(ctor);
+ }
+
+ if (typeof options.props === "string") {
+ return [options.props];
+ }
+
+ if (Array.isArray(options.props)) {
+ return options.props;
+ }
+
+ return [];
+}
+
+function _getMethods(ctor: any, options: Required): string[] {
+ /* This env variable should be used to prevent throw error while calling methods if needed */
+ if (process.env.SKIP_METHODS_VALIDATION) {
+ return [];
+ }
+
+ if (options.methods === true) {
+ const methods = Object.getOwnPropertyNames(ctor.prototype);
+ const constructorIndex = methods.indexOf("constructor");
+ if (constructorIndex !== -1) {
+ methods.splice(constructorIndex, 1);
+ }
+ return methods;
+ }
+
+ if (typeof options.methods === "string") {
+ return [options.methods];
+ }
+
+ if (Array.isArray(options.methods)) {
+ return options.methods;
+ }
+
+ return [];
+}
+
+export { validateObject, validateInput, validateHexSeed, type ValidationOptions, ValidateMembers };
diff --git a/packages/grid_client/src/zos/computecapacity.ts b/packages/grid_client/src/zos/computecapacity.ts
index 6b9ac9f33d..d4939a3e2f 100644
--- a/packages/grid_client/src/zos/computecapacity.ts
+++ b/packages/grid_client/src/zos/computecapacity.ts
@@ -1,6 +1,9 @@
import { Expose } from "class-transformer";
import { IsInt, Max, Min } from "class-validator";
+import { ValidateMembers } from "../helpers/validator";
+
+@ValidateMembers()
class ComputeCapacity {
@Expose() @IsInt() @Min(1) @Max(32) cpu: number;
@Expose() @IsInt() @Min(256 * 1024 ** 2) @Max(256 * 1024 ** 3) memory: number; // in bytes
diff --git a/packages/grid_client/tests/modules/compute_capacity.test.ts b/packages/grid_client/tests/modules/compute_capacity.test.ts
index 81501d6908..197bfcd074 100644
--- a/packages/grid_client/tests/modules/compute_capacity.test.ts
+++ b/packages/grid_client/tests/modules/compute_capacity.test.ts
@@ -6,62 +6,66 @@ beforeEach(() => {
computeCapacity = new ComputeCapacity();
});
describe("Compute Capacity module", () => {
- test.skip("Compute Capacity instance is of type ComputeCapacity.", () => {
+ test("Compute Capacity instance is of type ComputeCapacity.", () => {
expect(computeCapacity).toBeInstanceOf(ComputeCapacity);
});
// The following tests are skipped as there's an issue w input validation. Should be returned once validation is fixed here: https://github.com/threefoldtech/tfgrid-sdk-ts/issues/2821
- test.skip("Min values for cpu & memory.", () => {
+ test("Min values for cpu & memory.", () => {
const cpu = 0;
const mem = 255 * 1024 ** 2;
- computeCapacity.cpu = cpu;
- computeCapacity.memory = mem;
-
+ const setCPU = () => (computeCapacity.cpu = cpu);
+ const setMem = () => (computeCapacity.memory = mem);
const result = () => computeCapacity.challenge();
+ expect(setCPU).toThrow();
+ expect(setMem).toThrow();
expect(result).toThrow();
});
- test.skip("Max values for cpu & memory.", () => {
+ test("Max values for cpu & memory.", () => {
const cpu = 33;
const mem = 255 * 1024 ** 4;
- computeCapacity.cpu = cpu;
- computeCapacity.memory = mem;
-
+ const setCPU = () => (computeCapacity.cpu = cpu);
+ const setMem = () => (computeCapacity.memory = mem);
const result = () => computeCapacity.challenge();
+ expect(setCPU).toThrow();
+ expect(setMem).toThrow();
expect(result).toThrow();
});
- test.skip("cpu & memory doesn't accept decimal values.", () => {
+ test("cpu & memory doesn't accept decimal values.", () => {
const cpu = 1.5;
const mem = 1.2;
- computeCapacity.cpu = cpu;
- computeCapacity.memory = mem;
-
+ const setCPU = () => (computeCapacity.cpu = cpu);
+ const setMem = () => (computeCapacity.memory = mem);
const result = () => computeCapacity.challenge();
+ expect(setCPU).toThrow();
+ expect(setMem).toThrow();
expect(result).toThrow();
});
- test.skip("cpu & memory empty values.", () => {
+ test("cpu & memory empty values.", () => {
const result = () => computeCapacity.challenge();
expect(result).toThrow();
});
- test.skip("An error should be thrown if cpu & memory negative values.", () => {
+ test("An error should be thrown if cpu & memory negative values.", () => {
const negative_cpu = -1;
const negative_mem = -1;
- computeCapacity.cpu = negative_cpu;
- computeCapacity.memory = negative_mem;
-
+ const setCPU = () => (computeCapacity.cpu = negative_cpu);
+ const setMem = () => (computeCapacity.memory = negative_mem);
const result = () => computeCapacity.challenge();
+ expect(setCPU).toThrow();
+ expect(setMem).toThrow();
expect(result).toThrow();
});
});