Commit 33fb990e authored by Matija Obreza's avatar Matija Obreza

Merge branch '425-versioned-datasets-update' into 'master'

Resolve "Versioned Datasets: update"

Closes #425

See merge request genesys-pgr/genesys-server!376
parents 84694442 4a52ffab
/* /*
* Copyright 2018 Global Crop Diversity Trust * Copyright 2019 Global Crop Diversity Trust
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -17,6 +17,7 @@ package org.genesys.catalog.model.dataset; ...@@ -17,6 +17,7 @@ package org.genesys.catalog.model.dataset;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
...@@ -35,12 +36,16 @@ import javax.persistence.ManyToMany; ...@@ -35,12 +36,16 @@ import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.OrderColumn; import javax.persistence.OrderColumn;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist; import javax.persistence.PrePersist;
import javax.persistence.PreUpdate; import javax.persistence.PreUpdate;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonGetter;
import org.genesys.blocks.auditlog.annotations.Audited; import org.genesys.blocks.auditlog.annotations.Audited;
import org.genesys.blocks.model.JsonViews; import org.genesys.blocks.model.JsonViews;
import org.genesys.blocks.model.Publishable; import org.genesys.blocks.model.Publishable;
...@@ -66,10 +71,10 @@ import com.fasterxml.jackson.annotation.JsonView; ...@@ -66,10 +71,10 @@ import com.fasterxml.jackson.annotation.JsonView;
* *
* @author Matija Obreza * @author Matija Obreza
* @author Andrey Lugovskoy * @author Andrey Lugovskoy
* @author Maxim Borodenko * @author Maxym Borodenko
*/ */
@Entity @Entity
@Table(name = "dataset") @Table(name = "dataset", uniqueConstraints = @UniqueConstraint(name = "UQ_current_dataset_version", columnNames={"versionsId", "current"}))
@Cacheable @Cacheable
@Audited @Audited
@Document(indexName = "dataset") @Document(indexName = "dataset")
...@@ -79,11 +84,13 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl ...@@ -79,11 +84,13 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl
private static final long serialVersionUID = -4601980446454791177L; private static final long serialVersionUID = -4601980446454791177L;
/** The versions. */ /** The versions. */
@ManyToOne(cascade = CascadeType.ALL, optional = false) @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
@JoinColumn(name = "versionsId", updatable = false) @JoinColumn(name = "versionsId", updatable = false)
@JsonIgnore @JsonIgnore
private DatasetVersions versions; private DatasetVersions versions;
private Boolean current;
/** The owner. */ /** The owner. */
@ManyToOne(cascade = {}, optional = false) @ManyToOne(cascade = {}, optional = false)
@JoinColumn(name = "partnerId", updatable = false) @JoinColumn(name = "partnerId", updatable = false)
...@@ -195,6 +202,16 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl ...@@ -195,6 +202,16 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl
@Enumerated(EnumType.ORDINAL) @Enumerated(EnumType.ORDINAL)
private PublishState state = PublishState.DRAFT; private PublishState state = PublishState.DRAFT;
@Transient
private UUID currentVersion;
@PostLoad
protected void postLoad() {
if (this.versions != null && versions.getCurrentVersion() != null && !this.uuid.equals(versions.getCurrentVersion().getUuid())) {
this.currentVersion = versions.getCurrentVersion().getUuid();
}
}
/** /**
* Preupdate. * Preupdate.
*/ */
...@@ -215,6 +232,43 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl ...@@ -215,6 +232,43 @@ public class Dataset extends UuidModel implements Publishable, SelfCleaning, Acl
trimStringsToNull(); trimStringsToNull();
} }
/**
* Gets the current.
*
* @return the current
*/
public Boolean getCurrent() {
return current;
}
/**
* Sets current value
*
* @param current the new value of current
*/
public void setCurrent(final Boolean current) {
this.current = current;
}
/**
* Gets the UUID of current dataset
*
* @return the UUID of current dataset
*/
@JsonGetter
public UUID getCurrentVersion() {
return currentVersion;
}
/**
* Sets the UUID of current dataset
*
* @param currentVersion the UUID of current dataset
*/
public void setCurrentVersion(final UUID currentVersion) {
this.currentVersion = currentVersion;
}
/** /**
* Owner is the ACL parent object for the dataset * Owner is the ACL parent object for the dataset
*/ */
......
...@@ -463,4 +463,12 @@ public interface DatasetService { ...@@ -463,4 +463,12 @@ public interface DatasetService {
*/ */
void writeXlsxMCPD(Dataset dataset, OutputStream outputStream) throws IOException; void writeXlsxMCPD(Dataset dataset, OutputStream outputStream) throws IOException;
/**
* Method creating a new version of Dataset based on an existing published Dataset.
*
* @param source the source
* @return saved Dataset in db.
*/
Dataset createNewVersion(@Valid Dataset source);
} }
...@@ -568,4 +568,16 @@ public class DatasetController extends ApiBaseController { ...@@ -568,4 +568,16 @@ public class DatasetController extends ApiBaseController {
LOG.warn("Download was aborted: {}", e.getMessage()); LOG.warn("Download was aborted: {}", e.getMessage());
} }
} }
/**
* Create a new version of Dataset based on an existing published Dataset.
*
* @param uuid Dataset UUID
* @return the new version of Dataset
*/
@PostMapping(value = "/create-new-version")
public Dataset createNewVersion(@RequestParam(value = "uuid", required = true) final UUID uuid) {
final Dataset dataset = datasetService.loadDataset(uuid);
return datasetService.createNewVersion(dataset);
}
} }
...@@ -33,7 +33,6 @@ import org.genesys.blocks.model.JsonViews; ...@@ -33,7 +33,6 @@ import org.genesys.blocks.model.JsonViews;
import org.genesys.blocks.model.SelfCleaning; import org.genesys.blocks.model.SelfCleaning;
import org.genesys2.server.model.impl.AccessionIdentifier3; import org.genesys2.server.model.impl.AccessionIdentifier3;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.annotation.JsonView;
/** /**
...@@ -86,10 +85,6 @@ public class AccessionRef implements SelfCleaning, AccessionIdentifier3, Seriali ...@@ -86,10 +85,6 @@ public class AccessionRef implements SelfCleaning, AccessionIdentifier3, Seriali
@JoinColumn(name = "accessionId", nullable = true) @JoinColumn(name = "accessionId", nullable = true)
@JsonView({ JsonViews.Public.class }) @JsonView({ JsonViews.Public.class })
protected Accession accession; protected Accession accession;
@Transient
@JsonIgnore
private boolean stored;
public AccessionRef() { public AccessionRef() {
} }
...@@ -321,15 +316,6 @@ public class AccessionRef implements SelfCleaning, AccessionIdentifier3, Seriali ...@@ -321,15 +316,6 @@ public class AccessionRef implements SelfCleaning, AccessionIdentifier3, Seriali
return true; return true;
} }
@Transient
public boolean isStored() {
return stored;
}
public void setStored(boolean stored) {
this.stored = stored;
}
@Override @Override
public String toString() { public String toString() {
return "AccessionRef " + instCode + " " + acceNumb + " " + genus; return "AccessionRef " + instCode + " " + acceNumb + " " + genus;
......
...@@ -471,19 +471,39 @@ public class Subset extends UuidModel implements AclAwareModel, SelfCleaning { ...@@ -471,19 +471,39 @@ public class Subset extends UuidModel implements AclAwareModel, SelfCleaning {
this.source = source; this.source = source;
} }
/**
* Gets the owner.
*
* @return the owner
*/
public Partner getOwner() { public Partner getOwner() {
return owner; return owner;
} }
/**
* Sets the owner.
*
* @param owner the owner
*/
public void setOwner(Partner owner) { public void setOwner(Partner owner) {
this.owner = owner; this.owner = owner;
} }
/**
* Gets the UUID of current subset
*
* @return the UUID of current subset
*/
@JsonGetter @JsonGetter
public UUID getCurrentVersion() { public UUID getCurrentVersion() {
return currentVersion; return currentVersion;
} }
/**
* Sets the UUID of current subset
*
* @param currentVersion the UUID of current subset
*/
public void setCurrentVersion(UUID currentVersion) { public void setCurrentVersion(UUID currentVersion) {
this.currentVersion = currentVersion; this.currentVersion = currentVersion;
} }
......
...@@ -726,24 +726,98 @@ public class SubsetServiceImpl implements SubsetService { ...@@ -726,24 +726,98 @@ public class SubsetServiceImpl implements SubsetService {
subset.setCurrent(null); subset.setCurrent(null);
subset.setUuid(null); subset.setUuid(null);
subset.setVersions(source.getVersions()); subset.setVersions(source.getVersions());
Subset saved = lazyLoad(subsetRepository.save(subset)); Subset saved = subsetRepository.save(subset);
// Copy accessionRefs
copyAccessionRefs(saved, source.getAccessionRefs());
// Copy creators // Copy creators
source.getCreators().forEach(creator -> { copyCreators(saved, source.getCreators());
entityManager.detach(creator);
creator.setSubset(subset);
creator.setId(null);
creator.setVersion(null);
creator.setUuid(null);
subsetCreatorRepository.save(creator);
});
setAccessionRefs(saved, new HashSet<>(source.getAccessionRefs()));
saved.setCurrentVersion(source.getUuid()); saved.setCurrentVersion(source.getUuid());
// Make Subset publicly not-readable // Make Subset publicly not-readable
aclService.makePubliclyReadable(saved, false); aclService.makePubliclyReadable(saved, false);
return lazyLoad(saved); return saved;
}
/**
* Copy and save subset accessionRefs.
*
* @param target the target
* @param accessionRefs the subset accessionRefs
* @return
*/
private Subset copyAccessionRefs(final Subset target, final List<SubsetAccessionRef> accessionRefs) {
if (accessionRefs == null || accessionRefs.size() == 0) {
return target;
}
final Subset loadedSubset = getSubset(target);
List<SubsetAccessionRef> copiedAccessionRefs = Lists.newArrayList();
accessionRefs.forEach(sAccessionRef -> {
SubsetAccessionRef copy = new SubsetAccessionRef();
copyAccessionRef(copy, sAccessionRef);
copy.setSubset(loadedSubset);
copiedAccessionRefs.add(copy);
});
accessionRefRepository.save(copiedAccessionRefs);
loadedSubset.setAccessionCount((int) accessionRefRepository.countBySubset(loadedSubset));
LOG.info("Done saving {} accession refs, have {} in subset", accessionRefs.size(), loadedSubset.getAccessionCount());
return subsetRepository.save(loadedSubset);
}
/**
* Copy and save subset creators.
*
* @param target the target
* @param creators the subset creators
*/
private void copyCreators(final Subset target, final List<SubsetCreator> creators) {
if (creators == null || creators.size() == 0) {
return;
}
List<SubsetCreator> copiedCreators = Lists.newArrayList();
creators.forEach(creator -> {
SubsetCreator copy = new SubsetCreator();
copyCreator(copy, creator);
copy.setSubset(target);
copiedCreators.add(copy);
});
target.setCreators(subsetCreatorRepository.save(copiedCreators));
}
/**
* Copy creator values.
*
* @param target the target
* @param source the source
*/
private void copyCreator(final SubsetCreator target, final SubsetCreator source) {
target.setFullName(source.getFullName());
target.setEmail(source.getEmail());
target.setPhoneNumber(source.getPhoneNumber());
target.setFax(source.getFax());
target.setInstituteAddress(source.getInstituteAddress());
target.setInstitutionalAffiliation(source.getInstitutionalAffiliation());
target.setRole(source.getRole());
}
/**
* Copy subset accessionRef values.
*
* @param target the target
* @param source the source
*/
private void copyAccessionRef(final SubsetAccessionRef target, final SubsetAccessionRef source) {
target.setDoi(source.getDoi());
target.setInstCode(source.getInstCode());
target.setAcceNumb(source.getAcceNumb());
target.setGenus(source.getGenus());
target.setSpecies(source.getSpecies());
target.setAccession(source.getAccession());
} }
} }
...@@ -5783,3 +5783,28 @@ databaseChangeLog: ...@@ -5783,3 +5783,28 @@ databaseChangeLog:
- sql: - sql:
comment: Organization renamed to PGRFANetwork comment: Organization renamed to PGRFANetwork
sql: update classpk set classname='org.genesys2.server.model.impl.PGRFANetwork', shortName='pgrfanetwork' where classname='org.genesys2.server.model.impl.Organization'; sql: update classpk set classname='org.genesys2.server.model.impl.PGRFANetwork', shortName='pgrfanetwork' where classname='org.genesys2.server.model.impl.Organization';
- changeSet:
id: 1552920444624-1
author: mborodenko
comment: Extend dataset table
changes:
- addColumn:
tableName: dataset
columns:
- column:
name: current
type: BIT(1)
defaultValue: null
- sql:
comment: dataset#current should be true if dataset is published
sql: update dataset set current = true where state = 1
- changeSet:
id: 1552920444624-2
author: mborodenko
changes:
- sql:
comment: dataset_version#currentVersion_id must be null if subset is not published
sql: update dataset_version dv inner join dataset d on dv.currentVersion_id = d.id
set dv.currentVersion_id = null where d.state != 1
...@@ -15,23 +15,35 @@ ...@@ -15,23 +15,35 @@
*/ */
package org.genesys.test.base; package org.genesys.test.base;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import org.genesys.catalog.model.Partner; import org.genesys.catalog.model.Partner;
import org.genesys.catalog.model.dataset.Dataset; import org.genesys.catalog.model.dataset.Dataset;
import org.genesys.catalog.model.dataset.DatasetCreator;
import org.genesys.catalog.model.traits.Descriptor; import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.persistence.PartnerRepository; import org.genesys.catalog.persistence.PartnerRepository;
import org.genesys.catalog.persistence.dataset.DatasetAccessionRefRepository;
import org.genesys.catalog.persistence.dataset.DatasetCreatorRepository;
import org.genesys.catalog.persistence.dataset.DatasetRepository; import org.genesys.catalog.persistence.dataset.DatasetRepository;
import org.genesys.catalog.persistence.dataset.DatasetVersionsRepository;
import org.genesys.catalog.persistence.traits.DescriptorRepository; import org.genesys.catalog.persistence.traits.DescriptorRepository;
import org.genesys.catalog.service.DatasetService; import org.genesys.catalog.service.DatasetService;
import org.genesys.catalog.service.ShortFilterService; import org.genesys.catalog.service.ShortFilterService;
import org.genesys.filerepository.persistence.RepositoryFilePersistence; import org.genesys.filerepository.persistence.RepositoryFilePersistence;
import org.genesys.test.config.ApplicationConfig; import org.genesys.test.config.ApplicationConfig;
import org.genesys2.server.model.PublishState; import org.genesys2.server.model.PublishState;
import org.genesys2.server.model.genesys.AccessionRef;
import org.genesys2.server.model.impl.Subset;
import org.genesys2.server.model.impl.SubsetCreator;
import org.genesys2.server.persistence.FaoInstituteRepository; import org.genesys2.server.persistence.FaoInstituteRepository;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
...@@ -58,14 +70,25 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -58,14 +70,25 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class AbstractApiTest extends AbstractTest { public abstract class AbstractApiTest extends AbstractTest {
@Autowired @Autowired
protected ObjectMapper objectMapper; protected static final ObjectMapper objectMapper;
protected static final ObjectMapper verboseMapper = new ObjectMapper(); protected static final ObjectMapper verboseMapper = new ObjectMapper();
static {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
}
@Autowired @Autowired
private WebApplicationContext webApplicationContext; private WebApplicationContext webApplicationContext;
@Autowired @Autowired
protected DatasetRepository datasetRepository; protected DatasetRepository datasetRepository;
@Autowired @Autowired
protected DatasetVersionsRepository datasetVersionsRepository;
@Autowired
protected DatasetCreatorRepository datasetCreatorRepository;
@Autowired
private PartnerRepository partnerRepository; private PartnerRepository partnerRepository;
@Autowired @Autowired
protected DescriptorRepository descriptorRepository; protected DescriptorRepository descriptorRepository;
...@@ -77,6 +100,8 @@ public abstract class AbstractApiTest extends AbstractTest { ...@@ -77,6 +100,8 @@ public abstract class AbstractApiTest extends AbstractTest {
private FaoInstituteRepository instituteRepository; private FaoInstituteRepository instituteRepository;
@Autowired @Autowired
protected RepositoryFilePersistence repositoryFilePersistence; protected RepositoryFilePersistence repositoryFilePersistence;
@Autowired
protected DatasetAccessionRefRepository accessionRefRepository;
protected MockMvc mockMvc; protected MockMvc mockMvc;
@Rule @Rule
...@@ -99,6 +124,9 @@ public abstract class AbstractApiTest extends AbstractTest { ...@@ -99,6 +124,9 @@ public abstract class AbstractApiTest extends AbstractTest {
@Override @Override
@Transactional @Transactional
public void cleanup() throws Exception { public void cleanup() throws Exception {
accessionRefRepository.deleteAll();
datasetVersionsRepository.deleteAll();
datasetCreatorRepository.deleteAll();
datasetRepository.deleteAll(); datasetRepository.deleteAll();
repositoryFilePersistence.deleteAll(); repositoryFilePersistence.deleteAll();
partnerRepository.deleteAll(); partnerRepository.deleteAll();
...@@ -106,20 +134,64 @@ public abstract class AbstractApiTest extends AbstractTest { ...@@ -106,20 +134,64 @@ public abstract class AbstractApiTest extends AbstractTest {
super.cleanup(); super.cleanup();
} }
protected Dataset setUpDataset(final PublishState state) { protected Dataset setUpDataset() {
return setupDataset("Test Dataset Title", "Test Dataset Description", setUpPartner("Partner", true, "PartnerShortName", UUID.randomUUID()), state); return setupDataset("Test Dataset Title", "Test Dataset Description", setUpPartner("Partner", true, "PartnerShortName", UUID.randomUUID()));
} }
protected Dataset setupDataset(final String title, final String description, final Partner owner, final PublishState state) { protected Dataset setupDataset(final String title, final String description, final Partner owner) {
final Dataset input = new Dataset(); final Dataset input = new Dataset();
input.setTitle(title); input.setTitle(title);
input.setDescription(description); input.setDescription(description);
input.setOwner(owner); input.setOwner(owner);
input.setVersionTag("1.0");
// input.setAccessions(accessions); // input.setAccessions(accessions);
input.setState(state);
return input; return input;
} }
protected Dataset toPublished(Dataset dataset) {
if (datasetRepository.findByUuid(dataset.getUuid()) == null) {
dataset = datasetService.createDataset(dataset);
assertThat(dataset.getState(), is(PublishState.DRAFT));
}
if (dataset.getState() == PublishState.DRAFT) {
dataset = datasetService.reviewDataset(dataset);
assertThat(dataset.getState(), is(PublishState.REVIEWING));
}
if (dataset.getState() == PublishState.REVIEWING) {
dataset = datasetService.approveDataset(dataset);