Fix folders of project and creat update endpoint

This commit is contained in:
rayankonecny 2025-12-04 02:22:35 +00:00
parent 4afbe40da5
commit bf3e3c929b
21 changed files with 180 additions and 81 deletions

View file

@ -1,4 +1,5 @@
{ {
"java.compile.nullAnalysis.mode": "disabled", "java.compile.nullAnalysis.mode": "disabled",
"editor.fontFamily": "FiraCode Nerd Font Mono" "editor.fontFamily": "FiraCode Nerd Font Mono",
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable"
} }

21
eclipse-formatter.xml Normal file
View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<profiles version="14">
<profile kind="CodeFormatterProfile" name="MyProfile" version="14">
<!-- Sempre quebrar antes de anotações -->
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_annotation" value="insert" />
<!-- Quebra de linha após anotações (field, type, member, etc.) -->
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member" value="insert" />
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert" />
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert" />
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert" />
<!-- ESSENCIAL PARA RECORDS E PARÂMETROS -->
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="insert" />
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert" />
<!-- Outras pequenas melhorias -->
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120" />
</profile>
</profiles>

View file

@ -0,0 +1,33 @@
package models.requests;
import java.util.Set;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.With;
import io.swagger.v3.oas.annotations.media.Schema;
import models.enums.ProfileEnum;
@With
public record CreateUserRequest(
@Schema(description = "Name of the user", example = "John Doe", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "Name cannot be empty")
@Size(min = 3, max = 50, message = "Name must contain between 3 and 50 characters")
String name,
@Schema(description = "Email of the user", example = "rayanvix@gmail.com")
@Email(message = "Invalid email format")
@NotBlank(message = "Email cannot be blank")
@Size(min = 6, max = 50, message = "Email must contain between 6 and 50 characters")
String email,
@Schema(description = "Password of the user", example = "P@ssw0rd!", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "Password cannot be empty")
@Size(min = 6, max = 20, message = "Password must contain between 6 and 20 characters")
@NotBlank(message = "Password cannot be blank")
String password,
@Schema(description = "Profiles assigned to the user", example = "[\" ROLE_ADMIN \", \"ROLE_CUSTOMER \" ]", requiredMode = Schema.RequiredMode.REQUIRED)
Set<ProfileEnum> profiles) {
}

View file

@ -0,0 +1,30 @@
package models.requests;
import java.util.Set;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.With;
import io.swagger.v3.oas.annotations.media.Schema;
import models.enums.ProfileEnum;
@With
@Schema(description = "Request to update user")
public record UpdateUserRequest(
@Schema(description = "Name of the user", example = "John Doe", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 3, max = 50, message = "Name must contain between 3 and 50 characters")
String name,
@Schema(description = "Email of the user", example = "rayanvix@gmail.com")
@Email(message = "Invalid email format")
@Size(min = 6, max = 50, message = "Email must contain between 6 and 50 characters")
String email,
@Schema(description = "Password of the user", example = "P@ssw0rd!", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 6, max = 20, message = "Password must contain between 6 and 20 characters")
String password,
@Schema(description = "Profiles assigned to the user", example = "[\" ROLE_ADMIN \", \"ROLE_CUSTOMER \" ]", requiredMode = Schema.RequiredMode.REQUIRED)
Set<ProfileEnum> profiles) {
}

View file

@ -1,21 +0,0 @@
package models.requests;
import java.util.Set;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.With;
import io.swagger.v3.oas.annotations.media.Schema;
import models.enums.ProfileEnum;
@With
public record CreateUserRequest(
@Schema(description = "Name of the user", example = "John Doe", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "Name cannot be empty") @Size(min = 3, max = 50, message = "Name must contain between 3 and 50 characters") String name,
@Schema(description = "Email of the user", example = "rayanvix@gmail.com") @Email(message = "Invalid email format") @NotBlank(message = "Email cannot be blank") @Size(min = 6, max = 50, message = "Email must contain between 6 and 50 characters") String email,
@Schema(description = "Password of the user", example = "P@ssw0rd!", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "Password cannot be empty") @Size(min = 6, max = 20, message = "Password must contain between 6 and 20 characters") @NotBlank(message = "Password cannot be blank") String password,
@Schema(description = "Profiles assigned to the user", example = "[\" ROLE_ADMIN \", \"ROLE_CUSTOMER \" ]", requiredMode = Schema.RequiredMode.REQUIRED) Set<ProfileEnum> profiles) {
}

