Commit 8e2e97e8 authored by Andrey Lugovskiy's avatar Andrey Lugovskiy
Browse files

Merge branch '86-bulk-upload-of-descriptors' into 'master'

Resolve "Bulk upload of Descriptors"

Closes #86 and #85

See merge request !66
parents 45ff5280 1f72d82e
......@@ -28,10 +28,16 @@ import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.genesys.blocks.model.Copyable;
import org.genesys.blocks.model.Publishable;
import org.genesys.blocks.model.UuidEntity;
import org.genesys.catalog.model.vocab.ControlledVocabulary;
import org.genesys.common.model.Partner;
import org.springframework.dao.DataIntegrityViolationException;
/**
* A single descriptor definition belonging to a single "owner".
......@@ -39,7 +45,8 @@ import org.genesys.common.model.Partner;
* @author Matija Obreza
*/
@Entity
public class Descriptor extends UuidEntity implements Publishable, Cloneable {
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class Descriptor extends UuidEntity implements Publishable, Copyable<Descriptor> {
private static final long serialVersionUID = 7307818236681549484L;
public static enum DataType {
......@@ -100,10 +107,12 @@ public class Descriptor extends UuidEntity implements Publishable, Cloneable {
@JoinColumn(name = "vocabularyId")
private ControlledVocabulary vocabulary;
@JsonIdentityReference(alwaysAsId = false)
@ManyToOne(cascade = {}, optional = false)
@JoinColumn(name = "partnerId", updatable = false)
private Partner owner;
@JsonIdentityReference(alwaysAsId = false)
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "descriptors", cascade = { CascadeType.REFRESH })
private List<DescriptorList> descriptorLists;
......@@ -324,11 +333,57 @@ public class Descriptor extends UuidEntity implements Publishable, Cloneable {
}
@Override
public Descriptor clone() throws CloneNotSupportedException {
super.clone();
Descriptor descriptor = new Descriptor(this);
descriptor.setUuid(this.uuid);
descriptor.setId(this.getId());
return descriptor;
public Descriptor copy() {
return null;
}
@Override
public Descriptor apply(Descriptor source) {
Copyable.super.apply(source);
if (source.getDataType() == DataType.CODED && source.getVocabulary() == null) {
throw new DataIntegrityViolationException("Coded descriptor " + source.getTitle() + " requires a vocabulary");
}
if (source.getDataType() == DataType.SCALE && (source.getMinValue() == null || source.getMaxValue() == null)) {
throw new DataIntegrityViolationException("Scale descriptor " + source.getTitle() + " requires min and max values.");
}
// this.setDataType(source.getDataType());
// this.setTitle(source.getTitle());
// this.setDescription(source.getDescription());
// this.setIntegerOnly(source.getIntegerOnly());
// this.setMinValue(source.getMinValue());
// this.setMaxValue(source.getMaxValue());
// this.setPublished(source.isPublished());
// this.setOwner(source.getOwner());
// this.setVocabulary(source.getVocabulary());
// this.setColumnName(source.getColumnName());
// this.setUom(source.getUom());
// this.setColumnName(source.getColumnName());
// this.setCode(source.getCode());
// this.setCodeDescription(source.getCodeDescription());
// this.setBibliographicCitation(source.getBibliographicCitation());
switch (this.dataType) {
case BOOLEAN:
this.setVocabulary(null);
this.setMinValue(null);
this.setMaxValue(null);
break;
case CODED:
this.setMinValue(null);
this.setMaxValue(null);
break;
case DATE:
case TEXT:
case NUMERIC:
this.setVocabulary(null);
break;
default:
break;
}
return this;
}
}
......@@ -29,6 +29,9 @@ import javax.persistence.ManyToOne;
import javax.persistence.OrderColumn;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.genesys.blocks.model.Publishable;
import org.genesys.blocks.model.UuidEntity;
import org.genesys.common.model.Partner;
......@@ -38,6 +41,7 @@ import org.genesys.common.model.Partner;
*/
@Entity
@Table(name = "descriptorlist")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class DescriptorList extends UuidEntity implements Publishable {
private static final long serialVersionUID = 4873500390485739013L;
......
......@@ -15,6 +15,7 @@
*/
package org.genesys.catalog.service;
import java.util.List;
import java.util.UUID;
import org.genesys.catalog.model.traits.Descriptor;
......@@ -80,4 +81,11 @@ public interface DescriptorService {
*/
Page<Descriptor> listDescriptorLists(DescriptorFilter descriptorFilter, Pageable page);
/**
* Insert or update all Descriptor records
* @param source
* @return list of updated descriptors
*/
List<Descriptor> upsertDescriptors(List<Descriptor> source);
}
......@@ -15,10 +15,11 @@
*/
package org.genesys.catalog.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.Descriptor.DataType;
import org.genesys.catalog.persistence.traits.DescriptorRepository;
import org.genesys.catalog.service.DescriptorService;
import org.genesys.catalog.service.filters.DescriptorFilter;
......@@ -30,6 +31,7 @@ import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -51,11 +53,11 @@ public class DescriptorServiceImpl implements DescriptorService {
LOG.info("Creating descriptor: {} - {}", input.getTitle(), input.getDataType());
Descriptor descriptor = new Descriptor();
copyValues(descriptor, input);
descriptor.apply(input);
descriptor.setUuid(input.getUuid());
descriptor.setVersionTag(input.getVersionTag());
return descriptorRepository.save(descriptor);
return lazyLoad(descriptorRepository.save(descriptor));
}
/**
......@@ -71,9 +73,9 @@ public class DescriptorServiceImpl implements DescriptorService {
throw new DataIntegrityViolationException("Published descriptor can't be updated");
}
copyValues(descriptor, input);
descriptor.apply(input);
return descriptorRepository.save(descriptor);
return lazyLoad(descriptorRepository.save(descriptor));
}
/**
......@@ -125,8 +127,7 @@ public class DescriptorServiceImpl implements DescriptorService {
@Transactional
public Descriptor updateDescriptor(UUID uuid, int version, Descriptor source) {
Descriptor descriptor = descriptorRepository.findByUuidAndVersion(uuid, version);
copyValues(descriptor, source);
descriptor.apply(source);
return descriptorRepository.save(descriptor);
}
......@@ -138,60 +139,6 @@ public class DescriptorServiceImpl implements DescriptorService {
return descriptorRepository.findAll(page);
}
/**
* Method for copy value from source descriptor to target descriptor
* @param target target descriptor
* @param source source descriptor
* @return descriptor with new values
*/
private Descriptor copyValues(Descriptor target, Descriptor source) {
if (source.getDataType() == DataType.CODED && source.getVocabulary() == null) {
throw new DataIntegrityViolationException("Coded descriptor " + source.getTitle() + " requires a vocabulary");
}
if (source.getDataType() == DataType.SCALE && (source.getMinValue() == null || source.getMaxValue() == null)) {
throw new DataIntegrityViolationException("Scale descriptor " + source.getTitle() + " requires min and max values.");
}
target.setDataType(source.getDataType());
target.setTitle(source.getTitle());
target.setDescription(source.getDescription());
target.setIntegerOnly(source.getIntegerOnly());
target.setMinValue(source.getMinValue());
target.setMaxValue(source.getMaxValue());
target.setPublished(source.isPublished());
target.setOwner(source.getOwner());
target.setVocabulary(source.getVocabulary());
target.setColumnName(source.getColumnName());
target.setUom(source.getUom());
target.setColumnName(source.getColumnName());
target.setCode(source.getCode());
target.setCodeDescription(source.getCodeDescription());
target.setBibliographicCitation(source.getBibliographicCitation());
switch (source.getDataType()) {
case BOOLEAN:
target.setVocabulary(null);
target.setMinValue(null);
target.setMaxValue(null);
break;
case CODED:
target.setMinValue(null);
target.setMaxValue(null);
break;
case DATE:
case TEXT:
case NUMERIC:
target.setVocabulary(null);
break;
default:
break;
}
return target;
}
/**
* {@inheritDoc}
*/
......@@ -202,17 +149,44 @@ public class DescriptorServiceImpl implements DescriptorService {
/**
* Lazy load for objects in descriptor
*
* @param descriptor descriptor
* @return descriptor with loaded inner objects
*/
private Descriptor lazyLoad(Descriptor descriptor) {
if (descriptor == null) {
throw new DataRetrievalFailureException("No such descriptor");
} else {
descriptor.getDescriptorLists().size();
if (descriptor != null) {
if (descriptor.getDescriptorLists()!=null)
descriptor.getDescriptorLists().size();
if (descriptor.getVocabulary() != null)
descriptor.getVocabulary().getId();
}
return descriptor;
}
/*
* (non-Javadoc)
* @see
* org.genesys.catalog.service.DescriptorService#upsertDescriptors(java.util.
* List)
*/
@Override
@Transactional
@PreAuthorize("hasRole('ADMINISTRATOR')")
public List<Descriptor> upsertDescriptors(List<Descriptor> sources) {
List<Descriptor> updates = new ArrayList<>();
for (Descriptor source : sources) {
Descriptor target;
if (source.getVersion() != null) {
target = descriptorRepository.findByUuidAndVersion(source.getUuid(), source.getVersion());
} else {
target = new Descriptor();
target.setUuid(source.getUuid());
}
updates.add(target);
target.apply(source);
}
return descriptorRepository.save(updates);
}
}
......@@ -31,6 +31,9 @@ import javax.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import org.genesys.blocks.model.UuidEntity;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.DescriptorList;
......@@ -42,6 +45,7 @@ import org.genesys.catalog.model.vocab.ControlledVocabulary;
* @author Matija Obreza
*/
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class Partner extends UuidEntity {
private static final long serialVersionUID = 7972197553356837382L;
......@@ -191,14 +195,14 @@ public class Partner extends UuidEntity {
public void setVocabularies(List<ControlledVocabulary> vocabularies) {
this.vocabularies = vocabularies;
}
/**
* @return text about the partner (description)
*/
public String getDescription() {
return description;
}
/**
* @param description
*/
......
......@@ -15,6 +15,20 @@
*/
package org.genesys.catalog.service;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.RandomStringUtils;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.Descriptor.DataType;
import org.genesys.catalog.model.traits.DescriptorList;
......@@ -30,14 +44,6 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class DescriptorServiceTest extends CatalogServiceTest {
private static final String DESCRIPTOR_TITLE_1 = "D1";
......@@ -91,7 +97,7 @@ public class DescriptorServiceTest extends CatalogServiceTest {
assertThat(result.getVocabulary(), nullValue());
}
@Test(expected = DataRetrievalFailureException.class)
@Test
public void testFailLoadDescriptor() {
Descriptor result = descriptorService.getDescriptor(UUID.randomUUID());
assertThat(result, nullValue());
......@@ -340,10 +346,9 @@ public class DescriptorServiceTest extends CatalogServiceTest {
assertThat(result, not(nullValue()));
assertThat(result.getVersionTag(), is(VERSION_1_0));
input.setUuid(result.getUuid());
input.setVersionTag(VERSION_2_0);
input.setVersion(result.getVersion());
result = descriptorService.updateDescriptor(input);
result.setVersionTag(VERSION_2_0);
descriptorService.updateDescriptor(result);
result = descriptorService.getDescriptor(result.getUuid());
assertThat(result.getVersionTag(), is(VERSION_1_0));
}
......@@ -377,4 +382,62 @@ public class DescriptorServiceTest extends CatalogServiceTest {
assertThat(result.getMinValue(), nullValue());
assertThat(result.getMaxValue(), nullValue());
}
@Test
public void testUpsertMultiple() {
List<Descriptor> descriptors = new ArrayList<>();
long timestamp = System.currentTimeMillis();
String description = RandomStringUtils.randomAlphabetic(50, 100);
for (int i = 0; i < 5; i++) {
Descriptor d;
descriptors.add(d = new Descriptor());
d.setOwner(partner);
d.setTitle(DESCRIPTOR_TITLE_1 + timestamp + "-" + i);
d.setVersionTag(VERSION_1_0);
d.setDescription(description + "-" + i);
d.setDataType(DataType.TEXT);
}
List<Descriptor> descriptors2 = descriptorService.upsertDescriptors(descriptors);
assertThat(descriptors2, hasSize(descriptors.size()));
// Update some
for (Descriptor d : descriptors2) {
assertThat(d.getUuid(), not(nullValue()));
assertThat(d.getId(), not(nullValue()));
assertThat(d.getVersion(), not(nullValue()));
d.setDataType(DataType.NUMERIC);
d.setIntegerOnly(true);
d.setMinValue(0d);
d.setMaxValue(10d);
}
// Add some
for (int i = 0; i < 5; i++) {
Descriptor d;
descriptors2.add(d = new Descriptor());
d.setOwner(partner);
d.setTitle(DESCRIPTOR_TITLE_1 + timestamp + "-" + i);
d.setVersionTag(VERSION_2_0);
d.setDescription(description + "-" + i);
d.setDataType(DataType.TEXT);
}
List<Descriptor> descriptors3 = descriptorService.upsertDescriptors(descriptors2);
assertThat(descriptors3, hasSize(descriptors2.size()));
for (int i = 0; i < descriptors3.size(); i++) {
Descriptor d3 = descriptors3.get(i), d2 = descriptors2.get(i);
assertThat(d3.getUuid(), not(nullValue()));
assertThat(d3.getId(), not(nullValue()));
assertThat(d3.getVersion(), not(nullValue()));
assertThat(d3.getTitle(), is(d2.getTitle()));
assertThat(d3.getDescription(), is(d2.getDescription()));
assertThat(d3.getDataType(), is(d2.getDataType()));
assertThat(d3.getIntegerOnly(), is(d2.getIntegerOnly()));
assertThat(d3.getMinValue(), is(d2.getMinValue()));
assertThat(d3.getMaxValue(), is(d2.getMaxValue()));
}
}
}
......@@ -15,6 +15,9 @@
*/
package org.genesys.catalog.server.controller.api.v0;
import java.util.List;
import java.util.UUID;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.DescriptorList;
import org.genesys.catalog.service.DescriptorListService;
......@@ -25,9 +28,14 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Maxym Borodenko
......@@ -117,4 +125,11 @@ public class DescriptorController {
public Descriptor updateDescriptor(@PathVariable("UUID") UUID uuid, @PathVariable("version") int version, @RequestBody Descriptor source) {
return descriptorService.updateDescriptor(uuid, version, source);
}
@RequestMapping(value = "/descriptor/upsert", method = RequestMethod.POST)
@PreAuthorize("hasRole('ADMINISTRATOR')")
public List<Descriptor> updateDescriptor(@RequestBody List<Descriptor> source) {
return descriptorService.upsertDescriptors(source);
}
}
......@@ -16,9 +16,14 @@
package org.genesys.catalog.server.controller.api.v0;
import static org.hamcrest.Matchers.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.HashSet;
import java.util.UUID;
......@@ -46,6 +51,7 @@ import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
/**
......@@ -54,6 +60,8 @@ import org.springframework.web.context.WebApplicationContext;
*/
public class DescriptorControllerTest extends AbstractRestTest {
private static final String PARTNER_NAME = "Partner Name";
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
......@@ -71,11 +79,23 @@ public class DescriptorControllerTest extends AbstractRestTest {
MockMvc mockMvc;
private Partner partner;
@Before
@Transactional
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation).uris().withScheme("https").withHost("api.catalog.genesys-pgr.org").withPort(443)).build();
{
Partner partner = new Partner();
partner.setName(PARTNER_NAME);
partner.setActive(true);
partner.setShortName("P1");
partner.setUuid(UUID.randomUUID());
this.partner = partnerRepository.save(partner);
}
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(documentationConfiguration(restDocumentation).uris().withScheme("https").withHost(
"api.catalog.genesys-pgr.org").withPort(443)).build();
}
@After
......@@ -136,13 +156,14 @@ public class DescriptorControllerTest extends AbstractRestTest {
@Test
public void listDescriptorsTest() throws Exception {
// make something
createDescriptorTest();
DescriptorFilter descriptorFilter = new DescriptorFilter();
PartnerFilter partnerFilter = new PartnerFilter();