Commit b8649f16 authored by Matija Obreza's avatar Matija Obreza

ShortFilterService rewrites existing JSON incompatible with current filter model

parent a79c732f
......@@ -32,6 +32,7 @@ import org.genesys.catalog.mvc.DescriptorListExporter;
import org.genesys.catalog.service.DescriptorListService;
import org.genesys.catalog.service.DescriptorService;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.catalog.service.ShortFilterService.FilterInfo;
import org.genesys2.server.api.ApiBaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
......@@ -210,12 +211,8 @@ public class DescriptorListController {
@RequestParam(name = "f", required = false) String filterCode,
@RequestBody(required = false) DescriptorListFilter filter) throws IOException {
if (filterCode != null) {
filter = shortFilterService.filterByCode(filterCode, DescriptorListFilter.class);
} else {
filterCode = shortFilterService.getCode(filter);
}
return new FilteredPage<>(filterCode, filter, descriptorListService.listDescriptorListsForCurrentUser(filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort)));
FilterInfo<DescriptorListFilter> filterInfo = shortFilterService.processFilter(filterCode, filter, DescriptorListFilter.class);
return new FilteredPage<>(filterInfo.filterCode, filterInfo.filter, descriptorListService.listDescriptorListsForCurrentUser(filterInfo.filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort)));
}
/**
......
......@@ -192,4 +192,12 @@ public class DescriptorFilter extends UuidModelFilter<DescriptorFilter, Descript
return this;
}
public PartnerFilter owner() {
if (this.owner == null) {
return this.owner = new PartnerFilter();
} else {
return this.owner;
}
}
}
......@@ -34,7 +34,7 @@ public interface ShortFilterService {
* @return string with normalized filter
* @throws IOException Signals that an I/O exception has occurred.
*/
String normalizeFilter(BasicModelFilter filter) throws IOException;
String normalizeFilter(BasicModelFilter<?,?> filter) throws IOException;
/**
* Load short filter or create a new code.
......@@ -42,7 +42,7 @@ public interface ShortFilterService {
* @param filter original filter object
* @return found or created name of the shortened filter object
*/
String getCode(BasicModelFilter filter);
String getCode(BasicModelFilter<?,?> filter);
/**
* Load ShortFilter by short name.
......@@ -77,6 +77,21 @@ public interface ShortFilterService {
* @return the Instance of type T with data from JSON
* @throws IOException Signals that an I/O exception has occurred.
*/
<T> T filterByCode(String code, Class<T> clazz) throws IOException;
<T extends BasicModelFilter<?,?>> T filterByCode(String code, Class<T> clazz) throws IOException;
/**
* Handles incoming filterCode and filter object for specified target class
*
* @param filterCode
* @param filter
* @param clazz
* @return
* @throws IOException
*/
<T extends BasicModelFilter<?,?>> FilterInfo<T> processFilter(String filterCode, T filter, Class<T> clazz) throws IOException;
public static class FilterInfo<T> {
public T filter;
public String filterCode;
}
}
......@@ -37,6 +37,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
......@@ -52,19 +54,27 @@ public class ShortFilterServiceImpl implements ShortFilterService {
private ShortFilterRepository shortFilterRepository;
private final ObjectMapper mapper;
private final ObjectMapper strictMapper;
public ShortFilterServiceImpl() {
mapper = new ObjectMapper();
strictMapper = new ObjectMapper();
// ignore Null and empty fields
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
strictMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
// can sort keys but cannot sort values
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
strictMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
// ignore outdated properties
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
strictMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
}
@Override
public String normalizeFilter(final BasicModelFilter filter) throws IOException {
public String normalizeFilter(final BasicModelFilter<?, ?> filter) throws IOException {
// get serialized filter without invalid values; with sorted keys but unsorted
// values
......@@ -92,7 +102,7 @@ public class ShortFilterServiceImpl implements ShortFilterService {
@Override
@Transactional
public String getCode(final BasicModelFilter filter) {
public String getCode(final BasicModelFilter<?, ?> filter) {
try {
final String normalizedFilter = normalizeFilter(filter);
......@@ -150,9 +160,35 @@ public class ShortFilterServiceImpl implements ShortFilterService {
}
@Override
public <T> T filterByCode(String code, Class<T> clazz) throws IOException {
public <T extends BasicModelFilter<?, ?>> T filterByCode(String code, Class<T> clazz) throws IOException {
ShortFilter shortFilter = shortFilterRepository.findByCode(code == null ? "" : code);
return mapper.readValue(shortFilter.getJson(), clazz);
return strictMapper.readValue(shortFilter.getJson(), clazz);
}
@Override
@Transactional
public <T extends BasicModelFilter<?, ?>> FilterInfo<T> processFilter(final String filterCode, final T filter, Class<T> clazz) throws IOException {
FilterInfo<T> processedFilter = new FilterInfo<>();
if (filterCode != null) {
try {
processedFilter.filterCode = filterCode;
processedFilter.filter = filterByCode(filterCode, clazz);
} catch (JsonMappingException e) {
LOG.warn("Stored filter does not match target {}", clazz);
ShortFilter shortFilter = shortFilterRepository.findByCode(filterCode == null ? "" : filterCode);
// Reads recognized properties
T updatedFilter = mapper.readValue(shortFilter.getJson(), clazz);
processedFilter.filter = updatedFilter;
processedFilter.filterCode = getCode(updatedFilter);
}
} else {
processedFilter.filter = filter;
processedFilter.filterCode = getCode(filter);
}
return processedFilter;
}
/**
......@@ -178,7 +214,7 @@ public class ShortFilterServiceImpl implements ShortFilterService {
StringBuilder sb = new StringBuilder();
for (Object p : params) {
if (p instanceof BasicModelFilter) {
BasicModelFilter filter = (BasicModelFilter) p;
BasicModelFilter<?, ?> filter = (BasicModelFilter<?, ?>) p;
sb.append(sfs.getCode(filter));
} else if (p instanceof Pageable) {
Pageable page = (Pageable) p;
......
......@@ -25,13 +25,19 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils;
import org.genesys.blocks.model.filters.StringFilter;
import org.genesys.catalog.model.ShortFilter;
import org.genesys.catalog.model.filters.DescriptorFilter;
import org.genesys.catalog.model.filters.DescriptorListFilter;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys.catalog.service.ShortFilterService.FilterInfo;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.dao.DataIntegrityViolationException;
import com.fasterxml.jackson.databind.JsonMappingException;
/**
* @author Maxym Borodenko
* @author Matija Obreza
*/
public class ShortFilterServiceTest extends CatalogServiceTest {
......@@ -60,7 +66,7 @@ public class ShortFilterServiceTest extends CatalogServiceTest {
filter.title = new StringFilter();
filter.title.eq = "TEST";
filter.description = new StringFilter();
final String [] cropValues = { "yam", "banana", "wheat", "rice", "barley", "apple" };
final String[] cropValues = { "yam", "banana", "wheat", "rice", "barley", "apple" };
filter.description.eq = "THIS IS description";
filter.description.contains = "contains description";
filter.crop = Arrays.stream(cropValues).collect(Collectors.toSet());
......@@ -127,8 +133,8 @@ public class ShortFilterServiceTest extends CatalogServiceTest {
}
/**
* It should be able to insert a conflicting json.
* {@link ShortFilter#json} is indexed
* It should be able to insert a conflicting json. {@link ShortFilter#json} is
* indexed
*/
@Test
public void testSaveWithNotUniqueJsonFilters() {
......@@ -141,8 +147,8 @@ public class ShortFilterServiceTest extends CatalogServiceTest {
@Test
public void testNormalizeFilters() throws IOException {
final String [] unsortedCropValues1 = { "Banana", "Wheat", "apple", "yam", "rice", "barley" };
final String [] unsortedCropValues2 = { "yam", "Banana", "Wheat", "rice", "barley", "apple" };
final String[] unsortedCropValues1 = { "Banana", "Wheat", "apple", "yam", "rice", "barley" };
final String[] unsortedCropValues2 = { "yam", "Banana", "Wheat", "rice", "barley", "apple" };
final DescriptorListFilter filter1 = new DescriptorListFilter();
filter1.crop = Arrays.stream(unsortedCropValues1).collect(Collectors.toSet());
......@@ -155,7 +161,7 @@ public class ShortFilterServiceTest extends CatalogServiceTest {
assertEquals(normalizedFilter1, normalizedFilter2);
}
/**
* ShortFilter returns a code for very long filters.
*
......@@ -165,8 +171,55 @@ public class ShortFilterServiceTest extends CatalogServiceTest {
public void testVeryLongFilter() throws IOException {
final DescriptorListFilter filter = new DescriptorListFilter();
filter._text=RandomStringUtils.randomAlphabetic(1000);
filter._text = RandomStringUtils.randomAlphabetic(1000);
String code = shortFilterService.getCode(filter);
assertThat(code, not(nullValue()));
}
/**
* Using
* {@link ShortFilterService#getCode(org.genesys.blocks.model.filters.BasicModelFilter)}
* will throw {@link JsonMappingException} if existing JSON is not compatible
* with target filter model. Use processFilter method instead.
*
* @throws IOException
*/
@Test(expected = JsonMappingException.class)
public void testIncompatibleFilterFail() throws IOException {
final DescriptorFilter descriptorFilter = new DescriptorFilter();
descriptorFilter.owner().active = false;
String filterCode = shortFilterService.getCode(descriptorFilter);
// compatible
shortFilterService.filterByCode(filterCode, DescriptorListFilter.class);
descriptorFilter.used = false;
filterCode = shortFilterService.getCode(descriptorFilter);
// incompatible
shortFilterService.filterByCode(filterCode, DescriptorListFilter.class);
}
/**
* ProcessFilter method will return a different filterCode when existing JSON
* does not match target model.
*
* @throws IOException
*/
@Test
public void testProcessFilterGeneratesNewCode() throws IOException {
final DescriptorFilter descriptorFilter = new DescriptorFilter();
descriptorFilter.owner().active = false;
String filterCode = shortFilterService.getCode(descriptorFilter);
// compatible
DescriptorListFilter listFilter = shortFilterService.filterByCode(filterCode, DescriptorListFilter.class);
descriptorFilter.used = false;
filterCode = shortFilterService.getCode(descriptorFilter);
// new filter code
FilterInfo<DescriptorListFilter> filterInfo = shortFilterService.processFilter(filterCode, null, DescriptorListFilter.class);
assertThat(filterInfo.filter, notNullValue());
assertThat(filterInfo.filterCode, not(filterCode));
filterCode = shortFilterService.getCode(listFilter);
assertThat(filterInfo.filterCode, is(filterCode));
}
}
Markdown is supported
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