Commit 74056e05 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov
Browse files

Merge branch '458-requests-for-material-without-pid' into 'master'

Resolve "Requests for material without PID"

Closes #458

See merge request genesys-pgr/genesys-server!434
parents 64f60373 50799e91
......@@ -44,6 +44,7 @@ public class MaterialRequestSerializer extends JsonSerializer<MaterialRequest> {
jgen.writeObjectField("createdDate", request.getCreatedDate());
jgen.writeObjectField("lastModifiedDate", request.getLastModifiedDate());
jgen.writeObjectField("lastReminderDate", request.getLastReminderDate());
jgen.writeObjectField("internalRequest", request.isInternalRequest());
try {
List<MaterialSubRequest> list;
......
......@@ -76,6 +76,9 @@ public class MaterialRequest extends AuditedVersionedModel {
@Temporal(TemporalType.TIMESTAMP)
private Date lastReminderDate;
@Column(name = "internal")
private boolean internalRequest = false;
@PrePersist
void prepersist() {
if (this.uuid == null) {
......@@ -150,6 +153,14 @@ public class MaterialRequest extends AuditedVersionedModel {
return lastReminderDate;
}
public boolean isInternalRequest() {
return internalRequest;
}
public void setInternalRequest(boolean internalRequest) {
this.internalRequest = internalRequest;
}
public void setLastReminderDate(Date lastReminderDate) {
this.lastReminderDate = lastReminderDate;
}
......
......@@ -28,6 +28,9 @@ import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public interface EasySMTA {
EasySMTAUserData getUserData(String emailAddress) throws EasySMTAException;
......@@ -40,12 +43,24 @@ public interface EasySMTA {
public static class EasySMTAUserData {
private String pid;
// / "in" = Individual, "or" = Organization
@NotNull
@Size(max = 2)
private String type;
private String legalStatus;
@NotNull
@Size(max = 200)
private String name;
@NotNull
@Size(max = 200)
private String surname;
@NotNull
@Size(max = 100)
private String email;
@NotNull
@Size(max = 500)
private String address;
@NotNull
@Size(max = 3, min = 3)
private String country;
private String countryName;
private String telephone;
......
......@@ -109,12 +109,13 @@ public interface RequestService {
}
public static class RequestInfo {
@Size(max = 200)
@NotNull
private String email;
private int purposeType;
private boolean preacceptSMTA;
private String notes;
@Valid
private EasySMTA.EasySMTAUserData userData;
private boolean internalRequest;
public String getEmail() {
return email;
......@@ -147,6 +148,22 @@ public interface RequestService {
public void setNotes(String notes) {
this.notes = notes;
}
public EasySMTA.EasySMTAUserData getUserData() {
return userData;
}
public void setUserData(EasySMTA.EasySMTAUserData userData) {
this.userData = userData;
}
public boolean isInternalRequest() {
return internalRequest;
}
public void setInternalRequest(boolean internalRequest) {
this.internalRequest = internalRequest;
}
}
Page<MaterialRequest> list(MaterialRequestFilter filter, Pageable pageable);
......
/**
* Copyright 2014 Global Crop Diversity Trust
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -12,7 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
*/
package org.genesys2.server.service.impl;
......@@ -25,9 +25,11 @@ import org.genesys2.server.model.genesys.MaterialRequest;
import org.genesys2.server.model.genesys.MaterialSubRequest;
import org.genesys2.server.model.genesys.QMaterialSubRequest;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.Country;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.model.impl.FaoInstituteSetting;
import org.genesys2.server.model.impl.VerificationToken;
import org.genesys2.server.persistence.CountryRepository;
import org.genesys2.server.persistence.MaterialRequestRepository;
import org.genesys2.server.persistence.MaterialSubRequestRepository;
import org.genesys2.server.service.*;
......@@ -90,6 +92,9 @@ public class RequestServiceImpl implements RequestService {
@Autowired
private MaterialSubRequestRepository subRequestRepository;
@Autowired
private CountryRepository countryRepository;
@Value("${frontend.url}")
private String frontendUrl;
......@@ -100,44 +105,41 @@ public class RequestServiceImpl implements RequestService {
@Override
@Transactional
public MaterialRequest initiateRequest(RequestInfo requestInfo, Set<Long> accessionIds) throws RequestException {
public MaterialRequest initiateRequest(@Valid RequestInfo requestInfo, Set<Long> accessionIds) throws RequestException {
final Set<Long> availableAccessionIds = genesysService.filterAvailableForDistribution(accessionIds);
final Locale locale = LocaleContextHolder.getLocale();
System.err.println("Current locale: " + locale);
if (availableAccessionIds == null || availableAccessionIds.size() == 0) {
throw new RequestException("None of the selected accessions are available for distribution");
}
// Check Easy-SMTA for PID
EasySMTA.EasySMTAUserData pid;
try {
pid = pidChecker.getUserData(requestInfo.getEmail());
} catch (EasySMTAException e) {
throw new RequestException(e.getMessage(), e);
}
final MaterialRequest request = createRequest(requestInfo, pid, availableAccessionIds);
return sendValidationEmail(request);
return initiateRequest(availableAccessionIds, requestInfo);
}
@Override
@Transactional
public MaterialRequest initiateRequestByUuids(@Valid RequestInfo requestInfo, Set<UUID> accessionUuids) throws RequestException {
final Set<Long> availableAccessionIds = accessionService.filterAvailableForDistributionByUuid(accessionUuids);
return initiateRequest(availableAccessionIds, requestInfo);
}
private MaterialRequest initiateRequest(Set<Long> availableAccessionIds, RequestInfo requestInfo) throws RequestException {
final Locale locale = LocaleContextHolder.getLocale();
System.err.println("Current locale: " + locale);
LOG.error("Current locale: {}", locale);
if (availableAccessionIds == null || availableAccessionIds.size() == 0) {
throw new RequestException("None of the selected accessions are available for distribution");
}
// Check Easy-SMTA for PID
EasySMTA.EasySMTAUserData pid;
try {
pid = pidChecker.getUserData(requestInfo.getEmail());
} catch (EasySMTAException e) {
throw new RequestException(e.getMessage(), e);
if (!requestInfo.isInternalRequest()) {
// Check Easy-SMTA for PID
try {
pid = pidChecker.getUserData(requestInfo.getEmail());
} catch (EasySMTAException e) {
throw new RequestException(e.getMessage(), e);
}
} else {
pid = requestInfo.getUserData();
requestInfo.setEmail(pid.getEmail());
Country country = countryRepository.findByCode3(pid.getCountry());
if (country != null) {
pid.setCountryName(country.getName(locale));
}
}
final MaterialRequest request = createRequest(requestInfo, pid, availableAccessionIds);
......@@ -159,11 +161,11 @@ public class RequestServiceImpl implements RequestService {
}
Article article;
if (StringUtils.isBlank(materialRequest.getPid())) {
if (StringUtils.isNotBlank(materialRequest.getPid()) || materialRequest.isInternalRequest()) {
article = contentService.getGlobalArticle(ContentService.SMTP_MATERIAL_CONFIRM, Locale.ENGLISH);
} else {
LOG.warn("No such user in ITPGRFA system");
article = contentService.getGlobalArticle(ContentService.SMTP_MATERIAL_CONFIRM_NO_PID, Locale.ENGLISH);
} else {
article = contentService.getGlobalArticle(ContentService.SMTP_MATERIAL_CONFIRM, Locale.ENGLISH);
}
// Generate verification token+key
......@@ -175,6 +177,7 @@ public class RequestServiceImpl implements RequestService {
final Map<String, Object> root = new HashMap<String, Object>();
root.put("baseUrl", frontendUrl);
root.put("verificationToken", verificationToken);
root.put("internal", materialRequest.isInternalRequest());
root.put("pid", rb.pid);
root.put("accessions", accessions.getContent());
......@@ -200,6 +203,13 @@ public class RequestServiceImpl implements RequestService {
request.setEmail(requestInfo.getEmail());
if (pid != null) {
if (requestInfo.isInternalRequest()) {
request.setInternalRequest(true);
// set to null to avoid duplication of user data in the RequestBody for MaterialRequest#body
// we don't need to save this data in the storage
requestInfo.setUserData(null);
}
request.setPid(pid.getPid());
}
......@@ -301,7 +311,9 @@ public class RequestServiceImpl implements RequestService {
materialRequest.setState(MaterialRequest.VALIDATED);
materialRequest = requestRepository.save(materialRequest);
recheckPid(materialRequest);
if (!materialRequest.isInternalRequest()) {
recheckPid(materialRequest);
}
breakup(materialRequest);
relayRequests(materialRequest);
......@@ -318,7 +330,7 @@ public class RequestServiceImpl implements RequestService {
* @throws NoPidException
*/
private List<MaterialSubRequest> breakup(MaterialRequest materialRequest) throws NoPidException {
if (StringUtils.isBlank(materialRequest.getPid())) {
if (StringUtils.isBlank(materialRequest.getPid()) && !materialRequest.isInternalRequest()) {
LOG.warn("Material request has no PID, will not break it up.");
throw new NoPidException("Not breaking up request without PID.");
}
......
{
"template": true,
"en": {
"title": "Confirm request for Material",
"body": "## VELOCITY\n<h2><small><a href=\"${baseUrl}\" rel=\"nofollow\">genesys-pgr.org</a></small>\n<br />Request for Material</h2>\n\n<p>Your request for material is almost complete. The email address you have used is <strong>not yet registered</strong> with ITPGRFA's Easy-SMTA system. Please follow the following steps to complete your request:</p>\n\n<ol><li>Register with ITPGRFA's Easy-SMTA at <a href=\"https://mls.planttreaty.org/itt/index.php?r=user/register\">https://mls.planttreaty.org/itt/index.php?r=user/register</a></li><li>Validate your request for material by clicking <a href=\"${baseUrl}/request/${verificationToken.uuid}/validate?key=${verificationToken.key}\" rel=\"nofollow\">here</a> or visit the following link:<br />\n${baseUrl}/request/${verificationToken.uuid}/validate?key=${verificationToken.key}</li><li>Enter the following validation key: <strong>${verificationToken.key}</strong></li></ol>\n\n<h3>Accession list</h3>\n<table>\n#foreach( $acn in $accessions )\n <tr><td>${acn.accessionNumber}</td><td>${acn.instituteCode}</td><td>$!{acn.origin}</td><td>${acn.taxonomy.taxonName}</td></tr>\n#end\n</table>\n\n<p>Thank you,<br />Genesys team</p>"
}
}
{
"template": true,
"en": {
"title": "Request for Material",
"body": "## VELOCITY\n<h2><small><a href=\"${baseUrl}\" rel=\"nofollow\">genesys-pgr.org</a></small>\n<br />Request for Material</h2>\n\n#if ($internal)\n<p>Your request for material is almost complete. Please follow the following steps to complete your request:</p>\n#else\n<p>Your request for material is almost complete. The email address you have used is registered with ITPGRFA's Easy-SMTA system. Please follow the following steps to complete your request:</p>\n#end\n<ol><li>Validate your request for material by clicking <a href=\"${baseUrl}/request/${verificationToken.uuid}/validate?key=${verificationToken.key}\" rel=\"nofollow\">here</a> or visit the following link:<br />${baseUrl}/request/${verificationToken.uuid}/validate?key=${verificationToken.key}</li><li>Enter the following validation key: <b>${verificationToken.key}</b></li></ol>\n\n<h3>Accession list</h3>\n<table>\n#foreach( $acn in $accessions )\n <tr><td>${acn.accessionNumber}</td><td>${acn.instituteCode}</td><td>$!{acn.origin}</td><td>${acn.taxonomy.taxonName}</td></tr>\n#end\n</table>\n\n<p>Thank you,<br />Genesys team</p>\n"
}
}
\ No newline at end of file
{
"template": true,
"en": {
"title": "Verify your email address",
"body": "<h2><small>genesys-pgr.org</small>\n<br/>Request for Material</h2><p></p><p>Thanks,<br/ >Genesys team</p>"
"title": "Request for Material",
"body": "## VELOCITY\n<h2><small><a href=\"https://www.genesys-pgr.org\">www.genesys-pgr.org</a></small>\n<br />Request for Material</h2>\n\n<h3>Confirm receipt of request</h3>\n<ol><li>Confirm receipt of request for material by clicking <a href=\"${baseUrl}/request/${verificationToken.uuid}/confirm?key=${verificationToken.key}\" rel=\"nofollow\">here</a> or visit the following link:<br />${baseUrl}/request/${verificationToken.uuid}/confirm?key=${verificationToken.key}</li>\n<li>Enter the following validation key: <strong>${verificationToken.key}</strong></li>\n</ol>\n\n<p><b>PID:</b> ${pid.pid} [${pid.type}]</p>\n\n<h3>Individual</h3>\n#if ($pid.type == \"in\")\n<div>${pid.name} ${pid.surname}</div>\n<div>${pid.orgName}</div>\n<div>${pid.address}<br />${pid.countryName}</div>\n<div>Tel: $!{pid.telephone} Fax: $!{pid.fax}</div>\n#end\n<div><a href=\"mailto:${pid.email}\">${pid.email}</a></div>\n\n#if ($pid.type == \"or\")\n<h3>Organization</h3>\n<div><b>Authorized Official:</b> ${pid.AoName} ${pid.AoSurname}</div>\n<div>${pid.OrgName}</div>\n<div>${pid.OrgAddress}<br />${pid.OrgCountryName}</div>\n<div><a href=\"mailto:${pid.AoEmail}\">${pid.AoEmail}</a></div>\n<div>Tel: $!{pid.AoTelephone} Fax: $!{pid.AoFax}</div>\n#end\n\n#if ($pid.shipAddrFlag == \"d\")\n<h3>Shipping address</h3>\n<div>$!{pid.ShipAddress}<br />$!{pid.ShipCountry}</div>\n<div>Tel: $!{pid.ShipTelephone}</div>\n#else\n<p>No additional shipping information provided.</p>\n#end\n\n<h3>Request information</h3>\n#if ($requestInfo.preacceptSMTA)\n<p>Requestor gives advance notification that they have read the terms and conditions of the SMTA/MTA and that they intend to accept these terms and conditions on receipt of the seed with shrink-wrap SMTA/MTA.</p>\n#end\n\n#if ($requestInfo.notes)\n<h3>Notes</h3>\n<p>${requestInfo.notes}</p>\n#end\n\n<h3>Accession list</h3>\n<table>\n#foreach( $acn in $accessions )\n <tr><td><a href=\"${baseUrl}/acn/id/${acn.id}\">${acn.accessionNumber}</a></td><td>${acn.instituteCode}</td><td>$!{acn.origin}</td><td>$!{acn.taxonomy.taxonName}</td></tr>\n#end\n</table>\n\n<p>Thank you,<br />Genesys team</p>"
}
}
\ No newline at end of file
......@@ -5847,3 +5847,16 @@ databaseChangeLog:
name: redir
type: VARCHAR(50)
tableName: short_filter
- changeSet:
id: 1563359027832-1
author: mborodenko
comment: Add column `internal` to MaterialRequest
changes:
- addColumn:
columns:
- column:
name: internal
type: boolean
defaultValue: false
tableName: request
......@@ -17,7 +17,6 @@
package org.genesys.test.server.api.v1;
import static org.hamcrest.Matchers.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
......@@ -40,26 +39,20 @@ import org.genesys2.server.persistence.MaterialRequestRepository;
import org.genesys2.server.persistence.MaterialSubRequestRepository;
import org.genesys2.server.persistence.VerificationTokenRepository;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.service.EasySMTA;
import org.genesys2.server.service.InstituteService;
import org.genesys2.server.service.RequestService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
......@@ -68,23 +61,9 @@ import com.google.common.collect.Lists;
*/
public class RequestControllerTest extends AbstractApiTest {
private static final String TEST_INSTCODE = "XXX001";
private static final ObjectMapper objectMapper;
private static FaoInstitute institute;
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);
}
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private InstituteService instituteService;
@Autowired
......@@ -107,8 +86,6 @@ public class RequestControllerTest extends AbstractApiTest {
@Transactional
public void beforeTest() throws Exception {
super.beforeTest();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(documentationConfiguration(this.restDocumentation).uris().withScheme("https").withHost(
"sandbox.genesys-pgr.org").withPort(443)).build();
institute = new FaoInstitute();
institute.setCode(TEST_INSTCODE);
institute.setFullName("An institute");
......@@ -164,6 +141,7 @@ public class RequestControllerTest extends AbstractApiTest {
.andExpect(jsonPath("$.uuid", notNullValue()))
.andExpect(jsonPath("$.email", is(requestInfo.getEmail())))
.andExpect(jsonPath("$.state", is(0)))
.andExpect(jsonPath("$.internalRequest", is(false)))
.andExpect(jsonPath("$.body.requestInfo.email", is(requestInfo.getEmail())))
.andExpect(jsonPath("$.body.requestInfo.purposeType", is(0)))
......@@ -176,6 +154,51 @@ public class RequestControllerTest extends AbstractApiTest {
/*@formatter:on*/
}
@Test
public void initiateInternalRequestTest() throws Exception {
RequestService.RequestInfo requestInfo = new RequestService.RequestInfo();
requestInfo.setPreacceptSMTA(false);
requestInfo.setNotes("Notes " + System.currentTimeMillis());
requestInfo.setInternalRequest(true);
EasySMTA.EasySMTAUserData userData = new EasySMTA.EasySMTAUserData();
userData.setEmail("user@localhost");
userData.setAddress("Test address");
userData.setName("Test Name");
userData.setSurname("Test surname");
userData.setType("in");
userData.setCountry("UKR");
requestInfo.setUserData(userData);
Accession accession = accessionRepository.findAll().get(0);
Set<UUID> accUuids = new HashSet<>();
accUuids.add(accession.getUuid());
/*@formatter:off*/
mockMvc.perform(post(RequestsController.CONTROLLER_URL + "/r/initiate")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(accUuids))
.param("requestInfo", objectMapper.writeValueAsString(requestInfo)))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$", not(nullValue())))
.andExpect(jsonPath("$.uuid", notNullValue()))
.andExpect(jsonPath("$.state", is(0)))
.andExpect(jsonPath("$.internalRequest", is(true)))
.andExpect(jsonPath("$.body.pid.email", is(userData.getEmail())))
.andExpect(jsonPath("$.body.pid.name", is(userData.getName())))
.andExpect(jsonPath("$.body.requestInfo.purposeType", is(0)))
.andExpect(jsonPath("$.body.requestInfo.notes", is(requestInfo.getNotes())))
.andExpect(jsonPath("$.body.accessionIds[0]", is(accession.getId().intValue())))
;
/*@formatter:on*/
}
@Test
public void removeRequestTest() throws Exception {
initRequest();
......
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