@sinequa/atomic

Finding principals

Search-as-you-type over users and groups — for assigning owners and readers, with pagination and cancellation.

When a user has to pick people — the owners of a container, the readers of a resource, somebody to impersonate — you need a directory lookup, not a document search.

one call
const { principals, pagination } = await client.api.principal.search({
  offset: 0,
  limit: 20,
  search: "ali",
});

Concept — a security principal

A user or a group. Both can hold rights, so both appear in the same results, distinguished by isGroup. A picker that only offers users cannot express "everyone in Legal may read this".

What comes back

Prop

Type

A people picker

Same discipline as autocomplete: debounce, and cancel. This call takes an AbortSignal directly, which makes cancellation clean.

Debounced, cancelled, users and groups together
src/people-picker.ts
import { createAtomicClient } from "@sinequa/atomic";
import type { SecurityPrincipal } from "@sinequa/atomic";

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

await client.initializeConfig();
await client.auth.login();

let timer: ReturnType<typeof setTimeout> | undefined;
let inFlight: AbortController | undefined;

export function onInput(
  text: string,
  render: (principals: SecurityPrincipal[], total: number) => void,
) {
  clearTimeout(timer);
  // The user keeps typing: abandon the request that is already out.
  inFlight?.abort();

  if (text.length < 2) {
    render([], 0);
    return;
  }

  timer = setTimeout(async () => {
    inFlight = new AbortController();

    try {
      const response = await client.api.principal.search(
        { offset: 0, limit: 20, search: text },
        inFlight.signal,
      );

      render(response.principals, response.pagination.total);
    } catch (error) {
      // An abort is the expected outcome of typing, not a failure to report.
      if (error instanceof DOMException && error.name === "AbortError") return;
      throw error;
    }
  }, 200);
}

/** Groups and users are both selectable; label them so the choice is informed. */
export function label(principal: SecurityPrincipal) {
  return `${principal.isGroup ? "Group" : "User"}: ${principal.longName || principal.name}`;
}

Which lookup to use

Three calls touch principals, and they are not interchangeable:

CallEndpointFor
principal.searchsecurity.principalSearch-as-you-type. Paginated, cancellable, users and groups.
principal.listprincipal/listBrowsing, with isuser/isgroup filters. Different response shape.
principal.byIdssecurity.principalResolving domain|id identities (a container's owners/readers) to names.
principal.userid(s)principal?action=useridResolving identifiers you already hold.

For a picker, search is the one: it is the only one with pagination and an AbortSignal.

Pitfalls

What's next

On this page