package io.sphere.sdk.play.metrics;
import io.sphere.sdk.http.HttpMethod;
import io.sphere.sdk.utils.SphereInternalLogger;
import org.apache.commons.lang3.tuple.Pair;
import play.libs.F;
import play.mvc.Http;
import play.mvc.Result;
import java.util.*;
import java.util.function.Predicate;
import static java.lang.String.*;
import static java.util.stream.Collectors.joining;
public class MetricAction extends play.mvc.Action.Simple {
public static final String KEY = "io.sphere.sdk.play.metrics.reportRawData";
private static final SphereInternalLogger LOGGER = SphereInternalLogger.getLogger("metrics.simple");
public F.Promise<Result> call(final Http.Context ctx) throws Throwable {
final List<ReportRawData> rawDatas = Collections.synchronizedList(new LinkedList<>());
ctx.args.put(KEY, rawDatas);
final F.Promise<Result> resultPromise = delegate.call(ctx);
logRequestDataOnComplete(ctx, rawDatas, resultPromise);
return resultPromise;
}
private void logRequestDataOnComplete(final Http.Context ctx, final List<ReportRawData> rawDatas, final F.Promise<Result> resultPromise) {
resultPromise.onRedeem(r -> {
final Pair<List<ReportRawData>, List<ReportRawData>> queryCommandPair = splitByQueriesAndCommands(rawDatas);
final List<ReportRawData> queries = queryCommandPair.getLeft();
final List<ReportRawData> commands = queryCommandPair.getRight();
final int size = calculateTotalSize(rawDatas);
final String durations = rawDatas.stream().map(data -> data.getStopTimestamp() - data.getStartTimestamp()).map(l -> Long.toString(l) + " ms").collect(joining(", "));
LOGGER.debug(() -> format("%s used %d requests (%d queries, %d commands, %d bytes fetched, in (%s)).", ctx.request(), rawDatas.size(), queries.size(), commands.size(), size, durations));
});
}
private Pair<List<ReportRawData>, List<ReportRawData>> splitByQueriesAndCommands(final List<ReportRawData> rawDatas) {
return partition(rawDatas, data -> data.getHttpRequest().getHttpMethod() == HttpMethod.GET);
}
private Integer calculateTotalSize(final List<ReportRawData> rawDatas) {
return rawDatas.stream().map(elem -> Optional.ofNullable(elem.getHttpResponse().getResponseBody())
.map(b -> b.length).orElse(0)).reduce(0, (a, b) -> a + b);
}
/**
* Partitions <code>list</code> in two lists according to <code>predicate</code>.
* @param list the list which should be divided
* @param predicate returns true if the element of <code>list</code> should belong to the first result list
* @param <T> generic type of the list
* @return the first list satisfies <code>predicate</code>, the second one not.
*/
public static <T> Pair<List<T>, List<T>> partition(final List<T> list, final Predicate<T> predicate) {
final List<T> matchingPredicate = new ArrayList<>();
final List<T> notMatchingPredicate = new ArrayList<>();
for (final T element : list) {
if (predicate.test(element)) {
matchingPredicate.add(element);
} else {
notMatchingPredicate.add(element);
}
}
return Pair.of(matchingPredicate, notMatchingPredicate);
}
}