View file

@ -7,5 +7,6 @@ models/requests/CreateUserRequest.class
models/exceptions/ValidationException$FieldError.class models/exceptions/ValidationException$FieldError.class
models/exceptions/ValidationException.class models/exceptions/ValidationException.class
models/exceptions/StandardError$StandardErrorBuilder.class models/exceptions/StandardError$StandardErrorBuilder.class
models/requests/UpdateUserRequest.class
models/enums/ProfileEnum.class models/enums/ProfileEnum.class
models/exceptions/StandardError.class models/exceptions/StandardError.class

View file

@ -1,6 +1,7 @@
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/enums/ProfileEnum.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/enums/ProfileEnum.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/exceptions/ResourceNotFoundException.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/exceptions/ResourceNotFoundException.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/exceptions/StandardError.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/exceptions/StandardError.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/exceptions/ValidationException.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/exceptions/ValidationException.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/requests/CreateUserRequest.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/requests/CreateUserRequest.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/models/responses/UserResponse.java /home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/requests/UpdateUserRequest.java
/home/dev/projetos/Help Desk 2.0/hd-commons-lib/src/main/java/br/com/rayankonecny/hdcommoslib/models/responses/UserResponse.java

View file

@ -20,3 +20,19 @@ Accept: application/json
"password": "Pa$$w0rd", "password": "Pa$$w0rd",
"role": "user" "role": "user"
} }
#### Update an existing user (replace {{id}} or use a concrete id like 1)
PUT http://175.15.15.91:8080/api/users/692cca264e957945b99f731a
Content-Type: application/json
Accept: application/json
# Authorization: Bearer {{token}}
{
"id": "692cca264e957945b99f731a",
"name": "Edvaldo",
"email": "edinho@gmail.com",
"password": "123456",
"profiles": [
"ROLE_CUSTOMER"
]
}

View file

@ -7,6 +7,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -21,6 +22,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import models.exceptions.StandardError; import models.exceptions.StandardError;
import models.requests.CreateUserRequest; import models.requests.CreateUserRequest;
import models.requests.UpdateUserRequest;
import models.responses.UserResponse; import models.responses.UserResponse;
@Tag(name = "User Controller", description = "Controller responsible for user operations") @Tag(name = "User Controller", description = "Controller responsible for user operations")
@ -28,29 +30,38 @@ import models.responses.UserResponse;
public interface UserController { public interface UserController {
@Operation(summary = "Find User by ID", description = "Retrieve user information by user ID") @Operation(summary = "Find User by ID", description = "Retrieve user information by user ID")
@ApiResponses(value = { @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "User found successfully"),
@ApiResponse(responseCode = "200", description = "User found successfully"),
@ApiResponse(responseCode = "404", description = "User not found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))), @ApiResponse(responseCode = "404", description = "User not found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))), @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))), })
})
@GetMapping("/{id}") @GetMapping("/{id}")
ResponseEntity<UserResponse> findById( ResponseEntity<UserResponse> findById(
@Parameter(description = "User id", required = true, example = "6927c8c75efcfda14c3ea570") @PathVariable(name = "id") final String id); @Parameter(description = "User id", required = true, example = "6927c8c75efcfda14c3ea570")
@PathVariable(name = "id")
final String id);
@Operation(summary = "Save new user") @Operation(summary = "Save new user")
@ApiResponses(value = { @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "User created successfully"),
@ApiResponse(responseCode = "201", description = "User created successfully"),
@ApiResponse(responseCode = "400", description = "Invalid request data", content = @Content(mediaType = "application/json", schema = @Schema(implementation = StandardError.class))), @ApiResponse(responseCode = "400", description = "Invalid request data", content = @Content(mediaType = "application/json", schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = StandardError.class))) @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = StandardError.class))) })
})
@PostMapping @PostMapping
ResponseEntity<Void> save(@Valid @RequestBody final CreateUserRequest createUserRequest); ResponseEntity<Void> save(@Valid
@RequestBody
final CreateUserRequest createUserRequest);
@Operation(summary = "Find all users", description = "Retrieve a list of all users") @Operation(summary = "Find all users", description = "Retrieve a list of all users")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Users retrieved successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, array = @ArraySchema(schema = @Schema(implementation = UserResponse.class)))), @ApiResponse(responseCode = "200", description = "Users retrieved successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, array = @ArraySchema(schema = @Schema(implementation = UserResponse.class)))),
@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))), @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))), })
})
@GetMapping @GetMapping
ResponseEntity<List<UserResponse>> findAll(); ResponseEntity<List<UserResponse>> findAll();
@Operation(summary = "Update user information", description = "Update existing user details")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "User updated successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = UserResponse.class))),
@ApiResponse(responseCode = "400", description = "Bad request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "404", description = "User not found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
})
@PutMapping("/{id}")
ResponseEntity<UserResponse> update(@PathVariable(name = "id") final String id, @Valid @RequestBody final UpdateUserRequest updateUserRequest);
} }

