Skip to content

feat(organizations): add CRUD commands for organizations#762

Open
rexxars wants to merge 15 commits intomainfrom
feat/org-commands
Open

feat(organizations): add CRUD commands for organizations#762
rexxars wants to merge 15 commits intomainfrom
feat/org-commands

Conversation

@rexxars
Copy link
Copy Markdown
Member

@rexxars rexxars commented Mar 23, 2026

Description

Adds a new organizations topic to the CLI with five commands for managing organizations:

  • sanity organizations list - list all organizations you're a member of
  • sanity organizations get <organizationId> - show details of a specific organization
  • sanity organizations create - create a new organization (interactive or --name/--default-role flags)
  • sanity organizations update <organizationId> - update name, slug, or default role
  • sanity organizations delete <organizationId> - delete an organization (with confirmation prompt)

Hidden topic aliases so all of these also work: organization, organisations, organisation, org, orgs. Uses the two-layer alias approach from #714 (hiddenAliases on each command class + topicAliases.ts entry).

What to review

  • src/services/organizations.ts - extended with getOrganization, updateOrganization, deleteOrganization; createOrganization now accepts optional defaultRoleName; fixed ProjectOrganization.slug to be string | null
  • src/commands/organizations/ - five command files, all thin: parse flags, call service, format output
  • src/util/apiError.ts - shared hasStatusCode type guard to avoid as assertions in catch blocks
  • src/topicAliases.ts and oclif.config.js - alias wiring for the organizations topic
  • src/commands/organizations/__tests__/ - 23 tests across all five commands

Testing

  • 23 unit tests covering all five commands (success, error, edge cases)
  • Tests use module-level client mocking (getGlobalCliClient), no service file mocks
  • Success tests use if (error) throw error; error tests assert toBeInstanceOf(Error) + message
  • All tests pass locally

Notes

  • --slug flag only on update, not create - the API requires the authSAML feature for slugs, which a brand-new org won't have
  • delete blocks server-side if the org has associated projects - the command surfaces that API error clearly
  • Members management commands (organizations members list/invite) are deferred pending internal discussion

rexxars and others added 11 commits March 23, 2026 16:52
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…pe guard

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Adds `organizations update` command with --name, --slug, and --default-role flags. Includes tests for success, validation (no flags), missing arg, and API error cases.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Remove export from Organization and OrganizationDeleteResponse interfaces
as they are only used internally within the service module.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Wire up topic aliases (organization, organisations, organisation, org, orgs)
in topicAliases.ts and oclif.config.js. Fix linter-reported import and
property ordering in org command files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Mar 23, 2026

Claude finished @mttdnt's task in 1m 50s —— View job


Review

  • Gather context
  • Read changed files
  • Identify issues
  • Post review

1. validateOrganizationSlug accepts invalid characters

src/actions/organizations/validateOrganizationSlug.ts:1-12 — The validator only checks for empty, uppercase, and spaces. It allows characters that are invalid in URL slugs (e.g. acme!, org_name, org.name). Slugs should be constrained to [a-z0-9-].

if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(input) && !/^[a-z0-9]$/.test(input)) {
  return 'Organization slug may only contain lowercase letters, numbers, and hyphens'
}

Fix this →


2. Misleading "User cancelled" error in delete.ts

src/commands/organizations/delete.ts:103-107 — The catch block around input() labels any thrown error as "User cancelled", but input() could throw for other reasons. The error message should pass through the actual error text rather than hardcoding "User cancelled".

} catch (error) {
  deleteOrgDebug(`User cancelled or input error`, error)
  this.error(getErrorMessage(error), {exit: 1})
}

3. Falsy check in createOrganization inconsistent with !== undefined pattern

src/services/organizations.ts:83defaultRoleName ? {defaultRoleName} : {} uses truthy check, so an empty string would be omitted. The rest of the codebase (command layer) uses !== undefined. Should be:

body: {name, ...(defaultRoleName !== undefined ? {defaultRoleName} : {})},

4. getOrganization passes string 'false' for boolean query params

src/services/organizations.ts:115-116{includeFeatures: 'false', includeMembers: 'false'} uses strings instead of booleans. If the API is type-sensitive this could return unexpected data. Use false (boolean) instead.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 24, 2026

Coverage Delta

