Appearance
Contrato do lookup de papéis — GET /roles/lookup
Base URL
/api/v1. Erros emapplication/problem+json. Mesmo desenho da hidratação do diretório de usuários: user-directory-contract §3.2.
1. Para que serve
Alimentar o campo de referência de papel (type: 'reference' do gryd.ui): o combobox que grava só o id e mostra o nome. No admin-mfe ele substitui as listas de papéis escritas à mão — papéis de cada empresa na aba Acesso do usuário, papel padrão e mapeamentos de grupo do SSO.
Não é a listagem de papéis. GET /roles e GET /admin/tenants/{tenantId}/roles continuam sendo as telas de administração: devolvem o RoleDto com as permissões de cada papel e aceitam ordenação configurável. Usá-las num combobox carrega a matriz de permissões a cada tecla; e, para mostrar o nome de papéis já gravados, obrigava a tela a paginar todos os papéis da empresa (ou a mostrar o id cru do que ficava fora da 1ª página).
listagem (/roles, /admin/tenants/{id}/roles) | lookup (…/roles/lookup) | |
|---|---|---|
| Item | RoleDto com permissões | { id, name, description } |
| Ordenação | configurável | por nome, fixa |
pageSize máximo | 100 | 50 |
| Nome já gravado | paginar tudo | ?ids= em uma chamada |
| Permissão | a mesma do trilho | a mesma do trilho (§4) |
2. Rotas
| Trilho | Rota | Tenant lido | Permissão |
|---|---|---|---|
| tenant | GET /api/v1/roles/lookup | o do token | read:roles |
| admin | GET /api/v1/admin/tenants/{tenantId}/roles/lookup | o da rota | admin:system |
As duas rotas têm o mesmo contrato. Em nenhuma delas o cliente manda o tenant por query string.
3. Requisição
GET /api/v1/roles/lookup?search=gest&page=1&pageSize=20&includeSystemRoles=true
GET /api/v1/roles/lookup?ids=3f1c…,9a72…| Parâmetro | Default | Regras |
|---|---|---|
search | ausente | casa o nome do papel, em qualquer posição, sem distinguir maiúsculas nem acentos (gestao acha Gestão). Não casa a descrição. % e _ são texto, não curinga. Máx. 100 caracteres |
page | 1 | 1-based. Ignorado com ids |
pageSize | 20 | máximo 50; acima disso é 400. Ignorado com ids |
includeSystemRoles | true | false esconde papéis de sistema da busca. Ignorado com ids (§3.2) |
ids | ausente | hidratação: resolve estes ids em vez de buscar. Repetido (ids=a&ids=b) ou separado por vírgula (ids=a,b), com o mesmo resultado; duplicados ignorados; vazio = ausente. Máximo 50 ids distintos; acima disso, ou com um id malformado, 400 |
Não existe sortBy, sortDirection nem includeDeleted: a ordem é por nome e papel excluído nunca aparece.
3.1 Busca (sem ids)
Papéis do tenant, ordenados por nome, paginados. O envelope é o PagedResult<T> de sempre, com hasNext:
jsonc
{
"items": [
{ "id": "3f1c…", "name": "Admin", "description": "Administra a empresa" },
{ "id": "9a72…", "name": "Gestão Financeira", "description": null }
],
"totalCount": 12, "pageNumber": 1, "pageSize": 20,
"totalPages": 1, "hasNext": false, "hasPrevious": false,
"scope": null
}3.2 Hidratação (?ids=)
A busca responde "que papel eu posso escolher?". Quando a tela já tem o id (vínculo gravado, papel padrão do SSO, mapeamento de grupo), a pergunta é outra: "qual o nome deste id?".
GET /api/v1/roles/lookup?ids=3f1c…,9a72…,c0de…jsonc
{
"items": [
{ "id": "3f1c…", "name": "Admin", "description": "Administra a empresa" },
{ "id": "9a72…", "name": "Vendedor", "description": null }
],
"totalCount": 2, "pageNumber": 1, "pageSize": 3,
"totalPages": 1, "hasNext": false, "hasPrevious": false,
"scope": null
}- Uma chamada para a tela inteira. Junte os ids de todos os componentes (o
getManydo react-admin) e faça uma requisição; com mais de 50 ids distintos, quebre em lotes de 50. - Uma página só. Todo id encontrado volta na página 1;
pageepageSizenão se aplicam, e opageSizeda resposta é a quantidade de ids distintos pedidos. SemCOUNTe numa consulta só. - Papel de sistema vem sempre, mesmo com
includeSystemRoles=false: um papel já gravado num vínculo precisa mostrar o nome, ainda que a busca não o ofereça. - Id ausente da resposta = "Registro indisponível". Papel de outro tenant, excluído ou inexistente simplesmente não vem: status 200, sem 404 e sem nada que diferencie um caso do outro.
ids+search= AND (a interseção). Nenhum dos dois é ignorado em silêncio.- Não existe
isActive. Papel não tem esse estado: ou existe no tenant, ou não vem.
ts
// junta os ids da tela e hidrata em lotes de 50
const chunks = chunk([...new Set(ids)], 50);
const pages = await Promise.all(chunks.map(c =>
api.get(`/admin/tenants/${tenantId}/roles/lookup`, { params: { ids: c.join(",") } })));
const byId = new Map(pages.flatMap(p => p.items).map(r => [r.id, r]));
// byId.get(id) === undefined ⇒ "Registro indisponível"3.3 O item
| Campo | Tipo | Observação |
|---|---|---|
id | uuid | o valor que a tela grava |
name | string | nome do papel |
description | string | null | descrição do papel; null quando não há (ou está em branco). O campo sempre vem |
Sem permissões, sem isSystemRole, sem tenantId. O que o combobox precisa é nome e descrição; o resto é da tela de administração.
4. Permissão
Cada trilho usa a permissão da listagem do mesmo trilho. Nada novo para conceder:
| Rota | Abre com |
|---|---|
GET /roles/lookup | read:roles (no tenant do token) |
GET /admin/tenants/{tenantId}/roles/lookup | admin:system |
⚠️ Assimetria registrada, sem mudança nesta entrega. O admin de papéis (/admin/tenants/{tenantId}/roles e agora o /lookup) aceita só admin:system. Já o admin de tenants (GET /admin/tenants, GET /admin/tenants/{id}) aceita também manage:all-tenants, e o de usuários (/admin/users/*) aceita também manage:all-users. Resultado prático: um administrador global com escopo restrito (manage:all-users sem admin:system) edita os vínculos de um usuário, mas recebe 403 ao buscar ou hidratar os papéis da empresa. Se o admin-mfe for usado por esse perfil, a aba Acesso precisa tratar o 403 (ou o backend precisa abrir o admin de papéis a manage:all-*, o que é uma decisão à parte).
5. Erros
| Situação | HTTP | code |
|---|---|---|
| sem token | 401 | — |
| sem a permissão do trilho (§4) | 403 | — |
pageSize fora de 1–50, page < 1, search > 100 | 400 | validação |
mais de 50 ids distintos; ids com valor que não é um id | 400 | validação |
tenantId da rota que não é um uuid | 404 | — (a rota não casa) |
Ramifique sempre pelo code; title e detail são localizados (en, pt-BR, es-ES).
O endpoint não tem rate limit próprio (o do diretório existe porque a lista de pessoas é o alvo de enumeração). Mesmo assim, faça debounce de ~300 ms na busca e cancele a requisição anterior.