package pl.edu.icm.saos.api.search.parameters; import static org.apache.commons.lang3.StringUtils.removeStart; import static pl.edu.icm.saos.api.ApiConstants.PAGE_NUMBER; import static pl.edu.icm.saos.api.ApiConstants.PAGE_SIZE; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import pl.edu.icm.saos.api.services.dates.DatesFactory; import pl.edu.icm.saos.api.services.exceptions.WrongRequestParameterException; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; /** * Provides functionality for extracting request parameters. * @author pavtel */ @Service("parametersExtractor") public class ParametersExtractor { private static final Splitter SPLITTER = Splitter.on(",").trimResults().omitEmptyStrings(); private static final Splitter DATE_SPLITTER = Splitter.on("-").trimResults().omitEmptyStrings(); private static final Pattern DATE_PATTERN = Pattern.compile("^[1-9]\\d{3}-(1[0-2]|0[1-9])-(0[1-9]|[1-2]\\d|3[0-1])$"); private int defaultPageSize=20; @Value("${restful.api.max.page.size}") private int defaultMaxPageSize=100; @Value("${restful.api.min.page.size}") private int defaultMinPageSize=10; //------------------------ LOGIC -------------------------- /** * Extracts 'joined' request parameter. * If paramValue="valA,ValB", then joinedParameter=JoinedParameter("valA,valB", ["valA", "valB"]) * @param parameterValue to process. * @return joinedParameter */ public JoinedParameter extractJoinedParameter(String parameterValue){ String joinedValue = StringUtils.trimToEmpty(parameterValue); Iterable<String> splitedExpand = SPLITTER.split(joinedValue); String[] arrayValues = Iterables.toArray(splitedExpand, String.class); List<String> values = Arrays.asList(arrayValues); Collections.sort(values); return new JoinedParameter(joinedValue, values); } /** * Extracts date in the format "yyyy-MM-DD" into {@link org.joda.time.LocalDate localDate}. * @param value date to process in the format "yyyy-MM-DD". * @param paramName used for exception. * @return localDate representation of value. * @throws WrongRequestParameterException if value has incorrect format. */ public LocalDate extractLocalDate(String value, String paramName) throws WrongRequestParameterException { if(StringUtils.isBlank(value)) return null; if(!DATE_PATTERN.matcher(value).matches()){ throw new WrongRequestParameterException(paramName, "should have format yyyy-MM-DD"); } List<Integer> integers = DATE_SPLITTER .splitToList(value) .stream() .map(s -> removeStart(s, "0")) .map(Integer::valueOf) .collect(Collectors.toList()) ; int year = integers.get(0); int month = integers.get(1); int day = integers.get(2); LocalDate localDate = DatesFactory.utcLocalDate(year, month, day); return localDate; } /** * Constructs Pagination object. * Internally uses {@link #extractAndValidatePagination(int, int, int, int)} with * default minimum and maximum page size ({@link #getDefaultMinPageSize()}, {@link #getDefaultMaxPageSize()} respectively) */ public Pagination extractAndValidatePagination(int pageSize, int pageNumber) throws WrongRequestParameterException { return extractAndValidatePagination(pageSize, pageNumber, defaultMinPageSize, defaultMaxPageSize); } /** * Constructs Pagination object (sets default value for pageSize if necessary). * Validates pageSize and pageNumber parameter. * * @param pageSize to validate * @param pageNumber to validate * @param minPageSize - minimum value accepted for pageSize * @param maxPageSize - maximum value accepted for pageSize * @return pagination * @throws WrongRequestParameterException when pageSize or pageNumber are not correct */ public Pagination extractAndValidatePagination(int pageSize, int pageNumber, int minPageSize, int maxPageSize) throws WrongRequestParameterException { Preconditions.checkArgument(minPageSize <= maxPageSize && minPageSize > 0); int currentPageSize = normalizeAndValidatePageSize(pageSize, minPageSize, maxPageSize); int currentPageNumber = normalizeAndValidatePageNumber(pageNumber); return new Pagination(currentPageSize, currentPageNumber); } private int normalizeAndValidatePageNumber(int pageNumber) throws WrongRequestParameterException { validate(pageNumber, (n) -> n<0, PAGE_NUMBER, "may not be negative"); return pageNumber; } private int normalizeAndValidatePageSize(int pageSize, int minPageSize, int maxPageSize) throws WrongRequestParameterException { int currentPageSize = normalizePageSize(pageSize); validate(currentPageSize, (n) -> n<=0, PAGE_SIZE, "may not be negative"); validate(currentPageSize, (n) -> n>maxPageSize, PAGE_SIZE, "may not be greater than "+maxPageSize); validate(currentPageSize, (n) -> n<minPageSize, PAGE_SIZE, "may not be less than "+minPageSize); return currentPageSize; } private void validate(int value, Predicate<Integer> predicate, String parameterName, String exMsg) throws WrongRequestParameterException { if(predicate.test(value)){ throw new WrongRequestParameterException(parameterName, exMsg); } } private int normalizePageSize(int pageSize){ return pageSize != 0 ? pageSize : defaultPageSize; } //------------------------ GETTERS -------------------------- public int getDefaultMaxPageSize() { return defaultMaxPageSize; } public int getDefaultMinPageSize() { return defaultMinPageSize; } //------------------------ SETTERS -------------------------- public void setDefaultPageSize(int defaultPageSize) { this.defaultPageSize = defaultPageSize; } public void setDefaultMaxPageSize(int defaultMaxPageSize) { this.defaultMaxPageSize = defaultMaxPageSize; } public void setDefaultMinPageSize(int defaultMinPageSize) { this.defaultMinPageSize = defaultMinPageSize; } }