File Statements
packages/@sanity/cli/src/commands/organizations/create.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/delete.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/get.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/list.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/update.ts 100.0% (new)
packages/@sanity/cli/src/services/organizations.ts 100.0% (±0%)
packages/@sanity/cli/src/topicAliases.ts 100.0% (±0%)
packages/@sanity/cli/src/util/apiError.ts 100.0% (new)

Comparing 8 changed files against main @ 133be82f2a246f5cc1573c8e2733d50a2b2ea746

Overall Coverage

Metric Coverage
Statements 83.2% (+ 0.2%)
Branches 72.8% (+ 0.1%)
Functions 83.3% (+ 0.1%)
Lines 83.7% (+ 0.2%)

@rexxars rexxars force-pushed the feat/org-commands branch from fe45e78 to 740971e Compare March 24, 2026 21:17
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (133be82f)

@sanity/cli

Metric Value vs main (133be82)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 10.95 MB -120.3 KB, -1.1%
Bundled (gzip) 2.06 MB -20.4 KB, -1.0%
Import time 830ms +830ms, +0.0%

bin:sanity

Metric Value vs main (133be82)
Internal (raw) 975 B -
Internal (gzip) 460 B -
Bundled (raw) 9.84 MB -
Bundled (gzip) 1.77 MB -
Import time 1.97s +1.64s, +501.7% ⚠️

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (133be82f)

Metric Value vs main (133be82)
Internal (raw) 92.3 KB +3.2 KB, +3.5%
Internal (gzip) 21.6 KB +822 B, +3.9%
Bundled (raw) 21.53 MB -115.9 KB, -0.5%
Bundled (gzip) 3.41 MB -19.7 KB, -0.6%
Import time 786ms +786ms, +0.0%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (133be82f)

Metric Value vs main (133be82)
Internal (raw) 976 B -
Internal (gzip) 507 B -
Bundled (raw) 50.7 KB -
Bundled (gzip) 12.6 KB -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Coverage Delta

File Statements
packages/@sanity/cli/src/actions/organizations/validateOrganizationSlug.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/create.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/delete.ts 94.6% (new)
packages/@sanity/cli/src/commands/organizations/get.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/list.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/update.ts 100.0% (new)
packages/@sanity/cli/src/prompts/promptForOrganizationName.ts 100.0% (±0%)
packages/@sanity/cli/src/services/organizations.ts 100.0% (±0%)
packages/@sanity/cli/src/topicAliases.ts 100.0% (±0%)
packages/@sanity/cli/src/util/apiError.ts 100.0% (new)
packages/@sanity/cli/src/util/organizationAliases.ts 100.0% (new)

Comparing 11 changed files against main @ 7b98e0be69544a0a89ddf871219b8025128e622b

Overall Coverage

Metric Coverage
Statements 83.3% (+ 0.2%)
Branches 73.0% (+ 0.2%)
Functions 83.4% (+ 0.1%)
Lines 83.7% (+ 0.2%)

@mttdnt mttdnt marked this pull request as ready for review April 2, 2026 14:47
@mttdnt mttdnt requested a review from a team as a code owner April 2, 2026 14:47
@mttdnt mttdnt requested review from gu-stav and removed request for a team April 2, 2026 14:47
})

for (const {id, name, slug} of organizations) {
table.addRow({id, name, slug: slug ?? '-'})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if anything else is needing to be displayed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Coverage Delta

File Statements
packages/@sanity/cli/src/actions/organizations/validateOrganizationSlug.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/create.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/delete.ts 94.7% (new)
packages/@sanity/cli/src/commands/organizations/get.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/list.ts 100.0% (new)
packages/@sanity/cli/src/commands/organizations/update.ts 100.0% (new)
packages/@sanity/cli/src/prompts/promptForOrganizationName.ts 100.0% (±0%)
packages/@sanity/cli/src/services/organizations.ts 100.0% (±0%)
packages/@sanity/cli/src/topicAliases.ts 100.0% (±0%)
packages/@sanity/cli/src/util/apiError.ts 100.0% (new)
packages/@sanity/cli/src/util/organizationAliases.ts 100.0% (new)

Comparing 11 changed files against main @ ebc4e56198cd2071556e8687ec5f416ee2557873

Overall Coverage

Metric Coverage
Statements 83.3% (+ 0.2%)
Branches 73.0% (+ 0.1%)
Functions 83.4% (+ 0.1%)
Lines 83.7% (+ 0.2%)

@mttdnt mttdnt requested a review from binoy14 April 2, 2026 15:00
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