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

Merge branch '285-add-pid-to-cooperator' into 'main'

Resolve "Add PID to Cooperator"

Closes #285

See merge request grin-global/grin-global-server!373
parents 0335691b 78842eef
......@@ -82,7 +82,7 @@ public class FirehoseReindexListener implements InitializingBean {
if (m != null && m instanceof EmptyModel) {
var toReindex = new ElasticReindex((EmptyModel) m);
if (! elasticReindexQueue.contains(toReindex)) {
LOG.debug("Dependency added to queue? {} {}", elasticReindexQueue.add(toReindex), toReindex);
LOG.trace("Dependency added to queue? {} {}", elasticReindexQueue.add(toReindex), toReindex);
}
}
}
......@@ -100,7 +100,7 @@ public class FirehoseReindexListener implements InitializingBean {
if (m != null && m instanceof EmptyModel) {
var toReindex = new ElasticReindex((EmptyModel) m);
if (!elasticReindexQueue.contains(toReindex)) {
LOG.error("Dependency added to queue? {} {}", elasticReindexQueue.add(toReindex), toReindex);
LOG.trace("Dependency added to queue? {} {}", elasticReindexQueue.add(toReindex), toReindex);
}
}
}
......
/*
* Copyright 2022 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.gringlobal.custom.validation.javax;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Provided string value must be null or a trimmed string without line break characters or double white space.
*
* @author Matija Obreza
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = SimpleStringValidator.class)
public @interface SimpleString {
String message() default "value must be trimmed and must not contain double whitespace or line break characters";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
/*
* Copyright 2022 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.gringlobal.custom.validation.javax;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @author Matija Obreza
*/
public class SimpleStringValidator implements ConstraintValidator<SimpleString, String> {
/**
* Initializes the validator.
*/
@Override
public void initialize(SimpleString constraintAnnotation) {
}
/**
* Implements the validation logic.
* @return {@code false} if {@code value} contains line break characters, double white spaces or is not trimmed
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return !value.startsWith(" ") && !value.endsWith(" ") && value.indexOf('\n') == -1 && value.indexOf('\r') == -1 && value.indexOf(" ") == -1; // TODO Other UTF white space chars?
}
}
......@@ -50,6 +50,7 @@ import org.gringlobal.component.elastic.ElasticTrigger;
import org.gringlobal.custom.elasticsearch.BoostedFields;
import org.gringlobal.custom.elasticsearch.IgnoreField;
import org.gringlobal.custom.elasticsearch.SearchField;
import org.gringlobal.custom.validation.javax.SimpleString;
import org.gringlobal.custom.validation.javax.CodeValueField;
import org.gringlobal.custom.validation.javax.OneLine;
import org.gringlobal.model.community.CommunityCodeValues;
......@@ -100,7 +101,7 @@ public class Accession extends CooperatorOwnedModel implements IWebVisible, Copy
@Basic
@OneLine
@NotNull
@Pattern(regexp = "(^$)|(^\\S+(?:\\s+\\S+)?$)")
@Pattern(regexp = "(^$)|(^\\S+(?:\\s\\S+)*$)") // Blank string or a trimmed string that may contain single white space characters
@Column(name = "accession_number_part1", nullable = false, length = 50)
private String accessionNumberPart1;
......@@ -109,6 +110,8 @@ public class Accession extends CooperatorOwnedModel implements IWebVisible, Copy
private Long accessionNumberPart2;
@Basic
@OneLine
@SimpleString
@Column(name = "accession_number_part3", length = 50)
private String accessionNumberPart3;
......
......@@ -22,6 +22,7 @@ import javax.validation.constraints.Size;
import org.genesys.blocks.model.Copyable;
import org.gringlobal.component.elastic.ElasticLoader;
import org.gringlobal.custom.validation.javax.SimpleString;
import org.gringlobal.custom.validation.javax.CodeValueField;
import org.gringlobal.model.community.CommunityCodeValues;
import org.springframework.data.elasticsearch.annotations.Document;
......@@ -46,19 +47,28 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerators;
public class Cooperator extends CooperatorOwnedModel implements Copyable<Cooperator>, ElasticLoader {
private static final long serialVersionUID = 2324265908219917384L;
@Id
@JsonProperty
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cooperator_id", columnDefinition = "int") // GG legacy database type
private Long id;
@Basic
@Size(max = 100)
@Column(name = "address_line1", length = 100)
@SimpleString
private String addressLine1;
@Basic
@Size(max = 100)
@Column(name = "address_line2", length = 100)
@SimpleString
private String addressLine2;
@Basic
@Size(max = 100)
@Column(name = "address_line3", length = 100)
@SimpleString
private String addressLine3;
@Basic
......@@ -69,6 +79,7 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@Basic
@Size(max = 100)
@Column(length = 100)
@SimpleString
private String city;
@ManyToOne(fetch = FetchType.LAZY, cascade = {})
......@@ -85,16 +96,19 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@Basic
@Size(max = 100)
@Column(length = 100)
@SimpleString
private String email;
@Basic
@Size(max = 30)
@Column(length = 30)
@SimpleString
private String fax;
@Basic
@Size(max = 100)
@Column(name = "first_name", length = 100)
@SimpleString
private String firstName;
@ManyToOne(fetch = FetchType.LAZY, cascade = {})
......@@ -103,20 +117,16 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@JsonIgnoreProperties(value = { "createdBy", "modifiedBy", "ownedBy" })
private Geography geography;
@Id
@JsonProperty
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cooperator_id", columnDefinition = "int") // GG legacy database type
private Long id;
@Basic
@Size(max = 100)
@Column(length = 100)
@SimpleString
private String job;
@Basic
@Size(max = 100)
@Column(name = "last_name", length = 100)
@SimpleString
private String lastName;
@Basic
......@@ -127,11 +137,13 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@Basic
@Size(max = 200)
@Column(length = 200)
@SimpleString
private String organization;
@Basic
@Size(max = 10)
@Column(name = "organization_abbrev", length = 10)
@SimpleString
private String organizationAbbrev;
@Basic
......@@ -142,36 +154,43 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@Basic
@Size(max = 100)
@Column(name = "postal_index", length = 100)
@SimpleString
private String postalIndex;
@Basic
@Size(max = 30)
@Column(name = "primary_phone", length = 30)
@SimpleString
private String primaryPhone;
@Basic
@Size(max = 100)
@Column(name = "secondary_address_line1", length = 100)
@SimpleString
private String secondaryAddressLine1;
@Basic
@Size(max = 100)
@Column(name = "secondary_address_line2", length = 100)
@SimpleString
private String secondaryAddressLine2;
@Basic
@Size(max = 100)
@Column(name = "secondary_address_line3", length = 100)
@SimpleString
private String secondaryAddressLine3;
@Basic
@Size(max = 100)
@Column(name = "secondary_city", length = 100)
@SimpleString
private String secondaryCity;
@Basic
@Size(max = 100)
@Column(name = "secondary_email", length = 100)
@SimpleString
private String secondaryEmail;
@ManyToOne(fetch = FetchType.LAZY, cascade = {})
......@@ -183,21 +202,25 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
@Basic
@Size(max = 200)
@Column(name = "secondary_organization", length = 200)
@SimpleString
private String secondaryOrganization;
@Basic
@Size(max = 10)
@Column(name = "secondary_organization_abbrev", length = 10)
@SimpleString
private String secondaryOrganizationAbbrev;
@Basic
@Size(max = 30)
@Column(name = "secondary_phone", length = 30)
@SimpleString
private String secondaryPhone;
@Basic
@Size(max = 100)
@Column(name = "secondary_postal_index", length = 100)
@SimpleString
private String secondaryPostalIndex;
@ManyToOne(fetch = FetchType.LAZY, cascade = {})
......@@ -230,10 +253,16 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
private WebCooperator webCooperator;
@Basic
@Pattern(regexp = "[A-Z]{3}\\d{3,4}")
@Pattern(regexp = "^[A-Z]{3}\\d{3,4}$")
@Column(name = "fao_institute_number", length = 10)
private String faoInstituteNumber;
@Basic
@Size(min=1, max = 10)
@Column(name = "itpgrfa_pid", length = 10)
@SimpleString
private String itpgrfaPid;
public Cooperator() {
}
......@@ -529,6 +558,14 @@ public class Cooperator extends CooperatorOwnedModel implements Copyable<Coopera
this.faoInstituteNumber = faoInstituteNumber;
}
public String getItpgrfaPid() {
return itpgrfaPid;
}
public void setItpgrfaPid(String itpgrfaPid) {
this.itpgrfaPid = itpgrfaPid;
}
@Override
public void lazyLoad() {
super.lazyLoad();
......
......@@ -35,6 +35,7 @@ import org.gringlobal.component.elastic.ElasticLoader;
import org.gringlobal.component.elastic.ElasticTrigger;
import org.gringlobal.custom.elasticsearch.IgnoreField;
import org.gringlobal.custom.elasticsearch.SearchField;
import org.gringlobal.custom.validation.javax.SimpleString;
import org.gringlobal.custom.validation.javax.CodeValueField;
import org.gringlobal.custom.validation.javax.OneLine;
import org.gringlobal.model.community.CommunityCodeValues;
......@@ -167,6 +168,7 @@ public class Inventory extends CooperatorOwnedModel implements Copyable<Inventor
@Size(max = 50)
@Column(name = "inventory_number_part1", nullable = false, length = 50)
@OneLine
@SimpleString
private String inventoryNumberPart1;
@Basic
......@@ -177,6 +179,7 @@ public class Inventory extends CooperatorOwnedModel implements Copyable<Inventor
@Size(max = 50)
@Column(name = "inventory_number_part3", length = 50)
@OneLine
@SimpleString
private String inventoryNumberPart3;
@Basic
......
......@@ -135,6 +135,8 @@ public class CooperatorFilter extends CooperatorOwnedModelFilter<CooperatorFilte
/** The title. */
public StringFilter title;
public Set<String> itpgrfaPid;
// FIXME implement me
// public WebCooperatorFilter webCooperator;
......@@ -260,6 +262,9 @@ public class CooperatorFilter extends CooperatorOwnedModelFilter<CooperatorFilte
if (secondaryGeography != null) {
predicates.addAll(secondaryGeography.collectPredicates(cooperator.secondaryGeography));
}
if (itpgrfaPid != null && !itpgrfaPid.isEmpty()) {
predicates.add(cooperator.itpgrfaPid.in(itpgrfaPid));
}
return predicates;
}
......
......@@ -9230,4 +9230,23 @@ databaseChangeLog:
onUpdate: NO ACTION
referencedColumnNames: inventory_id
referencedTableName: inventory
validate: true
\ No newline at end of file
validate: true
- changeSet:
id: 1646669178710-1
author: ahrybeniuk
changes:
- addColumn:
tableName: cooperator
columns:
- column:
name: itpgrfa_pid
type: varchar(10)
- createIndex:
columns:
- column:
name: itpgrfa_pid
indexName: ndx_itpgrfa_pid
tableName: cooperator
......@@ -17,6 +17,7 @@ package org.gringlobal.test.api.v1;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThrows;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
......@@ -40,6 +41,10 @@ import org.springframework.security.test.context.support.WithUserDetails;
import com.google.common.collect.Sets;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;
import javax.validation.ConstraintViolationException;
/**
* @author Matija Obreza
*/
......@@ -55,6 +60,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
private static final String STATUS_CODE_DECEASED = "DECEASED";
private static final String STATUS_CODE_INACTIVE = "INACTIVE";
private static final String FAO_INST_NUMBER = "UKR001";
private static final String ITPGRFA_PID = "IDCODE";
@Autowired
private CooperatorService service;
......@@ -120,7 +126,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2));
Cooperator saved = service.create(build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2, ITPGRFA_PID));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(5L));
......@@ -158,7 +164,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
var coop = build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2);
var coop = build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2, ITPGRFA_PID);
coop.setFaoInstituteNumber(FAO_INST_NUMBER);
Cooperator saved = service.create(coop);
assertThat(saved, is(notNullValue()));
......@@ -211,6 +217,95 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
/*@formatter:on*/
}
@Test
public void testItpgrfaPidTest() throws Exception {
SysLang sysLang = sysLangRepository.findAll().get(0);
var coop = build(null, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2, " A123");
coop.setFaoInstituteNumber(FAO_INST_NUMBER);
assertThrows(ConstraintViolationException.class, () -> {
service.create(coop);
});
assertThrows(ConstraintViolationException.class, () -> {
coop.setItpgrfaPid("A123 A");
service.create(coop);
});
assertThrows(ConstraintViolationException.class, () -> {
coop.setItpgrfaPid("A123A ");
service.create(coop);
});
assertThrows(ConstraintViolationException.class, () -> {
coop.setItpgrfaPid(" A1 23A ");
service.create(coop);
});
coop.setItpgrfaPid(ITPGRFA_PID);
Cooperator saved = service.create(coop);
assertThat(saved, is(notNullValue()));
assertThat(saved.getFaoInstituteNumber(), is(FAO_INST_NUMBER));
assertThat(saved.getItpgrfaPid(), is(ITPGRFA_PID));
}
@Test
public void filterItpgrfaPidTest() throws Exception {
Cooperator currentCooperator = cooperatorRepository.findAll().get(0);
assertThat(currentCooperator, is(notNullValue()));
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
var coop = build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_2, ADDRESS_LINE1_2, ITPGRFA_PID);
coop.setFaoInstituteNumber(FAO_INST_NUMBER);
Cooperator saved = service.create(coop);
assertThat(saved, is(notNullValue()));
assertThat(saved.getFaoInstituteNumber(), is(FAO_INST_NUMBER));
assertThat(saved.getItpgrfaPid(), is(ITPGRFA_PID));
assertThat(cooperatorRepository.count(), equalTo(5L));
// build filter
CooperatorFilter filter = new CooperatorFilter();
filter.itpgrfaPid = Set.of("WRNGID");
/*@formatter:off*/
mockMvc
.perform(post(CooperatorController.API_URL.concat(CooperatorController.ENDPOINT_FILTER))
.contentType(MediaType.APPLICATION_JSON)
.content(verboseMapper.writeValueAsString(filter)))
// .andDo(org.springframework.test.web.servlet.result.MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$", not(nullValue())))
.andExpect(jsonPath("$.totalElements", is(0)))
.andExpect(jsonPath("$.filterCode").exists())
;
/*@formatter:on*/
filter.itpgrfaPid = Set.of(ITPGRFA_PID);
/*@formatter:off*/
mockMvc
.perform(post(CooperatorController.API_URL.concat(CooperatorController.ENDPOINT_FILTER))
.contentType(MediaType.APPLICATION_JSON)
.content(verboseMapper.writeValueAsString(filter)))
// .andDo(org.springframework.test.web.servlet.result.MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$", not(nullValue())))
.andExpect(jsonPath("$.totalElements", is(1)))
.andExpect(jsonPath("$.filterCode").exists())
.andExpect(jsonPath("$.sort[0]", not(nullValue())))
.andExpect(jsonPath("$.sort[0].direction", is("ASC")))
.andExpect(jsonPath("$.sort[0].property", is("id")))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0].statusCode", is(STATUS_CODE_DECEASED)))
.andExpect(jsonPath("$.content[0].id", is(saved.getId().intValue())))
.andExpect(jsonPath("$.content[0].currentCooperator", not(nullValue())))
.andExpect(jsonPath("$.content[0].sysLang", is(nullValue())))
.andExpect(jsonPath("$.content[0].faoInstituteNumber", is(FAO_INST_NUMBER)))
.andExpect(jsonPath("$.content[0].itpgrfaPid", is(ITPGRFA_PID)))
;
/*@formatter:on*/
}
@Test
public void getTest() throws Exception {
Cooperator currentCooperator = cooperatorRepository.findAll().get(0);
......@@ -218,7 +313,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2));
Cooperator saved = service.create(build(currentCooperator, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2, ITPGRFA_PID));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(5L));
......@@ -300,7 +395,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
Geography geography = geoService.create(build("DEU", TRUE));
assertThat(geography, notNullValue());
Cooperator source = build(null, STATUS_CODE_ACTIVE, sysLang, ADDRESS_LINE1, NOTE_1);
Cooperator source = build(null, STATUS_CODE_ACTIVE, sysLang, ADDRESS_LINE1, NOTE_1, ITPGRFA_PID);
source.setGeography(geography);
source.setLastName("Last Name");
source.setFirstName("First Name");
......@@ -339,7 +434,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2, ITPGRFA_PID));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(5L));
......@@ -384,7 +479,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2, ITPGRFA_PID));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(5L));
......@@ -425,7 +520,7 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
SysLang sysLang = sysLangRepository.findAll().get(0);
assertThat(sysLang, is(notNullValue()));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2));
Cooperator saved = service.create(build(null, STATUS_CODE_DECEASED, sysLang, NOTE_1, ADDRESS_LINE1_2, ITPGRFA_PID));
assertThat(saved, is(notNullValue()));
assertThat(cooperatorRepository.count(), equalTo(5L));
......@@ -453,13 +548,14 @@ public class CooperatorControllerTest extends AbstractApiV1Test {
return geography;
}
private static Cooperator build(Cooperator currentCooperator, String status, SysLang sysLang, String note, String addr) {
private static Cooperator build(Cooperator currentCooperator, String status, SysLang sysLang, String note, String addr