UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

118 lines (95 loc) 3.73 kB
import {type CliCommandDefinition} from '@sanity/cli' import {size, sortBy} from 'lodash' import {type Invite, type PartialProjectResponse, type User} from './types' const sortFields = ['id', 'name', 'role', 'date'] const helpText = ` Options --no-invitations Don't include pending invitations --no-robots Don't include robots (token users) --sort <field> Sort users by specified column: ${sortFields.join(', ')} --order <asc/desc> Sort output ascending/descending Examples # List all users of the project sanity users list # List all users of the project, but exclude pending invitations and robots sanity users list --no-invitations --no-robots # List all users, sorted by role sanity users list --sort role ` const listUsersCommand: CliCommandDefinition = { name: 'list', group: 'users', signature: '', helpText, description: 'List all users of the project', action: async (args, context) => { const {apiClient, output, chalk} = context const {sort, order, robots, invitations} = { sort: 'date', order: 'asc', robots: true, invitations: true, ...args.extOptions, } if (!sortFields.includes(sort)) { throw new Error(`Can't sort by field "${sort}". Must be one of ${sortFields.join(', ')}`) } if (order !== 'asc' && order !== 'desc') { throw new Error(`Unknown sort order "${order}", must be either "asc" or "desc"`) } const client = apiClient() const globalClient = client.clone().config({useProjectHostname: false}) const {projectId} = client.config() const useGlobalApi = true const [pendingInvitations, project] = await Promise.all([ invitations ? globalClient .request<Invite[]>({uri: `/invitations/project/${projectId}`, useGlobalApi}) .then(getPendingInvitations) : [], globalClient.request<PartialProjectResponse>({uri: `/projects/${projectId}`, useGlobalApi}), ]) const memberIds = project.members.map((member) => member.id) const users = await globalClient .request<User | User[]>({uri: `/users/${memberIds.join(',')}`, useGlobalApi}) .then((user) => (Array.isArray(user) ? user : [user])) const projectMembers = project.members .map((member) => ({ ...member, ...getUserProps(users.find((candidate) => candidate.id === member.id)), })) .filter((member) => !member.isRobot || robots) const members = [...projectMembers, ...pendingInvitations] const ordered = sortBy( members.map(({id, name, role, date}) => [id, name, role, date]), [sortFields.indexOf(sort)], ) const rows = order === 'asc' ? ordered : ordered.reverse() const maxWidths = rows.reduce( (max, row) => row.map((current, index) => Math.max(size(current), max[index])), sortFields.map((str) => size(str)), ) const printRow = (row: string[]) => { const isInvite = row[0] === '<pending>' const textRow = row.map((col, i) => `${col}`.padEnd(maxWidths[i])).join(' ') return isInvite ? chalk.dim(textRow) : textRow } output.print(chalk.cyan(printRow(sortFields))) rows.forEach((row) => output.print(printRow(row))) }, } function getUserProps(user: User | undefined) { const {displayName: name, createdAt: date} = user || {} return {name: name || '', date: date || ''} } function getPendingInvitations(invitations: Invite[]) { return invitations .filter((invite) => !invite.isAccepted && !invite.isRevoked && !invite.acceptedByUserId) .map((invite) => ({ id: '<pending>', name: invite.email, role: invite.role, date: invite.createdAt, })) } export default listUsersCommand