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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.opendevstack.apiservice.projectusers.exception;

import org.opendevstack.apiservice.projectusers.controller.ProjectUserController;
import org.opendevstack.apiservice.projectusers.model.ValidationErrorResponse;
import org.opendevstack.apiservice.projectusers.model.BaseApiResponse;
import org.opendevstack.apiservice.projectusers.model.FieldError;
Expand Down Expand Up @@ -32,7 +33,7 @@
* messages.
*/
@Slf4j
@ControllerAdvice
@ControllerAdvice(assignableTypes = ProjectUserController.class)
public class GlobalExceptionHandler {

/**
Expand Down Expand Up @@ -69,17 +70,17 @@ public ResponseEntity<ValidationErrorResponse> handleMethodArgumentNotValidExcep
fieldErrors.add(fieldError);
});

String errorMessage = String.format(
ErrorMessages.REQUEST_VALIDATION_FAILED,
fieldErrors.size());

ValidationErrorResponse errorResponse = new ValidationErrorResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setErrorCode(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setFieldErrors(fieldErrors);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
String errorMessage = String.format(
ErrorMessages.REQUEST_VALIDATION_FAILED,
fieldErrors.size());

ValidationErrorResponse errorResponse = new ValidationErrorResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setErrorCode(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setFieldErrors(fieldErrors);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

/**
Expand All @@ -96,13 +97,13 @@ public ResponseEntity<BaseApiResponse> handleConstraintViolationException(
.map(this::formatConstraintViolation)
.toList();

String errorMessage = String.format(ErrorMessages.PARAMETER_VALIDATION_FAILED, String.join("; ", errors));
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
String errorMessage = String.format(ErrorMessages.PARAMETER_VALIDATION_FAILED, String.join("; ", errors));
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

/**
Expand All @@ -115,8 +116,8 @@ public ResponseEntity<BaseApiResponse> handleHttpMessageNotReadableException(

log.warn("Invalid request body: {}", ex.getMessage());

String errorMessage = ErrorMessages.INVALID_REQUEST_BODY;
String errorCode = ErrorCodes.PROJECT_USER_ERROR;
String errorMessage = ErrorMessages.INVALID_REQUEST_BODY;
String errorCode = ErrorCodes.PROJECT_USER_ERROR;

Throwable cause = ex.getCause();

Expand Down Expand Up @@ -172,15 +173,15 @@ public ResponseEntity<BaseApiResponse> handleMissingPathVariableException(

log.warn("Missing path variable: {}", ex.getMessage());

String errorMessage = String.format(
ErrorMessages.REQUIRED_PATH_PARAMETER_MISSING,
ex.getVariableName());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
String errorMessage = String.format(
ErrorMessages.REQUIRED_PATH_PARAMETER_MISSING,
ex.getVariableName());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

/**
Expand All @@ -192,15 +193,15 @@ public ResponseEntity<BaseApiResponse> handleMissingServletRequestParameterExcep

log.warn("Missing request parameter: {}", ex.getMessage());

String errorMessage = String.format(
ErrorMessages.REQUIRED_REQUEST_PARAMETER_MISSING,
ex.getParameterName(), ex.getParameterType());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
String errorMessage = String.format(
ErrorMessages.REQUIRED_REQUEST_PARAMETER_MISSING,
ex.getParameterName(), ex.getParameterType());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

/**
Expand All @@ -212,17 +213,17 @@ public ResponseEntity<BaseApiResponse> handleMethodArgumentTypeMismatchException

log.warn("Method argument type mismatch: {}", ex.getMessage());

String errorMessage = String.format(
ErrorMessages.PARAMETER_TYPE_CONVERSION_FAILED,
ex.getName(),
ex.getValue(),
ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "unknown");
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.INVALID_ROLE);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
String errorMessage = String.format(
ErrorMessages.PARAMETER_TYPE_CONVERSION_FAILED,
ex.getName(),
ex.getValue(),
ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "unknown");
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(errorMessage);
errorResponse.setError(ErrorCodes.INVALID_ROLE);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

/**
Expand All @@ -233,11 +234,11 @@ public ResponseEntity<BaseApiResponse> handleProjectNotFoundException(
ProjectNotFoundException ex) {

log.warn("Project not found: {}", ex.getMessage());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}

Expand All @@ -249,11 +250,11 @@ public ResponseEntity<BaseApiResponse> handleUserNotFoundException(
UserNotFoundException ex) {

log.warn("User not found: {}", ex.getMessage());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}

Expand Down Expand Up @@ -297,11 +298,11 @@ public ResponseEntity<BaseApiResponse> handleInvalidRoleException(
InvalidRoleException ex) {

log.warn("Invalid role: {}", ex.getMessage());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ex.getMessage());
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

Expand All @@ -313,12 +314,12 @@ public ResponseEntity<BaseApiResponse> handleAutomationPlatformException(
AutomationPlatformException ex) {

log.error("Automation platform error: {}", ex.getMessage(), ex);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(String.format(ErrorMessages.EXTERNAL_SERVICE_ERROR, ex.getMessage()));
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(errorResponse);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(String.format(ErrorMessages.EXTERNAL_SERVICE_ERROR, ex.getMessage()));
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(errorResponse);
}

/**
Expand All @@ -327,12 +328,12 @@ public ResponseEntity<BaseApiResponse> handleAutomationPlatformException(
@ExceptionHandler(ProjectUserException.class)
public ResponseEntity<BaseApiResponse> handleProjectUserException(ProjectUserException ex) {
log.error("Project user operation failed: {}", ex.getMessage(), ex);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(String.format(ErrorMessages.OPERATION_FAILED, ex.getMessage()));
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(String.format(ErrorMessages.OPERATION_FAILED, ex.getMessage()));
errorResponse.setError(ex.getErrorCode());
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}

/**
Expand All @@ -341,12 +342,12 @@ public ResponseEntity<BaseApiResponse> handleProjectUserException(ProjectUserExc
@ExceptionHandler(Exception.class)
public ResponseEntity<BaseApiResponse> handleGenericException(Exception ex) {
log.error("Unexpected error occurred: {}", ex.getMessage(), ex);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ErrorMessages.UNEXPECTED_ERROR);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
BaseApiResponse errorResponse = new BaseApiResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage(ErrorMessages.UNEXPECTED_ERROR);
errorResponse.setError(ErrorCodes.PROJECT_USER_ERROR);
errorResponse.setTimestamp(java.time.OffsetDateTime.now());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}

/**
Expand Down
36 changes: 29 additions & 7 deletions api-project/openapi/api-project.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
openapi: 3.0.3
openapi: 3.0.4
info:
title: ODS API Server
description: API documentation for ODS (Open DevStack) API Service
Expand Down Expand Up @@ -124,22 +124,44 @@ components:
projectKey:
type: string
description: Optional project key. If not provided, a unique key will be generated.
projectKeyPattern:
type: string
description: Optional pattern for generating the project key (e.g. 'SS%06d').
pattern: "^[A-Z]{2}[A-Z0-9]{1,8}$"
minLength: 3
maxLength: 10
projectName:
type: string
description: Name of the project.
pattern: "^[A-Za-z0-9 ]{0,80}$"
maxLength: 80
projectDescription:
type: string
description: Description of the project.
required:
- projectName
maxLength: 255
projectFlavor:
type: string
description: Flavor of the project. Either projectFlavor or configurationItem must be provided.
configurationItem:
type: string
description: Configuration item for the project. Either projectFlavor or configurationItem must be provided.
location:
type: string
description: Location of the project.
x2OdsAccount:
type: string
description: Technical account of the project.
owner:
type: string
description: Owner of the project.
oneOf:
- required: [projectFlavor]
- required: [configurationItem]
additionalProperties: false
CreateProjectResponse:
type: object
properties:
projectKey:
type: string
httStatus:
type: string
status:
type: string
projectFlavor:
Expand All @@ -153,4 +175,4 @@ components:
errorDescription:
type: string
location:
type: string
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.opendevstack.apiservice.project.model.CreateProjectRequest;
import org.opendevstack.apiservice.project.model.CreateProjectResponse;
import org.opendevstack.apiservice.project.exception.ProjectKeyGenerationException;
import org.opendevstack.apiservice.project.validation.ProjectRequestValidator;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -30,9 +31,12 @@ public class ProjectController implements ProjectsApi {

private final ProjectsFacade projectsFacade;

private final ProjectRequestValidator projectRequestValidator;

@PostMapping
@Override
public ResponseEntity<CreateProjectResponse> createProject(@Valid @RequestBody CreateProjectRequest createProjectRequest) {
projectRequestValidator.validate(createProjectRequest);
try {
return ResponseEntity
.status(HttpStatus.OK)
Expand Down
Loading
Loading