diff --git a/package.json b/package.json index 61e6a75e..be57cc8f 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,14 @@ "test:e2e:common:client": "./scripts/test-e2e-common.sh client", "test:e2e:common:server": "./scripts/test-e2e-common.sh server", "test:unit": "jest --runInBand --detectOpenHandles", - "test:unit:all": "npm run test:unit:main && npm run test:unit:http && npm run test:unit:grpc && npm run test:unit:actors && npm run test:unit:logger && npm run test:unit:utils", + "test:unit:all": "npm run test:unit:main && npm run test:unit:http && npm run test:unit:grpc && npm run test:unit:actors && npm run test:unit:logger && npm run test:unit:utils && npm run test:unit:errors", "test:unit:main": "NODE_ENV=test npm run test:unit 'test/unit/main/.*\\.test\\.ts'", "test:unit:http": "NODE_ENV=test npm run test:unit 'test/unit/http/.*\\.test\\.ts'", "test:unit:grpc": "NODE_ENV=test npm run test:unit 'test/unit/grpc/.*\\.test\\.ts'", "test:unit:actors": "NODE_ENV=test npm run test:unit 'test/unit/actor/.*\\.test\\.ts'", "test:unit:logger": "NODE_ENV=test npm run test:unit 'test/unit/logger/.*\\.test\\.ts'", "test:unit:utils": "NODE_ENV=test npm run test:unit 'test/unit/utils/.*\\.test\\.ts'", + "test:unit:errors": "NODE_ENV=test npm run test:unit 'test/unit/errors/.*\\.test\\.ts'", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "prebuild": "./scripts/prebuild.sh", "build-ci": "npm run prebuild && ./scripts/build.sh", diff --git a/src/errors/GRPCNotSupportedError.ts b/src/errors/GRPCNotSupportedError.ts new file mode 100644 index 00000000..ea070a96 --- /dev/null +++ b/src/errors/GRPCNotSupportedError.ts @@ -0,0 +1,19 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export class GRPCNotSupportedError extends Error { + readonly name = "GRPCNotSupportedError"; + constructor() { + super("GRPC is currently not supported."); + } +} diff --git a/src/errors/HTTPNotSupportedError.ts b/src/errors/HTTPNotSupportedError.ts new file mode 100644 index 00000000..2798cbac --- /dev/null +++ b/src/errors/HTTPNotSupportedError.ts @@ -0,0 +1,19 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export class HTTPNotSupportedError extends Error { + readonly name = "HTTPNotSupportedError"; + constructor() { + super("HTTP is currently not supported."); + } +} diff --git a/src/errors/PropertyRequiredError.ts b/src/errors/PropertyRequiredError.ts new file mode 100644 index 00000000..18d35b73 --- /dev/null +++ b/src/errors/PropertyRequiredError.ts @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export class PropertyRequiredError extends Error { + readonly name = "PropertyRequiredError"; + constructor(public propertyName: string) { + super(`${propertyName} is required`); + if (!propertyName || propertyName === " ") { + throw new PropertyRequiredError("propertyName"); + } + } +} diff --git a/src/implementation/Client/GRPCClient/workflow.ts b/src/implementation/Client/GRPCClient/workflow.ts index 54f38251..ba7b6b21 100644 --- a/src/implementation/Client/GRPCClient/workflow.ts +++ b/src/implementation/Client/GRPCClient/workflow.ts @@ -14,6 +14,7 @@ limitations under the License. import GRPCClient from "./GRPCClient"; import IClientWorkflow from "../../../interfaces/Client/IClientWorkflow"; import { WorkflowGetResponseType } from "../../../types/workflow/WorkflowGetResponse.type"; +import { GRPCNotSupportedError } from "../../../errors/GRPCNotSupportedError"; export default class GRPCClientWorkflow implements IClientWorkflow { client: GRPCClient; @@ -23,7 +24,7 @@ export default class GRPCClientWorkflow implements IClientWorkflow { } get(_instanceId: string, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } start( @@ -32,26 +33,26 @@ export default class GRPCClientWorkflow implements IClientWorkflow { _instanceId?: string | undefined, _workflowComponent?: string | undefined, ): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } terminate(_instanceId: string, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } pause(_instanceId: string, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } resume(_instanceId: string, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } purge(_instanceId: string, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } raise(_instanceId: string, _eventName: string, _input?: any, _workflowComponent?: string | undefined): Promise { - throw new Error("Method not implemented."); + throw new GRPCNotSupportedError(); } } diff --git a/src/implementation/Client/HTTPClient/configuration.ts b/src/implementation/Client/HTTPClient/configuration.ts index e4fd773e..4390d900 100644 --- a/src/implementation/Client/HTTPClient/configuration.ts +++ b/src/implementation/Client/HTTPClient/configuration.ts @@ -17,6 +17,7 @@ import { GetConfigurationResponse as GetConfigurationResponseResult } from "../. import HTTPClient from "./HTTPClient"; import { SubscribeConfigurationCallback } from "../../../types/configuration/SubscribeConfigurationCallback"; import { SubscribeConfigurationStream } from "../../../types/configuration/SubscribeConfigurationStream"; +import { HTTPNotSupportedError } from "../../../errors/HTTPNotSupportedError"; export default class HTTPClientConfiguration implements IClientConfiguration { client: HTTPClient; @@ -26,7 +27,7 @@ export default class HTTPClientConfiguration implements IClientConfiguration { } async subscribe(_storeName: string, _cb: SubscribeConfigurationCallback): Promise { - throw new Error("HTTP is currently not supported."); + throw new HTTPNotSupportedError(); } async subscribeWithKeys( @@ -34,7 +35,7 @@ export default class HTTPClientConfiguration implements IClientConfiguration { _keys: string[], _cb: SubscribeConfigurationCallback, ): Promise { - throw new Error("HTTP is currently not supported."); + throw new HTTPNotSupportedError(); } async subscribeWithMetadata( @@ -43,10 +44,10 @@ export default class HTTPClientConfiguration implements IClientConfiguration { _metadata: KeyValueType, _cb: SubscribeConfigurationCallback, ): Promise { - throw new Error("HTTP is currently not supported."); + throw new HTTPNotSupportedError(); } async get(_storeName: string, _keys: string[], _metadata?: KeyValueType): Promise { - throw new Error("HTTP is currently not supported."); + throw new HTTPNotSupportedError(); } } diff --git a/src/implementation/Client/HTTPClient/proxy.ts b/src/implementation/Client/HTTPClient/proxy.ts index 32d3d66f..2966b580 100644 --- a/src/implementation/Client/HTTPClient/proxy.ts +++ b/src/implementation/Client/HTTPClient/proxy.ts @@ -15,6 +15,7 @@ import * as grpc from "@grpc/grpc-js"; import Class from "../../../types/Class"; import IClientProxy from "../../../interfaces/Client/IClientProxy"; import HTTPClient from "./HTTPClient"; +import { HTTPNotSupportedError } from "../../../errors/HTTPNotSupportedError"; export default class HTTPClientProxy implements IClientProxy { client: HTTPClient; @@ -24,6 +25,6 @@ export default class HTTPClientProxy implements IClientProxy { } async create(_cls: Class, _clientOptions?: Partial | undefined): Promise { - throw new Error("HTTP is currently not supported."); + throw new HTTPNotSupportedError(); } } diff --git a/src/implementation/Client/HTTPClient/workflow.ts b/src/implementation/Client/HTTPClient/workflow.ts index 8f95eea8..4cf2d80d 100644 --- a/src/implementation/Client/HTTPClient/workflow.ts +++ b/src/implementation/Client/HTTPClient/workflow.ts @@ -20,6 +20,7 @@ import { WorkflowStartOptions } from "../../../types/workflow/WorkflowStartOptio import { randomUUID } from "crypto"; import { createHTTPQueryParam } from "../../../utils/Client.util"; import { WorkflowRaiseOptions } from "../../../types/workflow/WorkflowRaiseOptions.type"; +import { PropertyRequiredError } from "../../../errors/PropertyRequiredError"; export default class HTTPClientWorkflow implements IClientWorkflow { private readonly client: HTTPClient; @@ -34,7 +35,7 @@ export default class HTTPClientWorkflow implements IClientWorkflow { async get(instanceID: string, workflowComponent?: string): Promise { if (!instanceID) { - throw new Error("instanceID is required"); + throw new PropertyRequiredError("instanceID"); } workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; @@ -76,7 +77,7 @@ export default class HTTPClientWorkflow implements IClientWorkflow { options: WorkflowStartOptions = {}, ): Promise { if (!workflowName) { - throw new Error("workflowName is required"); + throw new PropertyRequiredError("workflowName"); } if (!instanceId) { @@ -120,11 +121,11 @@ export default class HTTPClientWorkflow implements IClientWorkflow { options: WorkflowRaiseOptions = {}, ): Promise { if (!instanceId) { - throw new Error("instanceID is required"); + throw new PropertyRequiredError("instanceID"); } if (!eventName) { - throw new Error("eventName is required"); + throw new PropertyRequiredError("eventName"); } workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; @@ -170,11 +171,11 @@ export default class HTTPClientWorkflow implements IClientWorkflow { async _invokeMethod(instanceId: string, method: string, workflowComponent?: string | undefined): Promise { if (!instanceId) { - throw new Error("instanceID is required"); + throw new PropertyRequiredError("instanceID"); } if (!method) { - throw new Error("method is required"); + throw new PropertyRequiredError("method"); } workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; diff --git a/src/implementation/Server/GRPCServer/actor.ts b/src/implementation/Server/GRPCServer/actor.ts index e3c7774f..846d168f 100644 --- a/src/implementation/Server/GRPCServer/actor.ts +++ b/src/implementation/Server/GRPCServer/actor.ts @@ -15,6 +15,7 @@ import GRPCServer from "./GRPCServer"; import IServerActor from "../../../interfaces/Server/IServerActor"; import AbstractActor from "../../../actors/runtime/AbstractActor"; import Class from "../../../types/Class"; +import { GRPCNotSupportedError } from "../../../errors/GRPCNotSupportedError"; // https://docs.dapr.io/reference/api/bindings_api/ export default class GRPCServerActor implements IServerActor { @@ -25,18 +26,18 @@ export default class GRPCServerActor implements IServerActor { } deactivateActor(_actorType: string, _actorId: string): Promise { - throw new Error("GRPC is currently not supported."); + throw new GRPCNotSupportedError(); } init(): Promise { - throw new Error("GRPC is currently not supported."); + throw new GRPCNotSupportedError(); } getRegisteredActors(): Promise { - throw new Error("GRPC is currently not supported."); + throw new GRPCNotSupportedError(); } registerActor(_cls: Class): Promise { - throw new Error("GRPC is currently not supported."); + throw new GRPCNotSupportedError(); } } diff --git a/test/unit/errors/PropertyRequiredError.test.ts b/test/unit/errors/PropertyRequiredError.test.ts new file mode 100644 index 00000000..11e4bcb1 --- /dev/null +++ b/test/unit/errors/PropertyRequiredError.test.ts @@ -0,0 +1,41 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { PropertyRequiredError } from "../../../src/errors/PropertyRequiredError"; + +describe("PropertyRequiredError_class", () => { + // Tests that the constructor creates an instance of PropertyRequiredError + it("should be an instance of PropertyRequiredError", () => { + const error = new PropertyRequiredError("test"); + expect(error).toBeInstanceOf(PropertyRequiredError); + }); + + // Tests that the constructor sets the propertyName property correctly + it("should set `propertyName` property correctly", () => { + const propertyName = "test"; + const error = new PropertyRequiredError(propertyName); + expect(error.propertyName).toBe(propertyName); + }); + + // Tests that the constructor sets the message property correctly + it("should set `message` property correctly", () => { + const propertyName = "test"; + const error = new PropertyRequiredError(propertyName); + expect(error.message).toBe(`${propertyName} is required`); + }); + + // Tests that the constructor throws an error + it("should throw an error if propertyName is an empty string", () => { + expect(() => new PropertyRequiredError("")).toThrow(); + }); +}); diff --git a/test/unit/http/workflow.test.ts b/test/unit/http/workflow.test.ts new file mode 100644 index 00000000..a293e023 --- /dev/null +++ b/test/unit/http/workflow.test.ts @@ -0,0 +1,45 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import HTTPClient from "../../../src/implementation/Client/HTTPClient/HTTPClient"; +import { PropertyRequiredError } from "../../../src/errors/PropertyRequiredError"; +import HTTPClientWorkflow from "../../../src/implementation/Client/HTTPClient/workflow"; +import { randomUUID } from "crypto"; + +describe("workflow", () => { + const client = new HTTPClient({ + daprHost: "", + daprPort: "", + communicationProtocol: 0, + }); + const workflow = new HTTPClientWorkflow(client); + + it("should throw PropertyRequiredError when instanceID variable is not provided in get method", async () => { + await expect(workflow.get("")).rejects.toThrow(PropertyRequiredError); + }); + it("should throw PropertyRequiredError when workflowName variable is not provided in start method", async () => { + await expect(workflow.start("")).rejects.toThrow(PropertyRequiredError); + }); + it("should throw PropertyRequiredError when instanceID variable is not provided in _invokeMethod method", async () => { + await expect(workflow._invokeMethod("", "raise")).rejects.toThrow(PropertyRequiredError); + }); + it("should throw PropertyRequiredError when method variable is not provided in _invokeMethod method", async () => { + await expect(workflow._invokeMethod(randomUUID(), "")).rejects.toThrow(PropertyRequiredError); + }); + it("should throw PropertyRequiredError when instanceID variable is not provided in raise method", async () => { + await expect(workflow.raise("", "Event")).rejects.toThrow(PropertyRequiredError); + }); + it("should throw PropertyRequiredError when eventName variable is not provided in raise method", async () => { + await expect(workflow.raise(randomUUID(), "")).rejects.toThrow(PropertyRequiredError); + }); +});