Commit 618f6e1e authored by Martin Lowe's avatar Martin Lowe 🇨🇦
Browse files

Merge remote-tracking branch 'origin/dev' into malowe/master/1.4.1

parents 4bfb41c4 1c61c8a5
Pipeline #2837 passed with stage
quarkus.log.level=INFO
quarkus.http.port=8090
quarkus.http.port=8093
quarkus.http.cors=false
quarkus.cache.caffeine."default".expire-after-write=30M
quarkus.cache.caffeine."ttl".expire-after-write=${quarkus.cache.caffeine."default".expire-after-write}
......
......@@ -29,7 +29,7 @@ services:
context: ./portal/
dockerfile: ./src/main/docker/Dockerfile.jvm
ports:
- 8092:8090
- 8092:8092
environment:
- VIRTUAL_HOST=api.rem.docker
- CONFIG_SECRET_PATH=/var/run/secrets/secret.properties
......@@ -57,7 +57,7 @@ services:
context: ./application/
dockerfile: ./src/main/docker/Dockerfile.jvm
ports:
- 8093:8090
- 8093:8093
environment:
- VIRTUAL_HOST=api.rem.docker
- CONFIG_SECRET_PATH=/var/run/secrets/secret.properties
......
......@@ -130,12 +130,10 @@
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<version>${auto-value.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
......
......@@ -10,10 +10,32 @@
</parent>
<properties></properties>
<dependencies>
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
<dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package org.eclipsefoundation.api;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
......@@ -165,6 +166,8 @@ public interface OrganizationAPI {
* @return a bean mapping with values populated from the current request
*/
public static Builder populateFromWrap(RequestWrapper w) {
// default levels for membership calls
List<String> levels = w.getParams(FoundationDBParameterNames.LEVELS);
return builder().setPersonID(w.getFirstParam(FoundationDBParameterNames.USER_NAME).orElseGet(() -> null))
.setOrganizationID(
w.getFirstParam(FoundationDBParameterNames.ORGANIZATION_ID).orElseGet(() -> null))
......@@ -172,7 +175,7 @@ public interface OrganizationAPI {
.setEmail(w.getFirstParam(FoundationDBParameterNames.EMAIL).orElseGet(() -> null))
.setWorkingGroup(w.getFirstParam(FoundationDBParameterNames.WORKING_GROUP).orElseGet(() -> null))
.setDocumentIds(w.getParams(FoundationDBParameterNames.DOCUMENT_IDS))
.setLevels(w.getParams(FoundationDBParameterNames.LEVELS))
.setLevels(levels != null && !levels.isEmpty() ? levels : Arrays.asList("SD", "AP", "AS", "OHAP"))
.setIds(w.getParams(DefaultUrlParameterNames.IDS));
}
......
package org.eclipsefoundation.membership.portal.model;
import org.eclipsefoundation.api.model.PeopleData;
import org.eclipsefoundation.membership.portal.request.model.ContactRemovalRequest;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
@AutoValue
@JsonDeserialize(builder = AutoValue_ContactRemovalRequestData.Builder.class)
public abstract class ContactRemovalRequestData {
public abstract PeopleData getPerson();
public abstract MemberOrganization getOrganization();
public abstract String getReason();
public abstract ContactRemovalRequest getRawRequest();
public static Builder builder() {
return new AutoValue_ContactRemovalRequestData.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public static abstract class Builder {
public abstract Builder setPerson(PeopleData person);
public abstract Builder setOrganization(MemberOrganization organization);
public abstract Builder setReason(String reason);
public abstract Builder setRawRequest(ContactRemovalRequest rawRequest);
public abstract ContactRemovalRequestData build();
}
}
......@@ -56,6 +56,8 @@ public abstract class WorkingGroupMap {
public abstract String getDescription();
public abstract String getParentOrganization();
public abstract WorkingGroupResources getResources();
public abstract List<WorkingGroupParticipationLevel> getLevels();
......@@ -77,6 +79,8 @@ public abstract class WorkingGroupMap {
public abstract Builder setDescription(String description);
public abstract Builder setParentOrganization(String parentOrganization);
public abstract Builder setResources(WorkingGroupResources resources);
public abstract Builder setLevels(List<WorkingGroupParticipationLevel> levels);
......
package org.eclipsefoundation.membership.portal.model;
package org.eclipsefoundation.membership.portal.request.model;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
......
package org.eclipsefoundation.membership.portal.model;
package org.eclipsefoundation.membership.portal.request.model;
import javax.annotation.Nullable;
......
package org.eclipsefoundation.membership.portal.model;
package org.eclipsefoundation.membership.portal.request.model;
import javax.annotation.Nonnull;
import javax.ws.rs.FormParam;
......
......@@ -25,13 +25,17 @@ import javax.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.eclipsefoundation.api.SysAPI;
import org.eclipsefoundation.api.PeopleAPI.PeopleRequestParams;
import org.eclipsefoundation.api.model.OrganizationContactData;
import org.eclipsefoundation.api.model.PeopleData;
import org.eclipsefoundation.api.model.SysRelationData;
import org.eclipsefoundation.core.namespace.RequestHeaderNames;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequest;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequestData;
import org.eclipsefoundation.membership.portal.model.EnhancedPersonData;
import org.eclipsefoundation.membership.portal.model.MemberOrganization;
import org.eclipsefoundation.membership.portal.namespace.OrganizationalUserType;
import org.eclipsefoundation.membership.portal.request.RolesAllowed;
import org.eclipsefoundation.membership.portal.request.model.ContactRemovalRequest;
import org.eclipsefoundation.membership.portal.service.MailerService;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
......@@ -96,9 +100,10 @@ public class OrganizationContactsResource extends AbstractRESTResource {
throw new BadRequestException(
"The organization that is being updated should match the organization in the URL");
}
ContactRemovalRequestData data = buildRemovalRequestData(request);
// fire an email request to remove user from organization
mailer.sendContactRemovalRequest(request);
mailer.sendContactRemovalNotification(request);
mailer.sendContactRemovalNotification(data);
return Response.ok().build();
}
......@@ -202,4 +207,32 @@ public class OrganizationContactsResource extends AbstractRESTResource {
}
return relations.get().stream().anyMatch(r -> r.getRelation().equalsIgnoreCase(relation));
}
/**
* Supplements a removal request with more data about the organization and person that should be acted on for the
* notifications that are sent out by the system for a removal request.
*
* @param r the removal request to retrieve more contextual information for
* @return a processed removal request with more contextual data on the org and person involved in the transaction.
* @throws BadRequestException if either the organization ID or username do not translate to a real entity, then
* this is thrown as the state cannot be recovered.
*/
private ContactRemovalRequestData buildRemovalRequestData(ContactRemovalRequest r) {
// should be cached by service, should always have a result
Optional<MemberOrganization> org = orgService.getByID(Integer.toString(r.getOrganizationId()));
if (org.isEmpty()) {
throw new BadRequestException("The passed username is not a valid username");
}
// get cached people. Should only return one entry as usernames should be unique keys
Optional<List<PeopleData>> people = cache.get(r.getUsername(), new MultivaluedMapImpl<>(), PeopleData.class,
() -> middle.getAll(
p -> peopleAPI.getPeople(p, PeopleRequestParams.builder().setUsername(r.getUsername()).build()),
PeopleData.class));
if (people.isEmpty()) {
throw new BadRequestException("The passed username is not a valid username");
}
// create the output object, including the passed reason.
return ContactRemovalRequestData.builder().setOrganization(org.get()).setPerson(people.get().get(0))
.setReason(r.getReason()).setRawRequest(r).build();
}
}
......@@ -59,8 +59,6 @@ import org.eclipsefoundation.membership.portal.helper.ImageFileHelper;
import org.eclipsefoundation.membership.portal.model.EnhancedPersonData;
import org.eclipsefoundation.membership.portal.model.MemberOrganization;
import org.eclipsefoundation.membership.portal.model.OrganizationActivity;
import org.eclipsefoundation.membership.portal.model.OrganizationInfoUpdateRequest;
import org.eclipsefoundation.membership.portal.model.OrganizationLogoUpdateRequest;
import org.eclipsefoundation.membership.portal.model.OrganizationProductData;
import org.eclipsefoundation.membership.portal.model.OrganizationYearlyActivity;
import org.eclipsefoundation.membership.portal.model.WorkingGroupMap.WorkingGroup;
......@@ -68,6 +66,8 @@ import org.eclipsefoundation.membership.portal.model.mappers.OrganizationProduct
import org.eclipsefoundation.membership.portal.namespace.ImageStoreFormat;
import org.eclipsefoundation.membership.portal.namespace.ImageStoreFormat.ImageStoreFormats;
import org.eclipsefoundation.membership.portal.request.RolesAllowed;
import org.eclipsefoundation.membership.portal.request.model.OrganizationInfoUpdateRequest;
import org.eclipsefoundation.membership.portal.request.model.OrganizationLogoUpdateRequest;
import org.eclipsefoundation.membership.portal.service.ImageStoreService;
import org.eclipsefoundation.membership.portal.service.WorkingGroupsService;
import org.eclipsefoundation.persistence.dao.impl.DefaultHibernateDao;
......@@ -528,4 +528,5 @@ public class OrganizationResource extends AbstractRESTResource {
}
return activityList;
}
}
......@@ -11,12 +11,15 @@
*/
package org.eclipsefoundation.membership.portal.resources;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
......@@ -36,14 +39,15 @@ public class WorkingGroupLevelsResource extends AbstractRESTResource {
WorkingGroupsService wgService;
@GET
public Response getWorkingGroups() {
public Response getWorkingGroups(@QueryParam("parent_organization") String parentOrganization,
@QueryParam("status") List<String> statuses) {
// return the results as a response
return Response.ok(wgService.get()).build();
return Response.ok(wgService.get(parentOrganization, statuses)).build();
}
@GET
@Path("{name}")
public Response getWorkingGroups(@PathParam("name") String name) {
public Response getWorkingGroup(@PathParam("name") String name) {
// return the results as a response
return Response.ok(wgService.getByName(name)).build();
}
......
package org.eclipsefoundation.membership.portal.service;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequest;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequestData;
import org.eclipsefoundation.membership.portal.request.model.ContactRemovalRequest;
/**
* Interface defining emails that need to be generated and sent as part of the submission of the membership forms to the
......@@ -21,5 +22,5 @@ public interface MailerService {
*
* @param request the information for the user removal request
*/
void sendContactRemovalNotification(ContactRemovalRequest request);
void sendContactRemovalNotification(ContactRemovalRequestData request);
}
......@@ -25,9 +25,17 @@ import org.eclipsefoundation.membership.portal.model.WorkingGroupMap.WorkingGrou
*
*/
public interface WorkingGroupsService {
public Set<WorkingGroup> get();
/**
* Returns a set of working groups, with an optional filter of the parent organization and project status.
*
* @param parentOrganization optional, name of org to filter working groups by
* @param projectStatus optional, statuses to include in result set
* @return set of working groups matching optional filters if present, otherwise all available working groups
*/
public Set<WorkingGroup> get(String parentOrganization, List<String> projectStatus);
public WorkingGroup getByName(String name);
public Map<String, List<String>> getWGPADocumentIDs();
}
......@@ -9,7 +9,8 @@ import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequest;
import org.eclipsefoundation.membership.portal.model.ContactRemovalRequestData;
import org.eclipsefoundation.membership.portal.request.model.ContactRemovalRequest;
import org.eclipsefoundation.membership.portal.service.MailerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -97,9 +98,9 @@ public class DefaultMailerService implements MailerService {
}
@Override
public void sendContactRemovalNotification(ContactRemovalRequest request) {
public void sendContactRemovalNotification(ContactRemovalRequestData request) {
// check access and data integrity before sending notification
checkRemovalRequest(request);
checkRemovalRequest(request.getRawRequest());
// get the logged in user who will receive the notification
DefaultJWTCallerPrincipal defaultPrin = (DefaultJWTCallerPrincipal) ident.getPrincipal();
String name = generateName(defaultPrin);
......@@ -107,7 +108,7 @@ public class DefaultMailerService implements MailerService {
Mail m = Mail
.withHtml(defaultPrin.getClaim("email"),
String.format("[Contact Removal Request] - Request to remove user '%s' from organization %s",
request.getUsername(), request.getOrganizationId()),
request.getPerson().getPersonID(), request.getOrganization().getOrganizationID()),
contactRemovalNotificationWeb.data(EMAIL_DATA_VAR, request, EMAIL_NAME_VAR, name, EMAIL_NOW_VAR,
LocalDateTime.now(), IS_PDF_VAR, false, EMAIL_CURRENT_USER_VAR, defaultPrin.getName())
.render())
......
......@@ -14,22 +14,19 @@ package org.eclipsefoundation.membership.portal.service.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.membership.portal.model.WorkingGroupMap;
import org.eclipsefoundation.membership.portal.model.WorkingGroupMap.WorkingGroup;
......@@ -53,10 +50,6 @@ import io.quarkus.runtime.Startup;
public class DefaultWorkingGroupsService implements WorkingGroupsService {
public static final Logger LOGGER = LoggerFactory.getLogger(DefaultWorkingGroupsService.class);
@ConfigProperty(name = "eclipse.working-groups.deny-list", defaultValue = "")
Instance<List<String>> denyList;
@ConfigProperty(name = "eclipse.working-groups.allow-list", defaultValue = "")
Optional<List<String>> allowList;
@ConfigProperty(name = "eclipse.working-groups.filepath")
String filepath;
......@@ -83,14 +76,11 @@ public class DefaultWorkingGroupsService implements WorkingGroupsService {
}
@Override
public Set<WorkingGroup> get() {
// if allow list present, use it, otherwise use denylist
Predicate<Entry<String, WorkingGroup>> checkAllowed = allowList().isEmpty()
? entry -> !denyList().contains(entry.getKey())
: entry -> allowList().contains(entry.getKey());
return new HashSet<>(wgs.entrySet().stream().filter(checkAllowed::test)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)).values());
public Set<WorkingGroup> get(String parentOrganization, List<String> projectStatuses) {
return new HashSet<>(
wgs.entrySet().stream().filter(e -> filterByParentOrganization(parentOrganization, e.getValue()))
.filter(e -> filterByProjectStatuses(projectStatuses, e.getValue()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)).values());
}
@Override
......@@ -105,12 +95,18 @@ public class DefaultWorkingGroupsService implements WorkingGroupsService {
return wgToDocument;
}
private List<String> denyList() {
return denyList.stream().flatMap(List::stream).collect(Collectors.toList());
private boolean filterByParentOrganization(String parentOrganization, WorkingGroup wg) {
if (StringUtils.isBlank(parentOrganization)) {
return true;
}
return wg.getParentOrganization().equals(parentOrganization);
}
private List<String> allowList() {
return allowList.isPresent() ? new ArrayList<>(allowList.get()) : Collections.emptyList();
private boolean filterByProjectStatuses(List<String> statuses, WorkingGroup wg) {
if (statuses == null || statuses.isEmpty()) {
return true;
}
return statuses.contains(wg.getStatus());
}
private List<String> extractWGPADocumentIDs(WorkingGroup wg) {
......
......@@ -12,6 +12,7 @@
package org.eclipsefoundation.membership.portal.service.impl;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
......@@ -408,7 +409,12 @@ public class FoundationDBOrganizationService implements OrganizationsService {
// retrieve the descriptions of the organization
out.setDescription(
MemberOrganizationDescription.builder().setLongDescription(info.getLongDescription()).build());
out.setWebsite(info.getCompanyUrl());
try {
new URI(info.getCompanyUrl());
out.setWebsite(info.getCompanyUrl());
} catch (URISyntaxException e) {
LOGGER.debug("Error while converting URL for organization '{}', leaving blank", info.getOrganizationID(), e);
}
}
// retrieve the logos of the organization
......
## DEV SETTINGS (creates a more test-friendly internal env)
security.csrf.enabled = true
security.csrf.enabled = false
eclipse.dataloader.user-ids=malowe, cguindon, epoirier, zhoufang
eclipse.app.base-url=http://localhost:8090/
eclipse.image-store.file-path=/home/martin/localdev/membership.eclipse.org/volumes/imagestore
......
......@@ -2,4 +2,3 @@
%ohwg.eclipse.app.base-url=https://membership.openhwgroup.org
%ohwg.eclipse.api.application-group=openhwgroup
%ohwg.eclipse.mailer.membership.inbox=membership.coordination@openhwgroup.org
%ohwg.eclipse.working-groups.allow-list=openhw-group,openhw-europe
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