Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions source/commands/api/users/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ export const options = object({
alias: 'a',
}),
),
includeResourceInstanceRoles: boolean()
.optional()
.default(false)
.describe(
option({
description: 'Include resource instance roles in the response',
alias: 'i',
}),
),
});

type Props = {
Expand Down
5 changes: 4 additions & 1 deletion source/components/api/PermitUsersListComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import Spinner from 'ink-spinner';
import { usersApi, type UserData } from '../../utils/permitApi.js';

type Props = {
options: zInfer<typeof options>;
options: zInfer<typeof options> & {
includeResourceInstanceRoles?: boolean;
};
};

// Transforms API data into table-friendly format while preserving type safety
Expand Down Expand Up @@ -79,6 +81,7 @@ export default function PermitUsersListComponent({ options }: Props) {
perPage: options.perPage,
role: options.role,
tenant: options.tenant,
include_resource_instance_roles: options.includeResourceInstanceRoles,
});

if (!response.success) {
Expand Down
6 changes: 6 additions & 0 deletions source/utils/permitApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export interface ListUsersRequest extends PermitApiOptions {
perPage?: number;
role?: string;
tenant?: string;
include_resource_instance_roles?: boolean;
}

export interface RoleAssignmentRequest extends PermitApiOptions {
Expand All @@ -88,6 +89,11 @@ export const usersApi = {
page: String(options.page || 1),
per_page: String(options.perPage || 50),
...(options.role && { role: options.role }),
...(options.include_resource_instance_roles !== undefined && {
include_resource_instance_roles: String(
options.include_resource_instance_roles,
),
}),
},
);
},
Expand Down
50 changes: 50 additions & 0 deletions tests/api-users.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,56 @@ describe('API Users List Command', () => {
await delay(100);
expect(lastFrame()?.toString()).toMatch(/Error/);
});

it('should include resource instance roles when option is set', async () => {
(fetch as any).mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: [
{
key: 'user1',
email: 'test@test.com',
first_name: 'Test',
last_name: 'User',
roles: [
{
role: 'admin',
tenant: 'tenant1',
resource_instance: {
type: 'project',
key: 'proj1',
},
},
],
},
],
total_count: 1,
page: 1,
}),
});

const options = {
apiKey: demoPermitKey,
page: 1,
perPage: 10,
all: false,
expandKey: false,
includeResourceInstanceRoles: true,
};

const { lastFrame } = render(<List options={options} />);
expect(lastFrame()?.toString()).toMatch(/Loading Token/);

await delay(100);
expect(lastFrame()).toMatch(/Showing 1 items/);

// Verify the API was called with the correct parameter
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('include_resource_instance_roles=true'),
expect.any(Object),
);
});
});

describe('API Users Commands', () => {
Expand Down