Skip to content

Commit

Permalink
Send notification to frontend when user reach a defined threshold of …
Browse files Browse the repository at this point in the history
…cases storage usage (#101)

Signed-off-by: Hugo Marcellin <[email protected]>
  • Loading branch information
Meklo authored Oct 11, 2024
1 parent 4c6d03d commit 90c7196
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 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;

/**
* @author Hugo Marcellin <hugo.marcelin at rte-france.com>
*/
public record CaseAlertThresholdMessage(Integer userUsagePercentage, Integer casesCount) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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);
}

}
37 changes: 37 additions & 0 deletions src/test/java/org/gridsuite/explore/server/ExploreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -304,6 +306,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");
Expand All @@ -328,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");
Expand All @@ -343,11 +351,17 @@ 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("40").setResponseCode(200)
.addHeader("Content-Type", "application/json; charset=utf-8");
}
} else if ("DELETE".equals(request.getMethod())) {
if (path.matches("/v1/filters/" + FILTER_UUID)) {
Expand Down Expand Up @@ -950,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();
Expand Down

0 comments on commit 90c7196

Please sign in to comment.