feat(types): tornar tipos configuráveis

- converte entidades de servidores para armazenar type/application/db como texto
- adiciona modelo e API para registrar/listar TypeOptions com normalização
- centraliza schema/data scripts para criar schema e seedar tipos e usuário padrão
master
Artur Oliveira 2025-12-16 17:39:56 -03:00
parent ed247c423e
commit 6d5a64be89
11 changed files with 187 additions and 26 deletions

View File

@ -0,0 +1,29 @@
package com.hitcommunications.servermanager.controllers;
import com.hitcommunications.servermanager.model.enums.TypeCategory;
import com.hitcommunications.servermanager.services.TypeOptionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/type-options")
@RequiredArgsConstructor
@Tag(name = "Type Options")
public class TypeOptionsController {
private final TypeOptionService typeOptionService;
@GetMapping("/{category}")
@Operation(summary = "Lista os valores disponíveis para um tipo de categoria (SERVER_TYPE, APPLICATION, DATABASE).")
public ResponseEntity<List<String>> list(@PathVariable TypeCategory category) {
return ResponseEntity.ok(typeOptionService.list(category));
}
}

View File

@ -5,15 +5,10 @@ import java.sql.Timestamp;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import com.hitcommunications.servermanager.model.enums.Applications;
import com.hitcommunications.servermanager.model.enums.DatabaseType;
import com.hitcommunications.servermanager.model.enums.ServersType;
import com.hitcommunications.servermanager.utils.ServerIdGenerator;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
@ -51,17 +46,14 @@ public class Servers {
@Column(nullable = false)
private String password;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private ServersType type;
@Column(nullable = false, length = 80)
private String type;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Applications application;
@Column(nullable = false, length = 80)
private String application;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private DatabaseType dbType;
@Column(nullable = false, length = 80, name = "db_type")
private String dbType;
@CreationTimestamp
private Timestamp createdAt;

View File

@ -0,0 +1,33 @@
package com.hitcommunications.servermanager.model;
import com.hitcommunications.servermanager.model.enums.TypeCategory;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Table(name = "tab_type_options", schema = "server-manager", uniqueConstraints = {
@UniqueConstraint(columnNames = {"category", "value"})
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TypeOption {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, updatable = false)
private Long id;
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 32)
private TypeCategory category;
@Column(nullable = false, length = 80)
private String value;
}

View File

@ -0,0 +1,7 @@
package com.hitcommunications.servermanager.model.enums;
public enum TypeCategory {
SERVER_TYPE,
APPLICATION,
DATABASE
}

View File

@ -0,0 +1,13 @@
package com.hitcommunications.servermanager.repositories;
import com.hitcommunications.servermanager.model.TypeOption;
import com.hitcommunications.servermanager.model.enums.TypeCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface TypeOptionRepository extends JpaRepository<TypeOption, Long> {
Optional<TypeOption> findByCategoryAndValue(TypeCategory category, String value);
List<TypeOption> findAllByCategoryOrderByValueAsc(TypeCategory category);
}

View File

@ -29,8 +29,6 @@ public class ServersService {
private final ServersRepository repo;
public ServerDTO create(NewServerDTO createDTO) {
ensureUniqueIpAndPort(createDTO.ip(), createDTO.port(), null);
Servers entity = mapper.toEntity(createDTO);
entity = repo.save(entity);
return mapper.toDTO(entity);
@ -166,8 +164,6 @@ public class ServersService {
Servers entity = repo.findById(id)
.orElseThrow(() -> new RuntimeException("Server not found with id: " + id));
ensureUniqueIpAndPort(updateDTO.ip(), updateDTO.port(), id);
mapper.partialUpdate(updateDTO, entity);
entity = repo.save(entity);
return mapper.toDTO(entity);
@ -180,11 +176,4 @@ public class ServersService {
repo.deleteById(id);
}
private void ensureUniqueIpAndPort(String ip, Integer port, String currentServerId) {
repo.findByIpAndPort(ip, port).ifPresent(existingServer -> {
if (currentServerId == null || !existingServer.getId().equals(currentServerId)) {
throw new RuntimeException("Server already exists with IP: " + ip + " and port: " + port);
}
});
}
}

View File

@ -0,0 +1,42 @@
package com.hitcommunications.servermanager.services;
import com.hitcommunications.servermanager.model.TypeOption;
import com.hitcommunications.servermanager.model.enums.TypeCategory;
import com.hitcommunications.servermanager.repositories.TypeOptionRepository;
import com.hitcommunications.servermanager.utils.TypeNormalizer;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class TypeOptionService {
private final TypeOptionRepository repository;
@Transactional
public void register(TypeCategory category, String rawValue) {
String normalized = TypeNormalizer.normalize(rawValue);
if (normalized == null) {
return;
}
repository.findByCategoryAndValue(category, normalized)
.orElseGet(() -> repository.save(
TypeOption.builder()
.category(category)
.value(normalized)
.build()
));
}
public List<String> list(TypeCategory category) {
return repository.findAllByCategoryOrderByValueAsc(category)
.stream()
.map(TypeOption::getValue)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,30 @@
package com.hitcommunications.servermanager.utils;
import java.text.Normalizer;
import java.util.Locale;
import java.util.regex.Pattern;
public final class TypeNormalizer {
private static final Pattern DIACRITICS = Pattern.compile("\\p{M}");
private TypeNormalizer() {
}
public static String normalize(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
if (trimmed.isEmpty()) {
return null;
}
String normalized = Normalizer.normalize(trimmed, Normalizer.Form.NFD);
normalized = DIACRITICS.matcher(normalized).replaceAll("");
normalized = normalized.replaceAll("[^A-Za-z0-9_\\- ]", "");
normalized = normalized.replaceAll("\\s+", "_");
return normalized.toUpperCase(Locale.ROOT);
}
}

View File

@ -6,6 +6,7 @@ spring:
init:
mode: always
schema-locations: classpath:schema.sql
data-locations: classpath:data.sql
datasource:
url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}

View File

@ -18,3 +18,26 @@ INSERT INTO "server-manager".tab_users (
CURRENT_TIMESTAMP
)
ON CONFLICT DO NOTHING;
INSERT INTO "server-manager".tab_type_options (id, category, value)
VALUES
(101, 'SERVER_TYPE', 'PRODUCTION'),
(102, 'SERVER_TYPE', 'HOMOLOGATION'),
(103, 'SERVER_TYPE', 'DATABASE'),
(201, 'APPLICATION', 'ASTERISK'),
(202, 'APPLICATION', 'HITMANAGER'),
(203, 'APPLICATION', 'HITMANAGER_V2'),
(204, 'APPLICATION', 'OMNIHIT'),
(205, 'APPLICATION', 'HITPHONE'),
(206, 'APPLICATION', 'CDR'),
(207, 'APPLICATION', 'FUNCIONALIDADE'),
(208, 'APPLICATION', 'VOICEMAIL'),
(301, 'DATABASE', 'MYSQL'),
(302, 'DATABASE', 'POSTGRESQL'),
(303, 'DATABASE', 'SQLSERVER'),
(304, 'DATABASE', 'ORACLE'),
(305, 'DATABASE', 'REDIS'),
(306, 'DATABASE', 'MONGODB'),
(307, 'DATABASE', 'MARIADB'),
(308, 'DATABASE', 'NONE')
ON CONFLICT DO NOTHING;

View File

@ -1 +1,3 @@
CREATE SCHEMA IF NOT EXISTS "server-manager";
CREATE SCHEMA IF NOT EXISTS "server-manager";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";