View file

@ -27,28 +27,17 @@ public class ControllerExceptionHandler {
final HttpServletRequest request) { final HttpServletRequest request) {
return ResponseEntity.status(NOT_FOUND).body( return ResponseEntity.status(NOT_FOUND).body(
StandardError.builder() StandardError.builder().timestamp(now()).status(NOT_FOUND.value())
.timestamp(now()) .error(NOT_FOUND.getReasonPhrase()).message(ex.getMessage())
.status(NOT_FOUND.value()) .path(request.getRequestURI()).build());
.error(NOT_FOUND.getReasonPhrase())
.message(ex.getMessage())
.path(request.getRequestURI())
.build());
} }
@ExceptionHandler(MethodArgumentNotValidException.class) @ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<StandardError> handleMethodArgumentNotValidException( ResponseEntity<StandardError> handleMethodArgumentNotValidException(final MethodArgumentNotValidException ex,
final MethodArgumentNotValidException ex,
final HttpServletRequest request) { final HttpServletRequest request) {
var error = ValidationException var error = ValidationException.builder().timestamp(now()).status(NOT_FOUND.value())
.builder() .error("Validation Exception").message("Exception in validation attributes")
.timestamp(now()) .path(request.getRequestURI()).errors(new ArrayList<>()).build();
.status(NOT_FOUND.value())
.error("Validation Exception")
.message("Exception in validation attributes")
.path(request.getRequestURI())
.errors(new ArrayList<>())
.build();
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) { for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
error.addError(fieldError.getField(), fieldError.getDefaultMessage()); error.addError(fieldError.getField(), fieldError.getDefaultMessage());
@ -58,16 +47,12 @@ public class ControllerExceptionHandler {
} }
@ExceptionHandler(DataIntegrityViolationException.class) @ExceptionHandler(DataIntegrityViolationException.class)
ResponseEntity<StandardError> handleDataIntegrityViolationException( ResponseEntity<StandardError> handleDataIntegrityViolationException(final DataIntegrityViolationException ex,
final DataIntegrityViolationException ex, final HttpServletRequest request) { final HttpServletRequest request) {
return ResponseEntity.status(CONFLICT).body( return ResponseEntity.status(CONFLICT)
StandardError.builder() .body(StandardError.builder().timestamp(now()).status(CONFLICT.value())
.timestamp(now()) .error(CONFLICT.getReasonPhrase()).message(ex.getMessage())
.status(CONFLICT.value()) .path(request.getRequestURI()).build());
.error(CONFLICT.getReasonPhrase())
.message(ex.getMessage())
.path(request.getRequestURI())
.build());
} }
} }

View file

