Commit b8649f16 authored by Matija Obreza's avatar Matija Obreza
Browse files

ShortFilterService rewrites existing JSON incompatible with current filter model

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