Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cactus-core): add handleRestEndpointException utility to public API #2868

Commits on Nov 15, 2023

  1. feat(cactus-core): add handleRestEndpointException utility to public API

    1. This is a function that is designed to be called by all the REST API
    endpoint implementations to (more) correctly handle errors.
    2. The problem right now is that we do not differentiate between invalid
    request errors (e.g. expected exceptions) vs.
    legitimate crashes (e.g. unexpected exceptions)
    What the above means is that a lot of our endpoints will crash with an
    HTTP 500 error code returned to the client even if the problem as user-
    error (such as a missing parameter that is mandatory).
    3. With the new utility function the REST endpoint code can easily
    apply the decision logic at runtime in their own catch blocks' and
    set the HTTP response status code based on the information (context)
    provided by the callee (most often the connector plugin's underlying
    method that was called)
    
    An example usage of this utility method can be described as:
    1. Add the necessary dependencies to your plugin (`http-errors`, `@types/http-errors`)
    2. `yarn install` (which will update the lock file)
    3. Choose the endpoint you wish to update to be using the new handleRestEndpointException
    function internally when handling HTTP requests that involve the plugin.
    For example this file:
    ```
    packages/cactus-plugin-ledger-connector-besu/src/main/typescript/
    web-services/deploy-contract-solidity-bytecode-endpoint.ts
    ```
    4. Update the `catch() { ... }` block of the `handleRequest` method to
    invoke the handleRestEndpointException method:
    
    ```typescript
    public async handleRequest(req: Request, res: Response): Promise<void> {
    const fnTag = `${this.className}#handleRequest()`;
    const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`;
    this.log.debug(reqTag);
    const reqBody: DeployContractSolidityBytecodeV1Request = req.body;
    try {
        const resBody = await this.options.connector.deployContract(reqBody);
        res.json(resBody);
    } catch (ex) {
        const errorMsg = `${reqTag} ${fnTag} Failed to deploy contract:`;
        handleRestEndpointException({ errorMsg, log: this.log, error: ex, res });
    }
    }
    ```
    
    Then proceed to also update the implementation of the method that is  being
    called by the REST endpoint request handler such that it uses the HTTP
    errors according to their intended status codes, e.g. 400 is user error
    and 5xx is something that is a developer error (e.g. indicating that
    a bug is in the code of the plugin and should be fixed)
    
    ```typescript
    import createHttpError from "http-errors";
    
    export class SomePluginImplementration {
    
      public async deployContract(
        req: DeployContractSolidityBytecodeV1Request,
      ): Promise<DeployContractSolidityBytecodeV1Response> {
        const fnTag = `${this.className}#deployContract()`;
        Checks.truthy(req, `${fnTag} req`);
        if (isWeb3SigningCredentialNone(req.web3SigningCredential)) {
          throw createHttpError[400](
            `${fnTag} Cannot deploy contract with pre-signed TX`,
          );
        }
        const { keychainId, contractName } = req;
        if (!keychainId || !req.contractName) {
          const errorMessage = `${fnTag} Cannot deploy contract without keychainId and the contractName.`;
          throw createHttpError[400](errorMessage);
        }
    
        const keychainPlugin = this.pluginRegistry.findOneByKeychainId(keychainId);
    
        if (!keychainPlugin) {
          const errorMessage =
            `${fnTag} The plugin registry does not contain` +
            ` a keychain plugin for ID:"${req.keychainId}"`;
          throw createHttpError[400](errorMessage);
        }
    
        if (!keychainPlugin.has(contractName)) {
          const errorMessage =
            `${fnTag} Cannot create an instance of the contract instance because` +
            `the contractName in the request does not exist on the keychain`;
          throw new createHttpError[400](errorMessage);
        }
        // rest of the implementation goes here
    }
    ```
    
    [skip ci]
    
    Related to, but does NOT conclude: https://github.com/hyperledger/cacti/issues/1747
    
    Signed-off-by: Peter Somogyvari <[email protected]>
    petermetz committed Nov 15, 2023
    Configuration menu
    Copy the full SHA
    9b57905 View commit details
    Browse the repository at this point in the history