/**
  * Takes a list of results (in json value wrapped form) and calls the handleResource for each on
  * the handler.
  *
  * @param resultList the list of results, possibly with id and rev entries
  * @param handler the handle to set the results on
  */
 private void handleQueryResultList(JsonValue resultList, QueryResourceHandler handler) {
   for (JsonValue entry : resultList) {
     // These can end up null
     String id = null;
     String rev = null;
     if (entry.isMap()) {
       id = entry.get(ResourceResponse.FIELD_ID).asString();
       rev = entry.get(ResourceResponse.FIELD_REVISION).asString();
     }
     handler.handleResource(newResourceResponse(id, rev, entry));
   }
 }
  /**
   * Allows users to query OAuth2 applications that they have given their consent access to and that
   * have active access and/or refresh tokens.
   *
   * <p>Applications consist of an id, a name (the client id), a set of scopes and an expiry time.
   * The scopes field is the union of the scopes of the individual access/refresh tokens. The expiry
   * time is the time when the last access/refresh token will expire, or null if the server is
   * configured to allow tokens to be refreshed indefinitely.
   *
   * @param context The request context.
   * @param queryHandler The query handler.
   * @param request Unused but necessary for used of the {@link @Query} annotation.
   * @return A promise of a query response.
   */
  @Query
  public Promise<QueryResponse, ResourceException> query(
      Context context, QueryResourceHandler queryHandler, QueryRequest request) {
    String userId = contextHelper.getUserId(context);
    String realm = contextHelper.getRealm(context);

    try {
      QueryFilter<CoreTokenField> queryFilter = getQueryFilter(userId, realm);

      JsonValue tokens = tokenStore.query(queryFilter);

      Map<String, Set<JsonValue>> applicationTokensMap = new HashMap<>();

      for (JsonValue token : tokens) {
        String clientId = getAttributeValue(token, CLIENT_ID.getOAuthField());
        Set<JsonValue> applicationTokens = applicationTokensMap.get(clientId);
        if (applicationTokens == null) {
          applicationTokens = new HashSet<>();
          applicationTokensMap.put(clientId, applicationTokens);
        }
        applicationTokens.add(token);
      }

      for (Map.Entry<String, Set<JsonValue>> applicationTokens : applicationTokensMap.entrySet()) {
        ResourceResponse resource =
            getResourceResponse(context, applicationTokens.getKey(), applicationTokens.getValue());
        queryHandler.handleResource(resource);
      }

      return Promises.newResultPromise(Responses.newQueryResponse());
    } catch (CoreTokenException | ServerException | InvalidClientException | NotFoundException e) {
      debug.message("Failed to query OAuth2 clients for user {}", userId, e);
      return new InternalServerErrorException(e).asPromise();
    } catch (InternalServerErrorException e) {
      debug.message("Failed to query OAuth2 clients for user {}", userId, e);
      return e.asPromise();
    }
  }
 @Override
 public Promise<QueryResponse, ResourceException> queryCollection(
     final Context serverContext,
     final QueryRequest queryRequest,
     final QueryResourceHandler queryResultHandler) {
   QueryFilter<JsonPointer> queryFilter = queryRequest.getQueryFilter();
   if (queryFilter == null) {
     return new BadRequestException(getUsageString()).asPromise();
   }
   try {
     final QueryFilter<CoreTokenField> coreTokenFieldQueryFilter =
         convertToCoreTokenFieldQueryFilter(queryFilter);
     final List<STSIssuedTokenState> issuedTokens =
         ctsTokenPersistence.listTokens(coreTokenFieldQueryFilter);
     for (STSIssuedTokenState tokenState : issuedTokens) {
       queryResultHandler.handleResource(
           newResourceResponse(tokenState.getTokenId(), EMPTY_STRING, tokenState.toJson()));
     }
     return newResultPromise(newQueryResponse());
   } catch (CTSTokenPersistenceException e) {
     logger.error("Exception caught obtaining list of sts-issued tokens: " + e, e);
     return e.asPromise();
   }
 }
  /**
   * Performs the query on the specified object and returns the associated results.
   *
   * <p>Queries are parametric; a set of named parameters is provided as the query criteria. The
   * query result is a JSON object structure composed of basic Java types.
   *
   * <p>The returned map is structured as follow: - The top level map contains meta-data about the
   * query, plus an entry with the actual result records. - The <code>QueryConstants</code> defines
   * the map keys, including the result records (QUERY_RESULT)
   *
   * @param context identifies the object to query.
   * @param request the parameters of the query to perform.
   * @return the query results, which includes meta-data and the result records in JSON object
   *     structure format.
   * @throws NotFoundException if the specified object could not be found.
   * @throws BadRequestException if the specified params contain invalid arguments, e.g. a query id
   *     that is not configured, a query expression that is invalid, or missing query substitution
   *     tokens.
   * @throws ForbiddenException if access to the object or specified query is forbidden.
   */
  @Override
  public Promise<QueryResponse, ResourceException> handleQuery(
      final Context context, final QueryRequest request, final QueryResourceHandler handler) {

    // If paged results are requested then decode the cookie in order to determine
    // the index of the first result to be returned.
    final int requestPageSize = request.getPageSize();

    // Cookie containing offset of last request
    final String pagedResultsCookie = request.getPagedResultsCookie();

    final boolean pagedResultsRequested = requestPageSize > 0;

    // index of first record (used for SKIP/OFFSET)
    final int firstResultIndex;

    if (pagedResultsRequested) {
      if (StringUtils.isNotEmpty(pagedResultsCookie)) {
        try {
          firstResultIndex = Integer.parseInt(pagedResultsCookie);
        } catch (final NumberFormatException e) {
          return new BadRequestException("Invalid paged results cookie").asPromise();
        }
      } else {
        firstResultIndex = Math.max(0, request.getPagedResultsOffset());
      }
    } else {
      firstResultIndex = 0;
    }

    // Once cookie is processed Queries.query() can rely on the offset.
    request.setPagedResultsOffset(firstResultIndex);

    try {
      List<ResourceResponse> results = query(request);
      for (ResourceResponse result : results) {
        handler.handleResource(result);
      }

      /*
       * Execute additional -count query if we are paging
       */
      final String nextCookie;
      // The number of results (if known)
      final int resultCount;

      if (pagedResultsRequested) {
        // count if requested
        switch (request.getTotalPagedResultsPolicy()) {
          case ESTIMATE:
          case EXACT:
            // Get total if -count query is available
            final String countQueryId = request.getQueryId() + "-count";
            if (queries.queryIdExists(countQueryId)) {
              QueryRequest countRequest = Requests.copyOfQueryRequest(request);
              countRequest.setQueryId(countQueryId);

              // Strip pagination parameters
              countRequest.setPageSize(0);
              countRequest.setPagedResultsOffset(0);
              countRequest.setPagedResultsCookie(null);

              List<ResourceResponse> countResult = query(countRequest);

              if (countResult != null && !countResult.isEmpty()) {
                resultCount = countResult.get(0).getContent().get("total").asInteger();
              } else {
                logger.debug("Count query {} failed.", countQueryId);
                resultCount = NO_COUNT;
              }
            } else {
              logger.debug("No count query found with id {}", countQueryId);
              resultCount = NO_COUNT;
            }
            break;
          case NONE:
          default:
            resultCount = NO_COUNT;
            break;
        }

        if (results.size() < requestPageSize) {
          nextCookie = null;
        } else {
          final int remainingResults = resultCount - (firstResultIndex + results.size());
          if (remainingResults == 0) {
            nextCookie = null;
          } else {
            nextCookie = String.valueOf(firstResultIndex + requestPageSize);
          }
        }
      } else {
        resultCount = NO_COUNT;
        nextCookie = null;
      }

      if (resultCount == NO_COUNT) {
        return newQueryResponse(nextCookie).asPromise();
      } else {
        return newQueryResponse(nextCookie, EXACT, resultCount).asPromise();
      }
    } catch (ResourceException e) {
      return e.asPromise();
    }
  }