Guia canonico para publicar selects metadata-driven no
praxis-metadata-starter.
Endpoints canonicos:
POST /{resource}/options/filterGET /{resource}/options/by-ids?ids=1&ids=2POST /{resource}/option-sources/{sourceKey}/options/filterGET /{resource}/option-sources/{sourceKey}/options/by-ids?ids=a&ids=bForma publica de retorno:
options/filter retorna
Page<OptionDTO<...>>options/by-ids retorna
List<OptionDTO<...>>option-sources/* preserva a mesma forma publica de
OptionDTO{id,label,extra?}/{resource}/options/*
funcionarios, cargos,
departamentos/{resource}/option-sources/{sourceKey}/options/*
Regra pratica:
/{resource}/options/*option-sourcespublic class Funcionario {
@Id
private Long id;
@OptionLabel
private String nomeCompleto;
}Se nao houver anotacao, o framework aplica heuristicas como
getLabel, getNomeCompleto e
getNome.
option-sources vem do OptionSourceRegistry
exposto pelo service.
private static final OptionSourceRegistry OPTION_SOURCES = OptionSourceRegistry.builder()
.add(VwAnalyticsFolhaPagamento.class, new OptionSourceDescriptor(
"payrollProfile",
OptionSourceType.DISTINCT_DIMENSION,
ApiPaths.HumanResources.VW_ANALYTICS_FOLHA_PAGAMENTO,
"payrollProfile",
"payrollProfile",
"payrollProfile",
"payrollProfile",
List.of("competenciaBetween", "universo"),
new OptionSourcePolicy(true, true, "contains", 0, 25, 100, true, false, "label")
))
.build();
@Override
public OptionSourceRegistry getOptionSourceRegistry() {
return OPTION_SOURCES;
}/schemas/filteredQuando o ApiDocsController encontra um campo cujo nome
coincide com a source registrada para o resourcePath, ele
enriquece o schema com x-ui.optionSource.
Importante:
x-ui.optionSource.resourcePath e o path base do
recurso.../option-sources/{key}/options/filteroptionSource existe, ele e mais canonico do que
um endpoint legadoO praxis-ui-angular ja tem consumo explicito para
option-sources.
Fluxo real:
x-ui.optionSourceoptionSource.resourcePathfilterOptionSourceOptions(sourceKey, ...)getOptionSourceOptionsByIds(sourceKey, ids)Cobertura confirmada hoje:
ApiDocsController publica
resource.capabilities.optionSourcesoptionSourceExecucao atualmente implementada no starter:
DISTINCT_DIMENSIONCATEGORICAL_BUCKETTipos ainda nao implementados de ponta a ponta no executor JPA:
RESOURCE_ENTITYLIGHT_LOOKUPSTATIC_CANONICALoption-sources para dimensoes derivadasfilterField explicito quando necessariodependsOn para cascataexcludeSelfField=true quando a source nao deve se
autofiltrarOptionDTO, mantenha
optionLabelKey=label e optionValueKey=idAbstractCrudController ou
AbstractReadOnlyController e se o
OptionSourceRegistry está registrado no service.@UISchema tem nome idêntico à key do
descriptor. Use o browser dev tools para inspecionar
/schemas/filtered e ver se x-ui.optionSource
está presente.allowSearch: true na OptionSourcePolicy e que
o searchMode (ex.: “contains”) é suportado pelo
executor.defaultPageSize e maxPageSize na policy; o
frontend deve enviar page e size no
request.dependsOn lista campos corretos e que o filtro aplicado
inclui esses valores. Teste manualmente com
POST /option-sources/{key}/options/filter incluindo filtros
dependentes.cacheable: true se apropriado, ou otimize queries no
service (ex.: use índices no banco para distinct values).key nula) lançam
IllegalArgumentException na criação.Use ferramentas como Postman ou curl para testar endpoints:
Filtro de Opções:
curl -X POST "http://localhost:8080/api/resource/option-sources/universo/options/filter" \
-H "Content-Type: application/json" \
-d '{"search": "Empresa", "page": 0, "size": 10}'Esperado: Page<OptionDTO> com id,
label.
Reidratação por IDs:
curl "http://localhost:8080/api/resource/option-sources/universo/options/by-ids?ids=1&ids=2"Esperado: List<OptionDTO> para IDs
específicos.
Verificar Schema:
curl "http://localhost:8080/schemas/filtered"Procure por x-ui.optionSource nos campos.
org.praxisplatform.uischema para ver
resolução de descriptors.Para DISTINCT_DIMENSION, o executor pode usar
StatsFieldRegistry como backend:
// No service, registre no StatsFieldRegistry primeiro
statsFieldRegistry.register(VwAnalyticsFolhaPagamento.class, "universo");
// Depois, no OptionSourceRegistry
OptionSourceDescriptor descriptor = new OptionSourceDescriptor(
"universo",
OptionSourceType.DISTINCT_DIMENSION,
// ... outros campos
);Isso permite reaproveitar lógica de stats para options.
Para adicionar metadados (ex.: count de ocorrências):
// No service, sobrescreva filterOptionSourceOptions
@Override
public Page<OptionDTO> filterOptionSourceOptions(String sourceKey, GenericFilterDTO filter, Pageable pageable) {
// Lógica customizada
return options.map(option -> option.withExtra(Map.of("count", 42)));
}Campo antigo:
{
"campo": {
"endpoint": "/options/filter",
"valueField": "id",
"displayField": "label"
}
}Novo com OptionSource:
{
"campo": {
"x-ui.optionSource": {
"key": "campo",
"type": "RESOURCE_ENTITY",
"resourcePath": "/api/campo"
}
}
}Durante transição, publique ambos para compatibilidade.