Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cases alert threshold and ability to send message to directory notification server #44

Merged
merged 8 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ public class UserAdminApplicationProps {

private Integer defaultMaxAllowedCases;

private Integer casesAlertThreshold;

private Integer defaultMaxAllowedBuilds;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,22 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* @author Etienne Homer <etienne.homer at rte-france.com>
*
* @implNote /!\ TO DEV: remember to maintain list access restricted in operations' description
*/
@RestController
Expand Down Expand Up @@ -111,8 +120,8 @@ public ResponseEntity<Void> userExists(@PathVariable("sub") String sub) {
@ApiResponse(responseCode = "200", description = "user authorized and admin")
public ResponseEntity<Void> userIsAdmin(@PathVariable("sub") String userId) {
return service.userIsAdmin(userId)
? ResponseEntity.ok().build()
: ResponseEntity.status(HttpStatus.FORBIDDEN).build();
? ResponseEntity.ok().build()
: ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}

@GetMapping(value = "/users/{sub}/profile")
Expand All @@ -131,6 +140,13 @@ public ResponseEntity<Integer> getUserProfileMaxStudies(@PathVariable("sub") Str
return ResponseEntity.ok().body(service.getUserProfileMaxAllowedCases(sub));
}

@GetMapping(value = "/cases-alert-threshold")
@Operation(summary = "Get the cases alert threshold")
@ApiResponse(responseCode = "200", description = "The cases alert threshold")
public ResponseEntity<Integer> getCasesAlertThreshold() {
return ResponseEntity.ok().body(service.getCasesAlertThreshold());
}

@GetMapping(value = "/users/{sub}/profile/max-builds")
@Operation(summary = "Get the user's max allowed builds")
@ApiResponse(responseCode = "200", description = "The user max allowed builds")
Expand Down Expand Up @@ -169,4 +185,16 @@ public ResponseEntity<Void> sendCancelMaintenanceMessage(@RequestHeader("userId"
service.sendCancelMaintenanceMessage(userId);
return ResponseEntity.ok().build();
}

@PostMapping(value = "/messages/{sub}/user-message")
@Operation(summary = "send a message to a specific user")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "message sent"),
})
public ResponseEntity<Void> sendUserMessage(@PathVariable("sub") String sub,
@Parameter(description = "the message id to be displayed to the user") @RequestParam(value = "messageId") String messageId,
@Parameter(description = "the message values attached to the message") @RequestBody(required = false) String messageValues) {
service.sendUserMessage(sub, messageId, messageValues);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import static com.powsybl.ws.commons.computation.service.NotificationService.HEADER_USER_ID;

/**
* @author Abdelsalem Hedhili <abdelsalem.hedhili at rte-france.com
*/
@Service
public class NotificationService {

public static final String GLOBAL_CONFIG_BINDING = "publishMessage-out-0";

public static final String DIRECTORY_UPDATE_BINDING = "publishDirectoryUpdate-out-0";

public static final String HEADER_MESSAGE_TYPE = "messageType";

public static final String MESSAGE_TYPE_MAINTENANCE = "maintenance";
Expand All @@ -28,6 +34,12 @@ public class NotificationService {

public static final String HEADER_DURATION = "duration";

public static final String HEADER_USER_MESSAGE = "userMessage";

public static final String HEADER_UPDATE_TYPE = "updateType";

public static final String HEADER_UPDATE_TYPE_DIRECTORY = "directories";

public static final String MESSAGE_LOG = "Sending message : {}";

private static final String CATEGORY_BROKER_OUTPUT = UserAdminService.class.getName() + ".output-broker-messages";
Expand All @@ -37,27 +49,35 @@ public class NotificationService {
@Autowired
private StreamBridge updatePublisher;

private void sendMessage(Message<String> message) {
private void sendMessage(Message<String> message, String bindingName) {
MESSAGE_OUTPUT_LOGGER.debug(MESSAGE_LOG, message);
updatePublisher.send("publishMessage-out-0", message);
updatePublisher.send(bindingName, message);
}

public void emitMaintenanceMessage(String message, int duration) {
sendMessage(MessageBuilder.withPayload(message)
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_MAINTENANCE)
.setHeader(HEADER_DURATION, duration)
.build());
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_MAINTENANCE)
.setHeader(HEADER_DURATION, duration)
.build(), GLOBAL_CONFIG_BINDING);
}

public void emitMaintenanceMessage(String message) {
sendMessage(MessageBuilder.withPayload(message)
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_MAINTENANCE)
.build());
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_MAINTENANCE)
.build(), GLOBAL_CONFIG_BINDING);
}

public void emitCancelMaintenanceMessage() {
sendMessage(MessageBuilder.withPayload("")
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_CANCEL_MAINTENANCE)
.build());
.setHeader(HEADER_MESSAGE_TYPE, MESSAGE_TYPE_CANCEL_MAINTENANCE)
.build(), GLOBAL_CONFIG_BINDING);
}

