module.exports = function()

in lib/instrumentation/modules/@smithy/smithy-client.js [106:198]


module.exports = function (mod, agent, { name, version, enabled }) {
  if (!enabled) return mod;

  // As of `@aws-sdk/*@3.363.0` the underlying smithy-client is under the
  // `@smithy/` npm org.
  if (
    name === '@smithy/smithy-client' &&
    !semver.satisfies(version, '>=1 <5')
  ) {
    agent.logger.debug(
      'cannot instrument @aws-sdk/client-*: @smithy/smithy-client version %s not supported',
      version,
    );
    return mod;
  } else if (
    name === '@aws-sdk/smithy-client' &&
    !semver.satisfies(version, '>=3 <4')
  ) {
    agent.logger.debug(
      'cannot instrument @aws-sdk/client-*: @aws-sdk/smithy-client version %s not supported',
      version,
    );
    return mod;
  }

  shimmer.wrap(mod.Client.prototype, 'send', function (orig) {
    return function _wrappedSmithyClientSend() {
      const clientName = this.constructor && this.constructor.name;
      const clientConfig = clientsConfig[clientName];

      if (!clientConfig) {
        return orig.apply(this, arguments);
      }

      if (!this[elasticAPMMiddlewares]) {
        const factory = clientConfig && clientConfig.factory;
        const middlewares =
          typeof factory === 'function' ? factory(this, agent) : [];

        // We do the instrumentation by leveraging the middleware mechanism provided by the
        // middlewareStack property of the Client instance. We add the instrumentation middlewares
        // once at the client level so they persist for the whole life of the client instance
        // https://github.com/aws/aws-sdk-js-v3/tree/main/packages/middleware-stack
        this[elasticAPMMiddlewares] = middlewares;
        for (const item of this[elasticAPMMiddlewares]) {
          this.middlewareStack.add(item.middleware, item.options);
        }
      }

      const command = arguments[0];

      if (clientConfig.shouldIgnoreCommand(command, agent._conf)) {
        return orig.apply(this, arguments);
      }

      const opName = opNameFromCommandName(command.constructor.name);
      const name = clientConfig.NAME + ' ' + opName;

      const ins = agent._instrumentation;
      const span = ins.createSpan(
        name,
        clientConfig.TYPE,
        clientConfig.SUBTYPE,
        opName,
        { exitSpan: true },
      );

      if (!span) {
        return orig.apply(this, arguments);
      }

      // Run context notes: The `orig` should run in the context of the S3 span,
      // because that is the point. The user's callback `cb` should run outside of
      // the S3 span.
      const parentRunContext = ins.currRunContext();
      const spanRunContext = parentRunContext.enterSpan(span);

      // Although the client consumer may use the Promise API `S3Client.send(command).then(...)`
      // the clients may make use of the callback parameter on the super class method (SmithyClient)
      // therefore we need to have this check
      const cb = arguments[arguments.length - 1];
      if (typeof cb === 'function') {
        arguments[arguments.length - 1] = ins.bindFunctionToRunContext(
          parentRunContext,
          cb,
        );
      }

      return ins.withRunContext(spanRunContext, orig, this, ...arguments);
    };
  });
  return mod;
};