Commit 0eb7b3da authored by Matija Obreza's avatar Matija Obreza
Browse files

Update: application-blocks:4.0-SNAPSHOT

- Updated ShortFilterService from Genesys
parent bcb43e73
......@@ -63,7 +63,7 @@
<liquibase.version>4.4.0</liquibase.version>
<liquibase-hibernate5.version>4.3.5</liquibase-hibernate5.version>
<application.blocks.version>3.2-SNAPSHOT</application.blocks.version>
<application.blocks.version>4.0-SNAPSHOT</application.blocks.version>
<!--Container -->
<jetty.version>9.4.46.v20220331</jetty.version>
......
......@@ -105,49 +105,49 @@ public abstract class ActionController<T extends AbstractAction<T>, F extends Ac
}
@PostMapping(value = "/action/list/completed", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listCompletedActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listCompletedActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listCompletedActions(filter, pageable));
}
@PostMapping(value = "/action/list/inProgress", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listInProgressActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listInProgressActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listInProgressActions(filter, pageable));
}
@PostMapping(value = "/action/list/scheduled", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listScheduledActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listScheduledActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listScheduledActions(filter, pageable));
}
@PostMapping(value = "/action/list/added", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listAddedActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listAddedActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listAddedActions(filter, pageable));
}
@PostMapping(value = "/action/list/created", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listCreatedActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listCreatedActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listCreatedActions(filter, pageable));
}
@PostMapping(value = "/action/list/overdue", produces = { MediaType.APPLICATION_JSON_VALUE })
public FilteredPage<T, ASF> listOverdueActions(@Parameter(hidden = true) final Pagination page, @RequestBody(required = true) final ASF filter) throws IOException {
var normalizedFilter = shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
public FilteredPage<T, ASF> listOverdueActions(@Parameter(hidden = true) final Pagination page, @RequestBody final ASF filter) throws IOException {
var normalizedFilter = (ASF) shortFilterService.normalizeFilter(filter, getActionScheduleFilterType());
Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
return new FilteredPage<>(normalizedFilter, actionService.listOverdueActions(filter, pageable));
}
@PostMapping("/schedule")
public ActionScheduleOverview actionSchedule(@RequestBody(required = true) final ASF filter) {
public ActionScheduleOverview actionSchedule(@RequestBody final ASF filter) {
return actionService.actionScheduleOverview(filter);
}
......@@ -215,12 +215,12 @@ public abstract class ActionController<T extends AbstractAction<T>, F extends Ac
*/
@SuppressWarnings("unchecked")
protected Class<F> filterType() {
return ((Class<F>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]);
return (Class<F>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
}
@SuppressWarnings("unchecked")
private Class<ASF> getActionScheduleFilterType() {
return ((Class<ASF>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3]);
private Class<ActionScheduleFilter<T>> getActionScheduleFilterType() {
return (Class<ActionScheduleFilter<T>>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3];
}
}
......@@ -19,7 +19,6 @@ import java.util.List;
import org.gringlobal.model.ShortFilter;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
......@@ -51,6 +50,5 @@ public interface ShortFilterRepository extends JpaRepository<ShortFilter, Long>,
* @param json the json
* @return the current ShortFilter for JSON
*/
@Query("select sf from ShortFilter sf where json=?1 and redirect is null")
ShortFilter getCurrentForJson(String json);
ShortFilter findFirstByJsonAndRedirectIsNullOrderByIdAsc(String json);
}
......@@ -15,11 +15,11 @@
*/
package org.gringlobal.service;
import java.io.IOException;
import org.genesys.blocks.model.filters.SuperModelFilter;
import org.gringlobal.model.ShortFilter;
import java.io.IOException;
/**
* The Interface ShortFilterService.
*
......@@ -34,7 +34,7 @@ public interface ShortFilterService {
* @return string with normalized filter
* @throws IOException Signals that an I/O exception has occurred.
*/
String normalizeFilter(SuperModelFilter<?,?> filter) throws IOException;
<T extends SuperModelFilter<T, ?>> String normalizeFilter(T filter) throws IOException;
/**
* Normalize the filter.
......@@ -43,15 +43,16 @@ public interface ShortFilterService {
* @param clazz the clazz
* @throws IOException Signals that an I/O exception has occurred.
*/
<T extends SuperModelFilter<?, ?>> T normalizeFilter(SuperModelFilter<?, ?> filter, Class<T> clazz) throws IOException;
<T extends SuperModelFilter<T, ?>> T normalizeFilter(T filter, Class<T> clazz) throws IOException;
/**
* Load short filter or create a new code.
*
* @param filter original filter object
* @return found or created name of the shortened filter object
* @throws IOException
*/
String getCode(SuperModelFilter<?,?> filter);
<T extends SuperModelFilter<T, ?>> String getCode(T filter) throws IOException;
/**
* Load ShortFilter by short name.
......@@ -78,18 +79,7 @@ public interface ShortFilterService {
* @return the Instance of type T with data from JSON
* @throws IOException Signals that an I/O exception has occurred.
*/
<T extends SuperModelFilter<?,?>> T filterByCode(String code, Class<T> clazz) throws IOException;
/**
* Handles incoming filterCode and filter object for specified target class
*
* @param filterCode the filterCode
* @param filter the filter
* @param clazz the class
* @return processed filter
* @throws IOException
*/
<T extends SuperModelFilter<?,?>> FilterInfo<T> processFilter(String filterCode, T filter, Class<T> clazz) throws IOException;
<T extends SuperModelFilter<T,?>> T filterByCode(String code, Class<T> clazz) throws IOException;
public static class FilterInfo<T> {
public T filter;
......@@ -98,11 +88,33 @@ public interface ShortFilterService {
/**
* Upgrade a {@link ShortFilter} to the current version of the short code.
*
*
* Maintains the original JSON + registers a new entry!
*
*
* @param shortFilter the filter
* @return filterCode by current standards
*/
ShortFilter upgradeFilterCode(ShortFilter shortFilter);
/**
* Parse JSON to filter type
*
* @param json
* @param clazz
* @return
* @throws IOException
*/
<T extends SuperModelFilter<T,?>> T readFilter(String json, Class<T> clazz) throws IOException;
/**
* Handles incoming filterCode and filter object for specified target class
*
* @param filterCode the filterCode
* @param filter the filter
* @param clazz the class
* @return processed filter
* @throws IOException
*/
<T extends SuperModelFilter<T, ?>> FilterInfo<T> processFilter(String filterCode, T filter, Class<T> clazz) throws IOException;
}
......@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.genesys.blocks.model.filters.SuperModelFilter;
import org.gringlobal.api.exception.NotFoundElement;
import org.gringlobal.model.ShortFilter;
......@@ -49,17 +50,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Maxym Borodenko
* @author Matija Obreza
*/
@Service
@Transactional(readOnly = true)
public class ShortFilterServiceImpl implements ShortFilterService, InitializingBean {
private static final Logger LOG = LoggerFactory.getLogger(ShortFilterServiceImpl.class);
public static final String CODE_PREFIX_V1 = "v1";
public static final String CODE_PREFIX_V2 = "v2";
public static final String CODE_PREFIX_CURRENT = CODE_PREFIX_V1;
public static final String CODE_PREFIX_CURRENT = CODE_PREFIX_V2;
@Autowired
private ShortFilterRepository shortFilterRepository;
......@@ -72,6 +71,7 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
private Hashids hashids;
public ShortFilterServiceImpl() {
mapper = new ObjectMapper();
strictMapper = new ObjectMapper();
......@@ -87,10 +87,11 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
// ignore outdated properties
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
strictMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
// upgrade to arrays
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
// don't ignore Null fields in this mapper
mapperWithAllFields = mapper.copy().setSerializationInclusion(JsonInclude.Include.USE_DEFAULTS);
}
......@@ -101,9 +102,14 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
hashids = new Hashids(this.hashidsSalt, 4);
}
@Override
public <T extends SuperModelFilter<T, ?>> T readFilter(String json, Class<T> clazz) throws IOException {
return BasicModelFilter.normalize(mapper.readValue(json, clazz));
}
@Override
@SuppressWarnings(value = "unchecked")
public String normalizeFilter(final SuperModelFilter<?, ?> filter) throws IOException {
public <T extends SuperModelFilter<T, ?>> String normalizeFilter(final T filter) throws IOException {
// Defaults
SuperModelFilter<?, ?> defaultFilter = null;
......@@ -117,11 +123,12 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
// get serialized filter without invalid values; with sorted keys but unsorted
// values
final String serializedFilter = mapper.writeValueAsString(SuperModelFilter.normalize(filter));
final String serializedFilter = mapper.writeValueAsString(BasicModelFilter.normalize(filter));
// we have to deserialize filter object to be able to sort values
final Object filterObject = mapper.readValue(serializedFilter, Object.class);
if (defaultFilterMap == null || defaultFilterMap.size() == 0) {
sortFilterValues(filterObject);
return mapper.writeValueAsString(filterObject);
......@@ -140,7 +147,7 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
}
@Override
public <T extends SuperModelFilter<?, ?>> T normalizeFilter(SuperModelFilter<?, ?> filter, Class<T> clazz) throws IOException {
public <T extends SuperModelFilter<T, ?>> T normalizeFilter(T filter, Class<T> clazz) throws IOException {
if (filter == null) {
try {
return clazz.getDeclaredConstructor().newInstance();
......@@ -153,8 +160,11 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
}
}
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@SuppressWarnings(value = "unchecked")
private void sortFilterValues(final Object filterObject) {
if (filterObject == null || !(filterObject instanceof Map)) {
return;
}
final Map<String, Object> map = (Map<String, Object>) filterObject;
map.forEach((key, value) -> {
if (value instanceof List) {
......@@ -167,41 +177,53 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
@Override
@Transactional
public String getCode(final SuperModelFilter<?, ?> filter) {
try {
final String normalizedFilter = normalizeFilter(filter);
public <T extends SuperModelFilter<T, ?>> String getCode(final T filter) throws IOException {
final String normalizedFilter = normalizeFilter(filter);
ShortFilter shortFilter = shortFilterRepository.getCurrentForJson(normalizedFilter);
ShortFilter shortFilter = loadByJSON(normalizedFilter);
if (shortFilter != null) {
// return existing shortFilter
return shortFilter.getCode();
}
// store new filter
final String shortName = generateFilterCode();
shortFilter = shortFilterRepository.save(new ShortFilter(shortName, normalizedFilter));
if (shortFilter != null) {
// return existing shortFilter
return shortFilter.getCode();
} catch (Throwable e) {
LOG.error("Short Filter error: {}", e.getMessage(), e);
return null;
}
// store new filter
final String shortName = generateFilterCode();
shortFilter = shortFilterRepository.save(new ShortFilter(shortName, normalizedFilter));
return shortFilter.getCode();
}
private String generateFilterCode() {
return generateFilterCodeV1();
return generateFilterCodeV2();
}
// /**
// * Generate short name V1.
// *
// * @return the shortName
// * @deprecated Use {@link #generateFilterCodeV2()}
// */
// @Deprecated
// private String generateShortNameV1() {
// for (int i = 0; i < 10; i++) {
// final String code = CODE_PREFIX_V1.concat(UUID.randomUUID().toString().replace("-", ""));
// // return shortName if unique
// if (shortFilterRepository.findByCode(code) == null) {
// return code;
// }
// }
// throw new RuntimeException("Failed to generate a unique filters short name in several attempts");
// }
/**
* Generate short name V1.
* Generate short name V2.
*
* @return the shortName
*/
private String generateFilterCodeV1() {
private String generateFilterCodeV2() {
for (int i = 0; i < 10; i++) {
String hashId = hashids.encode(System.currentTimeMillis());
final String code = CODE_PREFIX_V1.concat(hashId);
final String code = CODE_PREFIX_V2.concat(hashId);
// return shortName if unique
if (shortFilterRepository.findByCode(code) == null) {
return code;
......@@ -216,17 +238,20 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
}
@Override
@Transactional(readOnly = true)
public ShortFilter loadByCode(final String code) {
return shortFilterRepository.findByCode(code);
}
@Override
@Transactional(readOnly = true)
public ShortFilter loadByJSON(final String jsonFilters) {
return shortFilterRepository.getCurrentForJson(jsonFilters);
return shortFilterRepository.findFirstByJsonAndRedirectIsNullOrderByIdAsc(jsonFilters);
}
@Override
public <T extends SuperModelFilter<?, ?>> T filterByCode(String code, Class<T> clazz) throws IOException {
@Transactional(readOnly = true)
public <T extends SuperModelFilter<T, ?>> T filterByCode(String code, Class<T> clazz) throws IOException {
ShortFilter shortFilter = shortFilterRepository.findByCode(code == null ? "" : code);
if (shortFilter == null) {
......@@ -239,18 +264,19 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
while (shortFilter.getRedirect() != null) {
if (codes.contains(shortFilter.getRedirect())) {
LOG.warn("Cyclic shortFilter redirect for {}", shortFilter.getRedirect());
return SuperModelFilter.normalize(strictMapper.readValue(shortFilter.getJson(), clazz));
return BasicModelFilter.normalize(strictMapper.readValue(shortFilter.getJson(), clazz));
}
shortFilter = shortFilterRepository.findByCode(shortFilter.getRedirect());
codes.add(shortFilter.getCode());
}
return SuperModelFilter.normalize(strictMapper.readValue(shortFilter.getJson(), clazz));
return BasicModelFilter.normalize(strictMapper.readValue(shortFilter.getJson(), clazz));
}
@Override
@Transactional
public <T extends SuperModelFilter<?, ?>> FilterInfo<T> processFilter(final String filterCode, final T filter, Class<T> clazz) throws IOException {
public <T extends SuperModelFilter<T, ?>> FilterInfo<T> processFilter(final String filterCode, final T filter, Class<T> clazz) throws IOException {
FilterInfo<T> processedFilter = new FilterInfo<>();
if (filterCode != null) {
try {
......@@ -296,9 +322,13 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
StringBuilder sb = new StringBuilder();
for (Object p : params) {
if (p instanceof SuperModelFilter) {
SuperModelFilter<?, ?> filter = (SuperModelFilter<?, ?>) p;
sb.append(sfs.getCode(filter));
if (p instanceof BasicModelFilter) {
BasicModelFilter filter = (BasicModelFilter) p;
try {
sb.append(sfs.getCode(filter));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (p instanceof Pageable) {
Pageable page = (Pageable) p;
sb.append("-").append(page.getOffset());
......@@ -308,26 +338,32 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
});
}
}
return sb.length() > 0 ? sb : "";
// if (sb.length() > 0) {
// System.err.println("Cachekey: " + sb);
// }
return sb.length() > 0 ? sb : null;
}
}
/* (non-Javadoc)
* @see org.genesys.catalog.service.ShortFilterService#upgradeFilterCode(org.genesys.catalog.model.ShortFilter)
*/
@Override
@Transactional
public ShortFilter upgradeFilterCode(ShortFilter shortFilter) {
assert (shortFilter.getJson() != null);
assert (shortFilter.getCode() != null);
// If filter code is current, ignore.
assert(shortFilter.getJson() != null);
assert(shortFilter.getCode() != null);
// If filter code is current, ignore.
if (shortFilter.getCode().startsWith(CODE_PREFIX_CURRENT)) {
return shortFilter;
}
// If there's a filter with CODE_PREFIX_CURRENT for the same JSON, ignore
List<ShortFilter> existingShortFilters = shortFilterRepository.findByJson(shortFilter.getJson());
ShortFilter filterWithCurrentCode = null;
int needUpgrade = 0;
for (ShortFilter existing : existingShortFilters) {
for (ShortFilter existing: existingShortFilters) {
if (existing.getCode().startsWith(CODE_PREFIX_CURRENT) && existing.getRedirect() == null) {
filterWithCurrentCode = existing;
} else {
......@@ -341,8 +377,8 @@ public class ShortFilterServiceImpl implements ShortFilterService, InitializingB
ShortFilter newFilter = filterWithCurrentCode != null ? filterWithCurrentCode : shortFilterRepository.save(new ShortFilter(generateFilterCode(), shortFilter.getJson()));
for (ShortFilter existing : existingShortFilters) {
if (!newFilter.getCode().equalsIgnoreCase(existing.getCode())) {
for (ShortFilter existing: existingShortFilters) {
if (! newFilter.getCode().equalsIgnoreCase(existing.getCode())) {
existing.setRedirect(newFilter.getCode());
shortFilterRepository.save(existing);
}
......
......@@ -69,14 +69,14 @@ public class ShortFilterServiceTest extends AbstractServicesTest {
}
@Test
public void testCreate() {
public void testCreate() throws IOException {
final TaxonomySpeciesFilter filter = new TaxonomySpeciesFilter();
filter.name().eq("TEST");
filter.alternateName().eq("This is test alternate name");
filter.alternateName().contains("test");
String shortName = shortFilterService.getCode(filter);
assertThat(shortName, startsWith(ShortFilterServiceImpl.CODE_PREFIX_V1));
assertThat(shortName, startsWith(ShortFilterServiceImpl.CODE_PREFIX_CURRENT));
final ShortFilter shortFilter = shortFilterService.loadByCode(shortName);
assertThat(shortFilter, not(nullValue()));
......@@ -142,7 +142,7 @@ public class ShortFilterServiceTest extends AbstractServicesTest {
* ShortFilter returns a code for very long filters.
*/
@Test
public void testVeryLongFilter() {
public void testVeryLongFilter() throws IOException {
final TaxonomySpeciesFilter filter = new TaxonomySpeciesFilter();
filter.name().eq(RandomStringUtils.randomAlphabetic(1000));
......@@ -201,7 +201,7 @@ public class ShortFilterServiceTest extends AbstractServicesTest {
* Test that short filter codes for different inputs don't collide.
*/
@Test
public void testCodeCollision() {
public void testCodeCollision() throws IOException {
Set<String> shortCodes = new HashSet<>();
final TaxonomyGenusFilter filter = new TaxonomyGenusFilter();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment