Skip to content

Commit

Permalink
feat(jans-auth-server): add support for ranges in statistic endpoint …
Browse files Browse the repository at this point in the history
…(UI team request)

#1089
  • Loading branch information
yuriyz committed Jun 8, 2022
1 parent 673859a commit fd66720
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static io.jans.as.model.util.Util.escapeLog;
Expand All @@ -43,7 +44,7 @@ public class StatResponseService {
.expireAfterWrite(1, TimeUnit.HOURS)
.build();

public StatResponse buildResponse(List<String> months) {
public StatResponse buildResponse(Set<String> months) {
final String cacheKey = months.toString();
final StatResponse cachedResponse = responseCache.getIfPresent(cacheKey);
if (cachedResponse != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.jans.as.server.ws.rs.stat;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashSet;
import java.util.Set;

import static java.time.temporal.TemporalAdjusters.firstDayOfMonth;

/**
* @author Yuriy Zabrovarnyy
*/
public class Months {

private static final Logger log = LoggerFactory.getLogger(Months.class);

public static final DateTimeFormatter YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd");
public static final DateTimeFormatter YYYYMM = DateTimeFormatter.ofPattern("yyyyMM");

private Months() {
}

public static boolean isValid(String months, String startMonth, String endMonth) {
boolean hasMonths = StringUtils.isNotBlank(months);
boolean hasRange = StringUtils.isNotBlank(startMonth) && StringUtils.isNotBlank(endMonth);
if (hasMonths && hasRange) { // if both are present then invalid
return false;
}
return hasMonths || hasRange;
}

public static Set<String> getMonths(String months, String startMonth, String endMonth) {
if (!isValid(months, startMonth, endMonth)) {
return new LinkedHashSet<>();
}

boolean hasMonths = StringUtils.isNotBlank(months);
if (hasMonths) {
return getMonths(months);
}
return getMonths(startMonth, endMonth);
}

public static LocalDate parse(String month) {
// append first day of month -> "01"
return LocalDate.parse(month + "01", YYYYMMDD).with(firstDayOfMonth());
}

public static Set<String> getMonths(String startMonth, String endMonth) {
Set<String> monthList = new LinkedHashSet<>();
if (!checkMonthFormat(startMonth) || !checkMonthFormat(endMonth)) {
return monthList;
}

LocalDate start = parse(startMonth);
LocalDate end = parse(endMonth);

LocalDate date = start;

while (date.isBefore(end)) {
monthList.add(date.format(YYYYMM));

date = date.plusMonths(1).with(firstDayOfMonth());
}

if (!monthList.isEmpty()) { // add last month
monthList.add(date.format(YYYYMM));
}
return monthList;
}

public static boolean checkMonthFormat(String month) {
if (month.length() == 6) {
return true;
}

log.error("Invalid month `{}`, month must be 6 chars length in format yyyyMM, e.g. 202212", month);
return false;
}

public static Set<String> getMonths(String months) {
Set<String> monthList = new LinkedHashSet<>();
if (StringUtils.isBlank(months)) {
return monthList;
}

for (String m : months.split(" ")) {
m = m.trim();
if (checkMonthFormat(m)) {
monthList.add(m);
}
}
return monthList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.exporter.common.TextFormat;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

import jakarta.enterprise.context.ApplicationScoped;
Expand All @@ -33,8 +32,8 @@
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static io.jans.as.model.util.Util.escapeLog;

Expand All @@ -59,9 +58,6 @@ public class StatWS {
@Inject
private ErrorResponseFactory errorResponseFactory;

@Inject
private StatService statService;

@Inject
private AppConfiguration appConfiguration;

Expand Down Expand Up @@ -143,23 +139,32 @@ private static long getToken(Map<String, Long> map, String key) {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response statGet(@HeaderParam("Authorization") String authorization, @QueryParam("month") String month, @QueryParam("format") String format) {
return stat(authorization, month, format);
public Response statGet(@HeaderParam("Authorization") String authorization,
@QueryParam("month") String months,
@QueryParam("start-month") String startMonth,
@QueryParam("end-month") String endMonth,
@QueryParam("format") String format) {
return stat(authorization, months, startMonth, endMonth, format);
}

@POST
@Produces(MediaType.APPLICATION_JSON)
public Response statPost(@HeaderParam("Authorization") String authorization, @FormParam("month") String month, @FormParam("format") String format) {
return stat(authorization, month, format);
public Response statPost(@HeaderParam("Authorization") String authorization,
@FormParam("month") String months,
@FormParam("start-month") String startMonth,
@FormParam("end-month") String endMonth,
@FormParam("format") String format) {
return stat(authorization, months, startMonth, endMonth, format);
}

public Response stat(String authorization, String month, String format) {
public Response stat(String authorization, String monthsParam, String startMonth, String endMonth, String format) {
if (log.isDebugEnabled())
log.debug("Attempting to request stat, month: {}, format: {}", escapeLog(month), escapeLog(format));
log.debug("Attempting to request stat, month: {}, startMonth: {}, endMonth: {}, format: {}",
escapeLog(monthsParam), escapeLog(startMonth), escapeLog(endMonth), escapeLog(format));

errorResponseFactory.validateComponentEnabled(ComponentType.STAT);
validateAuthorization(authorization);
final List<String> months = validateMonth(month);
final Set<String> months = validateMonths(monthsParam, startMonth, endMonth);

try {
if (log.isTraceEnabled())
Expand Down Expand Up @@ -213,25 +218,19 @@ private void validateAuthorization(String authorization) {
}
}

private List<String> validateMonth(String month) {
if (StringUtils.isBlank(month)) {
throw errorResponseFactory.createWebApplicationException(Response.Status.BAD_REQUEST, TokenErrorResponseType.INVALID_REQUEST, "`month` parameter can't be blank and should be in format yyyyMM (e.g. 202012)");
private Set<String> validateMonths(String months, String startMonth, String endMonth) {
if (!Months.isValid(months, startMonth, endMonth)) {
throw errorResponseFactory.createWebApplicationException(Response.Status.BAD_REQUEST, TokenErrorResponseType.INVALID_REQUEST, "`month` or `start-month`/`end-month` parameter(s) can't be blank and should be in format yyyyMM (e.g. 202012)");
}

month = ServerUtil.urlDecode(month);
months = ServerUtil.urlDecode(months);

List<String> months = new ArrayList<>();
for (String m : month.split(" ")) {
m = m.trim();
if (m.length() == 6) {
months.add(m);
}
}
Set<String> monthList = Months.getMonths(months, startMonth, endMonth);

if (months.isEmpty()) {
throw errorResponseFactory.createWebApplicationException(Response.Status.BAD_REQUEST, TokenErrorResponseType.INVALID_REQUEST, "`month` parameter can't be blank and should be in format yyyyMM (e.g. 202012)");
if (monthList.isEmpty()) {
throw errorResponseFactory.createWebApplicationException(Response.Status.BAD_REQUEST, TokenErrorResponseType.INVALID_REQUEST, "Unable to identify months. Check `month` or `start-month`/`end-month` parameter(s). It can't be blank and should be in format yyyyMM (e.g. 202012). start-month must be before end-month");
}

return months;
return monthList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.slf4j.Logger;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

Expand All @@ -27,13 +28,20 @@ public class StatResponseServiceTest {
@Mock
private PersistenceEntryManager entryManager;

@Mock
private StatService statService;

@Mock
private Logger log; // required, don't remove

@Test
public void buildResponse_whenCalled_shouldInvokeEntityManagerOneTimeBecauseSecondTimeResponseMustBeCached() {
when(entryManager.findEntries(any(), any(), any())).thenReturn(new ArrayList<>());
when(statService.getBaseDn()).thenReturn("");

statResponseService.buildResponse(Collections.singletonList("01"));
statResponseService.buildResponse(Collections.singletonList("01"));
statResponseService.buildResponse(Collections.singletonList("01"));
statResponseService.buildResponse(Collections.singleton("01"));
statResponseService.buildResponse(Collections.singleton("01"));
statResponseService.buildResponse(Collections.singleton("01"));

// must be called exactly 1 time, all further calls should use cached response
verify(entryManager, times(1)).findEntries(any(), any(), any());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.jans.as.server.ws.rs.stat;

import org.testng.annotations.Test;

import java.util.Arrays;
import java.util.Set;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

/**
* @author Yuriy Zabrovarnyy
*/
public class MonthsTest {

@Test
public void getMonths_forValidStartAndEndDates_shouldReturnCorrectResult() {
final Set<String> months = Months.getMonths("202112", "202205");
assertEquals(months.size(), 6);
assertTrue(months.containsAll(Arrays.asList("202112", "202201", "202202", "202203", "202204", "202205")));
}

@Test
public void getMonths_whenStartIsAfterEndDates_shouldReturnEmptyList() {
final Set<String> months = Months.getMonths("202212", "202205");
assertTrue(months.isEmpty());
}

@Test
public void isValid_checkDifferentCases() {
assertTrue(Months.isValid(null, "202112", "202205"));
assertTrue(Months.isValid("", "202112", "202205"));
assertTrue(Months.isValid("", "202012", "202205"));
assertTrue(Months.isValid("202012", "", ""));
assertTrue(Months.isValid("202012", null, null));
assertTrue(Months.isValid("202012 202101", null, null));

assertFalse(Months.isValid(null, null, null));
assertFalse(Months.isValid("", "", ""));
assertFalse(Months.isValid("202012 202101", "202012", "202205"));
}
}
1 change: 1 addition & 0 deletions jans-auth-server/server/src/test/resources/testng.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<test name="Unit Tests" enabled="true">
<classes>
<class name="io.jans.as.server.ws.rs.stat.MonthsTest" />
<class name="io.jans.as.server.service.MTLSServiceTest" />
<class name="io.jans.as.server.model.authorize.JwtAuthorizationRequestTest" />
<class name="io.jans.as.server.service.ScopeServiceTest" />
Expand Down

0 comments on commit fd66720

Please sign in to comment.