Este arquivo virou registro histórico de uma correção de plataforma
já implementada e validada. Ele é útil para auditoria técnica e para
reconstruir a causa raiz original do problema de
stats/*.
Não use este texto como fonte primária do contrato atual de stats; o contrato vivo está no código, nos testes e nos guias/specs sincronizados do starter.
AbstractCrudController publica
response schema explícito para group-by,
timeseries e distribution.ApiDocsController foi endurecido para
seleção consistente de content e resolução de wrappers de
stats.ApiDocsController e
DomainCatalogController passaram a compartilhar a política
de resolução via OpenApiDocsSupport.catalog e /schemas/filtered.quickstart foi validado com
StatsSchemaSmokeHttpTest, confirmando
BUILD SUCCESS para request/response schema e coerência de
schemaLinks.response schema estrutural de
/schemas/filtered para responseSchema de
/schemas/catalog; o fluxo correto segue sendo
catalog para discovery e filtered para
estrutura.SchemaRetrievalService permanece acoplado apenas a
/schemas/filtered, e a documentação do
praxis-config-starter agora explicita que
/schemas/catalog não substitui resolução estrutural.Corrigir em nível de plataforma a inconsistência entre:
POST /stats/group-byPOST /stats/timeseriesPOST /stats/distribution/schemas/filtered/schemas/catalogA meta não é apenas fazer o frontend funcionar. A meta é restaurar um contrato canônico, verificável e consistente entre a documentação OpenAPI, os schemas derivados e os consumidores metadata-driven.
O starter já possui DTOs explícitos e padronizados para stats:
GroupByStatsResponseTimeSeriesStatsResponseDistributionStatsResponseTambém já existe o wrapper canônico
RestApiResponse<T>.
Ou seja, o problema não é falta de modelagem de domínio.
Os endpoints de stats em AbstractCrudController publicam
exemplos no 200, mas não publicam explicitamente o
schema da resposta no conteúdo OpenAPI.
Na prática, isso permite que o OpenAPI final preserve:
components.schemas.RestApiResponseGroupByStatsResponsecomponents.schemas.GroupByStatsResponsemas perca o elo canônico:
paths -> ... -> responses -> 200 -> content -> application/json -> schemaQuando esse elo some, /schemas/filtered deixa de
conseguir resolver schemaType=response.
stats/*A investigação mostrou que isso não afeta só
vw-perfil-heroi/group-by. O padrão é repetido em toda a
família stats/*.
Na superfície pública verificada, os casos sem schema de
resposta estavam concentrados exclusivamente em
stats/*.
/schemas/catalog e /schemas/filtered não
estão operando com a mesma garantia contratualHoje o catálogo:
responseSchemaschemaLinks.responsemas o filtered:
400 quando esse vínculo não existeIsso significa que o catálogo pode expor um link contratual que o endpoint estrutural não consegue honrar.
Os dois controladores não resolvem grupo/documento da mesma forma:
ApiDocsController resolve grupo a partir do path real
solicitadoDomainCatalogController resolve grupo com uma
estratégia mais amplaMesmo que isso não fosse a causa raiz principal, isso já é um problema arquitetural porque permite inconsistência entre duas superfícies derivadas do mesmo contrato.
Garantir que todo endpoint stats/* publique um
response schema OpenAPI explícito, estável e resolvível por
/schemas/filtered.
Garantir que /schemas/catalog e
/schemas/filtered derivem suas respostas a partir de
premissas compatíveis e testadas.
Garantir a seguinte propriedade:
/schemas/catalog expõe
schemaLinks.response para uma operação/schemas/filtered deve conseguir resolver esse
response schemaEssa propriedade precisa virar contrato de plataforma, não convenção informal.
A solução correta começa na publicação OpenAPI dos endpoints de stats, não no frontend e não em fallbacks oportunistas do catálogo.
findResponseSchema(...) pode ter heurísticas defensivas,
mas stats não deve depender de inferência por nome ou estrutura parcial.
O contrato deve ser explícito.
/schemas/catalog e /schemas/filtered podem
ter finalidades diferentes, mas não podem divergir sobre a existência do
schema estrutural da mesma operação.
Não basta testar cada endpoint isoladamente. É necessário testar a coerência entre:
/schemas/catalog/schemas/filteredstats/group-by, stats/timeseries
e stats/distributionresponse schema em
/schemas/filtered/schemas/catalogDefinir explicitamente o contrato-alvo para cada operação de stats:
group-by retorna
RestApiResponse<GroupByStatsResponse>timeseries retorna
RestApiResponse<TimeSeriesStatsResponse>distribution retorna
RestApiResponse<DistributionStatsResponse>Também registrar a expectativa de documentação:
requestBody.content.application/json.schema
presenteresponses.200.content.application/json.schema
presenteexamples preservadoscomponents.schemasSaída esperada da fase:
Ajustar AbstractCrudController para que cada
@ApiResponse(responseCode = "200") de stats publique
explicitamente o schema do wrapper correto.
Direção recomendada:
mediaType = "application/json"schema = @Schema(implementation = ...)examples já existentes no mesmo
@ContentPonto crítico:
Object.classcomponents.schemasArquivos-alvo:
src/main/java/org/praxisplatform/uischema/controller/base/AbstractCrudController.javaresponse schema em
/schemas/filteredMesmo com a publicação corrigida, o ApiDocsController
deve ficar mais robusto.
Trabalho recomendado:
findResponseSchema(...)application/json,
*/* e primeiro content disponível quando segurox-ui.responseSchema quando
explicitamente publicadoObjetivo:
Arquivos-alvo:
src/main/java/org/praxisplatform/uischema/controller/docs/ApiDocsController.javaHoje os dois controladores podem consultar documentos/grupos diferentes para a mesma operação.
Direção recomendada:
DomainCatalogController usar a mesma lógica de
path real quando pathFilter estiver presenteOpção preferida:
Arquivos-alvo:
src/main/java/org/praxisplatform/uischema/controller/docs/ApiDocsController.javasrc/main/java/org/praxisplatform/uischema/controller/docs/DomainCatalogController.javacontroller/docs ou util/schemas/catalogO catálogo não deve publicar schemaLinks.response
cegamente.
Duas opções:
Recomendação:
Arquivos-alvo:
src/main/java/org/praxisplatform/uischema/controller/docs/DomainCatalogController.javaApiDocsControllerAdicionar cenários para:
schemaType=response com group-byschemaType=response com timeseriesschemaType=response com distributionexamples e schema
explícitoRestApiResponse<...>Também adicionar um teste negativo:
DomainCatalogControllerAdicionar cenários para:
schemaLinks.response consistente com a operaçãoAdicionar um teste de integração do starter com esta propriedade:
schemaLinks.response emitido deve ser resolvido com
sucesso por /schemas/filteredEsse é o teste mais importante do lote porque fecha a regressão estrutural.
Arquivos-alvo:
src/test/java/org/praxisplatform/uischema/controller/docs/ApiDocsControllerTest.javasrc/test/java/org/praxisplatform/uischema/controller/docs/DomainCatalogControllerTest.javaDepois da correção no starter:
stats/* continuam
operandoresponse schema/schemas/catalog e
/schemas/filtered convergem nos casos
funcionarios, vw_perfil_heroi e
vw_indicadores_incidentesValidações mínimas:
GET /schemas/filtered?...schemaType=request continua
200GET /schemas/filtered?...schemaType=response passa a
200GET /schemas/catalog?...stats... continua trazendo
request/response/examplesArquivos/superfícies-alvo:
praxis-api-quickstart consumindo a versão corrigida do
starterSe houver fallback temporário no frontend para usar
responseSchema do catálogo quando
/schemas/filtered falhar, esse fallback deve ser tratado
como contingência transitória.
Após a correção de plataforma:
/schemas/filtered como fonte estrutural
canônica/schemas/catalog como fonte
documental/discoveryResultado da auditoria de 2026-03-21:
praxis-ui-angular na feature
stats-examples já usa /schemas/filtered para
request/response e /schemas/catalog apenas
para discovery/exibição.praxis-config-starter usa
SchemaRetrievalService apontando somente para
/schemas/filtered.src/main/java/org/praxisplatform/uischema/controller/base/AbstractCrudController.java
schema do 200 em
group-by, timeseries,
distributionexamplessrc/main/java/org/praxisplatform/uischema/controller/docs/ApiDocsController.java
findResponseSchema(...)src/main/java/org/praxisplatform/uischema/controller/docs/DomainCatalogController.java
schemaLinks.responsesrc/test/java/org/praxisplatform/uischema/controller/docs/ApiDocsControllerTest.java
src/test/java/org/praxisplatform/uischema/controller/docs/DomainCatalogControllerTest.java
src/test/java/...
AbstractCrudControllerApiDocsControllerDomainCatalogControllerEssa ordem é importante porque força a correção a nascer no contrato e não no consumidor.
stats/* publica
responses.200.content.application/json.schemaexamples continuam presentescomponents.schemas/schemas/filteredschemaType=request continua resolvendoschemaType=response passa a resolver
group-by, timeseries e
distribution/schemas/catalogschemaLinks.response não aponta mais para um endpoint
estrutural quebradoAo explicitar schema no @ApiResponse, o
SpringDoc pode gerar nomes de wrapper diferentes dos atuais.
Mitigação:
components.schemasAo mexer em @Content, os exemplos podem desaparecer ou
ser sobrescritos.
Mitigação:
operationExamplesSe app.openapi.internal-base-url estiver apontando para
uma superfície OpenAPI diferente da pública, a inconsistência pode
continuar mascarada.
Mitigação:
stats/* publica response schema OpenAPI
explícito/schemas/filtered resolve request e response para
stats/schemas/catalog e /schemas/filtered ficam
coerentes para statsschemaLinks.responseEscopo da primeira PR:
AbstractCrudControllerApiDocsControllerEscopo da segunda PR: