Skip to content
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
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@
<dependencyManagement>
<dependencies>
<!-- overrides of imports -->

<!-- TODO remove when gridsuite-dependencies is released with this version -->
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-ws-commons</artifactId>
<version>1.30.0</version>
</dependency>
<!-- imports -->
<dependency>
<groupId>org.gridsuite</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2025, 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.useradmin.server;

import com.powsybl.ws.commons.error.ServerNameProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*/
@Component
public class PropertyServerNameProvider implements ServerNameProvider {

private final String name;

public PropertyServerNameProvider(@Value("${spring.application.name:user-admin-server}") String name) {
this.name = name;
}

@Override
public String serverName() {
return name;
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import org.gridsuite.useradmin.server.UserAdminApi;
Expand Down Expand Up @@ -56,7 +55,7 @@ public ResponseEntity<Announcement> getCurrentAnnouncement() {
@ApiResponse(responseCode = "403", description = "User is not an admin")
@ApiResponse(responseCode = "409", description = "There is a conflict in display time")
public ResponseEntity<Announcement> createAnnouncement(@RequestParam("startDate") Instant startDate,
@RequestParam("endDate") @Future Instant endDate,
@RequestParam("endDate") Instant endDate,
@RequestParam("severity") AnnouncementSeverity severity,
@RequestBody @NotBlank String message) {
return ResponseEntity.ok(service.createAnnouncement(startDate, endDate, message, severity));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2025, 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.useradmin.server.error;

import com.powsybl.ws.commons.error.AbstractBaseRestExceptionHandler;
import com.powsybl.ws.commons.error.ServerNameProvider;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*
* Handle exception catch from the {@link org.gridsuite.useradmin.server.controller controllers}.
*/
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends AbstractBaseRestExceptionHandler<UserAdminException, UserAdminBusinessErrorCode> {

public RestResponseEntityExceptionHandler(ServerNameProvider serverNameProvider) {
super(serverNameProvider);
}

@NotNull
@Override
protected UserAdminBusinessErrorCode getBusinessCode(UserAdminException ex) {
return ex.getBusinessErrorCode();
}

@Override
protected HttpStatus mapStatus(UserAdminBusinessErrorCode errorCode) {
return switch (errorCode) {
case USER_ADMIN_PERMISSION_DENIED -> HttpStatus.FORBIDDEN;
case USER_ADMIN_USER_NOT_FOUND,
USER_ADMIN_PROFILE_NOT_FOUND,
USER_ADMIN_GROUP_NOT_FOUND -> HttpStatus.NOT_FOUND;
case USER_ADMIN_USER_ALREADY_EXISTS,
USER_ADMIN_PROFILE_ALREADY_EXISTS,
USER_ADMIN_GROUP_ALREADY_EXISTS,
USER_ADMIN_ANNOUNCEMENT_INVALID_PERIOD,
USER_ADMIN_ANNOUNCEMENT_OVERLAP -> HttpStatus.BAD_REQUEST;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2025, 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.useradmin.server.error;

import com.powsybl.ws.commons.error.BusinessErrorCode;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*
* Business error codes exposed by the user-admin server.
*/
public enum UserAdminBusinessErrorCode implements BusinessErrorCode {
USER_ADMIN_PERMISSION_DENIED("useradmin.permissionDenied"),
USER_ADMIN_USER_NOT_FOUND("useradmin.userNotFound"),
USER_ADMIN_USER_ALREADY_EXISTS("useradmin.userAlreadyExists"),
USER_ADMIN_PROFILE_NOT_FOUND("useradmin.profileNotFound"),
USER_ADMIN_PROFILE_ALREADY_EXISTS("useradmin.profileAlreadyExists"),
USER_ADMIN_GROUP_NOT_FOUND("useradmin.groupNotFound"),
USER_ADMIN_GROUP_ALREADY_EXISTS("useradmin.groupAlreadyExists"),
USER_ADMIN_ANNOUNCEMENT_INVALID_PERIOD("useradmin.announcementInvalidPeriod"),
USER_ADMIN_ANNOUNCEMENT_OVERLAP("useradmin.announcementOverlap");

private final String value;

UserAdminBusinessErrorCode(String value) {
this.value = value;
}

public String value() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2025, 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.useradmin.server.error;

import com.powsybl.ws.commons.error.AbstractBusinessException;
import org.jetbrains.annotations.NotNull;

import java.time.Instant;
import java.util.Objects;
import java.util.UUID;

import static org.gridsuite.useradmin.server.error.UserAdminBusinessErrorCode.*;

/**
* @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
*
* User admin specific runtime exception enriched with a business error code.
*/
public class UserAdminException extends AbstractBusinessException {

private final UserAdminBusinessErrorCode errorCode;

public UserAdminException(UserAdminBusinessErrorCode errorCode, String message) {
super(Objects.requireNonNull(message, "message must not be null"));
this.errorCode = Objects.requireNonNull(errorCode, "errorCode must not be null");
}

public static UserAdminException forbidden() {
return new UserAdminException(USER_ADMIN_PERMISSION_DENIED, "User is not allowed to perform this action");
}

public static UserAdminException userAlreadyExists(String sub) {
return new UserAdminException(USER_ADMIN_USER_ALREADY_EXISTS, String.format("User '%s' already exists", sub));
}

public static UserAdminException userNotFound(String sub) {
return new UserAdminException(USER_ADMIN_USER_NOT_FOUND, String.format("User '%s' was not found", sub));
}

public static UserAdminException profileAlreadyExists(String name) {
return new UserAdminException(USER_ADMIN_PROFILE_ALREADY_EXISTS, String.format("Profile '%s' already exists", name));
}

public static UserAdminException profileNotFound(UUID profileId) {
return new UserAdminException(USER_ADMIN_PROFILE_NOT_FOUND, String.format("Profile '%s' was not found", profileId));
}

public static UserAdminException groupAlreadyExists(String name) {
return new UserAdminException(USER_ADMIN_GROUP_ALREADY_EXISTS, String.format("Group '%s' already exists", name));
}

public static UserAdminException groupNotFound(UUID groupId) {
return new UserAdminException(USER_ADMIN_GROUP_NOT_FOUND, String.format("Group '%s' was not found", groupId));
}

public static UserAdminException groupNotFound(String name) {
return new UserAdminException(USER_ADMIN_GROUP_NOT_FOUND, String.format("Group '%s' was not found", name));
}

public static UserAdminException announcementInvalidPeriod(Instant startDate, Instant endDate) {
return new UserAdminException(USER_ADMIN_ANNOUNCEMENT_INVALID_PERIOD,
String.format("Announcement end date '%s' must be after start date '%s'", endDate, startDate));
}

public static UserAdminException announcementOverlap(Instant startDate, Instant endDate) {
return new UserAdminException(USER_ADMIN_ANNOUNCEMENT_OVERLAP,
String.format("Announcement period [%s, %s] overlaps with an existing announcement", startDate, endDate));
}

public static UserAdminException of(UserAdminBusinessErrorCode errorCode, String message, Object... args) {
return new UserAdminException(errorCode, args.length == 0 ? message : String.format(message, args));
}

@NotNull
@Override
public UserAdminBusinessErrorCode getBusinessErrorCode() {
return errorCode;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package org.gridsuite.useradmin.server.service;

import org.gridsuite.useradmin.server.UserAdminApplicationProps;
import org.gridsuite.useradmin.server.UserAdminException;
import org.gridsuite.useradmin.server.error.UserAdminException;
import org.springframework.stereotype.Service;

import java.util.Objects;
Expand All @@ -31,7 +31,7 @@ public void assertIsAdmin() throws UserAdminException {
Set<String> userRoles = getCurrentUserRoles();

if (userRoles.isEmpty() || !userRoles.contains(userAdminApplicationProps.getAdminRole())) {
throw new UserAdminException(UserAdminException.Type.FORBIDDEN);
throw UserAdminException.forbidden();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package org.gridsuite.useradmin.server.service;

import lombok.AllArgsConstructor;
import org.gridsuite.useradmin.server.UserAdminException;
import org.gridsuite.useradmin.server.error.UserAdminException;
import org.gridsuite.useradmin.server.dto.Announcement;
import org.gridsuite.useradmin.server.entity.AnnouncementEntity;
import org.gridsuite.useradmin.server.entity.AnnouncementSeverity;
Expand All @@ -19,8 +19,6 @@
import java.util.Optional;
import java.util.UUID;

import static org.gridsuite.useradmin.server.UserAdminException.Type.*;

/**
* @author Abdelsalem Hedhili <abdelsalem.hedhili at rte-france.com>
*/
Expand All @@ -37,11 +35,11 @@ public Announcement createAnnouncement(Instant startDate,
AnnouncementSeverity severity) {
adminRightService.assertIsAdmin();
if (!startDate.isBefore(endDate)) { // internally compare in seconds
throw new UserAdminException(START_DATE_SAME_OR_AFTER_END_DATE);
throw UserAdminException.announcementInvalidPeriod(startDate, endDate);
}
// Start is inclusive, End is exclusive — [start, end)
if (announcementRepository.existsByStartDateLessThanAndEndDateGreaterThan(endDate, startDate)) {
throw new UserAdminException(OVERLAPPING_ANNOUNCEMENTS);
throw UserAdminException.announcementOverlap(startDate, endDate);
}
return announcementRepository.save(new AnnouncementEntity(startDate, endDate, message.trim(), severity)).toDto();
}
Expand Down
Loading