/**
   * List external service of given instance.
   *
   * @param appId the application identification
   * @param instanceId the instance identification, need to check relation with app
   * @param from time range begin
   * @param to time range end
   * @param limit top n limit
   * @return instance level top n service
   */
  @RequestMapping(
      value = {"instances"},
      method = RequestMethod.GET)
  public List<ExternalServiceVo> instanceTransactions(
      @RequestParam(value = "appId") Long appId,
      @RequestParam(value = "instanceId") Long instanceId,
      @RequestParam(value = "from", required = false)
          @DateTimeFormat(pattern = Constraints.TIME_PATTERN)
          LocalDateTime from,
      @RequestParam(value = "to", required = false)
          @DateTimeFormat(pattern = Constraints.TIME_PATTERN)
          LocalDateTime to,
      @RequestParam(value = "limit") Integer limit) {
    validApplicationId(appId, RETRIEVE_INSTANCE_TOP_N_TRANS);
    validInstanceId(instanceId, RETRIEVE_INSTANCE_TOP_N_TRANS);
    validTimeRange(from, to, RETRIEVE_INSTANCE_TOP_N_TRANS);
    validLimit(limit, RETRIEVE_INSTANCE_TOP_N_TRANS);

    final Long period = DEFAULT_PERIOD;

    TimeRange timeRange = TimeUtils.createTimeRange(from, to);

    Iterable<ExternalService> externalServices =
        automaticService.getExternalServicesOfInstance(instanceId);
    Iterable<ExternalServiceStatistic> statistic =
        automaticService.getExternalServiceStatistic(externalServices, timeRange, period);

    return ExternalServiceUtils.topByAvgResponseTime(statistic, externalServices, timeRange, limit);
  }
  /**
   * Get top n external service response time trend data.
   *
   * @param appId the application identification
   * @param time time range, only one
   * @param period period in second
   * @param limit top n limit
   * @return application level top n
   */
  @RequestMapping(value = {"trend/rt"})
  public TrendResponse responseTime(
      @RequestParam(value = "appId") Long appId,
      @RequestParam(value = "time[]") String[] time,
      @RequestParam(value = "period") Long period,
      @RequestParam(value = "limit", defaultValue = "5") Integer limit) {
    validApplicationId(appId, RETRIEVE_APP_RT_TREND);
    validSingleTimeRanges(time, RETRIEVE_APP_RT_TREND);
    validPeriod(period, RETRIEVE_APP_RT_TREND);
    validLimit(limit, RETRIEVE_APP_RT_TREND);

    final StatisticMetricValue[] metricName = new StatisticMetricValue[] {RESPONSE_TIME, PV, CPM};
    List<TimeRange> timeRanges = TimeUtils.convertToRange(time);

    final TimeRange timeRange = timeRanges.get(0);

    Iterable<ExternalService> transactions = automaticService.getExternalServicesOfApp(appId);

    Iterable<ExternalServiceStatistic> transactionsStatistic =
        automaticService.getExternalServiceStatistic(transactions, timeRange, period);

    TrendContext<String> trendContext =
        ExternalServiceUtils.topByAvgResponseTime(
            period, limit, timeRange, transactions, transactionsStatistic);

    return TrendUtils.toTrendResponse(trendContext, metricName);
  }
  /**
   * Get all external service cpm of the application.
   *
   * @param appId the application identification
   * @param time time range, only one
   * @param period period in second
   * @return application level cpm
   */
  @RequestMapping(value = {"trend/cpm"})
  public TrendResponse cpm(
      @RequestParam(value = "appId") Long appId,
      @RequestParam(value = "time[]") String[] time,
      @RequestParam(value = "period") Long period) {
    validApplicationId(appId, RETRIEVE_APP_CPM_TREND);
    validTimeRanges(time, RETRIEVE_APP_CPM_TREND);
    validPeriod(period, RETRIEVE_APP_CPM_TREND);

    final StatisticMetricValue[] metricName = new StatisticMetricValue[] {CPM, RESPONSE_TIME, PV};
    final ServiceType serviceType = ServiceType.EXTERNAL;
    List<TimeRange> timeRanges = TimeUtils.convertToRange(time);

    TrendContext trendContext =
        automaticService.getMetricDataOfApp(appId, timeRanges, period, serviceType);

    return TrendUtils.toTrendResponse(trendContext, metricName);
  }