From b8da53a02f1cd0012902567b1861ad15c59a5250 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 8 Oct 2024 11:45:55 +0200 Subject: [PATCH 1/6] Send notification to frontend when user reach a defined threshold of cases storage usage --- .../server/dto/CaseAlertThresholdMessage.java | 9 ++++++ .../server/services/ExploreService.java | 12 ++++++++ .../server/services/UserAdminService.java | 30 ++++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java diff --git a/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java new file mode 100644 index 0000000..eaa723c --- /dev/null +++ b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.explore.server.dto; + +public record CaseAlertThresholdMessage (Integer userUsagePercentage, Integer casesCount) {} diff --git a/src/main/java/org/gridsuite/explore/server/services/ExploreService.java b/src/main/java/org/gridsuite/explore/server/services/ExploreService.java index 0d012d4..27932d0 100644 --- a/src/main/java/org/gridsuite/explore/server/services/ExploreService.java +++ b/src/main/java/org/gridsuite/explore/server/services/ExploreService.java @@ -295,6 +295,18 @@ public void assertCanCreateCase(String userId) { if (userCasesCount >= userMaxAllowedStudiesAndCases) { throw new ExploreException(MAX_ELEMENTS_EXCEEDED, "max allowed cases : " + userMaxAllowedStudiesAndCases); } + notifyCasesThresholdReached(userCasesCount, userMaxAllowedStudiesAndCases, userId); + } + } + + public void notifyCasesThresholdReached(int userCasesCount, int userMaxAllowedStudiesAndCases, String userId) { + Integer casesAlertThreshold = userAdminService.getCasesAlertThreshold(); + if (casesAlertThreshold != null) { + int userCasesUsagePercentage = (100 * userCasesCount) / userMaxAllowedStudiesAndCases; + if (userCasesUsagePercentage >= casesAlertThreshold) { + CaseAlertThresholdMessage caseAlertThresholdMessage = new CaseAlertThresholdMessage(userCasesUsagePercentage, userCasesCount); + userAdminService.sendUserCasesAlertThresholdMessage(userId, "casesAlertThreshold", caseAlertThresholdMessage); + } } } diff --git a/src/main/java/org/gridsuite/explore/server/services/UserAdminService.java b/src/main/java/org/gridsuite/explore/server/services/UserAdminService.java index 9274691..9befdd6 100644 --- a/src/main/java/org/gridsuite/explore/server/services/UserAdminService.java +++ b/src/main/java/org/gridsuite/explore/server/services/UserAdminService.java @@ -7,7 +7,12 @@ package org.gridsuite.explore.server.services; import lombok.Setter; +import org.gridsuite.explore.server.dto.CaseAlertThresholdMessage; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; @@ -23,7 +28,8 @@ public class UserAdminService { private static final String USER_ADMIN_API_VERSION = "v1"; private static final String USERS_MAX_ALLOWED_CASES_URI = "/users/{sub}/profile/max-cases"; - + private static final String CASES_ALERT_THRESHOLD_URI = "/cases-alert-threshold"; + private static final String USER_MESSAGE_URI = "/messages/{sub}/user-message"; private static final String DELIMITER = "/"; private final RestTemplate restTemplate; @Setter @@ -49,4 +55,26 @@ public Integer getUserMaxAllowedCases(String sub) { } } + public Integer getCasesAlertThreshold() { + String path = UriComponentsBuilder.fromPath(DELIMITER + USER_ADMIN_API_VERSION + CASES_ALERT_THRESHOLD_URI) + .buildAndExpand().toUriString(); + try { + return restTemplate.getForObject(userAdminServerBaseUri + path, Integer.class); + } catch (HttpStatusCodeException e) { + throw wrapRemoteError(e.getMessage(), e.getStatusCode()); + } + } + + public void sendUserCasesAlertThresholdMessage(String sub, String messageId, CaseAlertThresholdMessage caseAlertThresholdMessage) { + UriComponentsBuilder uri = UriComponentsBuilder + .fromPath(DELIMITER + USER_ADMIN_API_VERSION + USER_MESSAGE_URI) + .queryParam("messageId", messageId); + + String path = uri.buildAndExpand(sub) + .toUriString(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + restTemplate.exchange(userAdminServerBaseUri + path, HttpMethod.POST, new HttpEntity<>(caseAlertThresholdMessage, headers), Void.class); + } + } From 29aa67e895f181996cb93eea9ae215eb493681d1 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 8 Oct 2024 11:48:37 +0200 Subject: [PATCH 2/6] Formatting --- .../explore/server/dto/CaseAlertThresholdMessage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java index eaa723c..325b7dc 100644 --- a/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java +++ b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java @@ -6,4 +6,5 @@ */ package org.gridsuite.explore.server.dto; -public record CaseAlertThresholdMessage (Integer userUsagePercentage, Integer casesCount) {} +public record CaseAlertThresholdMessage(Integer userUsagePercentage, Integer casesCount) { +} From 8e3d0ddd82c2796068c236a179d8aa8bd796ea7e Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 8 Oct 2024 15:11:25 +0200 Subject: [PATCH 3/6] Fix tests --- src/test/java/org/gridsuite/explore/server/ExploreTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/org/gridsuite/explore/server/ExploreTest.java b/src/test/java/org/gridsuite/explore/server/ExploreTest.java index a6d29e9..e7011dd 100644 --- a/src/test/java/org/gridsuite/explore/server/ExploreTest.java +++ b/src/test/java/org/gridsuite/explore/server/ExploreTest.java @@ -348,6 +348,9 @@ public MockResponse dispatch(RecordedRequest request) { .addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/elements/" + ELEMENT_UUID)) { return new MockResponse().setBody(invalidElementAsString).setResponseCode(200).addHeader("Content-Type", "application/json; charset=utf-8"); + } else if (path.matches("/v1/cases-alert-threshold")) { + return new MockResponse().setBody("90").setResponseCode(200) + .addHeader("Content-Type", "application/json; charset=utf-8"); } } else if ("DELETE".equals(request.getMethod())) { if (path.matches("/v1/filters/" + FILTER_UUID)) { From 19e4fab3ac6516525bb9994fd00d5e8a4324b7a7 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 8 Oct 2024 15:29:33 +0200 Subject: [PATCH 4/6] Increase coverage --- src/test/java/org/gridsuite/explore/server/ExploreTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/explore/server/ExploreTest.java b/src/test/java/org/gridsuite/explore/server/ExploreTest.java index e7011dd..cfa5ba0 100644 --- a/src/test/java/org/gridsuite/explore/server/ExploreTest.java +++ b/src/test/java/org/gridsuite/explore/server/ExploreTest.java @@ -304,6 +304,9 @@ public MockResponse dispatch(RecordedRequest request) { return new MockResponse().setResponseCode(200); } else if (path.matches("/v1/network-composite-modifications")) { return new MockResponse().setBody(compositeModificationIdAsString).setResponseCode(200).addHeader("Content-Type", "application/json; charset=utf-8"); + } else if (path.matches("/v1/messages/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED + "/user-message.*")) { + return new MockResponse().setResponseCode(200) + .addHeader("Content-Type", "application/json; charset=utf-8"); } else if ("GET".equals(request.getMethod())) { if (path.matches("/v1/elements/" + INVALID_ELEMENT_UUID)) { return new MockResponse().setBody(invalidElementAsString).setResponseCode(200).addHeader("Content-Type", "application/json; charset=utf-8"); @@ -349,7 +352,7 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.matches("/v1/elements/" + ELEMENT_UUID)) { return new MockResponse().setBody(invalidElementAsString).setResponseCode(200).addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/cases-alert-threshold")) { - return new MockResponse().setBody("90").setResponseCode(200) + return new MockResponse().setBody("1").setResponseCode(200) .addHeader("Content-Type", "application/json; charset=utf-8"); } } else if ("DELETE".equals(request.getMethod())) { From 850deb9a5fa4102345c8e16a46ff75ff833963f1 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 8 Oct 2024 16:08:18 +0200 Subject: [PATCH 5/6] Add dedicated test --- .../gridsuite/explore/server/ExploreTest.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/explore/server/ExploreTest.java b/src/test/java/org/gridsuite/explore/server/ExploreTest.java index cfa5ba0..fc978ae 100644 --- a/src/test/java/org/gridsuite/explore/server/ExploreTest.java +++ b/src/test/java/org/gridsuite/explore/server/ExploreTest.java @@ -84,6 +84,8 @@ public class ExploreTest { private static final String USER1 = "user1"; private static final String USER_WITH_CASE_LIMIT_EXCEEDED = "limitedUser"; private static final String USER_WITH_CASE_LIMIT_NOT_EXCEEDED = "limitedUser2"; + private static final String USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2 = "limitedUser3"; + private static final String USER_NOT_FOUND = "userNotFound"; private static final String USER_UNEXPECTED_ERROR = "unexpectedErrorUser"; public static final String FILTER_CONTINGENCY_LIST = "filterContingencyList"; @@ -331,6 +333,9 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.matches("/v1/users/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED + "/profile/max-cases")) { return new MockResponse().setBody("5").setResponseCode(200) .addHeader("Content-Type", "application/json; charset=utf-8"); + } else if (path.matches("/v1/users/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2 + "/profile/max-cases")) { + return new MockResponse().setBody("5").setResponseCode(200) + .addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/users/" + USER_NOT_FOUND + "/profile/max-cases")) { return new MockResponse().setResponseCode(404) .addHeader("Content-Type", "application/json; charset=utf-8"); @@ -346,13 +351,16 @@ public MockResponse dispatch(RecordedRequest request) { } else if (path.matches("/v1/users/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED + "/cases/count")) { return new MockResponse().setBody("2").setResponseCode(200) .addHeader("Content-Type", "application/json; charset=utf-8"); + } else if (path.matches("/v1/users/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2 + "/cases/count")) { + return new MockResponse().setBody("1").setResponseCode(200) + .addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/users/.*/cases/count")) { return new MockResponse().setBody("0").setResponseCode(200) .addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/elements/" + ELEMENT_UUID)) { return new MockResponse().setBody(invalidElementAsString).setResponseCode(200).addHeader("Content-Type", "application/json; charset=utf-8"); } else if (path.matches("/v1/cases-alert-threshold")) { - return new MockResponse().setBody("1").setResponseCode(200) + return new MockResponse().setBody("40").setResponseCode(200) .addHeader("Content-Type", "application/json; charset=utf-8"); } } else if ("DELETE".equals(request.getMethod())) { @@ -956,6 +964,29 @@ public void testMaxCaseCreationWithRemoteException() throws Exception { } } + @Test + public void testCaseAlertThreshold() throws Exception { + //Perform a study creation while USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2 has not yet reached the defined case alert threshold, no message sent to him + mockMvc.perform(post("/v1/explore/studies/" + STUDY1 + "/cases/" + CASE_UUID + "?description=desc&parentDirectoryUuid=" + PARENT_DIRECTORY_UUID) + .param("duplicateCase", "false") + .header("userId", USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2) + .param("caseFormat", "XIIDM") + .contentType(APPLICATION_JSON) + ).andExpect(status().isOk()); + var requests = TestUtils.getRequestsWithBodyDone(5, server); + assertTrue(requests.stream().noneMatch(r -> r.getPath().contains("/messages/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED_2 + "/user-message"))); + + //Perform a study creation while USER_WITH_CASE_LIMIT_NOT_EXCEEDED has reached the defined case alert threshold, a message has been sent to him + mockMvc.perform(post("/v1/explore/studies/" + STUDY1 + "/cases/" + CASE_UUID + "?description=desc&parentDirectoryUuid=" + PARENT_DIRECTORY_UUID) + .param("duplicateCase", "false") + .header("userId", USER_WITH_CASE_LIMIT_NOT_EXCEEDED) + .param("caseFormat", "XIIDM") + .contentType(APPLICATION_JSON) + ).andExpect(status().isOk()); + requests = TestUtils.getRequestsWithBodyDone(6, server); + assertTrue(requests.stream().anyMatch(r -> r.getPath().contains("/messages/" + USER_WITH_CASE_LIMIT_NOT_EXCEEDED + "/user-message"))); + } + @Test public void testUpdateElement() throws Exception { ElementAttributes elementAttributes = new ElementAttributes(); From 86b4b42516a6e9ff74516a4093956306f0e882cd Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 10 Oct 2024 14:10:02 +0200 Subject: [PATCH 6/6] Add author --- .../explore/server/dto/CaseAlertThresholdMessage.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java index 325b7dc..4b34677 100644 --- a/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java +++ b/src/main/java/org/gridsuite/explore/server/dto/CaseAlertThresholdMessage.java @@ -6,5 +6,8 @@ */ package org.gridsuite.explore.server.dto; +/** + * @author Hugo Marcellin + */ public record CaseAlertThresholdMessage(Integer userUsagePercentage, Integer casesCount) { }