lib/plugins/aws/invokeLocal/index.js/AwsInvokeLocal/invokeLocalNodeJs
class AwsInvokeLocal:  invokeLocalNodeJs(handlerPath, handlerName, event, customContext) {
    this.serverless.cli.log('INVOKING INVOKE');
    let lambda;
    let pathToHandler;
    let hasResponded = false;
    try {
      /*
       * we need require() here to load the handler from the file system
       * which the user has to supply by passing the function name
       */
      pathToHandler = path.join(
        this.serverless.config.servicePath,
        this.options.extraServicePath || '',
        handlerPath
      );
      const handlersContainer = require( // eslint-disable-line global-require
        pathToHandler
      );
      lambda = handlersContainer[handlerName];
    } catch (error) {
      this.serverless.cli.consoleLog(chalk.red(JSON.stringify(error, null, 4)));
      throw new Error(`Exception encountered when loading ${pathToHandler}`);
    }

    function handleError(err) {
      let errorResult;
      if (err instanceof Error) {
        errorResult = {
          errorMessage: err.message,
          errorType: err.constructor.name,
        };
      } else {
        errorResult = {
          errorMessage: err,
        };
      }

      this.serverless.cli.consoleLog(chalk.red(JSON.stringify(errorResult, null, 4)));
      process.exitCode = 1;
    }

    function handleResult(result) {
      if (result instanceof Error) {
        handleError.call(this, result);
        return;
      } else if (result.headers && result.headers['Content-Type'] === 'application/json') {
        if (result.body) {
          try {
            Object.assign(result, {
              body: JSON.parse(result.body),
            });
          } catch (e) {
            throw new Error('Content-Type of response is application/json but body is not json');
          }
        }
      }

      this.serverless.cli.consoleLog(JSON.stringify(result, null, 4));
    }

    const callback = (err, result) => {
      if (!hasResponded) {
        hasResponded = true;
        if (err) {
          handleError.call(this, err);
        } else if (result) {
          handleResult.call(this, result);
        }
      }
    };

    const startTime = new Date();
    const timeout = Number(this.options.functionObj.timeout)
      || Number(this.serverless.service.provider.timeout)
      || 6;
    let context = {
      awsRequestId: 'id',
      invokeid: 'id',
      logGroupName: this.provider.naming.getLogGroupName(this.options.functionObj.name),
      logStreamName: '2015/09/22/[HEAD]13370a84ca4ed8b77c427af260',
      functionVersion: 'HEAD',
      isDefaultFunctionVersion: true,

      functionName: this.options.functionObj.name,
      memoryLimitInMB: '1024',

      succeed(result) {
        return callback(null, result);
      },
      fail(error) {
        return callback(error);
      },
      done(error, result) {
        return callback(error, result);
      },
      getRemainingTimeInMillis() {
        return Math.max((timeout * 1000) - ((new Date()).valueOf() - startTime.valueOf()), 0);
      },
    };

    if (customContext) {
      context = customContext;
    }

    const maybeThennable = lambda(event, context, callback);
    if (!_.isUndefined(maybeThennable) && _.isFunction(maybeThennable.then)) {
      return maybeThennable
        .then(
          callback.bind(this, null),
          callback.bind(this)
        );
    }

    return maybeThennable;
  }