class SecretSeriesDAO {

  public ImmutableList<SecretSeries> getSecretSeries(@Nullable Long expireMaxTime,
      @Nullable Group group, @Nullable Long expireMinTime, @Nullable String minName,
      @Nullable Integer limit) {
    SelectQuery<Record> select = dslContext
          .select()
          .from(SECRETS)
          .join(SECRETS_CONTENT)
          .on(SECRETS.CURRENT.equal(SECRETS_CONTENT.ID))
          .where(SECRETS.CURRENT.isNotNull())
          .getQuery();
    select.addOrderBy(SECRETS_CONTENT.EXPIRY.asc().nullsLast(), SECRETS.NAME.asc().nullsLast());

    // Set an upper bound on expiration dates
    if (expireMaxTime != null && expireMaxTime > 0) {
      // Set a lower bound of "now" on the expiration only if it isn't configured separately
      if (expireMinTime == null || expireMinTime == 0) {
        long now = System.currentTimeMillis() / 1000L;
        select.addConditions(SECRETS_CONTENT.EXPIRY.greaterOrEqual(now));
      }
      select.addConditions(SECRETS_CONTENT.EXPIRY.lessThan(expireMaxTime));
    }

    if (expireMinTime != null && expireMinTime > 0) {
      // set a lower bound on expiration dates, using the secret name as a tiebreaker
      select.addConditions(SECRETS_CONTENT.EXPIRY.greaterThan(expireMinTime)
          .or(SECRETS_CONTENT.EXPIRY.eq(expireMinTime)
              .and(SECRETS.NAME.greaterOrEqual(minName))));
    }

    if (group != null) {
      select.addJoin(ACCESSGRANTS, SECRETS.ID.eq(ACCESSGRANTS.SECRETID));
      select.addJoin(GROUPS, GROUPS.ID.eq(ACCESSGRANTS.GROUPID));
      select.addConditions(GROUPS.NAME.eq(group.getName()));
    }

    if (limit != null && limit >= 0) {
      select.addLimit(limit);
    }

    List<SecretSeries> r = select.fetchInto(SECRETS).map(secretSeriesMapper);
    return ImmutableList.copyOf(r);
  }

  private  SecretSeriesDAO(DSLContext dslContext, ObjectMapper mapper,
      SecretSeriesMapper secretSeriesMapper, RowHmacGenerator rowHmacGenerator);

}