Commit 236f4e20 authored by Martin Lowe's avatar Martin Lowe 🇨🇦
Browse files

Merge branch 'malowe/master/1.4.0-rc' into 'master'

Merge dev v1.4.0-rc into master

See merge request !541
parents 5d29a2a9 6960638e
Pipeline #2403 passed with stage
in 0 seconds
......@@ -79,6 +79,7 @@ docker.secret.properties
.env
#Test resources
src/test/resources/schemas
application/src/test/resources/schemas
portal/src/test/resources/schemas
/.apt_generated/
/.apt_generated_tests/
......@@ -76,8 +76,11 @@ pipeline {
environment {
APP_NAME = 'eclipsefdn-react-membership'
NAMESPACE = 'foundation-internal-webdev-apps'
IMAGE_NAME = 'eclipsefdn/eclipsefdn-react-membership'
CONTAINER_NAME = 'api'
WEB_IMAGE_NAME = 'eclipsefdn/eclipsefdn-react-membership-www'
APPLICATION_IMAGE_NAME = 'eclipsefdn/eclipsefdn-membership-application'
APPLICATION_CONTAINER_NAME = 'application-api'
PORTAL_IMAGE_NAME = 'eclipsefdn/eclipsefdn-membership-portal'
PORTAL_CONTAINER_NAME = 'portal-api'
ENVIRONMENT = sh(
script: """
printf "${env.BRANCH_NAME}"
......@@ -110,7 +113,7 @@ pipeline {
readTrusted '.mvn/wrapper/MavenWrapperDownloader.java'
readTrusted 'pom.xml'
sh 'make compile'
stash name: "target", includes: "target/**/*"
stash name: "target", includes: "**/target/**/*"
stash name: "build", includes: "src/main/www/build/**/*"
}
}
......@@ -121,14 +124,16 @@ pipeline {
label 'docker-build'
}
steps {
readTrusted 'src/main/docker/Dockerfile.jvm'
readTrusted 'portal/src/main/docker/Dockerfile.jvm'
readTrusted 'application/src/main/docker/Dockerfile.jvm'
unstash 'target'
unstash 'build'
sh '''
docker build -f src/main/docker/Dockerfile.jvm --no-cache -t ${IMAGE_NAME}:${TAG_NAME} -t ${IMAGE_NAME}:latest .
docker build -f src/main/docker/Dockerfile.www --no-cache -t ${IMAGE_NAME}-www:${TAG_NAME} -t ${IMAGE_NAME}-www:latest .
docker build -f portal/src/main/docker/Dockerfile.jvm --no-cache -t ${PORTAL_IMAGE_NAME}:${TAG_NAME} -t ${PORTAL_IMAGE_NAME}:latest ./portal
docker build -f application/src/main/docker/Dockerfile.jvm --no-cache -t ${APPLICATION_IMAGE_NAME}:${TAG_NAME} -t ${APPLICATION_IMAGE_NAME}:latest ./application
docker build -f src/main/docker/Dockerfile.www --no-cache -t ${WEB_IMAGE_NAME}:${TAG_NAME} -t ${WEB_IMAGE_NAME}:latest .
'''
}
}
......@@ -146,10 +151,12 @@ pipeline {
steps {
withDockerRegistry([credentialsId: '04264967-fea0-40c2-bf60-09af5aeba60f', url: 'https://index.docker.io/v1/']) {
sh '''
docker push ${IMAGE_NAME}:${TAG_NAME}
docker push ${IMAGE_NAME}:latest
docker push ${IMAGE_NAME}-www:${TAG_NAME}
docker push ${IMAGE_NAME}-www:latest
docker push ${PORTAL_IMAGE_NAME}:${TAG_NAME}
docker push ${PORTAL_IMAGE_NAME}:latest
docker push ${APPLICATION_IMAGE_NAME}:${TAG_NAME}
docker push ${APPLICATION_IMAGE_NAME}:latest
docker push ${WEB_IMAGE_NAME}:${TAG_NAME}
docker push ${WEB_IMAGE_NAME}:latest
'''
}
}
......@@ -200,14 +207,20 @@ pipeline {
updateContainerImage([
namespace: "${env.NAMESPACE}",
selector: "app=${env.APP_NAME},environment=${env.ENVIRONMENT}",
containerName: "${env.CONTAINER_NAME}",
newImageRef: "${env.IMAGE_NAME}:${env.TAG_NAME}"
containerName: "${env.PORTAL_CONTAINER_NAME}",
newImageRef: "${env.PORTAL_IMAGE_NAME}:${env.TAG_NAME}"
])
updateContainerImage([
namespace: "${env.NAMESPACE}",
selector: "app=${env.APP_NAME},environment=${env.ENVIRONMENT}",
containerName: "${env.APPLICATION_CONTAINER_NAME}",
newImageRef: "${env.APPLICATION_IMAGE_NAME}:${env.TAG_NAME}"
])
updateContainerImage([
namespace: "${env.NAMESPACE}",
selector: "app=${env.APP_NAME},environment=${env.ENVIRONMENT}",
containerName: "nginx",
newImageRef: "${env.IMAGE_NAME}-www:${env.TAG_NAME}"
newImageRef: "${env.WEB_IMAGE_NAME}:${env.TAG_NAME}"
])
}
}
......
compile-java: validate-spec;
compile-java:compile-test-resources;
mvn compile package
compile-java-quick: validate-spec;
compile-java-quick: ;
mvn compile package -Dmaven.test.skip=true
compile-react: install-react;
yarn --cwd src/main/www build
compile: clean compile-react compile-java;
compile-test-resources:;
yarn --cwd application run generate-json-schema
yarn --cwd portal run generate-json-schema
compile-quick: clean compile-react compile-java-quick;
clean:;
mvn clean
rm -rf src/main/resources/META-INF/*
yarn --cwd application run clean
yarn --cwd portal run clean
install-react:;
yarn --cwd src/main/www install --frozen-lockfile
validate-spec: install-react;
yarn --cwd src/main/www generate-json-schema
yarn --cwd src/main/www test-spec
yarn --cwd src/main/www install --frozen-lockfile --audit
yarn --cwd application install --frozen-lockfile
yarn --cwd portal install --frozen-lockfile
generate-cert:;
rm -rf certs && mkdir -p certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/www.rem.docker.key -out certs/www.rem.docker.crt
compile-start: compile-quick;
docker-compose down
docker-compose build
docker-compose up
start-spec: validate-spec;
yarn --cwd src/main/www start-spec
\ No newline at end of file
docker-compose up
\ No newline at end of file
......@@ -95,6 +95,37 @@ As a side note, the insertion of data into the database can be enabled/disabled
1. Setting `%dev.eclipse.dataloader.enabled` to true/false. This property is what enables the Data bootstrap to load in mock data.
[^ Top](#react-eclipsefdn-members)
#### Migrating from 1.x to 1.3.3 configuration
##### Portal secrets
- Create the baseline secret file `./config/portal/secret.properties` by copying `./config/secret.properties`.
- Apply the following updates from the base secret properties, removing entries marked N/A:
| Base Secret Name | Portal Secret Name |
|------------------|--------------------|
|quarkus.datasource."eclipsedb".username|quarkus.datasource.username|
|quarkus.datasource."eclipsedb".password|quarkus.datasource.password|
|quarkus.datasource."eclipsedb".jdbc.url|quarkus.datasource.jdbc.url|
|n/a|quarkus.datasource."eclipsedb".username|
|n/a|quarkus.datasource."eclipsedb".password|
|n/a|quarkus.datasource."eclipsedb".jdbc.url|
##### Application form secrets
- Create the baseline secret file `./config/portal/secret.properties` by copying `./config/secret.properties`.
- Retrieve a set of credentials for direct auth with accounts.eclipse.org, with the applicable scope of eclipsefdn_view_all_profiles (contact Martin for a set of working creds). These credentials should be set under the config values `quarkus.oidc-client.client-id` and `quarkus.oidc-client.credentials.secret`.
- Apply the following updates from the base secret properties, removing entries marked N/A:
| Base Secret Name | Application Secret Name |
|------------------|--------------------|
|n/a|quarkus.datasource."eclipsedb".username|
|n/a|quarkus.datasource."eclipsedb".password|
|n/a|quarkus.datasource."eclipsedb".jdbc.url|
|n/a|quarkus.datasource."dashboard".username|
|n/a|quarkus.datasource."dashboard".password|
|n/a|quarkus.datasource."dashboard".jdbc.url|
[^ Top](#react-eclipsefdn-members)
### Running
......
{
"name": "eclipse-membership-application-support",
"version": "1.0.0",
"dependencies": {
"@redocly/openapi-cli": "^1.0.0-beta.54",
"@openapi-contrib/openapi-schema-to-json-schema": "^3.1.1",
"@stoplight/json-ref-resolver": "^3.1.2",
"decamelize": "^5.0.0",
"js-yaml": "^4.1.0",
"yargs": "^17.0.1"
},
"private": true,
"scripts": {
"start": "yarn run generate-json-schema && npx @redocly/openapi-cli preview-docs spec/openapi.yaml -p 8097",
"test": "yarn run generate-json-schema && npx @redocly/openapi-cli lint spec/openapi.yaml",
"generate-json-schema": "yarn run clean && node src/main/js/openapi2schema.js -s spec/openapi.yaml -t src/test/resources",
"clean": "rm -rf src/test/resources/schemas/"
}
}
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<artifactId>application-container</artifactId>
<parent>
<groupId>org.eclipsefoundation</groupId>
<artifactId>react-commons</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<openhtml.version>1.0.9</openhtml.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
<!-- Required for PDF output. -->
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-core</artifactId>
<version>${openhtml.version}</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>${openhtml.version}</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-java2d</artifactId>
<version>${openhtml.version}</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
This diff is collapsed.
ARG NGINX_IMAGE_TAG=stable-alpine-for-hugo
FROM eclipsefdn/nginx:${NGINX_IMAGE_TAG}
COPY config/nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY src/main/www/build /usr/share/nginx/html/
\ No newline at end of file
/*******************************************************************************
* Copyright (C) 2020 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipsefoundation.membership.application.api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
import io.quarkus.oidc.client.NamedOidcClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.quarkus.security.Authenticated;
/**
* Binding interface for the Eclipse Foundation user account API. Runtime implementations are automatically generated by
* Quarkus at compile time. As the API deals with sensitive information, authentication is required to access this
* endpoint.
*
* @author Martin Lowe
*
*/
@OidcClientFilter
@Authenticated
@Path("/account")
@RegisterRestClient(configKey = "accounts")
public interface AccountsAPI {
/**
* Retrieves all user objects that match the given query parameters.
*
* @param id user ID of the Eclipse account to retrieve
* @return all matching eclipse accounts
*/
@GET
@Path("/profile/{uid}")
@Produces("application/json")
EclipseUser getUsers(@PathParam("uid") String id);
@AutoValue
@JsonDeserialize(builder = AutoValue_AccountsAPI_EclipseUser.Builder.class)
public abstract static class EclipseUser {
public abstract int getUid();
public abstract String getName();
public abstract String getMail();
public abstract ECA getECA();
public abstract String getFullName();
public abstract boolean getIsCommitter();
public static Builder builder() {
return new AutoValue_AccountsAPI_EclipseUser.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setUid(int id);
public abstract Builder setName(String name);
public abstract Builder setMail(String mail);
public abstract Builder setECA(ECA eca);
public abstract Builder setFullName(String fullName);
public abstract Builder setIsCommitter(boolean isCommitter);
public abstract EclipseUser build();
}
}
@AutoValue
@JsonDeserialize(builder = AutoValue_AccountsAPI_ECA.Builder.class)
public abstract static class ECA {
public abstract boolean getSigned();
public abstract boolean getCanContributeSpecProject();
public static Builder builder() {
return new AutoValue_AccountsAPI_ECA.Builder();
}
@AutoValue.Builder
@JsonPOJOBuilder(withPrefix = "set")
public abstract static class Builder {
public abstract Builder setSigned(boolean signed);
public abstract Builder setCanContributeSpecProject(boolean canContributeSpecProject);
public abstract ECA build();
}
}
}
package org.eclipsefoundation.membership.application.api;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.eclipsefoundation.membership.application.model.WorkingGroupMap.WorkingGroup;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
@Path("working_groups")
@Produces(MediaType.APPLICATION_JSON)
@RegisterRestClient(configKey = "membership-api")
public interface MembershipAPI {
Response getWorkingGroups(@QueryParam("page") int page);
@Path("{name}")
WorkingGroup getWorkingGroup(@PathParam("name") String name);
}
package org.eclipsefoundation.react.bootstrap;
package org.eclipsefoundation.membership.application.config;
import java.text.SimpleDateFormat;
......
......@@ -9,8 +9,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.react.bootstrap;
package org.eclipsefoundation.membership.application.config;
import java.net.URI;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
......@@ -25,24 +27,27 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipsefoundation.core.model.FlatRequestWrapper;
import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.membership.application.dto.Address;
import org.eclipsefoundation.membership.application.dto.Contact;
import org.eclipsefoundation.membership.application.dto.FormOrganization;
import org.eclipsefoundation.membership.application.dto.FormWorkingGroup;
import org.eclipsefoundation.membership.application.dto.MembershipForm;
import org.eclipsefoundation.membership.application.helper.TimeHelper;
import org.eclipsefoundation.membership.application.namespace.ContactTypes;
import org.eclipsefoundation.membership.application.namespace.FormState;
import org.eclipsefoundation.membership.application.namespace.OrganizationTypes;
import org.eclipsefoundation.persistence.dao.impl.DefaultHibernateDao;
import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.persistence.service.FilterService;
import org.eclipsefoundation.react.dto.Address;
import org.eclipsefoundation.react.dto.Contact;
import org.eclipsefoundation.react.dto.FormOrganization;
import org.eclipsefoundation.react.dto.FormWorkingGroup;
import org.eclipsefoundation.react.dto.MembershipForm;
import org.eclipsefoundation.react.helper.TimeHelper;
import org.eclipsefoundation.react.namespace.ContactTypes;
import org.eclipsefoundation.react.namespace.FormState;
import org.eclipsefoundation.react.namespace.OrganizationTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.quarkus.runtime.StartupEvent;
import io.quarkus.runtime.configuration.ProfileManager;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
/**
* Injects data into the dataset once persistence engine is loaded in the given contexts. This allows for random data to
......@@ -57,7 +62,7 @@ public class DataLoader {
public static final Logger LOGGER = LoggerFactory.getLogger(DataLoader.class);
@Inject
DataLoaderConfig config;
Config config;
@Inject
DefaultHibernateDao dao;
......@@ -77,21 +82,21 @@ public class DataLoader {
public void init(@Observes StartupEvent ev) {
// if running in dev mode, preload a bunch of data using dao
LOGGER.debug("Current mode: {}", ProfileManager.getActiveProfile());
if (config.isEnabled() && config.getDataLoaderProfiles().contains(ProfileManager.getActiveProfile())) {
RequestWrapper wrap = new RequestWrapper();
List<MembershipForm> forms = new ArrayList<>(config.getFormCount());
for (int i = 0; i < config.getFormCount(); i++) {
if (config.enabled() && config.dataLoaderProfiles().contains(ProfileManager.getActiveProfile())) {
RequestWrapper wrap = new FlatRequestWrapper(URI.create("https://membership.eclipse.org"));
List<MembershipForm> forms = new ArrayList<>(config.formCount());
for (int i = 0; i < config.formCount(); i++) {
MembershipForm mf = new MembershipForm();
String userID = config.getUserIds().get(r.nextInt(config.getUserIds().size()));
String userID = config.userIds().get(r.nextInt(config.userIds().size()));
mf.setUserID(userID);
mf.setMembershipLevel(config.getMembershipLevels().get(r.nextInt(config.getMembershipLevels().size())));
mf.setMembershipLevel(config.membershipLevels().get(r.nextInt(config.membershipLevels().size())));
mf.setSigningAuthority(Math.random() > 0.5);
mf.setRegistrationCountry("CA");
mf.setVatNumber(RandomStringUtils.randomNumeric(10));
mf.setPurchaseOrderRequired(Math.random() > 0.5 ? "yes" : "no");
mf.setDateCreated(
TimeHelper.getMillis() - ThreadLocalRandom.current().nextLong(config.getDaysOut().toMillis()));
TimeHelper.getMillis() - ThreadLocalRandom.current().nextLong(config.daysOut().toMillis()));
mf.setDateUpdated(mf.getDateCreated());
mf.setState(FormState.INPROGRESS);
forms.add(mf);
......@@ -142,9 +147,8 @@ public class DataLoader {
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())));
wg.setWorkingGroupID(config.workingGroups().get(r.nextInt(config.workingGroups().size())));
wg.setParticipationLevel(config.participationLevels().get(r.nextInt(config.participationLevels().size())));
// get a random instance of time
wg.setEffectiveDate(OffsetDateTime.now().minus(r.nextInt(1000000), ChronoUnit.SECONDS));
wg.setContact(generateContact(form, Optional.empty()));
......@@ -186,4 +190,31 @@ public class DataLoader {
sb.append(RandomStringUtils.randomAlphabetic(2));
return sb.toString();
}
@ConfigMapping(prefix = "eclipse.dataloader")
public interface Config {
@WithDefault("false")
boolean enabled();
@WithDefault("100")
int formCount();
@WithDefault("dev, staging")
List<String> dataLoaderProfiles();
@WithDefault("test1, test2, test3")
List<String> userIds();
@WithDefault("internet-things-iot, jakarta-ee, cloud-tools-development")
List<String> workingGroups();
@WithDefault("strategic, contributing, associate, committer")
List<String> membershipLevels();
@WithDefault("platinum, gold, silver")
List<String> participationLevels();
@WithDefault("P60D")
Duration daysOut();
}
}
package org.eclipsefoundation.membership.application.config;
import java.util.List;
import java.util.Map;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
@ConfigMapping(prefix = "eclipse.cors")
public interface EnhancedCORSConfig {
@WithDefault("false")
boolean enabled();
Map<String, EnhancedCORSSet> config();
public static interface EnhancedCORSSet {
List<String> endpointPatterns();
List<String> origins();
List<String> methods();
}
}
......@@ -9,7 +9,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.react.dto;
package org.eclipsefoundation.membership.application.dto;
import java.util.Objects;
......
......@@ -9,7 +9,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.react.dto;
package org.eclipsefoundation.membership.application.dto;
import java.util.List;
import java.util.Objects;
......@@ -30,13 +30,13 @@ import javax.validation.constraints.NotNull;
import javax.ws.rs.core.MultivaluedMap;
import org.eclipsefoundation.core.namespace.DefaultUrlParameterNames;
import org.eclipsefoundation.membership.application.namespace.ContactTypes;
import org.eclipsefoundation.membership.application.namespace.MembershipFormAPIParameterNames;
import org.eclipsefoundation.persistence.dto.BareNode;
import org.eclipsefoundation.persistence.dto.filter.DtoFilter;
import org.eclipsefoundation.persistence.model.DtoTable;
import org.eclipsefoundation.persistence.model.ParameterizedSQLStatement;
import org.eclipsefoundation.persistence.model.ParameterizedSQLStatementBuilder;