🏗️ Auto-Configuração do Praxis UI Schema

🏗️ Auto-Configuração do Praxis UI Schema

Status do documento

Este documento continua relevante para a auto-configuração viva do starter, mas mistura exemplos do baseline atual com referências de compatibilidade do core legado.

Leia assim:

📋 Visão Geral

O OpenApiUiSchemaAutoConfiguration é o “bootstrap” de todo o sistema Praxis UI Schema, implementando o padrão “Convention over Configuration” do Spring Boot para que desenvolvedores apenas adicionem a dependência e todo o sistema fique automaticamente disponível.

🎯 Objetivo Principal

Objetivo prático da auto-configuração: 1. adicionar a dependência no pom.xml 2. expor controllers compatíveis com o baseline atual do starter 3. deixar discovery, docs filtrados e infraestrutura interna disponíveis sem boilerplate manual

⚙️ Propriedades de Configuração

Propriedade Padrão Descrição
praxis.query.by-ids.max 200 Limite de IDs aceitos pelo endpoint GET /{resource}/by-ids. Ajuda a evitar consultas excessivas em ambientes corporativos.
praxis.pagination.max-size 200 Tamanho máximo de página aceito pelos endpoints paginados (/filter e /options/filter). Evita requisições muito grandes que possam degradar o sistema.
app.openapi.internal-base-url vazio Origem interna explícita para consultas server-side ao SpringDoc (/v3/api-docs). Útil quando a URL pública difere da URL acessível pelo processo da aplicação.

🏗️ Duas Auto-Configurações Complementares

O sistema Praxis possui duas auto-configurações que trabalham em conjunto:

Auto-Configuração Responsabilidade Execução
PraxisMetadataAutoConfiguration 🏗️ Infraestrutura base + Component Scan 1º - Fundação
OpenApiUiSchemaAutoConfiguration 🎨 Beans funcionais do UI Schema 2º - Funcionalidade

⚡ Ordem de Execução

  1. PraxisMetadataAutoConfiguration → Detecta componentes via @ComponentScan
  2. OpenApiUiSchemaAutoConfiguration → Registra beans específicos
  3. DynamicSwaggerConfig → Detectado via component scan, executa @PostConstruct
  4. Validação → Executada via @EventListener após startup completo

🔧 Beans da PraxisMetadataAutoConfiguration

🔢 BigDecimal OpenApiCustomizer

🏗️ Grupo “praxis-metadata-infra”

🌐 Grupo “application” (Fallback)

🔧 Beans da OpenApiUiSchemaAutoConfiguration

📡 RestTemplate (openApiUiSchemaRestTemplate)

📝 ObjectMapper (openApiUiSchemaObjectMapper)

🔍 CustomOpenApiResolver

📋 GenericSpecificationsBuilder

🎯 OpenApiGroupResolver (CRÍTICO)

🌐 ApiDocsController

📚 DomainCatalogController

🔄 Fluxo de Inicialização

graph TD
    A[Spring Boot Startup] --> B[Detecta @AutoConfiguration]
    B --> C[OpenApiUiSchemaAutoConfiguration]
    C --> D[Registra 6 Beans]
    D --> E[DynamicSwaggerConfig usa beans]
    E --> F[Cria GroupedOpenApi dinamicamente]
    F --> G[OpenApiGroupResolver recebe grupos via DI]
    G --> H[ApiDocsController pronto para uso]
    H --> I[Sistema funcional]

🎪 Arquitetura Completa de Auto-Configuração

┌─────────────────────────────────────────────┐
│     PraxisMetadataAutoConfiguration         │
│  🏗️ Infraestrutura + Component Scan        │
└─────────────────┬───────────────────────────┘
                  │ @ComponentScan detecta
                  ▼
┌─────────────────────────────────────────────┐
│  🔢 BigDecimal Customizer                   │
│  🏗️ Grupo "praxis-metadata-infra"          │  
│  🌐 Grupo "application" (fallback)          │
│  📦 @ComponentScan packages                 │
└─────────────────┬───────────────────────────┘
                  │ + registra beans específicos
                  ▼
┌─────────────────────────────────────────────┐
│    OpenApiUiSchemaAutoConfiguration         │
│  🎨 Beans funcionais do UI Schema           │
└─────────────────┬───────────────────────────┘
                  │ registra beans
                  ▼
┌─────────────────────────────────────────────┐
│  📡 RestTemplate (HTTP interno)             │
│  📝 ObjectMapper (JSON + JavaTime)         │  
│  🔍 CustomOpenApiResolver (schemas + UI)    │
│  📋 GenericSpecificationsBuilder (filtros)  │
│  🎯 OpenApiGroupResolver (resolução grupos) │
│  🌐 ApiDocsController (endpoints REST)      │
└─────────────────┬───────────────────────────┘
                  │ componentes detectados + beans injetados
                  ▼
┌─────────────────────────────────────────────┐
│       DynamicSwaggerConfig                  │
│       AbstractCrudController               │
│       PraxisMetadataProvider               │
│       Outros componentes detectados        │
└─────────────────────────────────────────────┘

🚀 Benefícios para Desenvolvedores

✅ Plug & Play

