From 9e44e365aba0bd09fc0d695e0a20e0ac954c583d Mon Sep 17 00:00:00 2001 From: Artur Oliveira Date: Mon, 15 Dec 2025 18:02:52 -0300 Subject: [PATCH] feat: implement CRUD for Servers --- backend/postman_collection.json | 212 +++++++++++++++++- .../controllers/ServersController.java | 63 ++++++ .../servermanager/mappers/ServersMapper.java | 22 ++ .../model/dtos/NewServerDTO.java | 20 ++ .../servermanager/model/dtos/ServerDTO.java | 22 ++ .../repositories/ServersRepository.java | 17 ++ .../services/ServersService.java | 88 ++++++++ 7 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/controllers/ServersController.java create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/mappers/ServersMapper.java create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/model/dtos/NewServerDTO.java create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/model/dtos/ServerDTO.java create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/repositories/ServersRepository.java create mode 100644 backend/src/main/java/com/hitcommunications/servermanager/services/ServersService.java diff --git a/backend/postman_collection.json b/backend/postman_collection.json index a67c35b..ebd1ac1 100644 --- a/backend/postman_collection.json +++ b/backend/postman_collection.json @@ -1,7 +1,7 @@ { "info": { - "name": "Users API - Server Manager", - "description": "Collection de testes para a API de Usuários", + "name": "Server Manager API", + "description": "Collection de testes para a API de Usuários e Servidores", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ @@ -214,6 +214,209 @@ "response": [] } ] + }, + { + "name": "Servers", + "item": [ + { + "name": "Create Server", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Production DB Server\",\n \"ip\": \"192.168.1.100\",\n \"port\": 5432,\n \"user\": \"admin\",\n \"password\": \"admin123\",\n \"type\": \"PRODUCTION\",\n \"application\": \"DATABASE\",\n \"dbType\": \"POSTGRESQL\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/servers", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers" + ] + } + }, + "response": [] + }, + { + "name": "Create Server - Test 2", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Dev MySQL Server\",\n \"ip\": \"192.168.1.101\",\n \"port\": 3306,\n \"user\": \"root\",\n \"password\": \"root123\",\n \"type\": \"DEVELOPMENT\",\n \"application\": \"DATABASE\",\n \"dbType\": \"MYSQL\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/servers", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers" + ] + } + }, + "response": [] + }, + { + "name": "Get All Servers", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers" + ] + } + }, + "response": [] + }, + { + "name": "Get Server by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers/{{server_id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "{{server_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Get Server by Name", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers/name/Production DB Server", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "name", + "Production DB Server" + ] + } + }, + "response": [] + }, + { + "name": "Get Servers by Type", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers/type/PRODUCTION", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "type", + "PRODUCTION" + ] + } + }, + "response": [] + }, + { + "name": "Get Servers by Application", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers/application/DATABASE", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "application", + "DATABASE" + ] + } + }, + "response": [] + }, + { + "name": "Update Server", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Production DB Server Updated\",\n \"ip\": \"192.168.1.100\",\n \"port\": 5432,\n \"user\": \"admin\",\n \"password\": \"newpassword123\",\n \"type\": \"PRODUCTION\",\n \"application\": \"DATABASE\",\n \"dbType\": \"POSTGRESQL\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/servers/{{server_id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "{{server_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete Server", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}/api/servers/{{server_id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "servers", + "{{server_id}}" + ] + } + }, + "response": [] + } + ] } ], "variable": [ @@ -226,6 +429,11 @@ "key": "user_id", "value": "", "type": "string" + }, + { + "key": "server_id", + "value": "", + "type": "string" } ] } diff --git a/backend/src/main/java/com/hitcommunications/servermanager/controllers/ServersController.java b/backend/src/main/java/com/hitcommunications/servermanager/controllers/ServersController.java new file mode 100644 index 0000000..d564653 --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/controllers/ServersController.java @@ -0,0 +1,63 @@ +package com.hitcommunications.servermanager.controllers; + +import com.hitcommunications.servermanager.model.dtos.NewServerDTO; +import com.hitcommunications.servermanager.model.dtos.ServerDTO; +import com.hitcommunications.servermanager.model.enums.Applications; +import com.hitcommunications.servermanager.model.enums.ServersType; +import com.hitcommunications.servermanager.services.ServersService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/servers") +@RequiredArgsConstructor +public class ServersController { + + private final ServersService serversService; + + @PostMapping + public ResponseEntity create(@RequestBody @Valid NewServerDTO createDTO) { + return ResponseEntity.ok().body(serversService.create(createDTO)); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable String id) { + return ResponseEntity.ok().body(serversService.getById(id)); + } + + @GetMapping("/name/{name}") + public ResponseEntity getByName(@PathVariable String name) { + return ResponseEntity.ok().body(serversService.getByName(name)); + } + + @GetMapping("/type/{type}") + public ResponseEntity> getByType(@PathVariable ServersType type) { + return ResponseEntity.ok().body(serversService.getByType(type)); + } + + @GetMapping("/application/{application}") + public ResponseEntity> getByApplication(@PathVariable Applications application) { + return ResponseEntity.ok().body(serversService.getByApplication(application)); + } + + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.ok().body(serversService.getAll()); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable String id, @RequestBody @Valid NewServerDTO updateDTO) { + return ResponseEntity.ok().body(serversService.update(id, updateDTO)); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable String id) { + serversService.delete(id); + return ResponseEntity.noContent().build(); + } +} + diff --git a/backend/src/main/java/com/hitcommunications/servermanager/mappers/ServersMapper.java b/backend/src/main/java/com/hitcommunications/servermanager/mappers/ServersMapper.java new file mode 100644 index 0000000..ce462ea --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/mappers/ServersMapper.java @@ -0,0 +1,22 @@ +package com.hitcommunications.servermanager.mappers; + +import com.hitcommunications.servermanager.model.Servers; +import com.hitcommunications.servermanager.model.dtos.NewServerDTO; +import com.hitcommunications.servermanager.model.dtos.ServerDTO; +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValuePropertyMappingStrategy; + +import java.util.List; + +@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) +public interface ServersMapper { + Servers toEntity(NewServerDTO createDTO); + ServerDTO toDTO(Servers entity); + List toDTO(List entities); + + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + Servers partialUpdate(NewServerDTO updateDTO, @MappingTarget Servers entity); +} + diff --git a/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/NewServerDTO.java b/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/NewServerDTO.java new file mode 100644 index 0000000..3eaefe7 --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/NewServerDTO.java @@ -0,0 +1,20 @@ +package com.hitcommunications.servermanager.model.dtos; + +import com.hitcommunications.servermanager.model.enums.Applications; +import com.hitcommunications.servermanager.model.enums.DatabaseType; +import com.hitcommunications.servermanager.model.enums.ServersType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record NewServerDTO( + @NotBlank String name, + @NotBlank String ip, + @NotNull Integer port, + @NotBlank String user, + @NotBlank String password, + @NotNull ServersType type, + @NotNull Applications application, + @NotNull DatabaseType dbType +) { +} + diff --git a/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/ServerDTO.java b/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/ServerDTO.java new file mode 100644 index 0000000..4131cce --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/model/dtos/ServerDTO.java @@ -0,0 +1,22 @@ +package com.hitcommunications.servermanager.model.dtos; + +import com.hitcommunications.servermanager.model.enums.Applications; +import com.hitcommunications.servermanager.model.enums.DatabaseType; +import com.hitcommunications.servermanager.model.enums.ServersType; + +import java.sql.Timestamp; + +public record ServerDTO( + String id, + String name, + String ip, + Integer port, + String user, + ServersType type, + Applications application, + DatabaseType dbType, + Timestamp createdAt, + Timestamp updatedAt +) { +} + diff --git a/backend/src/main/java/com/hitcommunications/servermanager/repositories/ServersRepository.java b/backend/src/main/java/com/hitcommunications/servermanager/repositories/ServersRepository.java new file mode 100644 index 0000000..d0797ae --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/repositories/ServersRepository.java @@ -0,0 +1,17 @@ +package com.hitcommunications.servermanager.repositories; + +import com.hitcommunications.servermanager.model.Servers; +import com.hitcommunications.servermanager.model.enums.Applications; +import com.hitcommunications.servermanager.model.enums.ServersType; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface ServersRepository extends JpaRepository { + Optional findByName(String name); + List findByType(ServersType type); + List findByApplication(Applications application); + Optional findByIpAndPort(String ip, Integer port); +} + diff --git a/backend/src/main/java/com/hitcommunications/servermanager/services/ServersService.java b/backend/src/main/java/com/hitcommunications/servermanager/services/ServersService.java new file mode 100644 index 0000000..c5ffe13 --- /dev/null +++ b/backend/src/main/java/com/hitcommunications/servermanager/services/ServersService.java @@ -0,0 +1,88 @@ +package com.hitcommunications.servermanager.services; + +import com.hitcommunications.servermanager.mappers.ServersMapper; +import com.hitcommunications.servermanager.model.Servers; +import com.hitcommunications.servermanager.model.dtos.NewServerDTO; +import com.hitcommunications.servermanager.model.dtos.ServerDTO; +import com.hitcommunications.servermanager.model.enums.Applications; +import com.hitcommunications.servermanager.model.enums.ServersType; +import com.hitcommunications.servermanager.repositories.ServersRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ServersService { + private final ServersMapper mapper; + private final ServersRepository repo; + + public ServerDTO create(NewServerDTO createDTO) { + // Check if server with same IP and port already exists + repo.findByIpAndPort(createDTO.ip(), createDTO.port()).ifPresent(entity -> { + throw new RuntimeException("Server already exists with IP: " + createDTO.ip() + " and port: " + createDTO.port()); + }); + + Servers entity = mapper.toEntity(createDTO); + entity = repo.save(entity); + return mapper.toDTO(entity); + } + + public ServerDTO getById(String id) { + return repo.findById(id) + .map(mapper::toDTO) + .orElseThrow(() -> new RuntimeException("Server not found with id: " + id)); + } + + public ServerDTO getByName(String name) { + return repo.findByName(name) + .map(mapper::toDTO) + .orElseThrow(() -> new RuntimeException("Server not found with name: " + name)); + } + + public List getByType(ServersType type) { + return repo.findByType(type) + .stream() + .map(mapper::toDTO) + .toList(); + } + + public List getByApplication(Applications application) { + return repo.findByApplication(application) + .stream() + .map(mapper::toDTO) + .toList(); + } + + public List getAll() { + return repo.findAll() + .stream() + .map(mapper::toDTO) + .toList(); + } + + public ServerDTO update(String id, NewServerDTO updateDTO) { + Servers entity = repo.findById(id) + .orElseThrow(() -> new RuntimeException("Server not found with id: " + id)); + + // Check if IP/port combination already exists (excluding current server) + repo.findByIpAndPort(updateDTO.ip(), updateDTO.port()).ifPresent(existingServer -> { + if (!existingServer.getId().equals(id)) { + throw new RuntimeException("Server already exists with IP: " + updateDTO.ip() + " and port: " + updateDTO.port()); + } + }); + + mapper.partialUpdate(updateDTO, entity); + entity = repo.save(entity); + return mapper.toDTO(entity); + } + + public void delete(String id) { + if (!repo.existsById(id)) { + throw new RuntimeException("Server not found with id: " + id); + } + repo.deleteById(id); + } +} +