@ -4,9 +4,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import br.com.rayankonecny.userserviceapi.controller.UserController; import br.com.rayankonecny.userserviceapi.controller.UserController;
import br.com.rayankonecny.userserviceapi.service.UserService; import br.com.rayankonecny.userserviceapi.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import models.requests.CreateUserRequest; import models.requests.CreateUserRequest;
import models.requests.UpdateUserRequest;
import models.responses.UserResponse; import models.responses.UserResponse;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;
@ -35,4 +37,10 @@ public class UserControllerImpl implements UserController {
return ResponseEntity.ok().body(userService.findAll()); return ResponseEntity.ok().body(userService.findAll());
} }
@Override
public ResponseEntity<UserResponse> update(final String id, @Valid final UpdateUserRequest updateUserRequest) {
return ResponseEntity.ok().body(userService.update(id, updateUserRequest));
}
} }

View file

@ -2,9 +2,11 @@ package br.com.rayankonecny.userserviceapi.mapper;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import br.com.rayankonecny.userserviceapi.entity.User; import br.com.rayankonecny.userserviceapi.entity.User;
import models.requests.CreateUserRequest; import models.requests.CreateUserRequest;
import models.requests.UpdateUserRequest;
import models.responses.UserResponse; import models.responses.UserResponse;
import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE; import static org.mapstruct.NullValuePropertyMappingStrategy.IGNORE;
@ -17,4 +19,7 @@ public interface UserMapper {
@Mapping(target = "id", ignore = true) @Mapping(target = "id", ignore = true)
User fromRequest(CreateUserRequest createUserRequest); User fromRequest(CreateUserRequest createUserRequest);
@Mapping(target = "id", ignore = true)
User update(UpdateUserRequest updateUserRequest, @MappingTarget User entity);
} }

View file

@ -5,12 +5,15 @@ import java.util.List;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import br.com.rayankonecny.userserviceapi.entity.User;
import br.com.rayankonecny.userserviceapi.mapper.UserMapper; import br.com.rayankonecny.userserviceapi.mapper.UserMapper;
import br.com.rayankonecny.userserviceapi.repository.UserRepository; import br.com.rayankonecny.userserviceapi.repository.UserRepository;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import models.responses.UserResponse; import models.responses.UserResponse;
import models.exceptions.ResourceNotFoundException; import models.exceptions.ResourceNotFoundException;
import models.requests.CreateUserRequest; import models.requests.CreateUserRequest;
import models.requests.UpdateUserRequest;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -20,9 +23,7 @@ public class UserService {
private final UserMapper userMapper; private final UserMapper userMapper;
public UserResponse findById(final String id) { public UserResponse findById(final String id) {
return userMapper.fromEntity(userRepository.findById(id) return userMapper.fromEntity(find(id));
.orElseThrow(() -> new ResourceNotFoundException(
"Object not found! Id: " + id + ", Type: " + UserResponse.class.getSimpleName())));
} }
public void save(CreateUserRequest createUserRequest) { public void save(CreateUserRequest createUserRequest) {
@ -31,17 +32,24 @@ public class UserService {
} }
private void verifyIfEmailAlreadyEexists(final String email, final String id) { private void verifyIfEmailAlreadyEexists(final String email, final String id) {
userRepository.findByEmail(email) userRepository.findByEmail(email).filter(user -> !user.getId().equals(id)).ifPresent(user -> {
.filter(user -> !user.getId().equals(id))
.ifPresent(user -> {
throw new DataIntegrityViolationException("Email [" + email + "] already exists"); throw new DataIntegrityViolationException("Email [" + email + "] already exists");
}); });
} }
public List<UserResponse> findAll() { public List<UserResponse> findAll() {
return userRepository.findAll() return userRepository.findAll().stream().map(user -> userMapper.fromEntity(user)).toList();
.stream() }
.map(user -> userMapper.fromEntity(user))
.toList(); public UserResponse update(final String id, @Valid final UpdateUserRequest updateUserRequest) {
User entity = find(id);
verifyIfEmailAlreadyEexists(updateUserRequest.email(), id);
return userMapper.fromEntity(userRepository.save(userMapper.update(updateUserRequest, entity)));
}
private User find(final String id) {
return userRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(
"Object not found! Id: " + id + ", Type: " + UserResponse.class.getSimpleName()));
} }
} }