Commit c992ed4f authored by Martin Lowe's avatar Martin Lowe 🇨🇦 Committed by Martin Lowe
Browse files

Move db models, fix data loader + DDL for state, add validation for

complete

Validation for complete action makes use of hibernate/persistence
validation mechanisms, calling the logic in service before posting to
the mail service.
parent cc04d876
...@@ -93,6 +93,10 @@ ...@@ -93,6 +93,10 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-qute</artifactId> <artifactId>quarkus-resteasy-qute</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId> <artifactId>quarkus-junit5</artifactId>
......
...@@ -29,12 +29,13 @@ import org.eclipsefoundation.core.model.RequestWrapper; ...@@ -29,12 +29,13 @@ import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.persistence.dao.PersistenceDao; import org.eclipsefoundation.persistence.dao.PersistenceDao;
import org.eclipsefoundation.persistence.model.RDBMSQuery; import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.persistence.service.FilterService; import org.eclipsefoundation.persistence.service.FilterService;
import org.eclipsefoundation.react.model.Address; import org.eclipsefoundation.react.dto.Address;
import org.eclipsefoundation.react.model.Contact; import org.eclipsefoundation.react.dto.Contact;
import org.eclipsefoundation.react.model.MembershipForm; import org.eclipsefoundation.react.dto.FormOrganization;
import org.eclipsefoundation.react.model.FormOrganization; import org.eclipsefoundation.react.dto.FormWorkingGroup;
import org.eclipsefoundation.react.model.FormWorkingGroup; import org.eclipsefoundation.react.dto.MembershipForm;
import org.eclipsefoundation.react.namespace.ContactTypes; import org.eclipsefoundation.react.namespace.ContactTypes;
import org.eclipsefoundation.react.namespace.FormState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -74,8 +75,7 @@ public class DataLoader { ...@@ -74,8 +75,7 @@ public class DataLoader {
public void init(@Observes StartupEvent ev) { public void init(@Observes StartupEvent ev) {
// if running in dev mode, preload a bunch of data using dao // if running in dev mode, preload a bunch of data using dao
LOGGER.debug("Current mode: {}", ProfileManager.getActiveProfile()); LOGGER.debug("Current mode: {}", ProfileManager.getActiveProfile());
if (config.isEnabled() if (config.isEnabled() && config.getDataLoaderProfiles().contains(ProfileManager.getActiveProfile())) {
&& config.getDataLoaderProfiles().contains(ProfileManager.getActiveProfile())) {
RequestWrapper wrap = new RequestWrapper(); RequestWrapper wrap = new RequestWrapper();
List<MembershipForm> forms = new ArrayList<>(config.getFormCount()); List<MembershipForm> forms = new ArrayList<>(config.getFormCount());
for (int i = 0; i < config.getFormCount(); i++) { for (int i = 0; i < config.getFormCount(); i++) {
...@@ -87,8 +87,10 @@ public class DataLoader { ...@@ -87,8 +87,10 @@ public class DataLoader {
mf.setSigningAuthority(Math.random() > 0.5); mf.setSigningAuthority(Math.random() > 0.5);
mf.setRegistrationCountry("CA"); mf.setRegistrationCountry("CA");
mf.setVatNumber(RandomStringUtils.randomNumeric(10)); mf.setVatNumber(RandomStringUtils.randomNumeric(10));
mf.setPurchaseOrderRequired(Math.random() > 0.5 ?"yes": "no"); mf.setPurchaseOrderRequired(Math.random() > 0.5 ? "yes" : "no");
mf.setDateCreated(Math.random() > 0.5 ? System.currentTimeMillis() + r.nextInt(10000): System.currentTimeMillis() - r.nextInt(10000)); mf.setDateCreated(Math.random() > 0.5 ? System.currentTimeMillis() + r.nextInt(10000)
: System.currentTimeMillis() - r.nextInt(10000));
mf.setState(FormState.INPROGRESS);
forms.add(mf); forms.add(mf);
} }
...@@ -99,6 +101,7 @@ public class DataLoader { ...@@ -99,6 +101,7 @@ public class DataLoader {
List<Contact> contacts = new ArrayList<>(forms.size() * ContactTypes.values().length); List<Contact> contacts = new ArrayList<>(forms.size() * ContactTypes.values().length);
List<FormWorkingGroup> wgs = new ArrayList<>(); List<FormWorkingGroup> wgs = new ArrayList<>();
for (MembershipForm mf : forms) { for (MembershipForm mf : forms) {
// generate an org for form
FormOrganization o = new FormOrganization(); FormOrganization o = new FormOrganization();
o.setForm(mf); o.setForm(mf);
o.setLegalName(RandomStringUtils.randomAlphabetic(4, 10)); o.setLegalName(RandomStringUtils.randomAlphabetic(4, 10));
...@@ -112,36 +115,9 @@ public class DataLoader { ...@@ -112,36 +115,9 @@ public class DataLoader {
a.setOrganization(o); a.setOrganization(o);
o.setAddress(a); o.setAddress(a);
organizations.add(o); organizations.add(o);
for (int j = 0; j < ContactTypes.values().length; j++) { // generate contacts + wgs for form
// randomly skip contacts contacts.addAll(generateContacts(mf));
if (Math.random() > 0.5) { wgs.addAll(generateWorkingGroups(mf));
continue;
}
Contact c = new Contact();
c.setForm(mf);
c.setTitle("Sample Title");
c.setfName(RandomStringUtils.randomAlphabetic(4, 10));
c.setlName(RandomStringUtils.randomAlphabetic(4, 10));
c.setType(ContactTypes.values()[j]);
c.setEmail(RandomStringUtils.randomAlphabetic(4, 10));
contacts.add(c);
}
// randomly create WG entries
while (true) {
if (Math.random() > 0.5) {
break;
}
FormWorkingGroup wg = new FormWorkingGroup();
wg.setWorkingGroupID(config.getWorkingGroups().get(r.nextInt(config.getWorkingGroups().size())));
wg.setParticipationLevel(
config.getParticipationLevels().get(r.nextInt(config.getParticipationLevels().size())));
// get a random instance of time
Instant inst = Instant.now().minus(r.nextInt(1000000), ChronoUnit.SECONDS);
wg.setEffectiveDate(new Date(inst.getEpochSecond()));
wg.setContact(generateContact(mf, Optional.empty()));
wg.setForm(mf);
wgs.add(wg);
}
} }
organizations = dao.add(new RDBMSQuery<>(wrap, filters.get(FormOrganization.class)), organizations); organizations = dao.add(new RDBMSQuery<>(wrap, filters.get(FormOrganization.class)), organizations);
contacts = dao.add(new RDBMSQuery<>(wrap, filters.get(Contact.class)), contacts); contacts = dao.add(new RDBMSQuery<>(wrap, filters.get(Contact.class)), contacts);
...@@ -152,6 +128,39 @@ public class DataLoader { ...@@ -152,6 +128,39 @@ public class DataLoader {
} }
} }
private List<FormWorkingGroup> generateWorkingGroups(MembershipForm form) {
List<FormWorkingGroup> wgs = new ArrayList<>();
// randomly create WG entries
while (true) {
if (Math.random() > 0.5) {
break;
}
FormWorkingGroup wg = new FormWorkingGroup();
wg.setWorkingGroupID(config.getWorkingGroups().get(r.nextInt(config.getWorkingGroups().size())));
wg.setParticipationLevel(
config.getParticipationLevels().get(r.nextInt(config.getParticipationLevels().size())));
// get a random instance of time
Instant inst = Instant.now().minus(r.nextInt(1000000), ChronoUnit.SECONDS);
wg.setEffectiveDate(new Date(inst.getEpochSecond()));
wg.setContact(generateContact(form, Optional.empty()));
wg.setForm(form);
wgs.add(wg);
}
return wgs;
}
private List<Contact> generateContacts(MembershipForm form) {
List<Contact> out = new ArrayList<>();
for (int j = 0; j < ContactTypes.values().length; j++) {
// randomly skip contacts
if (Math.random() > 0.5) {
continue;
}
out.add(generateContact(form, Optional.of(ContactTypes.values()[j])));
}
return out;
}
private Contact generateContact(MembershipForm form, Optional<ContactTypes> type) { private Contact generateContact(MembershipForm form, Optional<ContactTypes> type) {
Contact c = new Contact(); Contact c = new Contact();
c.setForm(form); c.setForm(form);
...@@ -159,8 +168,17 @@ public class DataLoader { ...@@ -159,8 +168,17 @@ public class DataLoader {
c.setfName(RandomStringUtils.randomAlphabetic(4, 10)); c.setfName(RandomStringUtils.randomAlphabetic(4, 10));
c.setlName(RandomStringUtils.randomAlphabetic(4, 10)); c.setlName(RandomStringUtils.randomAlphabetic(4, 10));
c.setType(type.orElse(ContactTypes.WORKING_GROUP)); c.setType(type.orElse(ContactTypes.WORKING_GROUP));
c.setEmail(RandomStringUtils.randomAlphabetic(4, 10)); c.setEmail(generateEmail());
return c; return c;
} }
private String generateEmail() {
StringBuilder sb = new StringBuilder();
sb.append(RandomStringUtils.randomAlphabetic(4, 10));
sb.append("@");
sb.append(RandomStringUtils.randomAlphabetic(4, 10));
sb.append(".");
sb.append(RandomStringUtils.randomAlphabetic(2));
return sb.toString();
}
} }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
import java.util.Objects; import java.util.Objects;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
import java.util.Objects; import java.util.Objects;
...@@ -27,6 +27,7 @@ import javax.persistence.ManyToOne; ...@@ -27,6 +27,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.validation.constraints.Email; import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames; import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
...@@ -72,7 +73,7 @@ public class Contact extends BareNode implements TargetedClone<Contact> { ...@@ -72,7 +73,7 @@ public class Contact extends BareNode implements TargetedClone<Contact> {
@NotBlank(message = "Job title cannot be blank") @NotBlank(message = "Job title cannot be blank")
@JsonbProperty(value = "job_title") @JsonbProperty(value = "job_title")
private String title; private String title;
@NotBlank(message = "Contact type cannot be blank") @NotNull(message = "Contact type cannot be empty")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private ContactTypes type; private ContactTypes type;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
import java.util.Objects; import java.util.Objects;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
import java.util.Objects; import java.util.Objects;
...@@ -21,6 +21,7 @@ import javax.persistence.GeneratedValue; ...@@ -21,6 +21,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames; import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
...@@ -46,7 +47,7 @@ public class MembershipForm extends BareNode implements TargetedClone<Membership ...@@ -46,7 +47,7 @@ public class MembershipForm extends BareNode implements TargetedClone<Membership
private String userID; private String userID;
@NotBlank(message = "Membership level cannot be blank") @NotBlank(message = "Membership level cannot be blank")
private String membershipLevel; private String membershipLevel;
@NotBlank(message = "Signing authority cannot be blank") @NotNull(message = "Signing authority cannot be null")
private boolean signingAuthority; private boolean signingAuthority;
@NotBlank(message = "Purchase order state cannot be blank") @NotBlank(message = "Purchase order state cannot be blank")
private String purchaseOrderRequired; private String purchaseOrderRequired;
...@@ -54,7 +55,7 @@ public class MembershipForm extends BareNode implements TargetedClone<Membership ...@@ -54,7 +55,7 @@ public class MembershipForm extends BareNode implements TargetedClone<Membership
private String registrationCountry; private String registrationCountry;
@SortableField @SortableField
private Long dateCreated; private Long dateCreated;
@NotBlank(message = "The form state cannot be blank") @NotNull(message = "The form state cannot be null")
private FormState state; private FormState state;
/** @return the id */ /** @return the id */
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.eclipsefoundation.react.model; package org.eclipsefoundation.react.dto;
/** /**
* Indicates the the object should be able to clone itself into Target Object. This allows for entity refs retrieved * Indicates the the object should be able to clone itself into Target Object. This allows for entity refs retrieved
......
package org.eclipsefoundation.react.model;
import javax.validation.ConstraintViolation;
import org.eclipsefoundation.persistence.dto.BareNode;
public class ConstraintViolationWrapFactory {
public <T extends BareNode> ConstraintViolationWrap build(T object, ConstraintViolation<T> inner) {
return new ConstraintViolationWrap(object.getId(), object.getClass().getSimpleName(), inner.getInvalidValue(),
inner.getPropertyPath().toString());
}
public class ConstraintViolationWrap {
private Object rootID;
private String type;
private Object value;
private String path;
ConstraintViolationWrap(Object rootID, String type, Object value, String path) {
this.type = type;
this.rootID = rootID;
this.value = value;
this.path = path;
}
/**
* @return the rootID
*/
public Object getRootID() {
return rootID;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @return the value
*/
public Object getValue() {
return value;
}
/**
* @return the path
*/
public String getPath() {
return path;
}
}
}
...@@ -14,7 +14,6 @@ package org.eclipsefoundation.react.model; ...@@ -14,7 +14,6 @@ package org.eclipsefoundation.react.model;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public class WorkingGroup { public class WorkingGroup {
private String documentID; private String documentID;
...@@ -27,7 +26,6 @@ public class WorkingGroup { ...@@ -27,7 +26,6 @@ public class WorkingGroup {
this.levels = levels; this.levels = levels;
} }
public WorkingGroup() { public WorkingGroup() {
} }
......
...@@ -21,7 +21,7 @@ import org.eclipsefoundation.core.model.RequestWrapper; ...@@ -21,7 +21,7 @@ import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.core.service.CachingService; import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.persistence.dao.PersistenceDao; import org.eclipsefoundation.persistence.dao.PersistenceDao;
import org.eclipsefoundation.persistence.service.FilterService; import org.eclipsefoundation.persistence.service.FilterService;
import org.eclipsefoundation.react.model.MembershipForm; import org.eclipsefoundation.react.dto.MembershipForm;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
......
...@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response; ...@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response;
import org.eclipsefoundation.core.helper.CSRFHelper; import org.eclipsefoundation.core.helper.CSRFHelper;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames; import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
import org.eclipsefoundation.persistence.model.RDBMSQuery; import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.react.model.Contact; import org.eclipsefoundation.react.dto.Contact;
import org.eclipsefoundation.react.model.MembershipForm; import org.eclipsefoundation.react.dto.MembershipForm;
import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames; import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
......
...@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response; ...@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response;
import org.eclipsefoundation.core.helper.CSRFHelper; import org.eclipsefoundation.core.helper.CSRFHelper;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames; import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
import org.eclipsefoundation.persistence.model.RDBMSQuery; import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.react.model.FormOrganization; import org.eclipsefoundation.react.dto.FormOrganization;
import org.eclipsefoundation.react.model.MembershipForm; import org.eclipsefoundation.react.dto.MembershipForm;
import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames; import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
......
...@@ -12,10 +12,13 @@ ...@@ -12,10 +12,13 @@
package org.eclipsefoundation.react.request; package org.eclipsefoundation.react.request;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.NoResultException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.validation.Validator;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
...@@ -29,14 +32,18 @@ import javax.ws.rs.QueryParam; ...@@ -29,14 +32,18 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.eclipsefoundation.core.helper.CSRFHelper; import org.eclipsefoundation.core.helper.CSRFHelper;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames; import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
import org.eclipsefoundation.persistence.dto.BareNode;
import org.eclipsefoundation.persistence.model.RDBMSQuery; import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.react.model.Contact; import org.eclipsefoundation.react.dto.Contact;
import org.eclipsefoundation.react.model.FormOrganization; import org.eclipsefoundation.react.dto.FormOrganization;
import org.eclipsefoundation.react.model.FormWorkingGroup; import org.eclipsefoundation.react.dto.FormWorkingGroup;
import org.eclipsefoundation.react.model.MembershipForm; import org.eclipsefoundation.react.dto.MembershipForm;
import org.eclipsefoundation.react.model.ConstraintViolationWrapFactory;
import org.eclipsefoundation.react.model.ConstraintViolationWrapFactory.ConstraintViolationWrap;
import org.eclipsefoundation.react.namespace.FormState; import org.eclipsefoundation.react.namespace.FormState;
import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames; import org.eclipsefoundation.react.namespace.MembershipFormAPIParameterNames;
import org.eclipsefoundation.react.service.MailerService; import org.eclipsefoundation.react.service.MailerService;
...@@ -58,6 +65,8 @@ import io.quarkus.security.Authenticated; ...@@ -58,6 +65,8 @@ import io.quarkus.security.Authenticated;
public class MembershipFormResource extends AbstractRESTResource { public class MembershipFormResource extends AbstractRESTResource {
public static final Logger LOGGER = LoggerFactory.getLogger(MembershipFormResource.class); public static final Logger LOGGER = LoggerFactory.getLogger(MembershipFormResource.class);
@Inject
Validator validator;
@Inject @Inject
MailerService mailer; MailerService mailer;
...@@ -163,7 +172,7 @@ public class MembershipFormResource extends AbstractRESTResource { ...@@ -163,7 +172,7 @@ public class MembershipFormResource extends AbstractRESTResource {
MultivaluedMap<String, String> params = new MultivaluedMapImpl<>(); MultivaluedMap<String, String> params = new MultivaluedMapImpl<>();
params.add(DefaultUrlParameterNames.ID.getName(), formID); params.add(DefaultUrlParameterNames.ID.getName(), formID);
// retrieve the possible cached object // retrieve the membership form for the current post
List<MembershipForm> results = dao.get(new RDBMSQuery<>(wrap, filters.get(MembershipForm.class), params)); List<MembershipForm> results = dao.get(new RDBMSQuery<>(wrap, filters.get(MembershipForm.class), params));
if (results == null) { if (results == null) {
return Response.serverError().build(); return Response.serverError().build();
...@@ -174,23 +183,40 @@ public class MembershipFormResource extends AbstractRESTResource { ...@@ -174,23 +183,40 @@ public class MembershipFormResource extends AbstractRESTResource {
// dont send email if force param is not true and form already submitted // dont send email if force param is not true and form already submitted
return Response.status(204).build(); return Response.status(204).build();
} }
// send the form to the mailing service
MembershipForm mf = results.get(0); MembershipForm mf = results.get(0);
mailer.sendToFormAuthor(mf);
// retrieve all of the info needed to post the form email // retrieve all of the info needed to post the form email
MultivaluedMap<String, String> extraparams = new MultivaluedMapImpl<>();