Skip to content

Support users in multiple NDI Cloud organizations - #336

Merged
stevevanhooser merged 2 commits into
mainfrom
claude/intelligent-volta-3w728u
Oct 18, 2026
Merged

stevevanhooser merged 2 commits into
mainfrom
claude/intelligent-volta-3w728u

Conversation

@stevevanhooser

Copy link
Copy Markdown
Contributor

Summary

Port of VH-Lab/NDI-matlab#1037 (merged). An NDI Cloud user has one token but may belong to several organizations. Python kept only the first one (CloudConfig.org_id / NDI_CLOUD_ORGANIZATION_ID, with a "selection is not yet supported" warning) and used it everywhere — including as the destination of file uploads for datasets that belong to a different organization.

Organization list cached at login

  • ndi.cloud.auth.login caches the user's whole organization list (id, name, canUploadDataset) as JSON in NDI_CLOUD_ORGANIZATIONS — the same variable NDI-matlab uses. logout clears it (and any leftover NDI_CLOUD_ORGANIZATION_ID).
  • New in ndi/cloud/internal.py: normalizeOrganizations, setOrganizations, getOrganizations (cache, else users.me; refresh=True), chooseOrganization, userIsAdmin.
  • New ndi/cloud/ui/dialog.py: organizationLabels, selectOrganization (both were porting_deferred).

Choosing an organization

chooseOrganization: a named organization_name / organization_id, else the only qualifying organization, else ask, else CloudOrganizationError (carrying MATLAB's identifier, e.g. NDI:Cloud:OrganizationRequired) that lists the organizations by name and id. require_upload honours canUploadDataset and, as the server does, lets administrators upload to any of their organizations. MATLAB asks in a desktop dialog; Python asks on the console when stdin is a terminal (numbered from 1), or through a chooser callable — the Qt navigator passes its list dialog.

Datasets and uploads

  • createDataset and uploadDataset take organization_name / organization_id; a new dataset's organization comes from chooseOrganization(require_upload=True).
  • listDatasets() with no organization lists every page of every organization, tagging each dataset with organizationId / organizationName (NDI Cloud only lists per organization). With an organization it is unchanged. listAllDatasets() follows.
  • Bug fix: file uploads (uploadFilesForDatasetDocuments, zipForUpload, uploadSingleFile, sync) now use the organization that owns the dataset, read from its record as MATLAB's GetFileUploadURL does, instead of the login's first organization.
  • Navigator: the private cloud list covers all organizations, labelled [Org Name] when there is more than one; "Upload to Cloud" asks which organization.

Breaking changes

  • CloudConfig.org_id and NDI_CLOUD_ORGANIZATION_ID are removed.
  • ndi.cloud.auth.getActiveToken returns the token only.
  • authenticate's second output is deprecated (as in MATLAB): the user's only organization, or "" when there are several. Nothing in the repo reads it any more.

Bridge

Synced to NDI-matlab 293ec411, c24d338d (admin rule) and 9a3c972d (LoginDialog), all on NDI-matlab main via the merge:

  • New entries: chooseOrganization, getOrganizations, setOrganizations, normalizeOrganizations, userIsAdmin.
  • Ported: selectOrganization, organizationLabels. Retired: resolveOrganizationId (deleted in MATLAB).
  • Updated signatures: uploadDataset, createDataset, listDatasets, getActiveToken, authenticate.
  • Hash bumps with a decision_log naming the commit where nothing needed porting: testLogin, uilogin, LoginDialog, cloudPane, plus logout, selectCloudDataset, uploadFilesForDatasetDocuments, datasetsPane. The Python-only _resolve_org_id helper and its entry are gone.

Tests

  • New offline tests/test_cloud_organizations.py (26 tests): normalization, cache, every chooseOrganization branch, the chooser and console prompt, labels, login/logout caching, dataset-organization lookup.
  • Live tests name their organization via tests/_cloud_organizations.testOrganizationID (first uploadable organization; any, for an administrator) instead of cloud_config.org_id.
  • New guard tests/test_cloud_tests_name_organization.py (mirrors NDI-matlab's CloudTestsNameOrganizationTest): fails if a test calls createDataset / uploadDataset / newDataset without naming an organization, which CI's single-organization account could never catch.
  • Existing offline tests updated to cache organizations rather than set config.org_id.

Test plan

  • black --check src/ tests/, ruff check src/ tests/
  • pytest tests/ locally: 6043 passed, 609 skipped (live cloud tests skip without credentials)
  • Bridge checks (test_matlab_bridge_*, strict) against a full-history NDI-matlab checkout: 196 passed
  • Live cloud tests in CI (first run against the real API)
  • Manually: log in with a multi-organization account; ndi.cloud.internal.getOrganizations() lists all with names; uploadDataset asks on a terminal and accepts organization_name

Generated by Claude Code

Port of VH-Lab/NDI-matlab#1037. A user has one token but may belong to
several organizations, so login no longer keeps only the first.

- Login caches the user's whole organization list (id, name,
  canUploadDataset) as JSON in NDI_CLOUD_ORGANIZATIONS; logout clears it.
  New ndi.cloud.internal.normalizeOrganizations / setOrganizations /
  getOrganizations / chooseOrganization / userIsAdmin and
  ndi.cloud.ui.dialog.organizationLabels / selectOrganization.
- chooseOrganization: a named organization, else the only qualifying
  one, else ask (on a terminal, or through a supplied chooser), else
  CloudOrganizationError listing them by name and id. RequireUpload
  honours canUploadDataset, and lets administrators upload anywhere, as
  the server does.
- CloudConfig.org_id and NDI_CLOUD_ORGANIZATION_ID are gone;
  getActiveToken returns the token only; authenticate's second output is
  deprecated (the only organization, else "").
- createDataset / uploadDataset take organization_name /
  organization_id; listDatasets() with no organization lists every
  organization's datasets, tagged with organizationId/organizationName.
- File uploads go to the organization that owns the dataset (read from
  its record, as MATLAB's GetFileUploadURL does), not the login's first.
- The navigator lists cloud datasets across organizations and asks which
  organization on "Upload to Cloud".
- Bridge entries synced to NDI-matlab 293ec411 / c24d338d / 9a3c972d;
  resolveOrganizationId retired; new entries for the +internal helpers.
- Tests: offline test_cloud_organizations, live tests pass an explicit
  organization (tests/_cloud_organizations.testOrganizationID), and a
  guard that fails if a test creates a dataset without naming one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012b2n4N1dokzz1vXoRtdBQK
The User 2 Cloud API jobs failed at setup of test_cloud_file_series_round_trip:
that account may upload to none of its organizations and is not an
administrator, so testOrganizationID raised. Before, the same account got
HTTP 400 from createDataset and the fixture skipped ("cannot create cloud
dataset for this test"). Resolve the organization first and skip with the
same message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012b2n4N1dokzz1vXoRtdBQK
@stevevanhooser
stevevanhooser merged commit b98bfbb into main Oct 18, 2026
19 checks passed
@stevevanhooser
stevevanhooser deleted the claude/intelligent-volta-3w728u branch October 18, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants