From 195ca5321090bb900f882a76dd35e97718e227de Mon Sep 17 00:00:00 2001 From: Martin Lowe <martin.lowe@eclipse-foundation.org> Date: Wed, 19 Mar 2025 12:58:20 -0400 Subject: [PATCH 1/2] feat(ui): Add last validation time, disable revalidation if PR not open In the status UI, previously we never stated when the last validation occurred. As we have access to this data, we should tell the user when the last validation of a commit associated with a PR happened. This better supports users to know if their failure may be due to out of date validation. Additionally, in the case of GH PRs where we have logic to manage those calls separately, we can now check if the PR is open. We use this to now toggle whether the revalidation button will be usable and whether the hCaptcha is injected. --- .../config/EclipseQuteTemplateExtensions.java | 19 +++++++++++++++++++ .../git/eca/resource/StatusResource.java | 11 ++++++++++- .../templates/simple_fingerprint_ui.html | 12 ++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java index 0413b424..b79c2d61 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java +++ b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java @@ -11,6 +11,7 @@ **********************************************************************/ package org.eclipsefoundation.git.eca.config; +import java.time.format.DateTimeFormatter; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -49,6 +50,24 @@ public class EclipseQuteTemplateExtensions { return statuses.stream().flatMap(s -> s.getErrors().stream()).toList(); } + /** + * Formats and flattens a list of statuses to a list of errors encountered while validation a set of commits. + * + * @param statuses a list of commit validation statuses to retrieve errors from + * @return a list of flattened errors, or an empty list if (none are found. + */ + static String getLastKnownValidationTime(List<CommitValidationStatus> statuses) { + CommitValidationStatus latestStatus = statuses + .stream() + .sorted((o1, o2) -> o1.getLastModified().compareTo(o2.getLastModified())) + .findFirst() + .orElse(null); + if (latestStatus == null) { + return "Unknown"; + } + return latestStatus.getLastModified().format(DateTimeFormatter.RFC_1123_DATE_TIME); + } + /** * Converts the status list to a list of email addresses that were associated with validated commits. * 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 2625fa16..c864c251 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java +++ b/src/main/java/org/eclipsefoundation/git/eca/resource/StatusResource.java @@ -12,9 +12,11 @@ package org.eclipsefoundation.git.eca.resource; import java.util.List; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.eclipsefoundation.efservices.api.models.Project; +import org.eclipsefoundation.git.eca.api.models.GithubWebhookRequest.PullRequest; import org.eclipsefoundation.git.eca.config.EclipseQuteTemplateExtensions; import org.eclipsefoundation.git.eca.dto.CommitValidationStatus; import org.eclipsefoundation.git.eca.helper.GithubValidationHelper; @@ -187,7 +189,13 @@ public class StatusResource extends CommonResource { // retrieve the status of the commits to display on the status page statuses = validationStatus.getHistoricValidationStatusByShas(wrapper, r.getCommits().keySet().stream().toList()); } - + // fetch the PR so we can indicate whether it's closed, open, or merged + String lastKnownStatus = "unknown"; + Optional<PullRequest> pr = ghAppService.getPullRequest(installationId, org, repoName, prNo); + if (pr.isPresent()) { + lastKnownStatus = pr.get().getState(); + } + // get projects for use in queries + UI List<Project> ps = projects.retrieveProjectsForRepoURL(repoUrl, ProviderType.GITHUB); // render and return the status UI @@ -203,6 +211,7 @@ public class StatusResource extends CommonResource { .data("installationId", installationId) .data("currentUser", getUserForLoggedInAccount()) .data("redirectUri", info.getPath()) + .data("lastKnownStatus", lastKnownStatus) .render()) .build(); } catch (BadRequestException e) { diff --git a/src/main/resources/templates/simple_fingerprint_ui.html b/src/main/resources/templates/simple_fingerprint_ui.html index 4bc08da8..f5a64b5b 100644 --- a/src/main/resources/templates/simple_fingerprint_ui.html +++ b/src/main/resources/templates/simple_fingerprint_ui.html @@ -76,6 +76,9 @@ <strong>Pull request:</strong> <a class="underline" href="https://github.com/{orgName}/{repoName}/pull/{pullRequestNumber}" target="_blank" >#{pullRequestNumber}</a> </li> {/if} + <li><strong>Last Validation Time:</strong> + {statuses.getLastKnownValidationTime} + </li> </ul> <a href="{repoUrl}" target="_blank" rel="noopener" class="btn btn-primary margin-top-10">Project repository</a> </div> @@ -163,13 +166,18 @@ </div> {/if} {#if statuses.0.provider == ProviderType:GITHUB && pullRequestNumber != null && repoName != null && orgName != null} - <div> + <div data-state="{lastKnownStatus}"> + {#if lastKnownStatus ne null && lastKnownStatus ne 'open'} + <button class="btn-success margin-top-10 btn form-submit" type="submit" disabled>Revalidate</button> + <p class="small">Revalidation is disabled as the Pull Request is in a non-open state</p> + {#else} <form id="git-eca-hook-revalidation" data-request-number="{pullRequestNumber}" data-request-repo="{repoName}" data-request-org="{orgName}" data-request-installation="{installationId}"> <div class="captcha"> <div class="h-captcha" data-sitekey="{config:['eclipse.hcaptcha.site-key']}"></div> </div> - <button class="btn-success margin-top-10 btn form-submit" type="submit">Revalidate</button> + <button class="btn-success margin-top-10 btn form-submit" type="submit" { lastKnownStatus ne null && lastKnownStatus ne 'open' ? 'disabled' : '' }>Revalidate</button> </form> + {/if} </div> {/if} </section> -- GitLab From 0524ba5540604121cad861b8c7f6b1ef8760f1de Mon Sep 17 00:00:00 2001 From: Martin Lowe <martin.lowe@eclipse-foundation.org> Date: Thu, 20 Mar 2025 10:25:51 -0400 Subject: [PATCH 2/2] feat(test): add tests for the last known validation time Qute function --- .../config/EclipseQuteTemplateExtensions.java | 8 ++-- .../EclipseQuteTemplateExtensionsTest.java | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java index b79c2d61..3387e3b8 100644 --- a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java +++ b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java @@ -51,15 +51,15 @@ public class EclipseQuteTemplateExtensions { } /** - * Formats and flattens a list of statuses to a list of errors encountered while validation a set of commits. + * Sorts list and retrieves the most recently modified status, and returns a human readable format of the time. * - * @param statuses a list of commit validation statuses to retrieve errors from - * @return a list of flattened errors, or an empty list if (none are found. + * @param statuses a list of commit validation statuses to retrieve a modification time from + * @return the most recent modification time for any of the passed statuses. */ static String getLastKnownValidationTime(List<CommitValidationStatus> statuses) { CommitValidationStatus latestStatus = statuses .stream() - .sorted((o1, o2) -> o1.getLastModified().compareTo(o2.getLastModified())) + .sorted((o1, o2) -> o2.getLastModified().compareTo(o1.getLastModified())) .findFirst() .orElse(null); if (latestStatus == null) { diff --git a/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java b/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java index 988fc6b1..76899e3c 100644 --- a/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java +++ b/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java @@ -11,7 +11,17 @@ */ package org.eclipsefoundation.git.eca.config; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.eclipsefoundation.git.eca.dto.CommitValidationStatus; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -64,4 +74,32 @@ class EclipseQuteTemplateExtensionsTest { void obfuscateEmail_failure_invalidAddress(String input, String expected) { Assertions.assertEquals(expected, EclipseQuteTemplateExtensions.obfuscateEmail(input)); } + + @Test + void getLastKnownValidationTime_success() { + ZonedDateTime timeOne = ZonedDateTime.now().minus(3, ChronoUnit.DAYS); + ZonedDateTime timeTwo = ZonedDateTime.now().minus(5, ChronoUnit.DAYS); + ZonedDateTime timeThree = ZonedDateTime.now().minus(1, ChronoUnit.DAYS); + List<CommitValidationStatus> statuses = Arrays + .asList(createStatus(Optional.of(timeOne)), createStatus(Optional.of(timeTwo)), createStatus(Optional.of(timeThree))); + // should return time three in RFC 1123 format, as it is the most recent time. + Assertions + .assertEquals(timeThree.format(DateTimeFormatter.RFC_1123_DATE_TIME), + EclipseQuteTemplateExtensions.getLastKnownValidationTime(statuses)); + } + + @Test + void getLastKnownValidationTime_noCommits() { + // should return "Unknown" if we can't find an associated modification time at all + Assertions + .assertEquals("Unknown", + EclipseQuteTemplateExtensions.getLastKnownValidationTime(new ArrayList<>())); + } + + private CommitValidationStatus createStatus(Optional<ZonedDateTime> date) { + CommitValidationStatus status = new CommitValidationStatus(); + status.setCreationDate(ZonedDateTime.now()); + status.setLastModified(date.orElse(ZonedDateTime.now())); + return status; + } } -- GitLab