<!-- Apenas isso no pom.xml: -->
<dependency>
    <groupId>io.github.codexrodrigues</groupId>
    <artifactId>praxis-metadata-starter</artifactId>
</dependency>

✅ Zero Boilerplate

// Sem necessidade de configurar beans manualmente:
@Configuration
public class MyConfig {
    // ❌ NÃO precisa disso:
    // @Bean public RestTemplate restTemplate() { ... }
    // @Bean public ObjectMapper objectMapper() { ... }
    // @Bean public OpenApiGroupResolver resolver() { ... }
}

// ✅ Apenas use diretamente:
@ApiResource(ApiPaths.FUNCIONARIOS)
public class FuncionarioController extends AbstractCrudController<...> {
    // Tudo funciona automaticamente!
}

✅ Não Conflitante

⚙️ Customização Avançada

Definir a base interna do OpenAPI

Por padrão, os controllers internos de documentação tentam inferir a base atual da aplicação com ServletUriComponentsBuilder. Isso funciona bem quando a origem externa e a origem interna são equivalentes, mas pode falhar em topologias com proxy reverso.

Para eliminar ambiguidade, defina:

app.openapi.internal-base-url=http://localhost:4003

Ou via ambiente:

APP_OPENAPI_INTERNAL_BASE_URL=http://localhost:4003

Fluxo de resolução: 1. Se app.openapi.internal-base-url estiver preenchida, ela é usada como base. 2. Caso contrário, a base é inferida do contexto HTTP atual. 3. Em seguida, o starter concatena springdoc.api-docs.path e, quando aplicável, o nome do grupo OpenAPI.

Quando configurar explicitamente: - Render ou outras plataformas PaaS. - Docker Compose com porta pública diferente da porta interna. - Kubernetes com ingress/gateway. - Ambientes onde X-Forwarded-Host e a origem interna não representam o mesmo endpoint acessível pelo backend.

Exemplo final de URL construída:

http://localhost:4003/v3/api-docs
http://localhost:4003/v3/api-docs/api-human-resources-funcionarios

Sobrescrever ObjectMapper

@Configuration
public class MyCustomConfig {
    
    @Bean
    @Primary // substitui o bean padrão
    public ObjectMapper myObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // suas customizações específicas...
        return mapper;
    }
}

Sobrescrever RestTemplate

@Bean
@Primary
public RestTemplate myRestTemplate() {
    RestTemplate template = new RestTemplate();
    
    // Configurar timeouts:
    HttpComponentsClientHttpRequestFactory factory = 
        new HttpComponentsClientHttpRequestFactory();
    factory.setConnectTimeout(5000);
    factory.setReadTimeout(10000);
    template.setRequestFactory(factory);
    
    return template;
}

🔗 Integração com Spring Boot

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

org.praxisplatform.uischema.configuration.OpenApiUiSchemaAutoConfiguration
org.praxisplatform.uischema.configuration.PraxisMetadataAutoConfiguration

Ordem de Inicialização

  1. OpenApiUiSchemaAutoConfiguration registra beans base
  2. PraxisMetadataAutoConfiguration adiciona component scan
  3. DynamicSwaggerConfig usa beans para criar grupos
  4. Spring Boot completa inicialização
  5. Sistema pronto para uso

📊 Impacto na Performance

Documentação OpenAPI

Tempo de Resposta

❓ Troubleshooting

Bean não está sendo injetado

# Verificar se a auto-configuração está sendo detectada:
--debug

# Buscar logs:
[INFO] OpenApiUiSchemaAutoConfiguration matched:
   - @ConditionalOnMissingBean (types: org.springframework.web.client.RestTemplate)

Conflito de beans

// Se houver conflito, usar @Qualifier:
@Autowired
@Qualifier("openApiUiSchemaRestTemplate")
private RestTemplate restTemplate;

Debug de grupos criados

// No DynamicSwaggerConfig, os logs mostrarão:
[INFO] Bean GroupedOpenApi registrado: bean=funcionarios_ApiGroup, group=funcionarios, paths=/api/funcionarios/**

Falha ao buscar /v3/api-docs

Sintomas comuns: - erro ao montar URL como https://localhost:4003/v3/api-docs - falhas intermitentes apenas em produção - /schemas/filtered ou /schemas/catalog funcionando localmente, mas quebrando atrás de proxy

Causa típica: - a aplicação inferiu a base a partir da requisição externa, mas o processo Java precisa acessar outra origem internamente

Correção: - configure app.openapi.internal-base-url para a origem interna real do serviço - mantenha springdoc.api-docs.path coerente com a rota publicada pelo SpringDoc

Verificação: - a partir do ambiente da app, confirme que a URL final responde com o documento OpenAPI esperado

🤝 Complementaridade das Auto-Configurações

PraxisMetadataAutoConfiguration (Fundação)

OpenApiUiSchemaAutoConfiguration (Funcionalidade)

Como Trabalham Juntas

// 1. PraxisMetadataAutoConfiguration detecta via @ComponentScan:
@Configuration  // ← Detectado pelo component scan
public class DynamicSwaggerConfig {
    
    // 2. OpenApiUiSchemaAutoConfiguration fornece via DI:
    @Autowired 
    private OpenApiGroupResolver resolver; // ← Injetado automaticamente
}

Resultado Final

📚 Referências