Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(users): add api endpoint to list users in a team #65

Merged
merged 4 commits into from
Apr 9, 2024
Merged
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
185 changes: 185 additions & 0 deletions api/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,25 @@ export interface paths {
*/
patch: operations["teamSecrets-update"];
};
"/teams/{teamId}/users": {
/**
* List team members
* @description List team members
*/
get: operations["teamMemberships-listByTeamId"];
};
"/teams/{teamId}/users/{userId}": {
/**
* Update a team membership
* @description Update a team membership
*/
put: operations["teamMemberships-update"];
/**
* Remove a user from a team
* @description Remove a user from a team
*/
delete: operations["teamMemberships-removeUser"];
};
"/templates": {
/**
* List templates
Expand Down Expand Up @@ -15042,6 +15061,172 @@ export interface operations {
default: components["responses"]["error"];
};
};
/**
* List team members
* @description List team members
*/
"teamMemberships-listByTeamId": {
parameters: {
readonly query: {
/** @description Fetch the next page of results after this cursor. */
after?: string;
/** @description The number of items to fetch after this page. */
limit?: number;
/** @description Order results by one of these fields. */
orderBy?: "dtConfirmed";
/** @description The order to sort the results by. */
order?: "asc" | "desc";
/** @description Filter team members by their role on the team. */
role?: "member" | "admin" | "owner";
};
readonly path: {
/** @description The team's ID */
teamId: string;
};
};
responses: {
/** @description Successful response */
200: {
content: {
readonly "application/json": {
/** @description Whether there are more pages of results available. */
readonly hasMore: boolean;
/** @description The items on this page. */
readonly items: readonly ({
/**
* Format: date-time
* @description The date the user confirmed their membership
* @default null
*/
readonly dtConfirmed?: Date;
/** @description Whether the user is an admin of the team */
readonly isAdmin: boolean;
/** @description Whether the user is the owner of the team */
readonly isOwner: boolean;
readonly user: {
/** @description The email address of the user */
readonly email: string;
/**
* @description The first name of the user
* @default null
*/
readonly firstName?: string | null;
/** @description The ID of the user */
readonly id: string;
/**
* Format: date-time
* @description The date the user was last active.
* @default null
*/
readonly lastActive?: Date;
/**
* @description The last name of the user
* @default null
*/
readonly lastName?: string | null;
/**
* @description The URL of the team's profile image.
* @default null
*/
readonly publicProfileImageUrl?: string | null;
};
})[];
/** @description The cursor required to fetch the next page of results. i.e. `?after=nextPage`. This is `null` when there is no next page. */
readonly nextPage?: string;
};
};
};
default: components["responses"]["error"];
};
};
/**
* Update a team membership
* @description Update a team membership
*/
"teamMemberships-update": {
parameters: {
readonly path: {
/** @description The team's ID */
teamId: string;
/** @description The user's ID */
userId: string;
};
};
readonly requestBody: {
readonly content: {
readonly "application/json": {
/** @description Whether the user will gain admin access or not */
readonly isAdmin: boolean;
};
};
};
responses: {
/** @description Successful response */
200: {
content: {
readonly "application/json": {
/**
* Format: date-time
* @description The date the user confirmed their membership
* @default null
*/
readonly dtConfirmed?: Date;
/**
* Format: date-time
* @description The date the user was removed from the team
* @default null
*/
readonly dtDeleted?: Date;
/** @description Whether the user is an admin of the team */
readonly isAdmin: boolean;
/** @description Whether the user is the owner of the team */
readonly isOwner: boolean;
};
};
};
default: components["responses"]["error"];
};
};
/**
* Remove a user from a team
* @description Remove a user from a team
*/
"teamMemberships-removeUser": {
parameters: {
readonly path: {
/** @description The team's ID */
teamId: string;
/** @description The user's ID */
userId: string;
};
};
responses: {
/** @description Successful response */
200: {
content: {
readonly "application/json": {
/**
* Format: date-time
* @description The date the user confirmed their membership
* @default null
*/
readonly dtConfirmed?: Date;
/**
* Format: date-time
* @description The date the user was removed from the team
* @default null
*/
readonly dtDeleted?: Date;
/** @description Whether the user is an admin of the team */
readonly isAdmin: boolean;
/** @description Whether the user is the owner of the team */
readonly isOwner: boolean;
};
};
};
default: components["responses"]["error"];
};
};
/**
* List templates
* @description Fetches a list of templates.
Expand Down
4 changes: 4 additions & 0 deletions api/teams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export const teamSecrets = {
update: client("/teams/{id}/secrets/{name}").patch,
delete: client("/teams/{id}/secrets/{name}").delete,
};

export const teamUsers = {
list: client("/teams/{teamId}/users").get,
};
ps-kwang marked this conversation as resolved.
Show resolved Hide resolved
78 changes: 78 additions & 0 deletions commands/user/list/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { command, flag, flags } from "../../../zcli.ts";
import { asserts } from "../../../lib/asserts.ts";
import { dataTable } from "../../../lib/data-table.ts";
import { loading } from "../../../lib/loading.ts";
import * as psFlags from "../../../flags.ts";
import { pickJson } from "../../../lib/pick-json.ts";
import { config } from "../../../config.ts";
import { teamUsers } from "../../../api/teams.ts";
import { defaultFields } from "../mod.ts";

/**
* This variable is automatically generated by `zcli add`. Do not remove this
* or change its name unless you're no longer using `zcli add`.
*/
const subCommands: ReturnType<typeof command>[] = [];

export const list = command("list", {
short: "List users.",
long: ({ root }) => `
List users in your team.

Pick a subset of fields to display:
\`\`\`
${root.name} user list -F email -F dtCreated
\`\`\`
`,
commands: subCommands,
flags: psFlags.paginator.merge(flags({
"team-id": flag({
aliases: ["t"],
short: "The ID of the team to list users in",
long: `
The ID of the team to list users in. If not specified, the current team
will be used.
`,
}).ostring(),
})),
// We use command metadata in the `persistentPreRun` function to check if a
// command requires an API key. If it does, we'll check to see if one is
// set. If not, we'll throw an error.
meta: {
requireApiKey: true,
},
}).run(
async function* ({ flags }) {
let teamId = flags["team-id"];
if (!teamId) {
const team = await config.get("team");
asserts(team, "You must be in a team to list users.");
teamId = team;
}

const result = await loading(
teamUsers.list({
limit: flags.limit,
after: flags.after,
order: flags.asc ? "asc" : undefined,
teamId,
}),
{ enabled: !flags.json },
);

asserts(result.ok, result);

if (!flags.json) {
for await (
const line of dataTable(
result.data.items,
flags.fields ?? defaultFields,
)
) {
yield line;
}
} else {
yield pickJson(result.data, flags.fields);
}
},
);
27 changes: 27 additions & 0 deletions commands/user/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { command } from "../../zcli.ts";
import { list } from "./list/mod.ts";

export const defaultFields = [
"id",
"teamId",
];

/**
* This variable is automatically generated by `zcli add`. Do not remove this
* or change its name unless you're no longer using `zcli add`.
*/
const subCommands: ReturnType<typeof command>[] = [
list,
];

export const user = command("user", {
short: "Information about users",
long: `
Show information about users.
`,
commands: subCommands,
}).run(function* ({ ctx }) {
for (const line of user.help(ctx)) {
yield line;
}
});
Loading