From cdc7380eca31a4fce11add8f2e478ee909b35e70 Mon Sep 17 00:00:00 2001 From: Yaroslav Grishajev Date: Wed, 8 May 2024 20:35:53 +0200 Subject: [PATCH] refactor(sdl): change validation error name refs akash-network/cloudmos#168 --- src/error/SdlValidationError.ts | 14 +++++++++ ...mValidationError.ts => ValidationError.ts} | 6 ++-- src/error/index.ts | 2 +- src/sdl/SDL/SDL.spec.ts | 12 +++---- src/sdl/SDL/SDL.ts | 31 ++++++++----------- src/sdl/index.ts | 31 ++++++++----------- 6 files changed, 50 insertions(+), 46 deletions(-) create mode 100644 src/error/SdlValidationError.ts rename src/error/{CustomValidationError.ts => ValidationError.ts} (54%) diff --git a/src/error/SdlValidationError.ts b/src/error/SdlValidationError.ts new file mode 100644 index 0000000..658df1f --- /dev/null +++ b/src/error/SdlValidationError.ts @@ -0,0 +1,14 @@ +import { ValidationError } from "./ValidationError"; + +export class SdlValidationError extends ValidationError { + static assert(condition: unknown, message: string): asserts condition { + if (!condition) { + throw new SdlValidationError(message); + } + } + + constructor(message: string) { + super(message); + this.name = "SdlValidationError"; + } +} diff --git a/src/error/CustomValidationError.ts b/src/error/ValidationError.ts similarity index 54% rename from src/error/CustomValidationError.ts rename to src/error/ValidationError.ts index 7444e07..94c064c 100644 --- a/src/error/CustomValidationError.ts +++ b/src/error/ValidationError.ts @@ -1,12 +1,12 @@ -export class CustomValidationError extends Error { +export class ValidationError extends Error { static assert(condition: unknown, message: string): asserts condition { if (!condition) { - throw new CustomValidationError(message); + throw new ValidationError(message); } } constructor(message: string) { super(message); - this.name = "CustomValidationError"; + this.name = "SdlValidationError"; } } diff --git a/src/error/index.ts b/src/error/index.ts index 68d5b35..97bdf39 100644 --- a/src/error/index.ts +++ b/src/error/index.ts @@ -1 +1 @@ -export * from "./CustomValidationError"; +export * from "./SdlValidationError"; diff --git a/src/sdl/SDL/SDL.spec.ts b/src/sdl/SDL/SDL.spec.ts index 72333bc..bd4244a 100644 --- a/src/sdl/SDL/SDL.spec.ts +++ b/src/sdl/SDL/SDL.spec.ts @@ -1,11 +1,11 @@ +import { faker } from "@faker-js/faker"; import template from "lodash/template"; +import omit from "lodash/omit"; import { readYml } from "../../../test/read-yml"; +import { SdlValidationError } from "../../error"; import { SDL } from "./SDL"; -import { CustomValidationError } from "../../error"; -import { faker } from "@faker-js/faker"; import { v2ServiceImageCredentials } from "../types"; -import { omit } from "lodash"; const createYML = template(readYml("sdl-hello-world-basic-with-creds")); @@ -58,7 +58,7 @@ describe("SDL", () => { expect(() => { SDL.fromString(yml, "beta3", "sandbox"); - }).toThrowError(new CustomValidationError(`service "web" credentials missing "${field}"`)); + }).toThrowError(new SdlValidationError(`service "web" credentials missing "${field}"`)); }); it.each(fields)('should throw an error when credentials "%s" is empty', field => { @@ -69,7 +69,7 @@ describe("SDL", () => { expect(() => { SDL.fromString(yml, "beta3", "sandbox"); - }).toThrowError(new CustomValidationError(`service "web" credentials missing "${field}"`)); + }).toThrowError(new SdlValidationError(`service "web" credentials missing "${field}"`)); }); it.each(fields)('should throw an error when credentials "%s" contains spaces only', field => { @@ -80,7 +80,7 @@ describe("SDL", () => { expect(() => { SDL.fromString(yml, "beta3", "sandbox"); - }).toThrowError(new CustomValidationError(`service "web" credentials missing "${field}"`)); + }).toThrowError(new SdlValidationError(`service "web" credentials missing "${field}"`)); }); }); }); diff --git a/src/sdl/SDL/SDL.ts b/src/sdl/SDL/SDL.ts index b93297a..ad080df 100644 --- a/src/sdl/SDL/SDL.ts +++ b/src/sdl/SDL/SDL.ts @@ -36,7 +36,7 @@ import { default as stableStringify } from "json-stable-stringify"; import crypto from "node:crypto"; import { MAINNET_ID, USDC_IBC_DENOMS } from "../../config/network"; import { NetworkId } from "../../types/network"; -import { CustomValidationError } from "../../error"; +import { SdlValidationError } from "../../error"; const Endpoint_SHARED_HTTP = 0; const Endpoint_RANDOM_PORT = 1; @@ -182,7 +182,7 @@ export class SDL { .map(resource => resource.price.denom); const invalidDenom = denoms.find(denom => denom !== "uakt" && denom !== usdcDenom); - CustomValidationError.assert(!invalidDenom, `Invalid denom: "${invalidDenom}". Only uakt and ${usdcDenom} are supported.`); + SdlValidationError.assert(!invalidDenom, `Invalid denom: "${invalidDenom}". Only uakt and ${usdcDenom} are supported.`); } private validateEndpoints() { @@ -192,9 +192,9 @@ export class SDL { Object.keys(this.data.endpoints).forEach(endpointName => { const endpoint = this.data.endpoints[endpointName]; - CustomValidationError.assert(this.ENDPOINT_NAME_VALIDATION_REGEX.test(endpointName), `Endpoint named "${endpointName}" is not a valid name.`); - CustomValidationError.assert(!!endpoint.kind, `Endpoint named "${endpointName}" has no kind.`); - CustomValidationError.assert(endpoint.kind === this.ENDPOINT_KIND_IP, `Endpoint named "${endpointName}" has an unknown kind "${endpoint.kind}".`); + SdlValidationError.assert(this.ENDPOINT_NAME_VALIDATION_REGEX.test(endpointName), `Endpoint named "${endpointName}" is not a valid name.`); + SdlValidationError.assert(!!endpoint.kind, `Endpoint named "${endpointName}" has no kind.`); + SdlValidationError.assert(endpoint.kind === this.ENDPOINT_KIND_IP, `Endpoint named "${endpointName}" has an unknown kind "${endpoint.kind}".`); }); } @@ -204,29 +204,26 @@ export class SDL { if (credentials) { const credentialsKeys: (keyof v2ServiceImageCredentials)[] = ["host", "username", "password"]; credentialsKeys.forEach(key => { - CustomValidationError.assert(credentials[key]?.trim().length, `service "${serviceName}" credentials missing "${key}"`); + SdlValidationError.assert(credentials[key]?.trim().length, `service "${serviceName}" credentials missing "${key}"`); }); } } private validateDeploymentWithRelations(serviceName: string) { const deployment = this.data.deployment[serviceName]; - CustomValidationError.assert(deployment, `Service "${serviceName}" is not defined in the "deployment" section.`); + SdlValidationError.assert(deployment, `Service "${serviceName}" is not defined in the "deployment" section.`); Object.keys(this.data.deployment[serviceName]).forEach(deploymentName => { const serviceDeployment = this.data.deployment[serviceName][deploymentName]; const compute = this.data.profiles.compute[serviceDeployment.profile]; const infra = this.data.profiles.placement[deploymentName]; - CustomValidationError.assert(infra, `The placement "${deploymentName}" is not defined in the "placement" section.`); - CustomValidationError.assert( + SdlValidationError.assert(infra, `The placement "${deploymentName}" is not defined in the "placement" section.`); + SdlValidationError.assert( infra.pricing[serviceDeployment.profile], `The pricing for the "${serviceDeployment.profile}" profile is not defined in the "${deploymentName}" "placement" definition.` ); - CustomValidationError.assert( - compute, - `The compute requirements for the "${serviceDeployment.profile}" profile are not defined in the "compute" section.` - ); + SdlValidationError.assert(compute, `The compute requirements for the "${serviceDeployment.profile}" profile are not defined in the "compute" section.`); }); } @@ -236,19 +233,17 @@ export class SDL { expose.to?.forEach(to => { if (to.ip?.length > 0) { - CustomValidationError.assert(to.global, `Error on "${serviceName}", if an IP is declared, the directive must be declared as global.`); + SdlValidationError.assert(to.global, `Error on "${serviceName}", if an IP is declared, the directive must be declared as global.`); if (!this.data.endpoints?.[to.ip]) { - throw new CustomValidationError( - `Unknown endpoint "${to.ip}" in service "${serviceName}". Add to the list of endpoints in the "endpoints" section.` - ); + throw new SdlValidationError(`Unknown endpoint "${to.ip}" in service "${serviceName}". Add to the list of endpoints in the "endpoints" section.`); } this.endpointsUsed.add(to.ip); const portKey = `${to.ip}-${expose.as}-${proto}`; const otherServiceName = this.portsUsed.get(portKey); - CustomValidationError.assert( + SdlValidationError.assert( !this.portsUsed.has(portKey), `IP endpoint ${to.ip} port: ${expose.port} protocol: ${proto} specified by service ${serviceName} already in use by ${otherServiceName}` ); diff --git a/src/sdl/index.ts b/src/sdl/index.ts index cd75288..a83fcaa 100644 --- a/src/sdl/index.ts +++ b/src/sdl/index.ts @@ -36,7 +36,7 @@ import { default as stableStringify } from "json-stable-stringify"; import crypto from "node:crypto"; import { MAINNET_ID, USDC_IBC_DENOMS } from "../config/network"; import { NetworkId } from "../types/network"; -import { CustomValidationError } from "../error"; +import { SdlValidationError } from "../error"; const Endpoint_SHARED_HTTP = 0; const Endpoint_RANDOM_PORT = 1; @@ -182,7 +182,7 @@ export class SDL { .map(resource => resource.price.denom); const invalidDenom = denoms.find(denom => denom !== "uakt" && denom !== usdcDenom); - CustomValidationError.assert(!invalidDenom, `Invalid denom: "${invalidDenom}". Only uakt and ${usdcDenom} are supported.`); + SdlValidationError.assert(!invalidDenom, `Invalid denom: "${invalidDenom}". Only uakt and ${usdcDenom} are supported.`); } private validateEndpoints() { @@ -192,9 +192,9 @@ export class SDL { Object.keys(this.data.endpoints).forEach(endpointName => { const endpoint = this.data.endpoints[endpointName]; - CustomValidationError.assert(this.ENDPOINT_NAME_VALIDATION_REGEX.test(endpointName), `Endpoint named "${endpointName}" is not a valid name.`); - CustomValidationError.assert(!!endpoint.kind, `Endpoint named "${endpointName}" has no kind.`); - CustomValidationError.assert(endpoint.kind === this.ENDPOINT_KIND_IP, `Endpoint named "${endpointName}" has an unknown kind "${endpoint.kind}".`); + SdlValidationError.assert(this.ENDPOINT_NAME_VALIDATION_REGEX.test(endpointName), `Endpoint named "${endpointName}" is not a valid name.`); + SdlValidationError.assert(!!endpoint.kind, `Endpoint named "${endpointName}" has no kind.`); + SdlValidationError.assert(endpoint.kind === this.ENDPOINT_KIND_IP, `Endpoint named "${endpointName}" has an unknown kind "${endpoint.kind}".`); }); } @@ -204,29 +204,26 @@ export class SDL { if (credentials) { const credentialsKeys: (keyof v2ServiceImageCredentials)[] = ["host", "username", "password"]; credentialsKeys.forEach(key => { - CustomValidationError.assert(credentials[key]?.trim().length, `service "${serviceName}" credentials missing "${key}"`); + SdlValidationError.assert(credentials[key]?.trim().length, `service "${serviceName}" credentials missing "${key}"`); }); } } private validateDeploymentWithRelations(serviceName: string) { const deployment = this.data.deployment[serviceName]; - CustomValidationError.assert(deployment, `Service "${serviceName}" is not defined in the "deployment" section.`); + SdlValidationError.assert(deployment, `Service "${serviceName}" is not defined in the "deployment" section.`); Object.keys(this.data.deployment[serviceName]).forEach(deploymentName => { const serviceDeployment = this.data.deployment[serviceName][deploymentName]; const compute = this.data.profiles.compute[serviceDeployment.profile]; const infra = this.data.profiles.placement[deploymentName]; - CustomValidationError.assert(infra, `The placement "${deploymentName}" is not defined in the "placement" section.`); - CustomValidationError.assert( + SdlValidationError.assert(infra, `The placement "${deploymentName}" is not defined in the "placement" section.`); + SdlValidationError.assert( infra.pricing[serviceDeployment.profile], `The pricing for the "${serviceDeployment.profile}" profile is not defined in the "${deploymentName}" "placement" definition.` ); - CustomValidationError.assert( - compute, - `The compute requirements for the "${serviceDeployment.profile}" profile are not defined in the "compute" section.` - ); + SdlValidationError.assert(compute, `The compute requirements for the "${serviceDeployment.profile}" profile are not defined in the "compute" section.`); }); } @@ -236,19 +233,17 @@ export class SDL { expose.to?.forEach(to => { if (to.ip?.length > 0) { - CustomValidationError.assert(to.global, `Error on "${serviceName}", if an IP is declared, the directive must be declared as global.`); + SdlValidationError.assert(to.global, `Error on "${serviceName}", if an IP is declared, the directive must be declared as global.`); if (!this.data.endpoints?.[to.ip]) { - throw new CustomValidationError( - `Unknown endpoint "${to.ip}" in service "${serviceName}". Add to the list of endpoints in the "endpoints" section.` - ); + throw new SdlValidationError(`Unknown endpoint "${to.ip}" in service "${serviceName}". Add to the list of endpoints in the "endpoints" section.`); } this.endpointsUsed.add(to.ip); const portKey = `${to.ip}-${expose.as}-${proto}`; const otherServiceName = this.portsUsed.get(portKey); - CustomValidationError.assert( + SdlValidationError.assert( !this.portsUsed.has(portKey), `IP endpoint ${to.ip} port: ${expose.port} protocol: ${proto} specified by service ${serviceName} already in use by ${otherServiceName}` );