text: "use strict";
  /*
   * Copyright The OpenTelemetry 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
   *
   *      https://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.
   */
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.setSpanContext = exports.getMetadata = exports.makeGrpcClientRemoteCall = exports.getMethodsToWrap = void 0;
  const api_1 = require("@opentelemetry/api");
  const utils_1 = require("../utils");
  const serverUtils_1 = require("./serverUtils");
  const AttributeNames_1 = require("../enums/AttributeNames");
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
  /**
   * Parse a package method list and return a list of methods to patch
   * with both possible casings e.g. "TestMethod" & "testMethod"
   */
  function getMethodsToWrap(client, methods) {
      const methodList = [];
      // For a method defined in .proto as "UnaryMethod"
      Object.entries(methods).forEach(([name, { originalName }]) => {
          if (!utils_1._methodIsIgnored(name, this._config.ignoreGrpcMethods)) {
              methodList.push(name); // adds camel case method name: "unaryMethod"
              if (originalName &&
                  // eslint-disable-next-line no-prototype-builtins
                  client.prototype.hasOwnProperty(originalName) &&
                  name !== originalName // do not add duplicates
              ) {
                  // adds original method name: "UnaryMethod",
                  methodList.push(originalName);
              }
          }
      });
      return methodList;
  }
  exports.getMethodsToWrap = getMethodsToWrap;
  /**
   * Execute grpc client call. Apply completitionspan properties and end the
   * span on callback or receiving an emitted event.
   */
  function makeGrpcClientRemoteCall(original, args, metadata, self) {
      /**
       * Patches a callback so that the current span for this trace is also ended
       * when the callback is invoked.
       */
      function patchedCallback(span, callback) {
          const wrappedFn = (err, res) => {
              if (err) {
                  if (err.code) {
                      span.setStatus(utils_1._grpcStatusCodeToSpanStatus(err.code));
                      span.setAttribute(semantic_conventions_1.SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString());
                  }
                  span.setAttributes({
                      [AttributeNames_1.AttributeNames.GRPC_ERROR_NAME]: err.name,
                      [AttributeNames_1.AttributeNames.GRPC_ERROR_MESSAGE]: err.message,
                  });
              }
              else {
                  span.setStatus({ code: api_1.SpanStatusCode.UNSET });
                  span.setAttribute(semantic_conventions_1.SemanticAttributes.RPC_GRPC_STATUS_CODE, api_1.SpanStatusCode.UNSET.toString());
              }
              span.end();
              callback(err, res);
          };
          return api_1.context.bind(api_1.context.active(), wrappedFn);
      }
      return (span) => {
          // if unary or clientStream
          if (!original.responseStream) {
              const callbackFuncIndex = args.findIndex(arg => {
                  return typeof arg === 'function';
              });
              if (callbackFuncIndex !== -1) {
                  args[callbackFuncIndex] = patchedCallback(span, args[callbackFuncIndex]);
              }
          }
          span.setAttributes({
              [AttributeNames_1.AttributeNames.GRPC_METHOD]: original.path,
              [AttributeNames_1.AttributeNames.GRPC_KIND]: api_1.SpanKind.CLIENT,
          });
          setSpanContext(metadata);
          const call = original.apply(self, args);
          // if server stream or bidi
          if (original.responseStream) {
              // Both error and status events can be emitted
              // the first one emitted set spanEnded to true
              let spanEnded = false;
              const endSpan = () => {
                  if (!spanEnded) {
                      span.end();
                      spanEnded = true;
                  }
              };
              api_1.context.bind(api_1.context.active(), call);
              call.on('error', (err) => {
                  if (call[serverUtils_1.CALL_SPAN_ENDED]) {
                      return;
                  }
                  call[serverUtils_1.CALL_SPAN_ENDED] = true;
                  span.setStatus({
                      code: utils_1._grpcStatusCodeToOpenTelemetryStatusCode(err.code),
                      message: err.message,
                  });
                  span.setAttributes({
                      [AttributeNames_1.AttributeNames.GRPC_ERROR_NAME]: err.name,
                      [AttributeNames_1.AttributeNames.GRPC_ERROR_MESSAGE]: err.message,
                  });
                  endSpan();
              });
              call.on('status', (status) => {
                  if (call[serverUtils_1.CALL_SPAN_ENDED]) {
                      return;
                  }
                  call[serverUtils_1.CALL_SPAN_ENDED] = true;
                  span.setStatus(utils_1._grpcStatusCodeToSpanStatus(status.code));
                  endSpan();
              });
          }
          return call;
      };
  }
  exports.makeGrpcClientRemoteCall = makeGrpcClientRemoteCall;
  /**
   * Returns the metadata argument from user provided arguments (`args`)
   */
  function getMetadata(grpcClient, original, args) {
      let metadata;
      // This finds an instance of Metadata among the arguments.
      // A possible issue that could occur is if the 'options' parameter from
      // the user contains an '_internal_repr' as well as a 'getMap' function,
      // but this is an extremely rare case.
      let metadataIndex = args.findIndex((arg) => {
          return (arg &&
              typeof arg === 'object' &&
              arg['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88
              typeof arg.getMap === 'function');
      });
      if (metadataIndex === -1) {
          metadata = new grpcClient.Metadata();
          if (!original.requestStream) {
              // unary or server stream
              metadataIndex = 1;
          }
          else {
              // client stream or bidi
              metadataIndex = 0;
          }
          args.splice(metadataIndex, 0, metadata);
      }
      else {
          metadata = args[metadataIndex];
      }
      return metadata;
  }
  exports.getMetadata = getMetadata;
  /**
   * Inject opentelemetry trace context into `metadata` for use by another
   * grpc receiver
   * @param metadata
   */
  function setSpanContext(metadata) {
      api_1.propagation.inject(api_1.context.active(), metadata, {
          set: (metadata, k, v) => metadata.set(k, v),
      });
  }
  exports.setSpanContext = setSpanContext;
  //# sourceMappingURL=clientUtils.js.map