/*
 * Copyright (c) 2020 Nike, inc.
 *
 * 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.
 */

package com.nike.cerberus.error;

import com.codahale.metrics.Counter;
import com.nike.backstopper.apierror.ApiError;
import com.nike.backstopper.handler.ApiExceptionHandlerUtils;
import com.nike.backstopper.handler.RequestInfoForLogging;
import com.nike.cerberus.metric.MetricsService;
import com.nike.internal.util.Pair;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

/**
 * A SignalFx-aware ApiExceptionHandlerUtils that increments an api_errors {@link Counter} metric
 * with the following dimensions (based on the error info that gets logged): response_code,
 * contributing_errors, and exception_class.
 */
@Primary
@Component
public class SfxAwareApiExceptionHandlerUtils extends ApiExceptionHandlerUtils {

  /** The name of the API errors metric sent to SignalFx. */
  public static final String API_ERRORS_METRIC_NAME = "api_errors";
  /** The name/key of the HTTP response code dimension applied to the API errors metric. */
  public static final String RESPONSE_CODE_DIM_KEY = "response_code";
  /** The name/key of the contributing errors dimension applied to the API errors metric. */
  public static final String CONTRIBUTING_ERRORS_DIM_KEY = "contributing_errors";
  /** The name/key of the exception class dimension applied to the API errors metric. */
  public static final String EXCEPTION_CLASS_DIM_KEY = "exception_class";

  private final MetricsService metricsService;

  @Autowired
  public SfxAwareApiExceptionHandlerUtils(MetricsService metricsService) {
    this.metricsService = metricsService;
  }

  @Override
  public String buildErrorMessageForLogs(
      StringBuilder sb,
      RequestInfoForLogging request,
      Collection<ApiError> contributingErrors,
      Integer httpStatusCode,
      Throwable cause,
      List<Pair<String, String>> extraDetailsForLogging) {
    try {
      // Do the normal logging thing.
      return super.buildErrorMessageForLogs(
          sb, request, contributingErrors, httpStatusCode, cause, extraDetailsForLogging);
    } finally {
      // Update SignalFx metrics around API Errors.
      String contributingErrorsString =
          contributingErrors == null ? "[NONE]" : concatenateErrorCollection(contributingErrors);

      metricsService
          .getOrCreateCounter(
              API_ERRORS_METRIC_NAME,
              Map.of(
                  RESPONSE_CODE_DIM_KEY, String.valueOf(httpStatusCode),
                  CONTRIBUTING_ERRORS_DIM_KEY, contributingErrorsString,
                  EXCEPTION_CLASS_DIM_KEY, cause.getClass().getName()))
          .inc();
    }
  }
}

@@ -660,16 +660,63 @@
 ounter;%0A
+import com.google.common.collect.ImmutableSet;%0A
 import c
@@ -754,16 +754,16 @@
 iError;%0A
-
 import c
@@ -959,16 +959,41 @@
 l.Pair;%0A
+import java.util.Arrays;%0A
 import j
@@ -1058,16 +1058,74 @@
 il.Map;%0A
+import java.util.Set;%0Aimport java.util.stream.Collectors;%0A
 import o
@@ -2257,16 +2257,250 @@
 _class%22;
+%0A  /** The names/keys of sensitive HTTP headers in lower case. */%0A  public static final Set%3CString%3E sensitiveHeaderNamesInLowerCase =%0A      ImmutableSet.of(%22authorization%22, %22x-amz-security-token%22, %22x-cerberus-token%22, %22x-vault-token%22);
 %0A%0A  priv
@@ -2946,24 +2946,61 @@
 rLogging) %7B%0A
+    redactSensitiveHeaders(request);%0A
     try %7B%0A  
@@ -3726,16 +3726,16 @@
 .inc();%0A
-
     %7D%0A  
@@ -3735,11 +3735,508 @@
    %7D%0A  %7D
+%0A%0A  protected void redactSensitiveHeaders(RequestInfoForLogging request) %7B%0A    List%3CString%3E redactedHeaderValue = Arrays.asList(%22REDACTED%22);%0A%0A    Map%3CString, List%3CString%3E%3E headersMap = request.getHeadersMap();%0A    Set%3CString%3E headerNames =%0A        headersMap.keySet().stream()%0A            .filter(name -%3E sensitiveHeaderNamesInLowerCase.contains(name.toLowerCase()))%0A            .collect(Collectors.toSet());%0A    headerNames.stream().forEach(name -%3E headersMap.put(name, redactedHeaderValue));%0A  %7D
 %0A%7D%0A

redact sensitive header in error message (#254)