Conversation
|
Claude finished @refiito's task in 3m 1s —— View job Code Review
1.
// Current — insufficient
if (typeof item !== 'object' || item === null || !('key' in item) || !('value' in item)) {
// Fix — add type check
if (typeof item !== 'object' || item === null || typeof (item as any).key !== 'string' || !('value' in item)) {2.
3. this.log(`Attribute${keys.length === 1 ? '' : 's'} removed successfully for user ${userId}.`)
4. Only the |
📦 Bundle Stats —
|
| Metric | Value | vs main (512b79e) |
|---|---|---|
| Internal (raw) | 2.1 KB | - |
| Internal (gzip) | 799 B | - |
| Bundled (raw) | 10.95 MB | - |
| Bundled (gzip) | 2.06 MB | - |
| Import time | 845ms | +13ms, +1.5% |
bin:sanity
| Metric | Value | vs main (512b79e) |
|---|---|---|
| Internal (raw) | 975 B | - |
| Internal (gzip) | 460 B | - |
| Bundled (raw) | 9.84 MB | - |
| Bundled (gzip) | 1.77 MB | - |
| Import time | 2.04s | +56ms, +2.8% |
🗺️ 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 (512b79e1)
| Metric | Value | vs main (512b79e) |
|---|---|---|
| Internal (raw) | 92.3 KB | - |
| Internal (gzip) | 21.6 KB | - |
| Bundled (raw) | 21.53 MB | - |
| Bundled (gzip) | 3.41 MB | - |
| Import time | 815ms | +20ms, +2.6% |
🗺️ 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 (512b79e1)
| Metric | Value | vs main (512b79e) |
|---|---|---|
| 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.
Coverage Delta
Comparing 11 changed files against main @ Overall Coverage
|
- Add pagination notice when definitions list results are truncated (hasMore) - Move array validation outside the JSON.parse try/catch in set.ts to avoid fragile error re-throw - Extract duplicated formatValue helper to shared util/formatAttributeValue.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add per-item validation in set.ts to catch missing key/value fields early - Remove redundant `as UserAttribute[]` casts (already typed correctly via response interfaces) - Remove now-unused UserAttribute import from list.ts and set.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cngonzalez
left a comment
There was a problem hiding this comment.
Functionally looks pretty good! Most comments are nits about style and guidelines
| /** | ||
| * Get the authenticated user's own attributes within an organization | ||
| */ | ||
| export async function getMyAttributes(orgId: string): Promise<UserAttributesGetResponse> { |
There was a problem hiding this comment.
"My" is a little idiosyncratic in this repo -- maybe getCliUserAttributes (to match the getCliUser function in user.ts
| @@ -0,0 +1,56 @@ | |||
| export type AttributeType = | |||
There was a problem hiding this comment.
I'm a bit confused on why this and the constants file are in the actions directory when there's no corresponding actions. Should they be moved to commands where they're actually used? Or do we foresee a need for actions down the line (multi-step processes etc)
|
|
||
| export type AttributeValue = (number | string)[] | boolean | number | string | ||
|
|
||
| export interface UserAttributeValues { |
There was a problem hiding this comment.
It might be worth making a comment on this type -- I'm a little confused because it seems to be used as the value of an attribute as part of UserAttribute, which has an activeSource that indicates if it's saml or sanity but there's a possibility for an attribute to have a value that is not in line with with the activeSource on the attribute? Is it shared or updated perhaps?
| (baseDescription ?? 'Organization ID to use') + (isOverride ? OVERRIDE_SUFFIX : '') | ||
|
|
||
| return { | ||
| 'org-id': Flags.string({ |
There was a problem hiding this comment.
I'm not the final say on this, but I wanted to point out that elsewhere the CLI uses flags to specify an org, it's --organization (like in init and projects create). We may want to be consistent with the rest of the CLI
| export class UserAttributeDefinitionsCreateCommand extends SanityCommand< | ||
| typeof UserAttributeDefinitionsCreateCommand | ||
| > { | ||
| static override description = 'Create an attribute definition for an organization' |
There was a problem hiding this comment.
Would "user attribute definition" be clearer here? I'm on the fence since the command is sanity user attribute definition create.
| > { | ||
| static override description = 'Create an attribute definition for an organization' | ||
|
|
||
| static override examples = [ |
There was a problem hiding this comment.
Maybe not required for every command, but it might be nice to provide examples that illustrate that the organization ID is not needed for these commands, since we're prompting for it.
(I think we also have a style guide rule for progressive complexity which you're doing a great job of here! Just think the "no org" example could be nice)
| } | ||
|
|
||
| if (outputJson) { | ||
| this.log(JSON.stringify(result, null, 2)) |
There was a problem hiding this comment.
There is a colorizeJson helper available that might be nice for consistency
|
|
||
| if (result.hasMore) { | ||
| this.log( | ||
| '\nNote: Results are truncated. Use --json and the API directly with a cursor to fetch more.', |
There was a problem hiding this comment.
Would it make sense for the user to run another CLI command to get a cursor or just hit the API directly (assuming it already gives a cursor?) It might also be nice to give them the URL to hit, if they need a token, etc.
|
|
||
| static override examples = [ | ||
| { | ||
| command: '<%= config.bin %> <%= command.id %> --org-id o123 location', |
There was a problem hiding this comment.
Generally, examples tend to position arguments before flags (though technically I think OCLIF allows either)
| }, | ||
| { | ||
| command: | ||
| '<%= config.bin %> <%= command.id %> --org-id o123 --user-id u456 --attributes \'[{"key":"location","value":"UK"},{"key":"year_started","value":2020}]\'', |
There was a problem hiding this comment.
I'm not sure if we have a standard or recommendations about passing JSON on the command line. @binoy14 might have more thoughts about this or has encountered this before.
Add CLI support for User Attributes API
What
Adds CLI commands for managing User Attributes — the foundation for parameterised role-based access control. User attributes are key-value pairs (e.g. location="UK", departments=["hr","sales"]) that can be referenced in role definitions and access filters.
Commands
Attribute definitions (org-level schema — requires sanity.organization.manage):
User attribute values (list defaults to your own user; set/unset require sanity.organization.manage):
When --org-id is omitted in an interactive terminal, the CLI prompts for an organization selection.
Testing
pnpm test packages/@sanity/cli/src/commands/users/attributes