Skip to content

Contrato do lookup de usuários do admin — GET /admin/users/lookup ​

Base URL /api/v1. Erros em application/problem+json. Trilho admin global: admin-scoping-guide §5. Seletor de pessoas das telas de negócio: user-directory-contract.

1. Para que serve — e quando usar o outro ​

Preencher o seletor de pessoas do admin da plataforma: o campo de referência (type: 'reference') que guarda o id e mostra o nome, no trilho de administração. O caso que motivou a rota é "adicionar usuários ao tenant" (aba Usuários do detalhe do tenant), que procura quem ainda não está naquele tenant, em toda a plataforma.

O diretório não responde isso: por definição ele não lista quem está fora do escopo, e o admin da plataforma só enxerga nele o próprio tenant (ou o grupo). E a listagem GET /admin/users é a tela de administração — carrega vínculos, papéis, permissões e app ids de cada linha — cara demais para um autocomplete e sem como esconder no servidor quem já está no tenant.

GET /users/directoryGET /admin/users/lookup
Pergunta"quem está no meu escopo?""quem existe na plataforma?"
Onde usarseletores das telas de negócioseletores do admin (trilho global)
Públicoqualquer membroadmin da plataforma
Permissãoread:user-directory (ou read:user-email, read:users)admin:system ou manage:all-users
Item{ id, fullName } (+ email a pedido){ id, name, description, isActive? }
Busca casa e-mail?nãosim
Escopotenant do token ou grupoplataforma

Regra prática: se a tela está no trilho /admin/... e o operador precisa achar gente de qualquer tenant, use o lookup. Em qualquer outra tela use o diretório. Não existe versão do lookup no trilho tenant.

2. Requisição ​

GET /api/v1/admin/users/lookup?search=ana&page=1&pageSize=20&excludeTenantId=7d2e…
ParâmetroDefaultRegras
searchausentecasa nome, sobrenome, o nome como exibido ou o e-mail, sem distinguir maiúsculas nem acentos (joao acha João). Máx. 128 caracteres. Ausente ⇒ lista desde o começo
page11-based
pageSize20máximo 50 — acima disso é 400
excludeTenantIdausentetira da busca quem tem vínculo com esse tenant (§3.1). Não se aplica à hidratação
idsausentehidratação: resolve estes ids em vez de buscar. Repetido ou separado por vírgula; duplicados ignorados; vazio = ausente. Máximo 50 ids distintos — acima disso, ou com um id malformado, 400. Ver §4

Não existe sortBy, sortDirection, includeDeleted nem isActive. A ordem é por nome; na busca só aparecem contas ativas e não excluídas.

3. Resposta — busca ​

Envelope PagedResult<T> de sempre (sem o bloco scope: a rota não é scope-capable):

jsonc
{
  "items": [
    { "id": "3f1c…", "name": "Ana Silva", "description": "ana.silva@acme.com" },
    { "id": "9a72…", "name": "", "description": "sem.nome@acme.com" }
  ],
  "totalCount": 42, "pageNumber": 1, "pageSize": 20,
  "totalPages": 3, "hasNext": true, "hasPrevious": false
}
  • name é o nome completo (firstName + lastName). Vem "" quando o cadastro não tem nome (login federado sem claims de perfil é o caminho comum): renderize o seu texto de fallback. O backend não devolve placeholder que não saberia localizar.
  • description é o e-mail — o que separa dois homônimos. O admin já vê o endereço na listagem.
  • isActive não vem na busca (ausente ⇒ ativo): a busca só devolve contas ativas.
  • Uma consulta leve por chamada — sem vínculos, papéis, permissões nem app ids.

3.1 excludeTenantId — "adicionar ao tenant" ​

GET /api/v1/admin/users/lookup?search=ana&excludeTenantId={tenantId}

Tira da busca todo mundo que já tem vínculo com esse tenant: vínculo ativo e também vínculo desativado (ou fora do período). É exatamente o conjunto que POST /tenants/{id}/users recusaria como "já vinculado", então o seletor não oferece uma escolha que o submit vai rejeitar. Quem teve o vínculo removido volta a aparecer — pode ser vinculado de novo. Com isso o front não precisa mais esconder os já vinculados no cliente.

4. Hidratação — ?ids= ​

Quando a tela já tem o id (o selecionado fora da página carregada, um formulário em edição, um chip), a pergunta é "qual o nome deste id?":

GET /api/v1/admin/users/lookup?ids=3f1c…,9a72…,c0de…
jsonc
{
  "items": [
    { "id": "3f1c…", "name": "Ana Silva", "description": "ana.silva@acme.com" },
    { "id": "9a72…", "name": "Bruno Costa", "description": "bruno@acme.com", "isActive": false }
  ],
  "totalCount": 2, "pageNumber": 1, "pageSize": 3,
  "totalPages": 1, "hasNext": false, "hasPrevious": false
}
  • Uma página só, sem COUNT. Todo id encontrado volta na página 1; page e pageSize não se aplicam e o pageSize da resposta é a quantidade de ids distintos pedidos. Mais de 50 ids distintos: quebre em lotes de 50.
  • Conta desativada vem com isActive: false — mostre o nome, não ofereça de novo. O campo não vem nos demais itens (ausente ⇒ ativo).
  • Excluído ou inexistente não vem — status 200, sem 404. Id ausente da resposta = "Registro indisponível".
  • ids + search = AND (a interseção). Nenhum dos dois é ignorado em silêncio.
  • excludeTenantId NÃO se aplica. O selecionado precisa continuar mostrando o nome mesmo que já tenha sido vinculado ao tenant; pode mandar o parâmetro junto, ele é ignorado na hidratação.
ts
// referência de usuários no admin: busca paginada + hidratação em lote
const search = (q: string, page = 1) =>
  api.get("/admin/users/lookup", { params: { search: q, page, pageSize: 20, excludeTenantId } });

const hydrate = async (ids: string[]) => {
  const chunks = chunk([...new Set(ids)], 50);
  const pages = await Promise.all(chunks.map(c =>
    api.get("/admin/users/lookup", { params: { ids: c.join(",") } })));
  return new Map(pages.flatMap(p => p.items).map(u => [u.id, u]));
  // map.get(id) === undefined ⇒ "Registro indisponível"
  // map.get(id)?.isActive === false ⇒ mostra o nome, não oferece de novo
};

5. Permissão ​

A mesma da listagem do admin de usuários: admin:system ou manage:all-users. Como no resto do trilho, o front decide pela sessão: capabilities.users.default === "global" ⟺ o trilho /admin/users (e este lookup) responde 200. Um admin de tenant (read:users) recebe 403 aqui — o seletor dele é o /users/directory.

6. Erros ​

SituaçãoHTTPcode
sem token401—
sem admin:system nem manage:all-users403—
pageSize > 50 ou < 1, page < 1, search > 128400validação
mais de 50 ids distintos; ids com valor que não é um id400validação
excludeTenantId que não é um id400binding

Ramifique sempre pelo code; title e detail são localizados (en, pt-BR, es-ES). Faça debounce de ~300 ms na digitação e cancele a requisição anterior.

Released under the MIT License.