Document upload
Containers of user-uploaded documents — their roles, their locks, and the delete call that reports per-id results.
A container is a collection of documents a user uploaded, which the platform then indexes so they turn up in search. The library covers managing containers and listing their contents; the upload itself is not part of these endpoints.
Concept — a container
A named collection with an owner, readers, and a document count. It is a unit of sharing: rights are held on
the container, not on each file. currentUserRole tells you what the signed-in user may do with the one you are
looking at.
Roles decide what you may render
ContainerRole is "creator" | "owner" | "reader", and it comes back on every container as
currentUserRole — so an interface never has to guess:
| Role | May |
|---|---|
creator | Everything, including deleting |
owner | Update, manage documents |
reader | List and read |
Listing and creating
import { createAtomicClient } from "@sinequa/atomic";
import type { Container } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
/** Paginated, so show the total rather than implying the page is everything. */
export async function list(page = 1) {
const response = await client.api.containers.list({ page, pageSize: 20 });
console.log(`${response.containers.length} of ${response.total}`);
return response;
}
/** Owners and readers are principal ids — see Finding principals. */
export function create(name: string, owners: string[], readers: string[]) {
return client.api.containers.create({
name,
description: "",
owners,
readers,
tags: [],
});
}
export async function documentsOf(container: Container) {
const response = await client.api.containers.listDocuments(container.id, {
page: 1,
pageSize: 50,
});
for (const document of response.documents) {
// Note what a container document does NOT carry: no size, no source, no
// uploader. Only what the API returns is available.
console.log(document.name, document.createdAt);
}
return response;
}
/** Render controls from the role the server reported, not from a guess. */
export function mayEdit(container: Container) {
return container.currentUserRole === "creator" || container.currentUserRole === "owner";
}import { Injectable, inject, resource, signal } from "@angular/core";
import type { Container } from "@sinequa/atomic";
import { SINEQUA } from "./sinequa";
@Injectable({ providedIn: "root" })
export class ContainersService {
private readonly client = inject(SINEQUA);
readonly page = signal(1);
/** Paginated: show the total rather than implying the page is everything. */
readonly containers = resource({
params: () => ({ page: this.page() }),
loader: ({ params }) => this.client.api.containers.list({ page: params.page, pageSize: 20 }),
});
/** Owners and readers are principal ids — see Finding principals. */
create(name: string, owners: string[], readers: string[]) {
return this.client.api.containers.create({ name, description: "", owners, readers, tags: [] });
}
/** Render controls from the role the server reported, not from a guess. */
mayEdit(container: Container) {
return container.currentUserRole === "creator" || container.currentUserRole === "owner";
}
}import type { Container, ContainerListResponse } from "@sinequa/atomic";
import { useCallback, useEffect, useState } from "react";
import { ready, sinequa } from "./sinequa";
/** Render controls from the role the server reported, not from a guess. */
export function mayEdit(container: Container) {
return container.currentUserRole === "creator" || container.currentUserRole === "owner";
}
export function useContainers(page = 1) {
const [response, setResponse] = useState<ContainerListResponse>();
useEffect(() => {
let active = true;
ready
// Paginated: show the total rather than implying the page is everything.
.then(() => sinequa.api.containers.list({ page, pageSize: 20 }))
.then((next) => active && setResponse(next));
return () => {
active = false;
};
}, [page]);
/** Owners and readers are principal ids — see Finding principals. */
const create = useCallback(
(name: string, owners: string[], readers: string[]) =>
sinequa.api.containers.create({ name, description: "", owners, readers, tags: [] }),
[],
);
return { response, create };
}Use Finding principals to pick owners and readers, rather than asking for identifiers.
Deleting is bulk-only, and reports per id
There is no single-container route. deleteContainers takes an array and answers with what succeeded and what
did not:
const { succeeded, failed } = await client.api.containers.delete([a.id, b.id]);
for (const { containerId, error } of failed) {
console.error(`${containerId} was not deleted: ${error}`);
}So deleting three containers can leave one behind, and the promise still resolves. Report the failures rather than assuming the operation was atomic.
Locks during indexing
An upload or an indexing job holds the container. While it does, updateContainer and
deleteContainerDocuments throw an ApiError with status === 409:
import { ApiError } from "@sinequa/atomic";
try {
await client.api.containers.update(container.id, { name });
} catch (error) {
if (error instanceof ApiError && error.status === 409) {
notify("This container is being indexed. Try again in a moment.");
} else {
throw error;
}
}