public void emitUserMessage(String sub, String messageId, String messageValues) {
sendMessage(MessageBuilder.withPayload(messageValues)
.setHeader(HEADER_USER_MESSAGE, messageId)
.setHeader(HEADER_UPDATE_TYPE, HEADER_UPDATE_TYPE_DIRECTORY)
.setHeader(HEADER_USER_ID, sub)
.build(), DIRECTORY_UPDATE_BINDING);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
import org.gridsuite.useradmin.server.dto.UserConnection;
import org.gridsuite.useradmin.server.dto.UserInfos;
import org.gridsuite.useradmin.server.dto.UserProfile;
import org.gridsuite.useradmin.server.entity.UserInfosEntity;
import org.gridsuite.useradmin.server.entity.UserProfileEntity;
import org.gridsuite.useradmin.server.repository.UserInfosRepository;
import org.gridsuite.useradmin.server.entity.UserInfosEntity;
import org.gridsuite.useradmin.server.repository.UserProfileRepository;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.NonNull;
Expand Down Expand Up @@ -134,6 +134,10 @@ public Integer getUserProfileMaxAllowedCases(String sub) {
.orElse(applicationProps.getDefaultMaxAllowedCases());
}

public Integer getCasesAlertThreshold() {
return Optional.ofNullable(applicationProps.getCasesAlertThreshold()).orElse(90);
}

@Transactional(readOnly = true)
public Integer getUserProfileMaxAllowedBuilds(String sub) {
return self.getUserProfile(sub)
Expand Down Expand Up @@ -163,4 +167,9 @@ public void sendCancelMaintenanceMessage(String userId) {
}
notificationService.emitCancelMaintenanceMessage();
}

public void sendUserMessage(String sub, String messageId, String messageValues) {
String values = messageValues != null ? messageValues : "";
notificationService.emitUserMessage(sub, messageId, values);
}
}
5 changes: 4 additions & 1 deletion src/main/resources/config/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ spring:
bindings:
publishMessage-out-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}config.message
output-bindings: publishMessage-out-0
publishDirectoryUpdate-out-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}directory.update
output-bindings: publishMessage-out-0;publishDirectoryUpdate-out-0

powsybl-ws:
database:
Expand All @@ -18,3 +20,4 @@ useradmin:
# - admin2
# defaultMaxAllowedCases: 20 # Default allowed cases for a user (set here for testing purposes)
# defaultMaxAllowedBuilds: 10 # Default allowed builds for a user (set here for testing purposes)
# casesAlertThreshold: 90 # Default usage threshold (percentage) when user gets a warning when uploading cases (set here for testing purposes)
26 changes: 26 additions & 0 deletions src/test/java/org/gridsuite/useradmin/server/UserAdminTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.time.ZoneOffset;
import java.util.List;

import static com.powsybl.ws.commons.computation.service.NotificationService.HEADER_USER_ID;
import static org.gridsuite.useradmin.server.service.NotificationService.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.http.MediaType.APPLICATION_JSON;
Expand Down Expand Up @@ -68,6 +69,8 @@ class UserAdminTest {

private final String maintenanceMessageDestination = "config.message";

private final String userMessageDestination = "directory.update";

private static final long TIMEOUT = 1000;

@AfterEach
Expand Down Expand Up @@ -199,6 +202,12 @@ void testUserAdmin() throws Exception {
.content("[\"" + USER_UNKNOWN + "\"]"))
.andExpect(status().isNotFound())
.andReturn();

mockMvc.perform(get("/" + UserAdminApi.API_VERSION + "/cases-alert-threshold")
.header("userId", NOT_ADMIN)
)
.andExpect(status().isOk())
.andReturn();
}

@Test
Expand Down Expand Up @@ -344,6 +353,23 @@ void testCancelMaintenanceMessage() throws Exception {
.andExpect(status().isForbidden());
}

@Test
void testSendUserMessage() throws Exception {
mockMvc.perform(post("/" + UserAdminApi.API_VERSION + "/messages/{sub}/user-message", USER_SUB)
.queryParam("messageId", "messageIdTest")
).andExpect(status().isOk())
.andReturn();
assertUserMessageSent("messageIdTest", USER_SUB);
}

private void assertUserMessageSent(String messageId, String sub) {
Message<byte[]> message = output.receive(TIMEOUT, userMessageDestination);
MessageHeaders headers = message.getHeaders();
assertEquals(messageId, headers.get(HEADER_USER_MESSAGE));
assertEquals(sub, headers.get(HEADER_USER_ID));

}

private void assertMaintenanceMessageSent(String maintenanceMessage, Integer duration) {
Message<byte[]> message = output.receive(TIMEOUT, maintenanceMessageDestination);
assertEquals(maintenanceMessage, new String(message.getPayload()));
Expand Down
Loading