diff --git a/Jenkinsfile b/Jenkinsfile index 53f1a6106b8993756de54919198d2ab335df96ae..8216d104532320e6d2938732a454fa3dec5d6ef2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -125,7 +125,7 @@ readTrusted 'pom.xml' sh 'make generate-spec' withCredentials([string(credentialsId: 'sonarcloud-token-git-eca-rest-api', variable: 'SONAR_TOKEN')]) { - sh 'mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -B -Dsonar.login=${SONAR_TOKEN}' + sh 'mvn org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.login=${SONAR_TOKEN} -Dmaven.test.skip=true' } stash name: "target", includes: "target/**/*" } diff --git a/pom.xml b/pom.xml index fd2d12a4155bca867a0eb362688d1626457f273c..0905d4abd609365d071ba3068286704938c56fbf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ <artifactId>git-eca</artifactId> <version>1.1.0</version> <properties> - <eclipse-api-version>0.7.4</eclipse-api-version> + <eclipse-api-version>0.7.6-SNAPSHOT</eclipse-api-version> <compiler-plugin.version>3.8.1</compiler-plugin.version> <maven.compiler.parameters>true</maven.compiler.parameters> <maven.compiler.source>11</maven.compiler.source> @@ -14,7 +14,7 @@ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id> - <quarkus.platform.version>2.14.2.Final</quarkus.platform.version> + <quarkus.platform.version>2.16.3.Final</quarkus.platform.version> <surefire-plugin.version>2.22.1</surefire-plugin.version> <auto-value.version>1.8.2</auto-value.version> <org.mapstruct.version>1.4.1.Final</org.mapstruct.version> @@ -24,7 +24,6 @@ <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths> <sonar.junit.reportPath>${project.build.directory}/surefire-reports</sonar.junit.reportPath> - <sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.organization>eclipse-foundation-it</sonar.organization> <sonar.projectKey>eclipse-foundation-it_git-eca-rest-api</sonar.projectKey> <sonar.projectName>Git ECA REST API</sonar.projectName> @@ -63,6 +62,11 @@ <artifactId>quarkus-persistence</artifactId> <version>${eclipse-api-version}</version> </dependency> + <dependency> + <groupId>org.eclipsefoundation</groupId> + <artifactId>quarkus-efservices</artifactId> + <version>${eclipse-api-version}</version> + </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> @@ -87,7 +91,6 @@ <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> - <version>1.70</version> </dependency> <dependency> @@ -129,18 +132,6 @@ <scope>provided</scope> </dependency> - <!-- Third-party reqs --> - <dependency> - <groupId>com.github.scribejava</groupId> - <artifactId>scribejava-apis</artifactId> - <version>6.4.1</version> - </dependency> - <!-- Caching --> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - </dependency> - <!-- Test requirements --> <dependency> <groupId>io.quarkus</groupId> diff --git a/spec/openapi.yaml b/spec/openapi.yaml index c1b7bcecb9aee9d78b10dee01e9c84d817e4f699..906bde10bb40c02ec735c55ae7dfc8fa980908a5 100644 --- a/spec/openapi.yaml +++ b/spec/openapi.yaml @@ -2,6 +2,10 @@ openapi: "3.1.0" info: version: 1.1.0 title: Eclipse Foundation Git ECA API + description: Collection of API endpoints used in the validation and management of external Git services, such as Gitlab and Github. + contact: + name: IT support + url: https://gitlab.eclipse.org/eclipsefdn/it/api/git-eca-rest-api/-/issues license: name: Eclipse Public License - 2.0 url: https://www.eclipse.org/legal/epl-2.0/ @@ -11,10 +15,15 @@ servers: tags: - name: ECA Validation description: Definitions in relation to the validation of Git commits through ECA signage + - name: Reports + description: Reports on metadata associated with Git systems managed by the Eclipse Foundation + - name: Integration Webhooks + description: Endpoints related to binding to external Git services through a webhook paths: /eca: post: + operationId: validate tags: - ECA Validation summary: ECA validation @@ -25,29 +34,36 @@ paths: schema: $ref: "#/components/schemas/ValidationRequest" responses: - 200: + "200": description: Success content: application/json: schema: $ref: "#/components/schemas/ValidationResponse" - 500: + "500": description: Error while retrieving data - /eca/status/{fingerprint}: + parameters: + - name: fingerprint + in: path + description: Unique ID for the request group + required: true + schema: + type: string get: + operationId: getCommitValidation tags: - - ECA Validation Status + - ECA Validation summary: Historic ECA validation status description: Returns a set of validation messages for the given unique fingerprint responses: - 200: + "200": description: Success content: application/json: schema: $ref: "#/components/schemas/CommitValidationStatuses" - 500: + "500": description: Error while retrieving data /eca/status/{fingerprint}/ui: @@ -59,34 +75,41 @@ paths: schema: type: string get: + operationId: getCommitValidationUI + tags: + - ECA Validation summary: Historic ECA validation status in a HTML format description: Returns an HTMl page containing validation messages responses: - 200: + "200": description: Success. An HTML page containing status info - 404: + "404": description: Not Found - 500: + "500": description: Error while retrieving data /eca/lookup: get: + operationId: getUserStatus + tags: + - ECA Validation summary: User status lookup description: Returns wether or not the user has a signed ECA responses: - 200: + "200": description: Success - 403: + "403": description: User exists with no ECA - 404: + "404": description: User not found - 500: + "500": description: Error while retrieving data /webhooks/github: post: + operationId: processGithubWebhook tags: - - Github validation processing + - Integration Webhooks summary: Github incoming hook event processing description: Process incoming pull request hook events from Github parameters: @@ -111,9 +134,9 @@ paths: schema: $ref: "#/components/schemas/GithubWebhookEvent" responses: - 200: + "200": description: Success - 500: + "500": description: Error while processing data /webhooks/github/revalidate/{fingerprint}: parameters: @@ -124,8 +147,9 @@ paths: schema: type: string post: + operationId: revalidateWebhookRequest tags: - - Github validation processing + - Integration Webhooks summary: Gitlab webhook revalidation request description: Process incoming system hooks from GitLab requestBody: @@ -134,20 +158,21 @@ paths: schema: $ref: '#/components/schemas/RevalidationRequest' responses: - 200: + "200": description: Success - 400: + "400": description: Bad request content: application/json: schema: $ref: "#/components/schemas/Error" - 404: + "404": description: Not found /webhooks/gitlab/system: post: + operationId: processGitlabHook tags: - - Gitlab system event processing + - Integration Webhooks summary: Gitlab event processing description: Process incoming system hooks from GitLab parameters: @@ -162,9 +187,9 @@ paths: schema: $ref: "#/components/schemas/SystemHook" responses: - 200: + "200": description: Success - 500: + "500": description: Error while processing data /reports/gitlab/private-projects: @@ -194,26 +219,27 @@ paths: schema: type: string get: + operationId: getPrivateProjectEvents tags: - - Private project event report + - Reports summary: Gitlab private project event report description: Returns list of private project events using desired filters responses: - 200: + "200": description: Success content: application/json: schema: $ref: "#/components/schemas/PrivateProjectEvents" - 400: + "400": description: Bad Request - invalid non-null prams content: application/json: schema: $ref: "#/components/schemas/Error" - 401: + "401": description: Unauthorized - invalid key - 500: + "500": description: Error while processing request components: diff --git a/src/main/java/org/eclipsefoundation/git/eca/api/ProjectsAPI.java b/src/main/java/org/eclipsefoundation/git/eca/api/ProjectsAPI.java deleted file mode 100644 index 9e95121240aa50c101b224e1ddff06251bd0cfa3..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/api/ProjectsAPI.java +++ /dev/null @@ -1,52 +0,0 @@ -/********************************************************************* -* Copyright (c) 2022 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.api; - -import javax.enterprise.context.ApplicationScoped; -import javax.ws.rs.BeanParam; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Response; - -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.eclipsefoundation.core.service.APIMiddleware.BaseAPIParameters; -import org.jboss.resteasy.annotations.GZIP; - -/** - * Interface for interacting with the PMI Projects API. Used to link Git - * repos/projects with an Eclipse project to validate committer access. - * - * @author Martin Lowe - * - */ -@ApplicationScoped -@Path("/api") -@RegisterRestClient -@GZIP -public interface ProjectsAPI { - - /** - * Retrieves all projects with the given repo URL. - * - * @param repoUrl the target repos URL - * @return a list of Eclipse Foundation projects. - */ - @GET - @Path("projects") - Response getProjects(@BeanParam BaseAPIParameters baseParams); - - @GET - @Path("interest-groups") - @Produces("application/json") - Response getInterestGroups(@BeanParam BaseAPIParameters params); -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/api/models/InterestGroupData.java b/src/main/java/org/eclipsefoundation/git/eca/api/models/InterestGroupData.java deleted file mode 100644 index e70f830625227182bdd2af7faadd6dc385bbc79e..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/api/models/InterestGroupData.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.eclipsefoundation.git.eca.api.models; - -import java.util.List; - -import org.eclipsefoundation.git.eca.api.models.Project.GitlabProject; -import org.eclipsefoundation.git.eca.api.models.Project.User; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import com.google.auto.value.AutoValue; - -@AutoValue -@JsonDeserialize(builder = AutoValue_InterestGroupData.Builder.class) -public abstract class InterestGroupData { - public abstract String getId(); - public abstract String getTitle(); - public abstract String getLogo(); - public abstract String getState(); - public abstract String getProjectId(); - public abstract Descriptor getDescription(); - public abstract Descriptor getScope(); - public abstract List<User> getLeads(); - public abstract List<User> getParticipants(); - public abstract GitlabProject getGitlab(); - - public static Builder builder() { - return new AutoValue_InterestGroupData.Builder(); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setId(String id); - public abstract Builder setTitle(String title); - public abstract Builder setGitlab(GitlabProject gitlab); - public abstract Builder setLogo(String logo); - public abstract Builder setState(String state); - public abstract Builder setProjectId(String foundationdbProjectId); - public abstract Builder setDescription(Descriptor description); - public abstract Builder setScope(Descriptor scope); - public abstract Builder setLeads(List<User> leads); - public abstract Builder setParticipants(List<User> participants); - public abstract InterestGroupData build(); - } - - @AutoValue - @JsonDeserialize(builder = AutoValue_InterestGroupData_Descriptor.Builder.class) - public abstract static class Descriptor { - public abstract String getSummary(); - public abstract String getFull(); - - public static Builder builder() { - return new AutoValue_InterestGroupData_Descriptor.Builder(); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setSummary(String summary); - public abstract Builder setFull(String full); - public abstract Descriptor build(); - } - } -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/api/models/Project.java b/src/main/java/org/eclipsefoundation/git/eca/api/models/Project.java deleted file mode 100644 index 7b30c1be33557fbbf24efc8c0bf4957355b86c50..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/api/models/Project.java +++ /dev/null @@ -1,194 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.api.models; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import com.google.auto.value.AutoValue; -import com.google.auto.value.extension.memoized.Memoized; - -/** - * Represents a project in the Eclipse API, along with the users and repos that exist within the context of the project. - * - * @author Martin Lowe - * - */ -@AutoValue -@JsonDeserialize(builder = $AutoValue_Project.Builder.class) -public abstract class Project { - public abstract String getProjectId(); - - public abstract String getName(); - - public abstract List<User> getCommitters(); - - public abstract List<User> getProjectLeads(); - - @Nullable - public abstract List<Repo> getRepos(); - - public abstract List<Repo> getGitlabRepos(); - - public abstract List<Repo> getGithubRepos(); - - public abstract List<Repo> getGerritRepos(); - - public abstract Object getSpecProjectWorkingGroup(); - - public abstract GitlabProject getGitlab(); - - public abstract GithubProject getGithub(); - - @Nullable - @Memoized - public String getSpecWorkingGroup() { - // stored as map as empty returns an array instead of a map - Object specProjectWorkingGroup = getSpecProjectWorkingGroup(); - if (specProjectWorkingGroup instanceof Map) { - // we checked in line above that the map exists, so we can safely cast it - @SuppressWarnings("unchecked") - Object raw = ((Map<Object, Object>) specProjectWorkingGroup).get("id"); - if (raw instanceof String) { - return (String) raw; - } - } - return null; - } - - public static Builder builder() { - // adds empty lists as default values - return new AutoValue_Project.Builder() - .setRepos(new ArrayList<>()) - .setCommitters(new ArrayList<>()) - .setGithubRepos(new ArrayList<>()) - .setGitlabRepos(new ArrayList<>()) - .setGerritRepos(new ArrayList<>()); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setProjectId(String projectId); - - public abstract Builder setName(String name); - - public abstract Builder setProjectLeads(List<User> projectLeads); - - public abstract Builder setCommitters(List<User> committers); - - public abstract Builder setRepos(@Nullable List<Repo> repos); - - public abstract Builder setGitlabRepos(List<Repo> gitlabRepos); - - public abstract Builder setGithubRepos(List<Repo> githubRepos); - - public abstract Builder setGerritRepos(List<Repo> gerritRepos); - - public abstract Builder setSpecProjectWorkingGroup(Object specProjectWorkingGroup); - - public abstract Builder setGitlab(GitlabProject gitlab); - - public abstract Builder setGithub(GithubProject github); - - public abstract Project build(); - } - - @AutoValue - @JsonDeserialize(builder = AutoValue_Project_User.Builder.class) - public abstract static class User { - public abstract String getUsername(); - - public abstract String getUrl(); - - public static Builder builder() { - return new AutoValue_Project_User.Builder(); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setUsername(String username); - - public abstract Builder setUrl(String url); - - public abstract User build(); - } - } - - @AutoValue - @JsonDeserialize(builder = AutoValue_Project_GitlabProject.Builder.class) - public abstract static class GitlabProject { - public abstract String getProjectGroup(); - - public abstract List<String> getIgnoredSubGroups(); - - public static Builder builder() { - return new AutoValue_Project_GitlabProject.Builder(); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setProjectGroup(String projectGroup); - - public abstract Builder setIgnoredSubGroups(List<String> ignoredSubGroups); - - public abstract GitlabProject build(); - } - } - - @AutoValue - @JsonDeserialize(builder = AutoValue_Project_GithubProject.Builder.class) - public abstract static class GithubProject { - public abstract String getOrg(); - - public abstract List<String> getIgnoredRepos(); - - public static Builder builder() { - return new AutoValue_Project_GithubProject.Builder(); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "set") - public abstract static class Builder { - public abstract Builder setOrg(String org); - - public abstract Builder setIgnoredRepos(List<String> ignoredRepos); - - public abstract GithubProject build(); - } - } - - /** - * Does not use autovalue as the value should be mutable. - * - * @author Martin Lowe - * - */ - public static class Repo { - private String url; - - public String getUrl() { - return this.url; - } - - public void setUrl(String url) { - this.url = url; - } - } -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/helper/CommitHelper.java b/src/main/java/org/eclipsefoundation/git/eca/helper/CommitHelper.java index 0650a2e89a34d6c0c53f4fea19defbbac3c79a8f..eaa36924daf54e8bf40702963e0d780641b970ea 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/helper/CommitHelper.java +++ b/src/main/java/org/eclipsefoundation/git/eca/helper/CommitHelper.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import javax.ws.rs.core.MultivaluedMap; -import org.eclipsefoundation.git.eca.api.models.Project; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.model.Commit; import org.eclipsefoundation.git.eca.model.ValidationRequest; import org.eclipsefoundation.git.eca.namespace.GitEcaParameterNames; diff --git a/src/main/java/org/eclipsefoundation/git/eca/helper/ProjectHelper.java b/src/main/java/org/eclipsefoundation/git/eca/helper/ProjectHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..49c85eb298367dab23f4d75cf67083eceee346b9 --- /dev/null +++ b/src/main/java/org/eclipsefoundation/git/eca/helper/ProjectHelper.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2023 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/ + * + * Author: Martin Lowe <martin.lowe@eclipse-foundation.org> + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipsefoundation.git.eca.helper; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import org.apache.commons.lang3.StringUtils; +import org.eclipsefoundation.core.service.CachingService; +import org.eclipsefoundation.efservices.api.models.InterestGroup; +import org.eclipsefoundation.efservices.api.models.Project; +import org.eclipsefoundation.efservices.api.models.Project.GithubProject; +import org.eclipsefoundation.efservices.api.models.Project.ProjectParticipant; +import org.eclipsefoundation.efservices.services.ProjectService; +import org.eclipsefoundation.git.eca.model.ValidationRequest; +import org.eclipsefoundation.git.eca.namespace.ProviderType; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helps manage projects by providing filters on top of the generic service as well as operations like adapting interest + * groups to projects for easier processing. + * + * @author Martin Lowe + * + */ +@ApplicationScoped +public final class ProjectHelper { + private static final Logger LOGGER = LoggerFactory.getLogger(ProjectHelper.class); + + @Inject + CachingService cache; + @Inject + ProjectService projects; + + public List<Project> retrieveProjectsForRequest(ValidationRequest req) { + String repoUrl = req.getRepoUrl().getPath(); + if (repoUrl == null) { + LOGGER.warn("Can not match null repo URL to projects"); + return Collections.emptyList(); + } + return retrieveProjectsForRepoURL(repoUrl, req.getProvider()); + } + + public List<Project> retrieveProjectsForRepoURL(String repoUrl, ProviderType provider) { + if (repoUrl == null) { + LOGGER.warn("Can not match null repo URL to projects"); + return Collections.emptyList(); + } + // check for all projects that make use of the given repo + List<Project> availableProjects = getProjects(); + if (availableProjects.isEmpty()) { + LOGGER.warn("Could not find any projects to match against"); + return Collections.emptyList(); + } + LOGGER.debug("Checking projects for repos that end with: {}", repoUrl); + + String projectNamespace = URI.create(repoUrl).getPath().substring(1).toLowerCase(); + // filter the projects based on the repo URL. At least one repo in project must + // match the repo URL to be valid + switch (provider) { + case GITLAB: + return availableProjects + .stream() + .filter(p -> projectNamespace.startsWith(p.getGitlab().getProjectGroup() + "/") + && p.getGitlab().getIgnoredSubGroups().stream().noneMatch(sg -> projectNamespace.startsWith(sg + "/"))) + .collect(Collectors.toList()); + case GITHUB: + return availableProjects + .stream() + .filter(p -> p.getGithubRepos().stream().anyMatch(re -> re.getUrl() != null && re.getUrl().endsWith(repoUrl)) + || (StringUtils.isNotBlank(p.getGithub().getOrg()) && projectNamespace.startsWith(p.getGithub().getOrg()) + && p.getGithub().getIgnoredRepos().stream().noneMatch(repoUrl::endsWith))) + .collect(Collectors.toList()); + case GERRIT: + return availableProjects + .stream() + .filter(p -> p.getGerritRepos().stream().anyMatch(re -> re.getUrl() != null && re.getUrl().endsWith(repoUrl))) + .collect(Collectors.toList()); + default: + return Collections.emptyList(); + } + } + + /** + * Retrieve cached and adapted projects list to avoid having to create all interest group adaptations on every request. + * + * @return list of available projects or empty list if none found. + */ + public List<Project> getProjects() { + return cache.get("all-combined", new MultivaluedMapImpl<>(), Project.class, () -> { + List<Project> availableProjects = projects.getAllProjects(); + availableProjects.addAll(adaptInterestGroups(projects.getAllInterestGroups())); + return availableProjects; + }).orElseGet(Collections::emptyList); + } + + private List<Project> adaptInterestGroups(List<InterestGroup> igs) { + return igs + .stream() + .map(ig -> Project + .builder() + .setProjectId(ig.getProjectId()) + .setGerritRepos(Collections.emptyList()) + .setGithubRepos(Collections.emptyList()) + .setGitlabRepos(Collections.emptyList()) + .setGitlab(ig.getGitlab()) + .setCommitters(ig + .getParticipants() + .stream() + .map(p -> ProjectParticipant + .builder() + .setFullName(p.getFullName()) + .setUrl(p.getUrl()) + .setUsername(p.getUsername()) + .build()) + .collect(Collectors.toList())) + .setProjectLeads(ig + .getLeads() + .stream() + .map(p -> ProjectParticipant + .builder() + .setFullName(p.getFullName()) + .setUrl(p.getUrl()) + .setUsername(p.getUsername()) + .build()) + .collect(Collectors.toList())) + .setContributors(Collections.emptyList()) + .setShortProjectId(ig.getShortProjectId()) + .setSlsaLevel("") + .setSummary("") + .setWebsiteUrl("") + .setWebsiteRepo(Collections.emptyList()) + .setGitlab(ig.getGitlab()) + .setGithub(GithubProject.builder().setOrg("").setIgnoredRepos(Collections.emptyList()).build()) + .setWorkingGroups(Collections.emptyList()) + .setIndustryCollaborations(Collections.emptyList()) + .setReleases(Collections.emptyList()) + .setTopLevelProject("") + .setUrl("") + .setLogo(ig.getLogo()) + .setTags(Collections.emptyList()) + .setName(ig.getTitle()) + .setSpecProjectWorkingGroup(Collections.emptyMap()) + .build()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/eclipsefoundation/git/eca/oauth/EclipseApi.java b/src/main/java/org/eclipsefoundation/git/eca/oauth/EclipseApi.java deleted file mode 100644 index b89a37039614a0558baea6d562d24d45ddec0b0c..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/oauth/EclipseApi.java +++ /dev/null @@ -1,49 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.oauth; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; -import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; - -/** - * Wrapper around the OAuth API for Scribejava. Enables OAuth2.0 binding to the - * Eclipse Foundation OAuth server. - * - * @author Martin Lowe - * - */ -public class EclipseApi extends DefaultApi20 { - - @Override - public String getAccessTokenEndpoint() { - return "https://accounts.eclipse.org/oauth2/token"; - } - - @Override - protected String getAuthorizationBaseUrl() { - return null; - } - - @Override - public ClientAuthentication getClientAuthentication() { - return RequestBodyAuthenticationScheme.instance(); - } - - private static class InstanceHolder { - private static final EclipseApi INSTANCE = new EclipseApi(); - } - - public static EclipseApi instance() { - return InstanceHolder.INSTANCE; - } -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java b/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java index b2bd837117ac7fcebcb08dd14666957095215c24..8c612938e0e53d23b617d0dce85f31af71ba696f 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java +++ b/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java @@ -27,14 +27,14 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.api.models.GithubWebhookRequest; import org.eclipsefoundation.git.eca.api.models.GithubWebhookRequest.PullRequest; -import org.eclipsefoundation.git.eca.api.models.Project; import org.eclipsefoundation.git.eca.dto.CommitValidationStatus; import org.eclipsefoundation.git.eca.dto.GithubWebhookTracking; +import org.eclipsefoundation.git.eca.helper.ProjectHelper; import org.eclipsefoundation.git.eca.model.Commit; import org.eclipsefoundation.git.eca.model.ValidationRequest; -import org.eclipsefoundation.git.eca.service.ProjectsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +52,7 @@ public class StatusResource extends GithubAdjacentResource { private static final Logger LOGGER = LoggerFactory.getLogger(StatusResource.class); @Inject - ProjectsService projects; + ProjectHelper projects; // Qute templates, generates UI status page @Location("simple_fingerprint_ui") diff --git a/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java b/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java index 90df077eab27d987906b83d80308fa98d3aae66c..a567bcda314ed2d588a4b3595fdbea22c72ff9ca 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java +++ b/src/main/java/org/eclipsefoundation/git/eca/resource/ValidationResource.java @@ -29,11 +29,10 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipsefoundation.core.model.RequestWrapper; import org.eclipsefoundation.core.service.CachingService; import org.eclipsefoundation.git.eca.api.models.EclipseUser; +import org.eclipsefoundation.git.eca.helper.ProjectHelper; import org.eclipsefoundation.git.eca.model.ValidationRequest; import org.eclipsefoundation.git.eca.model.ValidationResponse; import org.eclipsefoundation.git.eca.namespace.APIStatusCode; -import org.eclipsefoundation.git.eca.service.InterestGroupService; -import org.eclipsefoundation.git.eca.service.ProjectsService; import org.eclipsefoundation.git.eca.service.UserService; import org.eclipsefoundation.git.eca.service.ValidationService; import org.jboss.resteasy.annotations.jaxrs.QueryParam; @@ -67,13 +66,11 @@ public class ValidationResource { @Inject CachingService cache; @Inject - ProjectsService projects; + ProjectHelper projects; @Inject UserService users; @Inject ValidationService validation; - @Inject - InterestGroupService ig; /** * Consuming a JSON request, this method will validate all passed commits, using the repo URL and the repository diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/InterestGroupService.java b/src/main/java/org/eclipsefoundation/git/eca/service/InterestGroupService.java deleted file mode 100644 index 8c2d182b10bf40196da01e8d4ce116339e34d6da..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/InterestGroupService.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.eclipsefoundation.git.eca.service; - -import java.util.List; - -import org.eclipsefoundation.git.eca.api.models.InterestGroupData; -import org.eclipsefoundation.git.eca.api.models.Project; - -/** - * Service for retrieving and interacting with interest groups. - * - * @author Martin Lowe - * - */ -public interface InterestGroupService { - - /** - * Retrieve all available interest groups. - * - * @return list of all available interest groups - */ - List<InterestGroupData> getInterestGroups(); - - /** - * Converts interest groups into projects for processing downstream. - * - * @param igs the interest groups to convert - * @return the converted interest groups - */ - List<Project> adaptInterestGroups(List<InterestGroupData> igs); -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/OAuthService.java b/src/main/java/org/eclipsefoundation/git/eca/service/OAuthService.java deleted file mode 100644 index 144601aeea1356da9a7a16ac5e20c3dd2cf633f3..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/OAuthService.java +++ /dev/null @@ -1,33 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.service; - -/** - * Used to generate OAuth tokens for use with internal services rather than - * bolted on introspection. This is required over the (now deprecated) Elytron - * plugin or the OIDC plugin as those plugins work with requests to validate - * incoming rather than outgoing requests. - * - * @author Martin Lowe - * - */ -public interface OAuthService { - - /** - * Retrieve an access token for the service from the Eclipse API for internal - * usage. - * - * @return current access token, or null if none could be retrieved for current - * API credentials/settings. - */ - String getToken(); -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/ProjectsService.java b/src/main/java/org/eclipsefoundation/git/eca/service/ProjectsService.java deleted file mode 100644 index 54c2199f477ffbe7b149a485617aaf04ad108b74..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/ProjectsService.java +++ /dev/null @@ -1,54 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.service; - -import java.util.List; - -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.model.ValidationRequest; -import org.eclipsefoundation.git.eca.namespace.ProviderType; - -/** - * Intermediate layer between resource and API layers that handles retrieval of all projects and caching of that data - * for availability purposes. - * - * @author Martin Lowe - * - */ -public interface ProjectsService { - - /** - * Retrieves all currently available projects from cache if available, otherwise going to API to retrieve a fresh copy - * of the data. - * - * @return list of projects available from API. - */ - List<Project> getProjects(); - - /** - * Retrieves projects valid for the current request, or an empty list if no data or matching project repos could be - * found. - * - * @param req the current request - * @return list of matching projects for the current request, or an empty list if none found. - */ - List<Project> retrieveProjectsForRequest(ValidationRequest req); - - /** - * Retrieves projects for given provider, using the repo URL to match to a stored repository. - * - * @param repoUrl the repo URL to match - * @param provider the provider that is being served for the request. - * @return a list of matching projects, or an empty list if none are found. - */ - List<Project> retrieveProjectsForRepoURL(String repoUrl, ProviderType provider); -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java b/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java index 06c01484868c90cb1d02529ff13e03990bf95226..1134f1d9a2ead650a6f9d65fb1521dc639af8f01 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java +++ b/src/main/java/org/eclipsefoundation/git/eca/service/UserService.java @@ -13,8 +13,8 @@ package org.eclipsefoundation.git.eca.service; import java.util.List; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.api.models.EclipseUser; -import org.eclipsefoundation.git.eca.api.models.Project; public interface UserService { diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/ValidationService.java b/src/main/java/org/eclipsefoundation/git/eca/service/ValidationService.java index 1f441ca0b783c220d831a8661b0734c336dbf081..b26a3fb815105bcd64ab33f3b4330fc74ddae726 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/service/ValidationService.java +++ b/src/main/java/org/eclipsefoundation/git/eca/service/ValidationService.java @@ -16,7 +16,7 @@ import java.security.NoSuchAlgorithmException; import java.util.List; import org.eclipsefoundation.core.model.RequestWrapper; -import org.eclipsefoundation.git.eca.api.models.Project; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.dto.CommitValidationStatus; import org.eclipsefoundation.git.eca.model.ValidationRequest; import org.eclipsefoundation.git.eca.model.ValidationResponse; diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java index 45ad50178491186e32490dd7c31a49ed7ce24f21..de6836a89bc78fcc25411b8ec63aed7b4c47e5c0 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java +++ b/src/main/java/org/eclipsefoundation/git/eca/service/impl/CachedUserService.java @@ -14,8 +14,8 @@ package org.eclipsefoundation.git.eca.service.impl; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.Map.Entry; +import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -28,11 +28,11 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipsefoundation.core.service.CachingService; +import org.eclipsefoundation.efservices.api.models.Project; +import org.eclipsefoundation.efservices.services.DrupalTokenService; import org.eclipsefoundation.git.eca.api.AccountsAPI; import org.eclipsefoundation.git.eca.api.BotsAPI; import org.eclipsefoundation.git.eca.api.models.EclipseUser; -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.service.OAuthService; import org.eclipsefoundation.git.eca.service.UserService; import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.slf4j.Logger; @@ -63,7 +63,7 @@ public class CachedUserService implements UserService { BotsAPI bots; @Inject - OAuthService oauth; + DrupalTokenService oauth; @Inject CachingService cache; diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultInterestGroupService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultInterestGroupService.java deleted file mode 100644 index c56f56da55063036bd5f9b404ad66e6a763700af..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultInterestGroupService.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.eclipsefoundation.git.eca.service.impl; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.eclipsefoundation.core.service.APIMiddleware; -import org.eclipsefoundation.core.service.CachingService; -import org.eclipsefoundation.git.eca.api.ProjectsAPI; -import org.eclipsefoundation.git.eca.api.models.InterestGroupData; -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.service.InterestGroupService; -import org.jboss.resteasy.specimpl.MultivaluedMapImpl; - -/** - * Default implementation of interest group service. - * - * @author Martin Lowe - */ -@ApplicationScoped -public class DefaultInterestGroupService implements InterestGroupService { - - @Inject - APIMiddleware middleware; - @Inject - CachingService cache; - - @Inject - @RestClient - ProjectsAPI api; - - @Override - public List<InterestGroupData> getInterestGroups() { - return cache - .get("all", new MultivaluedMapImpl<>(), InterestGroupData.class, - () -> middleware.getAll(api::getInterestGroups, InterestGroupData.class)) - .orElse(Collections.emptyList()); - } - - @Override - public List<Project> adaptInterestGroups(List<InterestGroupData> igs) { - return igs - .stream() - .map(ig -> Project - .builder() - .setProjectId(ig.getProjectId()) - .setGerritRepos(Collections.emptyList()) - .setGithubRepos(Collections.emptyList()) - .setGitlabRepos(Collections.emptyList()) - .setRepos(Collections.emptyList()) - .setGitlab(ig.getGitlab()) - .setCommitters(ig.getParticipants()) - .setProjectLeads(ig.getLeads()) - .setName(ig.getTitle()) - .setSpecProjectWorkingGroup(Collections.emptyMap()) - .build()) - .collect(Collectors.toList()); - } - -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultOAuthService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultOAuthService.java deleted file mode 100644 index 713b0988bde06a0983067fb5be8b6c197a1ddc6a..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultOAuthService.java +++ /dev/null @@ -1,93 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.service.impl; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import javax.annotation.PostConstruct; -import javax.enterprise.context.ApplicationScoped; - -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipsefoundation.git.eca.oauth.EclipseApi; -import org.eclipsefoundation.git.eca.service.OAuthService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.oauth.OAuth20Service; - -/** - * Default implementation for requesting an OAuth request token. The reason that - * this class is implemented over the other implementations baked into Quarkus - * is to better bind to the Drupal OAuth APIs. - * - * @author Martin Lowe - * - */ -@ApplicationScoped -public class DefaultOAuthService implements OAuthService { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultOAuthService.class); - - @ConfigProperty(name = "oauth2.client-id") - String id; - @ConfigProperty(name = "oauth2.client-secret") - String secret; - @ConfigProperty(name = "oauth2.scope") - String scope; - - // service reference (as we only need one) - private OAuth20Service service; - - // token state vars - private long expirationTime; - private String accessToken; - - /** - * Create an OAuth service reference. - */ - @PostConstruct - void createServiceRef() { - this.service = new ServiceBuilder(id).apiSecret(secret).scope(scope).build(EclipseApi.instance()); - } - - @Override - public String getToken() { - // lock on the class instance to stop multiple threads from requesting new - // tokens at the same time - synchronized (this) { - if (accessToken == null || System.currentTimeMillis() >= expirationTime) { - // clear access token - this.accessToken = null; - try { - OAuth2AccessToken requestToken = service.getAccessTokenClientCredentialsGrant(); - if (requestToken != null) { - this.accessToken = requestToken.getAccessToken(); - this.expirationTime = System.currentTimeMillis() - + TimeUnit.SECONDS.toMillis(requestToken.getExpiresIn().longValue()); - } - } catch (IOException e) { - LOGGER.error("Issue communicating with OAuth server for authentication", e); - } catch (InterruptedException e) { - LOGGER.error("Authentication communication was interrupted before completion", e); - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - LOGGER.error("Error while retrieving access token for request", e); - } - } - } - return accessToken; - } - -} diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java index 647252e019e68fa4badc619a08dab7bc97e487d4..7c45cd7888ee0499982a6fa20dd208cc2b869fe0 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java +++ b/src/main/java/org/eclipsefoundation/git/eca/service/impl/DefaultValidationService.java @@ -28,20 +28,19 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipsefoundation.core.helper.DateTimeHelper; import org.eclipsefoundation.core.model.RequestWrapper; import org.eclipsefoundation.core.service.CachingService; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.api.models.EclipseUser; -import org.eclipsefoundation.git.eca.api.models.Project; import org.eclipsefoundation.git.eca.dto.CommitValidationMessage; import org.eclipsefoundation.git.eca.dto.CommitValidationStatus; import org.eclipsefoundation.git.eca.dto.CommitValidationStatusGrouping; import org.eclipsefoundation.git.eca.helper.CommitHelper; +import org.eclipsefoundation.git.eca.helper.ProjectHelper; import org.eclipsefoundation.git.eca.model.Commit; import org.eclipsefoundation.git.eca.model.GitUser; import org.eclipsefoundation.git.eca.model.ValidationRequest; import org.eclipsefoundation.git.eca.model.ValidationResponse; import org.eclipsefoundation.git.eca.namespace.APIStatusCode; import org.eclipsefoundation.git.eca.namespace.GitEcaParameterNames; -import org.eclipsefoundation.git.eca.service.InterestGroupService; -import org.eclipsefoundation.git.eca.service.ProjectsService; import org.eclipsefoundation.git.eca.service.UserService; import org.eclipsefoundation.git.eca.service.ValidationService; import org.eclipsefoundation.persistence.dao.PersistenceDao; @@ -66,9 +65,7 @@ public class DefaultValidationService implements ValidationService { List<String> allowListUsers; @Inject - ProjectsService projects; - @Inject - InterestGroupService ig; + ProjectHelper projects; @Inject UserService users; @@ -386,7 +383,7 @@ public class DefaultValidationService implements ValidationService { if (p.getCommitters().stream().anyMatch(u -> u.getUsername().equals(user.getName()))) { // check if the current project is a committer project, and if the user can // commit to specs - if (p.getSpecWorkingGroup() != null && !user.getECA().getCanContributeSpecProject()) { + if (p.getSpecWorkingGroup().isPresent() && !user.getECA().getCanContributeSpecProject()) { // set error + update response status r .addError(hash, String diff --git a/src/main/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsService.java b/src/main/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsService.java deleted file mode 100644 index 9b48cac9ea16cec2a7aa5faf46d72f4f96897a9b..0000000000000000000000000000000000000000 --- a/src/main/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsService.java +++ /dev/null @@ -1,213 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* Zachary Sabourin <zachary.sabourin@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.service.impl; - -import java.net.URI; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.annotation.PostConstruct; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.context.ManagedExecutor; -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.eclipsefoundation.core.service.APIMiddleware; -import org.eclipsefoundation.core.service.CachingService; -import org.eclipsefoundation.git.eca.api.ProjectsAPI; -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.model.ValidationRequest; -import org.eclipsefoundation.git.eca.namespace.ProviderType; -import org.eclipsefoundation.git.eca.service.InterestGroupService; -import org.eclipsefoundation.git.eca.service.ProjectsService; -import org.jboss.resteasy.specimpl.MultivaluedMapImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFutureTask; - -import io.quarkus.runtime.Startup; - -/** - * Projects service implementation that handles pagination of data manually, as well as makes use of a loading cache to - * have data be always available with as little latency to the user as possible. - * - * @author Martin Lowe - * @author Zachary Sabourin - */ -@Startup -@ApplicationScoped -public class PaginationProjectsService implements ProjectsService { - private static final Logger LOGGER = LoggerFactory.getLogger(PaginationProjectsService.class); - - @ConfigProperty(name = "eclipse.projects.precache.enabled", defaultValue = "true") - boolean isEnabled; - @ConfigProperty(name = "cache.pagination.refresh-frequency-seconds", defaultValue = "3600") - long refreshAfterWrite; - - @Inject - @RestClient - ProjectsAPI projects; - @Inject - InterestGroupService ig; - @Inject - CachingService cache; - @Inject - APIMiddleware middleware; - - @Inject - ManagedExecutor exec; - // this class has a separate cache as this data is long to load and should be - // always available. - LoadingCache<String, List<Project>> internalCache; - - /** - * Initializes the internal loader cache and pre-populates the data with the one available key. If more than one key is - * used, eviction of previous results will happen and create degraded performance. - */ - @PostConstruct - public void init() { - // set up the internal cache - this.internalCache = CacheBuilder - .newBuilder() - .maximumSize(1) - .refreshAfterWrite(refreshAfterWrite, TimeUnit.SECONDS) - .build(new CacheLoader<String, List<Project>>() { - @Override - public List<Project> load(String key) throws Exception { - return getProjectsInternal(); - } - - /** - * Implementation required for refreshAfterRewrite to be async rather than sync and blocking while awaiting for - * expensive reload to complete. - */ - @Override - public ListenableFuture<List<Project>> reload(String key, List<Project> oldValue) throws Exception { - ListenableFutureTask<List<Project>> task = ListenableFutureTask.create(() -> { - LOGGER.debug("Retrieving new project data async"); - List<Project> newProjects = oldValue; - try { - newProjects = getProjectsInternal(); - } catch (Exception e) { - LOGGER.error("Error while reloading internal projects data, data will be stale for current cycle.", e); - } - LOGGER.debug("Done refreshing project values"); - return newProjects; - }); - // run the task using the Quarkus managed executor - exec.execute(task); - return task; - } - }); - - if (isEnabled) { - // pre-cache the projects to reduce load time for other users - LOGGER.debug("Starting pre-cache of projects"); - if (getProjects() == null) { - LOGGER.warn("Unable to populate pre-cache for Eclipse projects. Calls may experience degraded performance."); - } - LOGGER.debug("Completed pre-cache of projects assets"); - } - } - - @Override - public List<Project> getProjects() { - try { - return internalCache.get("projects"); - } catch (ExecutionException e) { - throw new RuntimeException("Could not load Eclipse projects", e); - } - } - - @Override - public List<Project> retrieveProjectsForRequest(ValidationRequest req) { - String repoUrl = req.getRepoUrl().getPath(); - if (repoUrl == null) { - LOGGER.warn("Can not match null repo URL to projects"); - return Collections.emptyList(); - } - return retrieveProjectsForRepoURL(repoUrl, req.getProvider()); - } - - @Override - public List<Project> retrieveProjectsForRepoURL(String repoUrl, ProviderType provider) { - if (repoUrl == null) { - LOGGER.warn("Can not match null repo URL to projects"); - return Collections.emptyList(); - } - // check for all projects that make use of the given repo - List<Project> availableProjects = getProjects(); - availableProjects - .addAll(cache - .get("all", new MultivaluedMapImpl<>(), Project.class, () -> ig.adaptInterestGroups(ig.getInterestGroups())) - .orElse(Collections.emptyList())); - if (availableProjects.isEmpty()) { - LOGGER.warn("Could not find any projects to match against"); - return Collections.emptyList(); - } - LOGGER.debug("Checking projects for repos that end with: {}", repoUrl); - - String projectNamespace = URI.create(repoUrl).getPath().substring(1).toLowerCase(); - // filter the projects based on the repo URL. At least one repo in project must - // match the repo URL to be valid - switch (provider) { - case GITLAB: - return availableProjects - .stream() - .filter(p -> projectNamespace.startsWith(p.getGitlab().getProjectGroup() + "/") - && p.getGitlab().getIgnoredSubGroups().stream().noneMatch(sg -> projectNamespace.startsWith(sg + "/"))) - .collect(Collectors.toList()); - case GITHUB: - return availableProjects - .stream() - .filter(p -> p.getGithubRepos().stream().anyMatch(re -> re.getUrl() != null && re.getUrl().endsWith(repoUrl)) - || (StringUtils.isNotBlank(p.getGithub().getOrg()) && projectNamespace.startsWith(p.getGithub().getOrg()) - && p.getGithub().getIgnoredRepos().stream().noneMatch(repoUrl::endsWith))) - .collect(Collectors.toList()); - case GERRIT: - return availableProjects - .stream() - .filter(p -> p.getGerritRepos().stream().anyMatch(re -> re.getUrl() != null && re.getUrl().endsWith(repoUrl))) - .collect(Collectors.toList()); - default: - return Collections.emptyList(); - } - } - - /** - * Logic for retrieving projects from API. - * - * @return list of projects for the cache - */ - private List<Project> getProjectsInternal() { - - return middleware.getAll(params -> projects.getProjects(params), Project.class).stream().map(proj -> { - proj.getGerritRepos().forEach(repo -> { - if (repo.getUrl().endsWith(".git")) { - repo.setUrl(repo.getUrl().substring(0, repo.getUrl().length() - 4)); - } - }); - return proj; - }).collect(Collectors.toList()); - } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 33b399d082fff9e6e59e2faafe8a824a623e71e8..09df93d127b12af156beed3fced69e970defe46b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,8 @@ quarkus.rest-client."org.eclipsefoundation.git.eca.api.AccountsAPI".scope=javax.enterprise.context.ApplicationScoped quarkus.rest-client."org.eclipsefoundation.git.eca.api.AccountsAPI".url=https://api.eclipse.org -org.eclipsefoundation.git.eca.api.ProjectsAPI/mp-rest/url=https://projects.eclipse.org +projects-api/mp-rest/url=https://projects.eclipse.org +eclipse.cache.loading."projects".timeout=10 +eclipse.cache.loading."projects".start-at-boot=true org.eclipsefoundation.git.eca.api.BotsAPI/mp-rest/url=https://api.eclipse.org quarkus.rest-client."org.eclipsefoundation.git.eca.api.GitlabAPI".url=https://gitlab.eclipse.org/api/v4/ diff --git a/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java b/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java index 231b401c7987c5d425c752cd7eac239b9e356996..b0c25a00f0a05328e649b82786af7ff7ad0d3102 100644 --- a/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java +++ b/src/test/java/org/eclipsefoundation/git/eca/service/impl/CachedUserServiceTest.java @@ -18,9 +18,9 @@ import java.util.Optional; import javax.inject.Inject; +import org.eclipsefoundation.efservices.api.models.Project; import org.eclipsefoundation.git.eca.api.models.EclipseUser; -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.service.ProjectsService; +import org.eclipsefoundation.git.eca.helper.ProjectHelper; import org.eclipsefoundation.git.eca.service.UserService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -40,7 +40,7 @@ class CachedUserServiceTest { @Inject UserService users; @Inject - ProjectsService projects; + ProjectHelper projects; @Test void getUser_success() { diff --git a/src/test/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsServiceTest.java b/src/test/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsServiceTest.java deleted file mode 100644 index 3ebbabcebc99b3cc6f9fa62801ff5139d66fafd8..0000000000000000000000000000000000000000 --- a/src/test/java/org/eclipsefoundation/git/eca/service/impl/PaginationProjectsServiceTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/********************************************************************* -* 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/ -* -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.git.eca.service.impl; - -import java.util.List; -import java.util.stream.Collectors; - -import javax.inject.Inject; - -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.api.models.Project.Repo; -import org.eclipsefoundation.git.eca.service.ProjectsService; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; - -@QuarkusTest -class PaginationProjectsServiceTest { - // get the projects service - @Inject - ProjectsService ps; - - @Test - void validateGerritUrlScrubbed() { - // get all projects - List<Project> projectsAll = ps.getProjects(); - // get projects that have gerrit repos - List<Project> projs = projectsAll - .stream() - .filter(p -> !p.getGerritRepos().isEmpty()) - .collect(Collectors.toList()); - // for all repos, check that none end with .git (this doesn't account for - // .git.git, but I assume that would be an entry error) - for (Project p : projs) { - for (Repo r : p.getGerritRepos()) { - Assertions.assertFalse(r.getUrl().endsWith(".git"), "Expected no URLs to end with '.git'"); - } - } - } - - @Test - void validateGithubUrlNotScrubbed() { - // get all projects - List<Project> projectsAll = ps.getProjects(); - // get projects that have github repos - List<Project> projs = projectsAll - .stream() - .filter(p -> !p.getGithubRepos().isEmpty()) - .collect(Collectors.toList()); - // for all repos, check that at least one ends with .git - boolean foundGitSuffix = false; - for (Project p : projs) { - for (Repo r : p.getGithubRepos()) { - if (r.getUrl().endsWith(".git")) { - foundGitSuffix = true; - } - } - } - Assertions.assertTrue(foundGitSuffix, "Expected a URL to end with '.git'"); - } - - @Test - void validateGitlabUrlNotScrubbed() { - // get all projects - List<Project> projectsAll = ps.getProjects(); - // get projects that have gitlab repos - List<Project> projs = projectsAll - .stream() - .filter(p -> !p.getGitlabRepos().isEmpty()) - .collect(Collectors.toList()); - // for all repos, check that at least one ends with .git - boolean foundGitSuffix = false; - for (Project p : projs) { - for (Repo r : p.getGitlabRepos()) { - if (r.getUrl().endsWith(".git")) { - foundGitSuffix = true; - } - } - } - Assertions.assertTrue(foundGitSuffix, "Expected a URL to end with '.git'"); - } -} diff --git a/src/test/java/org/eclipsefoundation/git/eca/test/api/MockProjectsAPI.java b/src/test/java/org/eclipsefoundation/git/eca/test/api/MockProjectsAPI.java index 0528b1ea0497f4772e52e731a45f168b74736f6c..4be050fc7877141f12d23b884d579173386dfb62 100644 --- a/src/test/java/org/eclipsefoundation/git/eca/test/api/MockProjectsAPI.java +++ b/src/test/java/org/eclipsefoundation/git/eca/test/api/MockProjectsAPI.java @@ -1,11 +1,11 @@ /********************************************************************* -* Copyright (c) 2020 Eclipse Foundation. +* Copyright (c) 2023 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/ * -* Author: Martin Lowe <martin.lowe@eclipse-foundation.org> +* Author: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org> * * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ @@ -14,24 +14,26 @@ package org.eclipsefoundation.git.eca.test.api; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.core.Response; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipsefoundation.core.service.APIMiddleware.BaseAPIParameters; -import org.eclipsefoundation.git.eca.api.ProjectsAPI; -import org.eclipsefoundation.git.eca.api.models.InterestGroupData; -import org.eclipsefoundation.git.eca.api.models.Project; -import org.eclipsefoundation.git.eca.api.models.InterestGroupData.Descriptor; -import org.eclipsefoundation.git.eca.api.models.Project.GithubProject; -import org.eclipsefoundation.git.eca.api.models.Project.GitlabProject; -import org.eclipsefoundation.git.eca.api.models.Project.Repo; -import org.eclipsefoundation.git.eca.api.models.Project.User; +import org.eclipsefoundation.efservices.api.ProjectsAPI; +import org.eclipsefoundation.efservices.api.models.GitlabProject; +import org.eclipsefoundation.efservices.api.models.InterestGroup; +import org.eclipsefoundation.efservices.api.models.InterestGroup.Descriptor; +import org.eclipsefoundation.efservices.api.models.InterestGroup.InterestGroupParticipant; +import org.eclipsefoundation.efservices.api.models.InterestGroup.Organization; +import org.eclipsefoundation.efservices.api.models.InterestGroup.Resource; +import org.eclipsefoundation.efservices.api.models.Project; +import org.eclipsefoundation.efservices.api.models.Project.GithubProject; +import org.eclipsefoundation.efservices.api.models.Project.ProjectParticipant; +import org.eclipsefoundation.efservices.api.models.Project.Repo; import io.quarkus.test.Mock; @@ -40,124 +42,169 @@ import io.quarkus.test.Mock; @ApplicationScoped public class MockProjectsAPI implements ProjectsAPI { - private List<Project> src; + private List<Project> projects; - @PostConstruct - public void build() { - this.src = new ArrayList<>(); + public MockProjectsAPI() { + this.projects = new ArrayList<>(); // sample repos - Repo r1 = new Repo(); - r1.setUrl("http://www.github.com/eclipsefdn/sample"); - Repo r2 = new Repo(); - r2.setUrl("http://www.github.com/eclipsefdn/test"); - Repo r3 = new Repo(); - r3.setUrl("http://www.github.com/eclipsefdn/prototype.git"); - Repo r5 = new Repo(); - r5.setUrl("/gitroot/sample/gerrit.project.git"); - Repo r6 = new Repo(); - r6.setUrl("/gitroot/sample/gerrit.other-project"); - Repo r7 = new Repo(); - r7.setUrl("https://gitlab.eclipse.org/eclipse/dash/dash.git"); - Repo r8 = new Repo(); - r8.setUrl("https://gitlab.eclipse.org/eclipse/dash-second/dash.handbook.test"); - - // sample users, correlates to users in Mock projects API - User u1 = User.builder().setUrl("").setUsername("da_wizz").build(); - User u2 = User.builder().setUrl("").setUsername("grunter").build(); - - // projects - Project p1 = Project - .builder() + Repo r1 = Repo.builder().setUrl("http://www.github.com/eclipsefdn/sample").build(); + Repo r2 = Repo.builder().setUrl("http://www.github.com/eclipsefdn/test").build(); + Repo r3 = Repo.builder().setUrl("http://www.github.com/eclipsefdn/prototype.git").build(); + Repo r4 = Repo.builder().setUrl("http://www.github.com/eclipsefdn/tck-proto").build(); + Repo r5 = Repo.builder().setUrl("/gitroot/sample/gerrit.project.git").build(); + Repo r6 = Repo.builder().setUrl("/gitroot/sample/gerrit.other-project").build(); + Repo r7 = Repo.builder().setUrl("https://gitlab.eclipse.org/eclipse/dash/dash.git").build(); + Repo r8 = Repo.builder().setUrl("https://gitlab.eclipse.org/eclipse/dash-second/dash.handbook.test").build(); + + // sample users + ProjectParticipant u1 = ProjectParticipant.builder().setUrl("").setUsername("da_wizz").setFullName("da_wizz") + .build(); + ProjectParticipant u2 = ProjectParticipant.builder().setUrl("").setUsername("grunter").setFullName("grunter") + .build(); + + this.projects.add(Project.builder() .setName("Sample project") .setProjectId("sample.proj") - .setSpecProjectWorkingGroup(Collections.emptyList()) + .setSpecProjectWorkingGroup(Collections.emptyMap()) .setGithubRepos(Arrays.asList(r1, r2)) .setGerritRepos(Arrays.asList(r5)) .setCommitters(Arrays.asList(u1, u2)) .setProjectLeads(Collections.emptyList()) - .setGitlab(GitlabProject.builder().setIgnoredSubGroups(Collections.emptyList()).setProjectGroup("").build()) - .setGithub(GithubProject.builder().setIgnoredRepos(Collections.emptyList()).setOrg("").build()) - .build(); - src.add(p1); + .setGitlab(GitlabProject.builder().setIgnoredSubGroups(Collections.emptyList()).setProjectGroup("") + .build()) + .setShortProjectId("sample.proj") + .setSummary("summary") + .setUrl("project.url.com") + .setWebsiteUrl("someproject.com") + .setWebsiteRepo(Collections.emptyList()) + .setLogo("logoUrl.com") + .setTags(Collections.emptyList()) + .setGithub(GithubProject.builder() + .setOrg("") + .setIgnoredRepos(Collections.emptyList()).build()) + .setGitlabRepos(Collections.emptyList()) + .setContributors(Collections.emptyList()) + .setWorkingGroups(Collections.emptyList()) + .setIndustryCollaborations(Collections.emptyList()) + .setReleases(Collections.emptyList()) + .setTopLevelProject("eclipse") + .setSlsaLevel("1") + .build()); - Project p2 = Project - .builder() + this.projects.add(Project.builder() .setName("Prototype thing") .setProjectId("sample.proto") - .setSpecProjectWorkingGroup(Collections.emptyList()) + .setSpecProjectWorkingGroup(Collections.emptyMap()) .setGithubRepos(Arrays.asList(r3)) .setGerritRepos(Arrays.asList(r6)) .setGitlabRepos(Arrays.asList(r8)) .setCommitters(Arrays.asList(u2)) .setProjectLeads(Collections.emptyList()) .setGitlab( - GitlabProject.builder().setIgnoredSubGroups(Collections.emptyList()).setProjectGroup("eclipse/dash-second").build()) - .setGithub(GithubProject.builder().setIgnoredRepos(Collections.emptyList()).setOrg("").build()) - .build(); - src.add(p2); + GitlabProject.builder().setIgnoredSubGroups(Collections.emptyList()) + .setProjectGroup("eclipse/dash-second").build()) + .setShortProjectId("sample.proto") + .setWebsiteUrl("someproject.com") + .setSummary("summary") + .setUrl("project.url.com") + .setWebsiteRepo(Collections.emptyList()) + .setLogo("logoUrl.com") + .setTags(Collections.emptyList()) + .setGithub(GithubProject.builder() + .setOrg("") + .setIgnoredRepos(Collections.emptyList()).build()) + .setContributors(Collections.emptyList()) + .setWorkingGroups(Collections.emptyList()) + .setIndustryCollaborations(Collections.emptyList()) + .setReleases(Collections.emptyList()) + .setTopLevelProject("eclipse") + .setSlsaLevel("1") + .build()); - Map<String, String> map = new HashMap<>(); - map.put("id", "proj1"); - Project p3 = Project - .builder() + this.projects.add(Project.builder() .setName("Spec project") .setProjectId("spec.proj") - .setSpecProjectWorkingGroup(map) - .setGithubRepos(Collections.emptyList()) + .setSpecProjectWorkingGroup(Map.of("id", "proj1", "name", "proj1")) + .setGithubRepos(Arrays.asList(r4)) .setGitlabRepos(Arrays.asList(r7)) + .setGerritRepos(Collections.emptyList()) .setGitlab(GitlabProject .builder() .setIgnoredSubGroups(Arrays.asList("eclipse/dash/mirror")) .setProjectGroup("eclipse/dash") .build()) - .setGithub(GithubProject - .builder() - .setIgnoredRepos(Arrays.asList("eclipsefdn-tck/tck-ignored")) - .setOrg("eclipsefdn-tck") - .build()) .setCommitters(Arrays.asList(u1, u2)) .setProjectLeads(Collections.emptyList()) - .build(); - src.add(p3); + .setShortProjectId("spec.proj") + .setSummary("summary") + .setUrl("project.url.com") + .setWebsiteUrl("someproject.com") + .setWebsiteRepo(Collections.emptyList()) + .setLogo("logoUrl.com") + .setTags(Collections.emptyList()) + .setGithub(GithubProject.builder() + .setOrg("eclipsefdn-tck") + .setIgnoredRepos(Arrays.asList("eclipsefdn-tck/tck-ignored")).build()) + .setContributors(Collections.emptyList()) + .setWorkingGroups(Collections.emptyList()) + .setIndustryCollaborations(Collections.emptyList()) + .setReleases(Collections.emptyList()) + .setTopLevelProject("eclipse") + .setSlsaLevel("1") + .build()); } @Override - public Response getProjects(BaseAPIParameters baseParams) { - return Response.ok(baseParams.getPage() == 1 ? new ArrayList<>(src) : Collections.emptyList()).build(); + public Response getProjects(BaseAPIParameters params, int isSpecProject) { + + if (isSpecProject == 1) { + return Response + .ok(projects.stream().filter(p -> p.getSpecWorkingGroup().isPresent()).collect(Collectors.toList())) + .build(); + } + + return Response.ok(projects).build(); } @Override public Response getInterestGroups(BaseAPIParameters params) { - return Response - .ok(Arrays - .asList(InterestGroupData - .builder() - .setProjectId("foundation-internal.ig.mittens") - .setId("1") - .setLogo("") - .setState("active") - .setTitle("Magical IG Tributed To Eclipse News Sources") - .setDescription(Descriptor.builder().setFull("Sample").setSummary("Sample").build()) - .setScope(Descriptor.builder().setFull("Sample").setSummary("Sample").build()) - .setGitlab(GitlabProject - .builder() - .setIgnoredSubGroups(Collections.emptyList()) - .setProjectGroup("eclipse-ig/mittens") - .build()) - .setLeads(Arrays - .asList(User - .builder() - .setUrl("https://api.eclipse.org/account/profile/zacharysabourin") - .setUsername("zacharysabourin") - .build())) - .setParticipants(Arrays - .asList(User - .builder() - .setUrl("https://api.eclipse.org/account/profile/skilpatrick") - .setUsername("skilpatrick") - .build())) - .build())) - .build(); + return Response.ok(Arrays.asList(InterestGroup + .builder() + .setProjectId("foundation-internal.ig.mittens") + .setId("1") + .setLogo("") + .setState("active") + .setTitle("Magical IG Tributed To Eclipse News Sources") + .setDescription(Descriptor.builder().setFull("Sample").setSummary("Sample").build()) + .setScope(Descriptor.builder().setFull("Sample").setSummary("Sample").build()) + .setGitlab(GitlabProject.builder() + .setIgnoredSubGroups(Collections.emptyList()) + .setProjectGroup("eclipse-ig/mittens") + .build()) + .setLeads(Arrays.asList(InterestGroupParticipant + .builder() + .setUrl("https://api.eclipse.org/account/profile/zacharysabourin") + .setUsername("zacharysabourin") + .setFullName("zachary sabourin") + .setOrganization(Organization.builder() + .setDocuments(Collections.emptyMap()) + .setId("id") + .setName("org").build()) + .build())) + .setParticipants(Arrays.asList(InterestGroupParticipant + .builder() + .setUrl("https://api.eclipse.org/account/profile/skilpatrick") + .setUsername("skilpatrick") + .setFullName("Skil Patrick") + .setOrganization(Organization.builder() + .setDocuments(Collections.emptyMap()) + .setId("id") + .setName("org").build()) + .build())) + .setShortProjectId("mittens") + .setResources(Resource.builder().setMembers("members").setWebsite("google.com").build()) + .setMailingList("mailinglist.com") + .build())).build(); } } diff --git a/src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockOAuthService.java b/src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockTokenService.java similarity index 83% rename from src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockOAuthService.java rename to src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockTokenService.java index 8ce32a299b03e3328ad8d5e69a2f60d7dde09b11..4b8bc5cec1d92c8ee06633141cb24b64584ce797 100644 --- a/src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockOAuthService.java +++ b/src/test/java/org/eclipsefoundation/git/eca/test/service/impl/MockTokenService.java @@ -13,13 +13,13 @@ package org.eclipsefoundation.git.eca.test.service.impl; import javax.inject.Singleton; -import org.eclipsefoundation.git.eca.service.OAuthService; +import org.eclipsefoundation.efservices.services.DrupalTokenService; import io.quarkus.test.Mock; @Mock @Singleton -public class MockOAuthService implements OAuthService { +public class MockTokenService implements DrupalTokenService { @Override public String getToken() {