openhands / src /hooks /query /use-cloud-current-user-id.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
9b906ea verified
Raw History Blame Contribute Delete
3.47 kB
import { useQueries } from "@tanstack/react-query";
import { getCloudOrganizationMe } from "#/api/cloud/organization-service.api";
import {
useActiveBackend,
useActiveBackendContext,
} from "#/contexts/active-backend-context";
import { useAllCloudOrganizations } from "./use-cloud-organizations";
/**
* Resolve the current user's `user_id` per cloud backend with one
* `/api/organizations/{orgId}/me` call per backend (NOT one per org).
*
* The cloud contract: `/me` returns `{ org_id, user_id, … }`. `user_id`
* is identical regardless of which org you ask, so we make a single
* call per backend.
*
* Path-param rule: when `backend.id === active.backend.id` and
* `active.orgId` is set, the call uses **that** orgId — i.e. `/me`
* always tracks the currently selected environment for the active
* backend. For inactive backends (or when no org is selected yet), the
* first org is used as a sentinel just to obtain `user_id`. This
* matches the requirement that `/me` reflect the selected org for the
* active environment, while still supporting the personal-workspace
* label across non-active backends in the dropdown.
*
* The query key includes `active.orgId`, so picking a different org
* via `setActive` re-keys this query and refetches `/me` with the new
* active orgId.
*/
export function useCloudCurrentUserId(): Record<
string,
{ isLoading: boolean; userId: string | null }
> {
const { backends } = useActiveBackendContext();
const active = useActiveBackend();
const cloudOrgs = useAllCloudOrganizations();
const targets: {
backendId: string;
connectionRevision: number;
orgIdForMe: string;
}[] = [];
for (const backend of backends) {
if (backend.kind === "cloud") {
const entry = cloudOrgs[backend.id];
// Prefer the active org when this backend IS the active one and
// an org has been selected; otherwise fall back to the first org
// we know about for that backend.
const preferredOrgId =
backend.id === active.backend.id && active.orgId
? active.orgId
: (entry?.orgs[0]?.id ?? null);
if (preferredOrgId) {
targets.push({
backendId: backend.id,
connectionRevision: backend.connectionRevision ?? 0,
orgIdForMe: preferredOrgId,
});
}
}
}
const results = useQueries({
queries: targets.map(({ backendId, connectionRevision, orgIdForMe }) => {
const backend = backends.find((b) => b.id === backendId);
return {
// `orgIdForMe` is included in the query key so re-resolving the
// active org also re-keys this query → React Query refetches
// automatically without relying on explicit invalidation.
queryKey: [
"cloud-current-user",
backendId,
orgIdForMe,
connectionRevision,
] as const,
queryFn: async () => {
if (!backend) return { orgId: orgIdForMe, userId: "" };
return getCloudOrganizationMe(orgIdForMe, backend);
},
enabled: !!backend,
staleTime: 1000 * 60 * 5,
retry: false,
meta: { disableToast: true },
};
}),
});
const out: Record<string, { isLoading: boolean; userId: string | null }> = {};
targets.forEach((target, index) => {
const q = results[index];
out[target.backendId] = {
isLoading: q.isLoading,
userId: q.data?.userId ?? null,
};
});
return out;
}