diff --git a/spec/openapi.yaml b/spec/openapi.yaml index 8bf0650538617ae73392947c49b9e2d8000b7d91..d786b925b12dbcc6e3d1ccd8d174aace3eedd8b1 100644 --- a/spec/openapi.yaml +++ b/spec/openapi.yaml @@ -118,7 +118,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/EfUser" + $ref: "#/components/schemas/EfUsers" 403: description: Invalid credentials 500: @@ -198,6 +198,11 @@ components: - type: "null" description: The URL + EfUsers: + type: array + items: + $ref: "#/components/schemas/EfUser" + EfUser: type: object properties: diff --git a/src/main/java/org/eclipsefoundation/openvsx/models/PublisherAgreementData.java b/src/main/java/org/eclipsefoundation/openvsx/models/PublisherAgreementData.java new file mode 100644 index 0000000000000000000000000000000000000000..ced2569177e7b566c1c9dc086ed970e311bdbbdb --- /dev/null +++ b/src/main/java/org/eclipsefoundation/openvsx/models/PublisherAgreementData.java @@ -0,0 +1,83 @@ +/********************************************************************* +* 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: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org> +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.openvsx.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +@AutoValue +@JsonDeserialize(builder = AutoValue_PublisherAgreementData.Builder.class) +public abstract class PublisherAgreementData { + + @JsonProperty("PersonID") + public abstract String getPersonID(); + + @JsonProperty("DocumentID") + public abstract String getDocumentID(); + + @JsonProperty("Version") + public abstract String getVersion(); + + @JsonProperty("EffectiveDate") + public abstract String getEffectiveDate(); + + @JsonProperty("ReceivedDate") + public abstract String getReceivedDate(); + + @JsonProperty("ScannedDocumentBLOB") + public abstract String getScannedDocumentBlob(); + + @JsonProperty("ScannedDocumentMime") + public abstract String getScannedDocumentMime(); + + @JsonProperty("ScannedDocumentBytes") + public abstract String getScannedDocumentBytes(); + + @JsonProperty("ScannedDocumentFileName") + public abstract String getScannedDocumentFileName(); + + @JsonProperty("Comments") + public abstract String getComments(); + + public static Builder builder() { + return new AutoValue_PublisherAgreementData.Builder(); + } + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "set") + public abstract static class Builder { + + public abstract Builder setPersonID(String id); + + public abstract Builder setDocumentID(String id); + + public abstract Builder setVersion(String version); + + public abstract Builder setEffectiveDate(String date); + + public abstract Builder setReceivedDate(String date); + + public abstract Builder setScannedDocumentBlob(String blob); + + public abstract Builder setScannedDocumentMime(String mime); + + public abstract Builder setScannedDocumentBytes(String bytes); + + public abstract Builder setScannedDocumentFileName(String name); + + public abstract Builder setComments(String commetns); + + public abstract PublisherAgreementData build(); + } +} diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java index 722d18e0fd790ac4de5c531fdb3b8c81434386c4..0c71cd1da855cf8a0a06304bdd1f5a72e4214c4e 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java @@ -11,6 +11,8 @@ **********************************************************************/ package org.eclipsefoundation.openvsx.resources; +import java.util.Arrays; + import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; @@ -21,6 +23,6 @@ public class ProfileResource extends OpenvsxResource { @GET public Response getProfileInfo() { // Returns the public profile data tied to the current token - return Response.ok(getTokenUser().toBuilder().setMail("").build()).build(); + return Response.ok(Arrays.asList(getTokenUser().toBuilder().setMail("").build())).build(); } } diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java index 4261a168c091d014ce456a89bc892e7445a56418..e2b701c848f26821d2a48d35e661f5a20f02a35d 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java @@ -31,6 +31,7 @@ import org.eclipsefoundation.core.model.Error; import org.eclipsefoundation.foundationdb.client.model.PeopleDocumentData; import org.eclipsefoundation.openvsx.api.models.EfUser; import org.eclipsefoundation.openvsx.models.AgreementSigningRequest; +import org.eclipsefoundation.openvsx.models.PublisherAgreementData; import org.eclipsefoundation.openvsx.services.FoundationOperationService; import org.eclipsefoundation.openvsx.services.PublisherAgreementService; @@ -54,7 +55,7 @@ public class PublisherAgreementResource extends OpenvsxResource { // Uses currently logged in user. Only an onwer can fetch their agreement String username = getTokenUser().getName(); - Optional<PeopleDocumentData> result = agreementService.getPublisherAgreementByUsername(username); + Optional<PublisherAgreementData> result = agreementService.getPublisherAgreementByUsername(username); if (result.isEmpty()) { throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); } @@ -83,7 +84,7 @@ public class PublisherAgreementResource extends OpenvsxResource { throw new ServerErrorException("Internal Server Error", 500); } - Optional<PeopleDocumentData> result = agreementService.createPublisherAgreement(user); + Optional<PublisherAgreementData> result = agreementService.createPublisherAgreement(user); if (result.isEmpty()) { throw new BadRequestException("Unable to create Publisher Agreement with current request parameters"); } @@ -98,7 +99,7 @@ public class PublisherAgreementResource extends OpenvsxResource { // The owner or admin can retrieve a specific agreement checkIfAdminOrSelf(username); - Optional<PeopleDocumentData> result = agreementService.getPublisherAgreementByUsername(username); + Optional<PublisherAgreementData> result = agreementService.getPublisherAgreementByUsername(username); if (result.isEmpty()) { throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); } @@ -113,7 +114,7 @@ public class PublisherAgreementResource extends OpenvsxResource { // The owner or admin can retrieve a specific agreement checkIfAdminOrSelf(username); - Optional<PeopleDocumentData> fetchResult = agreementService.getPublisherAgreementByUsername(username); + Optional<PeopleDocumentData> fetchResult = foundationService.fetchMostRecentDocument(username); if (fetchResult.isEmpty()) { throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); } diff --git a/src/main/java/org/eclipsefoundation/openvsx/services/PublisherAgreementService.java b/src/main/java/org/eclipsefoundation/openvsx/services/PublisherAgreementService.java index 8b9ef47c0f7f92502637f8c3d0e062206c790cd2..6eb9c8a1ac0de5da451fa17f39f5e768558fd139 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/services/PublisherAgreementService.java +++ b/src/main/java/org/eclipsefoundation/openvsx/services/PublisherAgreementService.java @@ -15,6 +15,7 @@ import java.util.Optional; import org.eclipsefoundation.foundationdb.client.model.PeopleDocumentData; import org.eclipsefoundation.openvsx.api.models.EfUser; +import org.eclipsefoundation.openvsx.models.PublisherAgreementData; /** * Defines the service for fetching, creating and revoking openvsx publisher @@ -27,9 +28,9 @@ public interface PublisherAgreementService { * agreement for the given user. * * @param username The desired user's EF username. - * @return An Optional containing a PeopleDocumentData entity if it exists. + * @return An Optional containing a PublisherAgreementData entity if it exists. */ - public Optional<PeopleDocumentData> getPublisherAgreementByUsername(String username); + public Optional<PublisherAgreementData> getPublisherAgreementByUsername(String username); /** * Creates a publisher agreement for the given user using the request @@ -37,17 +38,17 @@ public interface PublisherAgreementService { * event on fail or pass. Returns an empty Optional if it encounters an error. * * @param user The current logged in user - * @return An Optional containing the created PeopleDocumentData entity if it - * exists + * @return An Optional containing the created PublisherAgreementData entity if + * it exists */ - public Optional<PeopleDocumentData> createPublisherAgreement(EfUser user); + public Optional<PublisherAgreementData> createPublisherAgreement(EfUser user); /** * Expires a Publisher agreement and updates it in the foundationdb. Inserts a * modLog event on fail or pass. Returns an empty Optional if it encounters an * error. * - * @param document The current signed document + * @param document The current signed publisher agreement * @param currentUser The user bound to the token * @return An Optional containing the updated PeopleDocumentData if it exists */ diff --git a/src/main/java/org/eclipsefoundation/openvsx/services/impl/DefaultPublisherAgreementService.java b/src/main/java/org/eclipsefoundation/openvsx/services/impl/DefaultPublisherAgreementService.java index 59d224cd6ca8f2e5f3030eeee1cf9bab4630a252..55bc49ad88e08c97d0dd45d28650c248007eaf1b 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/services/impl/DefaultPublisherAgreementService.java +++ b/src/main/java/org/eclipsefoundation/openvsx/services/impl/DefaultPublisherAgreementService.java @@ -11,7 +11,9 @@ **********************************************************************/ package org.eclipsefoundation.openvsx.services.impl; +import java.text.SimpleDateFormat; import java.time.Instant; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -22,10 +24,12 @@ import javax.inject.Inject; import org.apache.commons.lang3.ArrayUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipsefoundation.core.helper.DateTimeHelper; import org.eclipsefoundation.foundationdb.client.model.PeopleDocumentData; import org.eclipsefoundation.openvsx.api.models.EfUser; import org.eclipsefoundation.openvsx.api.models.ModLogHeaders; import org.eclipsefoundation.openvsx.models.DocumentBody; +import org.eclipsefoundation.openvsx.models.PublisherAgreementData; import org.eclipsefoundation.openvsx.namespace.OpenvsxModLogActions; import org.eclipsefoundation.openvsx.services.FoundationOperationService; import org.eclipsefoundation.openvsx.services.PublisherAgreementService; @@ -41,7 +45,7 @@ public class DefaultPublisherAgreementService implements PublisherAgreementServi @ConfigProperty(name = "eclipse.openvsx.doc-id") String openvsxDocId; @ConfigProperty(name = "eclipse.openvsx.document-version", defaultValue = "1") - Double docVersion; + Integer docVersion; @Inject FoundationOperationService foundationService; @@ -50,13 +54,14 @@ public class DefaultPublisherAgreementService implements PublisherAgreementServi ObjectMapper objectMapper; @Override - public Optional<PeopleDocumentData> getPublisherAgreementByUsername(String username) { + public Optional<PublisherAgreementData> getPublisherAgreementByUsername(String username) { Optional<PeopleDocumentData> mostRecent = foundationService.fetchMostRecentDocument(username); - return mostRecent.isEmpty() || isdocumentExpired(mostRecent.get()) ? Optional.empty() : mostRecent; + return mostRecent.isEmpty() || isdocumentExpired(mostRecent.get()) ? Optional.empty() + : Optional.of(buildPublisherAgreement(mostRecent.get(), true)); } @Override - public Optional<PeopleDocumentData> createPublisherAgreement(EfUser user) { + public Optional<PublisherAgreementData> createPublisherAgreement(EfUser user) { try { LOGGER.debug("Creating publisher agreement for user: {}", user.getName()); @@ -77,9 +82,13 @@ public class DefaultPublisherAgreementService implements PublisherAgreementServi .setMail(user.getMail()) .build()); - return foundationService.persistDocumentWithModLog(headers, + Optional<PeopleDocumentData> creationResult = foundationService.persistDocumentWithModLog(headers, createDocumentSigningRequest(jsonDoc, user.getName())); + // Convert to PublisherAgreementData if persistence successful + return creationResult.isEmpty() ? Optional.empty() + : Optional.of(buildPublisherAgreement(creationResult.get(), false)); + } catch (Exception e) { LOGGER.error("Error while creating publisher agreement", e); foundationService.insertErrorSysModLog("_openvsx_publisher_agreement_create", user.getName()); @@ -150,12 +159,15 @@ public class DefaultPublisherAgreementService implements PublisherAgreementServi List<Byte> docAsBytes = Arrays.asList(ArrayUtils.toObject(jsonDoc.getBytes())); + // Set date to UTC timezone when creating + Date now = Date.from(DateTimeHelper.now().toInstant()); + return PeopleDocumentData.builder() .setPersonID(username) .setDocumentID(openvsxDocId) .setVersion(docVersion) - .setEffectiveDate(new Date()) - .setReceivedDate(new Date()) + .setEffectiveDate(now) + .setReceivedDate(now) .setScannedDocumentFileName("openvsx-publisher-agreement.json") .setScannedDocumentMime("application/json") .setScannedDocumentBLOB(docAsBytes) @@ -163,4 +175,49 @@ public class DefaultPublisherAgreementService implements PublisherAgreementServi .setComments("Generated by api.eclipse.org/openvsx/publisher_agreement") .build(); } + + /** + * Builds a PublisherAgreementData object using the given PeopleDocumentData + * entity. Has a flag to offset the received date by one day to compensate for + * the modification made by fdndb-api. + * + * @param document The given PeopleDocumentData entity + * @param adjustTime A Flag to increment the received date by a day + * @return A populated PublisherAgreementData object + */ + private PublisherAgreementData buildPublisherAgreement(PeopleDocumentData document, boolean adjustTime) { + + // Conversion of List<Byte> to byte[] to allow conversion to string + byte[] blob = document.getScannedDocumentBLOB() == null ? new byte[0] + : ArrayUtils.toPrimitive( + document.getScannedDocumentBLOB().toArray(new Byte[document.getScannedDocumentBLOB().size()])); + + SimpleDateFormat effectiveDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat receivedDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + String receivedDate = ""; + + // The 'received_date' column is only a 'date' type and gets rewinded a day when + // adjusted for UTC time by fdndb-api. Flag should onlybe set on fetch requests + if (adjustTime) { + ZonedDateTime adjustedTime = ZonedDateTime.parse(DateTimeHelper.toRFC3339(document.getReceivedDate())) + .plusDays(1); + receivedDate = receivedDateFormat.format(Date.from(adjustedTime.toInstant())); + } else { + receivedDate = receivedDateFormat.format(document.getReceivedDate()); + } + + return PublisherAgreementData.builder() + .setPersonID(document.getPersonID()) + .setDocumentID(document.getDocumentID()) + .setVersion(Integer.toString((int) document.getVersion())) + .setEffectiveDate(effectiveDateFormat.format(document.getEffectiveDate())) + .setReceivedDate(receivedDate) + .setScannedDocumentBlob(new String(blob)) + .setScannedDocumentMime(document.getScannedDocumentMime()) + .setScannedDocumentBytes(Integer.toString(blob.length)) + .setScannedDocumentFileName(document.getScannedDocumentFileName()) + .setComments(document.getComments()) + .build(); + } } diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java index 1dfe5e0bc91b3a264a7acdd8967fd2b1bf0049f8..f8f608810023acb726a44aee56cd181fda1f3e57 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java +++ b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java @@ -36,7 +36,7 @@ public class ProfileResourceTest { .of(Map.of("Authorization", "Bearer token1")); public static final EndpointTestCase GET_CURRENT_SUCCESS = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.EF_USER_SCHEMA_PATH) + .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.EF_USERS_SCHEMA_PATH) .setHeaderParams(userCreds).build(); /* diff --git a/src/test/java/org/eclipsefoundation/openvsx/test/api/MockPeopleAPI.java b/src/test/java/org/eclipsefoundation/openvsx/test/api/MockPeopleAPI.java index 066df24a7fe8af640bb28a5ce90f290482ef9988..07eb2ffd9b5dab73027129ba43747b732c9f5fb4 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/test/api/MockPeopleAPI.java +++ b/src/test/java/org/eclipsefoundation/openvsx/test/api/MockPeopleAPI.java @@ -24,6 +24,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import org.apache.commons.lang3.ArrayUtils; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipsefoundation.foundationdb.client.model.PeopleData; import org.eclipsefoundation.foundationdb.client.model.PeopleDocumentData; @@ -119,7 +120,7 @@ public class MockPeopleAPI implements PeopleAPI { .setReceivedDate(new Date()) .setScannedDocumentFileName("name") .setScannedDocumentMime("application/json") - .setScannedDocumentBLOB(null) + .setScannedDocumentBLOB(Arrays.asList(ArrayUtils.toObject("test blob".getBytes()))) .setScannedDocumentBytes(100) .setComments("Some comments") .build(); diff --git a/src/test/java/org/eclipsefoundation/openvsx/test/helpers/SchemaNamespaceHelper.java b/src/test/java/org/eclipsefoundation/openvsx/test/helpers/SchemaNamespaceHelper.java index 90a779119c03e7a93d8d70df505fa604800c66ba..029f54ab561f01f42237d022311e4d6b64a97007 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/test/helpers/SchemaNamespaceHelper.java +++ b/src/test/java/org/eclipsefoundation/openvsx/test/helpers/SchemaNamespaceHelper.java @@ -20,5 +20,6 @@ public class SchemaNamespaceHelper { public static final String PUBLISHER_AGREEMENT_SCHEMA_PATH = BASE_SCHEMA_PATH + "publisher-agreement" + BASE_SCHEMA_PATH_SUFFIX; public static final String EF_USER_SCHEMA_PATH = BASE_SCHEMA_PATH + "ef-user" + BASE_SCHEMA_PATH_SUFFIX; + public static final String EF_USERS_SCHEMA_PATH = BASE_SCHEMA_PATH + "ef-users" + BASE_SCHEMA_PATH_SUFFIX; public static final String ERROR_SCHEMA_PATH = BASE_SCHEMA_PATH + "error" + BASE_SCHEMA_PATH_SUFFIX; }