/**
   * 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();
    }
  }