From e000bd1d4a294502bdf8f0fd88ad2f506543db8e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 26 Aug 2024 22:46:05 +0000 Subject: [PATCH 01/15] feat: show organization name for groups on user profile --- coderd/database/dbauthz/dbauthz.go | 2 +- coderd/database/dbmem/dbmem.go | 28 +- coderd/database/dbmetrics/dbmetrics.go | 2 +- coderd/database/dbmock/dbmock.go | 4 +- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 80 ++- coderd/database/queries/groups.sql | 64 +- codersdk/groups.go | 11 +- site/src/api/api.ts | 12 +- site/src/api/queries/groups.ts | 40 +- site/src/api/typesGenerated.ts | 635 +++++++++++++++--- site/src/pages/GroupsPage/GroupsPage.tsx | 4 +- .../GroupsPage/GroupsPage.tsx | 8 +- .../AccountPage/AccountPage.tsx | 3 +- .../AccountPage/AccountUserGroups.tsx | 7 +- site/src/pages/UsersPage/UsersPage.tsx | 4 +- site/src/testHelpers/entities.ts | 2 + 17 files changed, 687 insertions(+), 221 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index c040df06196ec..17debdb986e34 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1503,7 +1503,7 @@ func (q *querier) GetGroupMembersCountByGroupID(ctx context.Context, groupID uui return memberCount, nil } -func (q *querier) GetGroups(ctx context.Context, arg database.GetGroupsParams) ([]database.Group, error) { +func (q *querier) GetGroups(ctx context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) { if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err == nil { // Optimize this query for system users as it is used in telemetry. // Calling authz on all groups in a deployment for telemetry jobs is diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 42fdd2b93f63e..e9252cd05a580 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2609,7 +2609,7 @@ func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, groupID return int64(len(users)), nil } -func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) ([]database.Group, error) { +func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) { err := validateDatabaseType(arg) if err != nil { return nil, err @@ -2632,9 +2632,11 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) groupIDs[orgMember.OrganizationID] = struct{}{} } } + } - filtered := make([]database.Group, 0) + organizationDisplayNames := make(map[uuid.UUID]string) + filtered := make([]database.GetGroupsRow, 0) for _, group := range q.groups { if arg.OrganizationID != uuid.Nil && group.OrganizationID != arg.OrganizationID { continue @@ -2645,7 +2647,27 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) continue } - filtered = append(filtered, group) + orgDisplayName, ok := organizationDisplayNames[group.ID] + if !ok { + for _, org := range q.organizations { + if group.OrganizationID == org.ID { + orgDisplayName = org.DisplayName + break + } + } + organizationDisplayNames[group.ID] = orgDisplayName + } + + filtered = append(filtered, database.GetGroupsRow{ + ID: group.ID, + Name: group.Name, + OrganizationID: group.OrganizationID, + AvatarURL: group.AvatarURL, + QuotaAllowance: group.QuotaAllowance, + DisplayName: group.DisplayName, + Source: group.Source, + OrganizationDisplayName: orgDisplayName, + }) } return filtered, nil diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 2215a45a6fc4b..23298032f8bb9 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -669,7 +669,7 @@ func (m metricsStore) GetGroupMembersCountByGroupID(ctx context.Context, groupID return r0, r1 } -func (m metricsStore) GetGroups(ctx context.Context, arg database.GetGroupsParams) ([]database.Group, error) { +func (m metricsStore) GetGroups(ctx context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) { start := time.Now() r0, r1 := m.s.GetGroups(ctx, arg) m.queryLatencies.WithLabelValues("GetGroups").Observe(time.Since(start).Seconds()) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 66f15a5e8c99d..765338a085a20 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -1330,10 +1330,10 @@ func (mr *MockStoreMockRecorder) GetGroupMembersCountByGroupID(arg0, arg1 any) * } // GetGroups mocks base method. -func (m *MockStore) GetGroups(arg0 context.Context, arg1 database.GetGroupsParams) ([]database.Group, error) { +func (m *MockStore) GetGroups(arg0 context.Context, arg1 database.GetGroupsParams) ([]database.GetGroupsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetGroups", arg0, arg1) - ret0, _ := ret[0].([]database.Group) + ret0, _ := ret[0].([]database.GetGroupsRow) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 5e662815a7780..f4559e2cef9e3 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -152,7 +152,7 @@ type sqlcQuerier interface { // count even if the caller does not have read access to ResourceGroupMember. // They only need ResourceGroup read access. GetGroupMembersCountByGroupID(ctx context.Context, groupID uuid.UUID) (int64, error) - GetGroups(ctx context.Context, arg GetGroupsParams) ([]Group, error) + GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetGroupsRow, error) GetHealthSettings(ctx context.Context) (string, error) GetHungProvisionerJobs(ctx context.Context, updatedAt time.Time) ([]ProvisionerJob, error) GetJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg GetJFrogXrayScanByWorkspaceAndAgentIDParams) (JfrogXrayScan, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 7a25b7f82533b..751b60ca96fa0 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1562,32 +1562,34 @@ func (q *sqlQuerier) GetGroupByOrgAndName(ctx context.Context, arg GetGroupByOrg const getGroups = `-- name: GetGroups :many SELECT - id, name, organization_id, avatar_url, quota_allowance, display_name, source + groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source, organizations.display_name AS organization_display_name FROM - groups + groups +INNER JOIN + organizations ON ((groups.organization_id = organizations.id)) WHERE - true - AND CASE - WHEN $1:: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - groups.organization_id = $1 - ELSE true - END - AND CASE - -- Filter to only include groups a user is a member of - WHEN $2::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - EXISTS ( - SELECT - 1 - FROM - -- this view handles the 'everyone' group in orgs. - group_members_expanded - WHERE - group_members_expanded.group_id = groups.id - AND - group_members_expanded.user_id = $2 - ) - ELSE true - END + true + AND CASE + WHEN $1:: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + groups.organization_id = $1 + ELSE true + END + AND CASE + -- Filter to only include groups a user is a member of + WHEN $2::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + EXISTS ( + SELECT + 1 + FROM + -- this view handles the 'everyone' group in orgs. + group_members_expanded + WHERE + group_members_expanded.group_id = groups.id + AND + group_members_expanded.user_id = $2 + ) + ELSE true + END ` type GetGroupsParams struct { @@ -1595,15 +1597,26 @@ type GetGroupsParams struct { HasMemberID uuid.UUID `db:"has_member_id" json:"has_member_id"` } -func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]Group, error) { +type GetGroupsRow struct { + ID uuid.UUID `db:"id" json:"id"` + Name string `db:"name" json:"name"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + AvatarURL string `db:"avatar_url" json:"avatar_url"` + QuotaAllowance int32 `db:"quota_allowance" json:"quota_allowance"` + DisplayName string `db:"display_name" json:"display_name"` + Source GroupSource `db:"source" json:"source"` + OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` +} + +func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetGroupsRow, error) { rows, err := q.db.QueryContext(ctx, getGroups, arg.OrganizationID, arg.HasMemberID) if err != nil { return nil, err } defer rows.Close() - var items []Group + var items []GetGroupsRow for rows.Next() { - var i Group + var i GetGroupsRow if err := rows.Scan( &i.ID, &i.Name, @@ -1612,6 +1625,7 @@ func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]Grou &i.QuotaAllowance, &i.DisplayName, &i.Source, + &i.OrganizationDisplayName, ); err != nil { return nil, err } @@ -1703,15 +1717,15 @@ INSERT INTO groups ( id, name, organization_id, - source + source ) SELECT - gen_random_uuid(), - group_name, - $1, - $2 + gen_random_uuid(), + group_name, + $1, + $2 FROM - UNNEST($3 :: text[]) AS group_name + UNNEST($3 :: text[]) AS group_name ON CONFLICT DO NOTHING RETURNING id, name, organization_id, avatar_url, quota_allowance, display_name, source ` diff --git a/coderd/database/queries/groups.sql b/coderd/database/queries/groups.sql index 5c90fee38fa88..467e55d3dee89 100644 --- a/coderd/database/queries/groups.sql +++ b/coderd/database/queries/groups.sql @@ -22,32 +22,34 @@ LIMIT -- name: GetGroups :many SELECT - * + groups.*, organizations.display_name AS organization_display_name FROM - groups + groups +INNER JOIN + organizations ON ((groups.organization_id = organizations.id)) WHERE - true - AND CASE - WHEN @organization_id:: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - groups.organization_id = @organization_id - ELSE true - END - AND CASE - -- Filter to only include groups a user is a member of - WHEN @has_member_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - EXISTS ( - SELECT - 1 - FROM - -- this view handles the 'everyone' group in orgs. - group_members_expanded - WHERE - group_members_expanded.group_id = groups.id - AND - group_members_expanded.user_id = @has_member_id - ) - ELSE true - END + true + AND CASE + WHEN @organization_id:: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + groups.organization_id = @organization_id + ELSE true + END + AND CASE + -- Filter to only include groups a user is a member of + WHEN @has_member_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + EXISTS ( + SELECT + 1 + FROM + -- this view handles the 'everyone' group in orgs. + group_members_expanded + WHERE + group_members_expanded.group_id = groups.id + AND + group_members_expanded.user_id = @has_member_id + ) + ELSE true + END ; -- name: InsertGroup :one @@ -70,15 +72,15 @@ INSERT INTO groups ( id, name, organization_id, - source + source ) SELECT - gen_random_uuid(), - group_name, - @organization_id, - @source + gen_random_uuid(), + group_name, + @organization_id, + @source FROM - UNNEST(@group_names :: text[]) AS group_name + UNNEST(@group_names :: text[]) AS group_name -- If the name conflicts, do nothing. ON CONFLICT DO NOTHING RETURNING *; @@ -113,5 +115,3 @@ DELETE FROM groups WHERE id = $1; - - diff --git a/codersdk/groups.go b/codersdk/groups.go index 8484250c13646..b3b6b4dda9b0d 100644 --- a/codersdk/groups.go +++ b/codersdk/groups.go @@ -26,11 +26,12 @@ type CreateGroupRequest struct { } type Group struct { - ID uuid.UUID `json:"id" format:"uuid"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` - Members []ReducedUser `json:"members"` + ID uuid.UUID `json:"id" format:"uuid"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` + OrganizationDisplayName string `json:"organization_display_name"` + Members []ReducedUser `json:"members"` // How many members are in this group. Shows the total count, // even if the user is not authorized to read group member details. // May be greater than `len(Group.Members)`. diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 4c4abdc5b75c9..0acb88fe5650f 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1603,14 +1603,20 @@ class ApiMethods { return response.data; }; + getGroups = async (): Promise => { + const response = await this.axios.get("/api/v2/groups"); + return response.data; + }; + /** * @param organization Can be the organization's ID or name */ - getGroups = async (organization: string): Promise => { + getGroupsByOrganization = async ( + organization: string, + ): Promise => { const response = await this.axios.get( - `/api/v2/organizations/${organization}/groups`, + `/api/v2/organization/${organization}/groups`, ); - return response.data; }; diff --git a/site/src/api/queries/groups.ts b/site/src/api/queries/groups.ts index 7202b8ffd19dd..f2d858d4fe57e 100644 --- a/site/src/api/queries/groups.ts +++ b/site/src/api/queries/groups.ts @@ -8,16 +8,19 @@ import type { QueryClient, UseQueryOptions } from "react-query"; type GroupSortOrder = "asc" | "desc"; -const getGroupsQueryKey = (organization: string) => [ - "organization", - organization, - "groups", -]; +const groupsQueryKey = ["groups"]; + +export const groups = () => { + return { + queryKey: groupsQueryKey, + queryFn: () => API.getGroups(), + } satisfies UseQueryOptions; +}; -export const groups = (organization: string) => { +export const groupsByOrganization = (organization: string) => { return { - queryKey: getGroupsQueryKey(organization), - queryFn: () => API.getGroups(organization), + queryKey: [organization, ...groupsQueryKey], + queryFn: () => API.getGroupsByOrganization(organization), } satisfies UseQueryOptions; }; @@ -37,9 +40,9 @@ export const group = (organization: string, groupName: string) => { export type GroupsByUserId = Readonly>; -export function groupsByUserId(organization: string) { +export function groupsByUserId() { return { - ...groups(organization), + ...groups(), select: (allGroups) => { // Sorting here means that nothing has to be sorted for the individual // user arrays later @@ -63,14 +66,13 @@ export function groupsByUserId(organization: string) { } satisfies UseQueryOptions; } -export function groupsForUser(organization: string, userId: string) { +export function groupsForUser(userId: string) { return { - ...groups(organization), + ...groups(), select: (allGroups) => { - const groupsForUser = allGroups.filter((group) => { - const groupMemberIds = group.members.map((member) => member.id); - return groupMemberIds.includes(userId); - }); + const groupsForUser = allGroups.filter((group) => + group.members.some((member) => member.id === userId), + ); return sortGroupsByName(groupsForUser, "asc"); }, @@ -106,7 +108,8 @@ export const createGroup = (queryClient: QueryClient, organization: string) => { mutationFn: (request: CreateGroupRequest) => API.createGroup(organization, request), onSuccess: async () => { - await queryClient.invalidateQueries(getGroupsQueryKey(organization)); + await queryClient.invalidateQueries(groupsQueryKey); + await queryClient.invalidateQueries([organization, ...groupsQueryKey]); }, }; }; @@ -155,7 +158,8 @@ export const invalidateGroup = ( groupId: string, ) => Promise.all([ - queryClient.invalidateQueries(getGroupsQueryKey(organization)), + queryClient.invalidateQueries(groupsQueryKey), + queryClient.invalidateQueries([organization, ...groupsQueryKey]), queryClient.invalidateQueries(getGroupQueryKey(organization, groupId)), ]); diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 5a27898ec3068..a2ea049276db5 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -71,7 +71,7 @@ export interface AssignableRoles extends Role { } // From codersdk/audit.go -export type AuditDiff = Record +export type AuditDiff = Record; // From codersdk/audit.go export interface AuditDiffField { @@ -150,7 +150,7 @@ export interface AuthorizationRequest { } // From codersdk/authorization.go -export type AuthorizationResponse = Record +export type AuthorizationResponse = Record; // From codersdk/deployment.go export interface AvailableExperiments { @@ -500,7 +500,7 @@ export interface Entitlements { } // From codersdk/deployment.go -export type Experiments = Readonly> +export type Experiments = Readonly>; // From codersdk/externalauth.go export interface ExternalAuth { @@ -617,6 +617,7 @@ export interface Group { readonly name: string; readonly display_name: string; readonly organization_id: string; + readonly organization_display_name: string; readonly members: Readonly>; readonly total_member_count: number; readonly avatar_url: string; @@ -1274,7 +1275,10 @@ export interface TemplateAutostopRequirement { } // From codersdk/templates.go -export type TemplateBuildTimeStats = Record +export type TemplateBuildTimeStats = Record< + WorkspaceTransition, + TransitionStats +>; // From codersdk/templates.go export interface TemplateExample { @@ -2004,187 +2008,551 @@ export interface WorkspacesResponse { } // From codersdk/apikey.go -export type APIKeyScope = "all" | "application_connect" -export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"] +export type APIKeyScope = "all" | "application_connect"; +export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"]; // From codersdk/workspaceagents.go -export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace" -export const AgentSubsystems: AgentSubsystem[] = ["envbox", "envbuilder", "exectrace"] +export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace"; +export const AgentSubsystems: AgentSubsystem[] = [ + "envbox", + "envbuilder", + "exectrace", +]; // From codersdk/audit.go -export type AuditAction = "create" | "delete" | "login" | "logout" | "register" | "start" | "stop" | "write" -export const AuditActions: AuditAction[] = ["create", "delete", "login", "logout", "register", "start", "stop", "write"] +export type AuditAction = + | "create" + | "delete" + | "login" + | "logout" + | "register" + | "start" + | "stop" + | "write"; +export const AuditActions: AuditAction[] = [ + "create", + "delete", + "login", + "logout", + "register", + "start", + "stop", + "write", +]; // From codersdk/workspaces.go -export type AutomaticUpdates = "always" | "never" -export const AutomaticUpdateses: AutomaticUpdates[] = ["always", "never"] +export type AutomaticUpdates = "always" | "never"; +export const AutomaticUpdateses: AutomaticUpdates[] = ["always", "never"]; // From codersdk/workspacebuilds.go -export type BuildReason = "autostart" | "autostop" | "initiator" -export const BuildReasons: BuildReason[] = ["autostart", "autostop", "initiator"] +export type BuildReason = "autostart" | "autostop" | "initiator"; +export const BuildReasons: BuildReason[] = [ + "autostart", + "autostop", + "initiator", +]; // From codersdk/workspaceagents.go -export type DisplayApp = "port_forwarding_helper" | "ssh_helper" | "vscode" | "vscode_insiders" | "web_terminal" -export const DisplayApps: DisplayApp[] = ["port_forwarding_helper", "ssh_helper", "vscode", "vscode_insiders", "web_terminal"] +export type DisplayApp = + | "port_forwarding_helper" + | "ssh_helper" + | "vscode" + | "vscode_insiders" + | "web_terminal"; +export const DisplayApps: DisplayApp[] = [ + "port_forwarding_helper", + "ssh_helper", + "vscode", + "vscode_insiders", + "web_terminal", +]; // From codersdk/externalauth.go -export type EnhancedExternalAuthProvider = "azure-devops" | "azure-devops-entra" | "bitbucket-cloud" | "bitbucket-server" | "gitea" | "github" | "gitlab" | "jfrog" | "slack" -export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = ["azure-devops", "azure-devops-entra", "bitbucket-cloud", "bitbucket-server", "gitea", "github", "gitlab", "jfrog", "slack"] +export type EnhancedExternalAuthProvider = + | "azure-devops" + | "azure-devops-entra" + | "bitbucket-cloud" + | "bitbucket-server" + | "gitea" + | "github" + | "gitlab" + | "jfrog" + | "slack"; +export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = [ + "azure-devops", + "azure-devops-entra", + "bitbucket-cloud", + "bitbucket-server", + "gitea", + "github", + "gitlab", + "jfrog", + "slack", +]; // From codersdk/deployment.go -export type Entitlement = "entitled" | "grace_period" | "not_entitled" -export const Entitlements: Entitlement[] = ["entitled", "grace_period", "not_entitled"] +export type Entitlement = "entitled" | "grace_period" | "not_entitled"; +export const Entitlements: Entitlement[] = [ + "entitled", + "grace_period", + "not_entitled", +]; // From codersdk/deployment.go -export type Experiment = "auto-fill-parameters" | "custom-roles" | "example" | "multi-organization" | "notifications" | "workspace-usage" -export const Experiments: Experiment[] = ["auto-fill-parameters", "custom-roles", "example", "multi-organization", "notifications", "workspace-usage"] +export type Experiment = + | "auto-fill-parameters" + | "custom-roles" + | "example" + | "multi-organization" + | "notifications" + | "workspace-usage"; +export const Experiments: Experiment[] = [ + "auto-fill-parameters", + "custom-roles", + "example", + "multi-organization", + "notifications", + "workspace-usage", +]; // From codersdk/deployment.go -export type FeatureName = "access_control" | "advanced_template_scheduling" | "appearance" | "audit_log" | "browser_only" | "control_shared_ports" | "custom_roles" | "external_provisioner_daemons" | "external_token_encryption" | "high_availability" | "multiple_external_auth" | "multiple_organizations" | "scim" | "template_rbac" | "user_limit" | "user_role_management" | "workspace_batch_actions" | "workspace_proxy" -export const FeatureNames: FeatureName[] = ["access_control", "advanced_template_scheduling", "appearance", "audit_log", "browser_only", "control_shared_ports", "custom_roles", "external_provisioner_daemons", "external_token_encryption", "high_availability", "multiple_external_auth", "multiple_organizations", "scim", "template_rbac", "user_limit", "user_role_management", "workspace_batch_actions", "workspace_proxy"] +export type FeatureName = + | "access_control" + | "advanced_template_scheduling" + | "appearance" + | "audit_log" + | "browser_only" + | "control_shared_ports" + | "custom_roles" + | "external_provisioner_daemons" + | "external_token_encryption" + | "high_availability" + | "multiple_external_auth" + | "multiple_organizations" + | "scim" + | "template_rbac" + | "user_limit" + | "user_role_management" + | "workspace_batch_actions" + | "workspace_proxy"; +export const FeatureNames: FeatureName[] = [ + "access_control", + "advanced_template_scheduling", + "appearance", + "audit_log", + "browser_only", + "control_shared_ports", + "custom_roles", + "external_provisioner_daemons", + "external_token_encryption", + "high_availability", + "multiple_external_auth", + "multiple_organizations", + "scim", + "template_rbac", + "user_limit", + "user_role_management", + "workspace_batch_actions", + "workspace_proxy", +]; // From codersdk/deployment.go -export type FeatureSet = "" | "enterprise" | "premium" -export const FeatureSets: FeatureSet[] = ["", "enterprise", "premium"] +export type FeatureSet = "" | "enterprise" | "premium"; +export const FeatureSets: FeatureSet[] = ["", "enterprise", "premium"]; // From codersdk/groups.go -export type GroupSource = "oidc" | "user" -export const GroupSources: GroupSource[] = ["oidc", "user"] +export type GroupSource = "oidc" | "user"; +export const GroupSources: GroupSource[] = ["oidc", "user"]; // From codersdk/insights.go -export type InsightsReportInterval = "day" | "week" -export const InsightsReportIntervals: InsightsReportInterval[] = ["day", "week"] +export type InsightsReportInterval = "day" | "week"; +export const InsightsReportIntervals: InsightsReportInterval[] = [ + "day", + "week", +]; // From codersdk/provisionerdaemons.go -export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" -export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"] +export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES"; +export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"]; // From codersdk/provisionerdaemons.go -export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" -export const LogLevels: LogLevel[] = ["debug", "error", "info", "trace", "warn"] +export type LogLevel = "debug" | "error" | "info" | "trace" | "warn"; +export const LogLevels: LogLevel[] = [ + "debug", + "error", + "info", + "trace", + "warn", +]; // From codersdk/provisionerdaemons.go -export type LogSource = "provisioner" | "provisioner_daemon" -export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"] +export type LogSource = "provisioner" | "provisioner_daemon"; +export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"]; // From codersdk/apikey.go -export type LoginType = "" | "github" | "none" | "oidc" | "password" | "token" -export const LoginTypes: LoginType[] = ["", "github", "none", "oidc", "password", "token"] +export type LoginType = "" | "github" | "none" | "oidc" | "password" | "token"; +export const LoginTypes: LoginType[] = [ + "", + "github", + "none", + "oidc", + "password", + "token", +]; // From codersdk/oauth2.go -export type OAuth2ProviderGrantType = "authorization_code" | "refresh_token" -export const OAuth2ProviderGrantTypes: OAuth2ProviderGrantType[] = ["authorization_code", "refresh_token"] +export type OAuth2ProviderGrantType = "authorization_code" | "refresh_token"; +export const OAuth2ProviderGrantTypes: OAuth2ProviderGrantType[] = [ + "authorization_code", + "refresh_token", +]; // From codersdk/oauth2.go -export type OAuth2ProviderResponseType = "code" -export const OAuth2ProviderResponseTypes: OAuth2ProviderResponseType[] = ["code"] +export type OAuth2ProviderResponseType = "code"; +export const OAuth2ProviderResponseTypes: OAuth2ProviderResponseType[] = [ + "code", +]; // From codersdk/deployment.go -export type PostgresAuth = "awsiamrds" | "password" -export const PostgresAuths: PostgresAuth[] = ["awsiamrds", "password"] +export type PostgresAuth = "awsiamrds" | "password"; +export const PostgresAuths: PostgresAuth[] = ["awsiamrds", "password"]; // From codersdk/provisionerdaemons.go -export type ProvisionerJobStatus = "canceled" | "canceling" | "failed" | "pending" | "running" | "succeeded" | "unknown" -export const ProvisionerJobStatuses: ProvisionerJobStatus[] = ["canceled", "canceling", "failed", "pending", "running", "succeeded", "unknown"] +export type ProvisionerJobStatus = + | "canceled" + | "canceling" + | "failed" + | "pending" + | "running" + | "succeeded" + | "unknown"; +export const ProvisionerJobStatuses: ProvisionerJobStatus[] = [ + "canceled", + "canceling", + "failed", + "pending", + "running", + "succeeded", + "unknown", +]; // From codersdk/workspaces.go -export type ProvisionerLogLevel = "debug" -export const ProvisionerLogLevels: ProvisionerLogLevel[] = ["debug"] +export type ProvisionerLogLevel = "debug"; +export const ProvisionerLogLevels: ProvisionerLogLevel[] = ["debug"]; // From codersdk/organizations.go -export type ProvisionerStorageMethod = "file" -export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"] +export type ProvisionerStorageMethod = "file"; +export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"]; // From codersdk/organizations.go -export type ProvisionerType = "echo" | "terraform" -export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"] +export type ProvisionerType = "echo" | "terraform"; +export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"]; // From codersdk/workspaceproxy.go -export type ProxyHealthStatus = "ok" | "unhealthy" | "unreachable" | "unregistered" -export const ProxyHealthStatuses: ProxyHealthStatus[] = ["ok", "unhealthy", "unreachable", "unregistered"] +export type ProxyHealthStatus = + | "ok" + | "unhealthy" + | "unreachable" + | "unregistered"; +export const ProxyHealthStatuses: ProxyHealthStatus[] = [ + "ok", + "unhealthy", + "unreachable", + "unregistered", +]; // From codersdk/rbacresources_gen.go -export type RBACAction = "application_connect" | "assign" | "create" | "delete" | "read" | "read_personal" | "ssh" | "start" | "stop" | "update" | "update_personal" | "use" | "view_insights" -export const RBACActions: RBACAction[] = ["application_connect", "assign", "create", "delete", "read", "read_personal", "ssh", "start", "stop", "update", "update_personal", "use", "view_insights"] +export type RBACAction = + | "application_connect" + | "assign" + | "create" + | "delete" + | "read" + | "read_personal" + | "ssh" + | "start" + | "stop" + | "update" + | "update_personal" + | "use" + | "view_insights"; +export const RBACActions: RBACAction[] = [ + "application_connect", + "assign", + "create", + "delete", + "read", + "read_personal", + "ssh", + "start", + "stop", + "update", + "update_personal", + "use", + "view_insights", +]; // From codersdk/rbacresources_gen.go -export type RBACResource = "*" | "api_key" | "assign_org_role" | "assign_role" | "audit_log" | "debug_info" | "deployment_config" | "deployment_stats" | "file" | "group" | "group_member" | "license" | "notification_preference" | "notification_template" | "oauth2_app" | "oauth2_app_code_token" | "oauth2_app_secret" | "organization" | "organization_member" | "provisioner_daemon" | "provisioner_keys" | "replicas" | "system" | "tailnet_coordinator" | "template" | "user" | "workspace" | "workspace_dormant" | "workspace_proxy" -export const RBACResources: RBACResource[] = ["*", "api_key", "assign_org_role", "assign_role", "audit_log", "debug_info", "deployment_config", "deployment_stats", "file", "group", "group_member", "license", "notification_preference", "notification_template", "oauth2_app", "oauth2_app_code_token", "oauth2_app_secret", "organization", "organization_member", "provisioner_daemon", "provisioner_keys", "replicas", "system", "tailnet_coordinator", "template", "user", "workspace", "workspace_dormant", "workspace_proxy"] +export type RBACResource = + | "*" + | "api_key" + | "assign_org_role" + | "assign_role" + | "audit_log" + | "debug_info" + | "deployment_config" + | "deployment_stats" + | "file" + | "group" + | "group_member" + | "license" + | "notification_preference" + | "notification_template" + | "oauth2_app" + | "oauth2_app_code_token" + | "oauth2_app_secret" + | "organization" + | "organization_member" + | "provisioner_daemon" + | "provisioner_keys" + | "replicas" + | "system" + | "tailnet_coordinator" + | "template" + | "user" + | "workspace" + | "workspace_dormant" + | "workspace_proxy"; +export const RBACResources: RBACResource[] = [ + "*", + "api_key", + "assign_org_role", + "assign_role", + "audit_log", + "debug_info", + "deployment_config", + "deployment_stats", + "file", + "group", + "group_member", + "license", + "notification_preference", + "notification_template", + "oauth2_app", + "oauth2_app_code_token", + "oauth2_app_secret", + "organization", + "organization_member", + "provisioner_daemon", + "provisioner_keys", + "replicas", + "system", + "tailnet_coordinator", + "template", + "user", + "workspace", + "workspace_dormant", + "workspace_proxy", +]; // From codersdk/audit.go -export type ResourceType = "api_key" | "convert_login" | "custom_role" | "git_ssh_key" | "group" | "health_settings" | "license" | "notifications_settings" | "oauth2_provider_app" | "oauth2_provider_app_secret" | "organization" | "template" | "template_version" | "user" | "workspace" | "workspace_build" | "workspace_proxy" -export const ResourceTypes: ResourceType[] = ["api_key", "convert_login", "custom_role", "git_ssh_key", "group", "health_settings", "license", "notifications_settings", "oauth2_provider_app", "oauth2_provider_app_secret", "organization", "template", "template_version", "user", "workspace", "workspace_build", "workspace_proxy"] +export type ResourceType = + | "api_key" + | "convert_login" + | "custom_role" + | "git_ssh_key" + | "group" + | "health_settings" + | "license" + | "notifications_settings" + | "oauth2_provider_app" + | "oauth2_provider_app_secret" + | "organization" + | "template" + | "template_version" + | "user" + | "workspace" + | "workspace_build" + | "workspace_proxy"; +export const ResourceTypes: ResourceType[] = [ + "api_key", + "convert_login", + "custom_role", + "git_ssh_key", + "group", + "health_settings", + "license", + "notifications_settings", + "oauth2_provider_app", + "oauth2_provider_app_secret", + "organization", + "template", + "template_version", + "user", + "workspace", + "workspace_build", + "workspace_proxy", +]; // From codersdk/serversentevents.go -export type ServerSentEventType = "data" | "error" | "ping" -export const ServerSentEventTypes: ServerSentEventType[] = ["data", "error", "ping"] +export type ServerSentEventType = "data" | "error" | "ping"; +export const ServerSentEventTypes: ServerSentEventType[] = [ + "data", + "error", + "ping", +]; // From codersdk/insights.go -export type TemplateAppsType = "app" | "builtin" -export const TemplateAppsTypes: TemplateAppsType[] = ["app", "builtin"] +export type TemplateAppsType = "app" | "builtin"; +export const TemplateAppsTypes: TemplateAppsType[] = ["app", "builtin"]; // From codersdk/insights.go -export type TemplateInsightsSection = "interval_reports" | "report" -export const TemplateInsightsSections: TemplateInsightsSection[] = ["interval_reports", "report"] +export type TemplateInsightsSection = "interval_reports" | "report"; +export const TemplateInsightsSections: TemplateInsightsSection[] = [ + "interval_reports", + "report", +]; // From codersdk/templates.go -export type TemplateRole = "" | "admin" | "use" -export const TemplateRoles: TemplateRole[] = ["", "admin", "use"] +export type TemplateRole = "" | "admin" | "use"; +export const TemplateRoles: TemplateRole[] = ["", "admin", "use"]; // From codersdk/templateversions.go -export type TemplateVersionWarning = "UNSUPPORTED_WORKSPACES" -export const TemplateVersionWarnings: TemplateVersionWarning[] = ["UNSUPPORTED_WORKSPACES"] +export type TemplateVersionWarning = "UNSUPPORTED_WORKSPACES"; +export const TemplateVersionWarnings: TemplateVersionWarning[] = [ + "UNSUPPORTED_WORKSPACES", +]; // From codersdk/workspaces.go -export type UsageAppName = "jetbrains" | "reconnecting-pty" | "ssh" | "vscode" -export const UsageAppNames: UsageAppName[] = ["jetbrains", "reconnecting-pty", "ssh", "vscode"] +export type UsageAppName = "jetbrains" | "reconnecting-pty" | "ssh" | "vscode"; +export const UsageAppNames: UsageAppName[] = [ + "jetbrains", + "reconnecting-pty", + "ssh", + "vscode", +]; // From codersdk/users.go -export type UserStatus = "active" | "dormant" | "suspended" -export const UserStatuses: UserStatus[] = ["active", "dormant", "suspended"] +export type UserStatus = "active" | "dormant" | "suspended"; +export const UserStatuses: UserStatus[] = ["active", "dormant", "suspended"]; // From codersdk/templateversions.go -export type ValidationMonotonicOrder = "decreasing" | "increasing" -export const ValidationMonotonicOrders: ValidationMonotonicOrder[] = ["decreasing", "increasing"] +export type ValidationMonotonicOrder = "decreasing" | "increasing"; +export const ValidationMonotonicOrders: ValidationMonotonicOrder[] = [ + "decreasing", + "increasing", +]; // From codersdk/workspaceagents.go -export type WorkspaceAgentLifecycle = "created" | "off" | "ready" | "shutdown_error" | "shutdown_timeout" | "shutting_down" | "start_error" | "start_timeout" | "starting" -export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = ["created", "off", "ready", "shutdown_error", "shutdown_timeout", "shutting_down", "start_error", "start_timeout", "starting"] +export type WorkspaceAgentLifecycle = + | "created" + | "off" + | "ready" + | "shutdown_error" + | "shutdown_timeout" + | "shutting_down" + | "start_error" + | "start_timeout" + | "starting"; +export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [ + "created", + "off", + "ready", + "shutdown_error", + "shutdown_timeout", + "shutting_down", + "start_error", + "start_timeout", + "starting", +]; // From codersdk/workspaceagentportshare.go -export type WorkspaceAgentPortShareLevel = "authenticated" | "owner" | "public" -export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = ["authenticated", "owner", "public"] +export type WorkspaceAgentPortShareLevel = "authenticated" | "owner" | "public"; +export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = [ + "authenticated", + "owner", + "public", +]; // From codersdk/workspaceagentportshare.go -export type WorkspaceAgentPortShareProtocol = "http" | "https" -export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] = ["http", "https"] +export type WorkspaceAgentPortShareProtocol = "http" | "https"; +export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] = + ["http", "https"]; // From codersdk/workspaceagents.go -export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking" -export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = ["blocking", "non-blocking"] +export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking"; +export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = + ["blocking", "non-blocking"]; // From codersdk/workspaceagents.go -export type WorkspaceAgentStatus = "connected" | "connecting" | "disconnected" | "timeout" -export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = ["connected", "connecting", "disconnected", "timeout"] +export type WorkspaceAgentStatus = + | "connected" + | "connecting" + | "disconnected" + | "timeout"; +export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = [ + "connected", + "connecting", + "disconnected", + "timeout", +]; // From codersdk/workspaceapps.go -export type WorkspaceAppHealth = "disabled" | "healthy" | "initializing" | "unhealthy" -export const WorkspaceAppHealths: WorkspaceAppHealth[] = ["disabled", "healthy", "initializing", "unhealthy"] +export type WorkspaceAppHealth = + | "disabled" + | "healthy" + | "initializing" + | "unhealthy"; +export const WorkspaceAppHealths: WorkspaceAppHealth[] = [ + "disabled", + "healthy", + "initializing", + "unhealthy", +]; // From codersdk/workspaceapps.go -export type WorkspaceAppSharingLevel = "authenticated" | "owner" | "public" -export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = ["authenticated", "owner", "public"] +export type WorkspaceAppSharingLevel = "authenticated" | "owner" | "public"; +export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = [ + "authenticated", + "owner", + "public", +]; // From codersdk/workspacebuilds.go -export type WorkspaceStatus = "canceled" | "canceling" | "deleted" | "deleting" | "failed" | "pending" | "running" | "starting" | "stopped" | "stopping" -export const WorkspaceStatuses: WorkspaceStatus[] = ["canceled", "canceling", "deleted", "deleting", "failed", "pending", "running", "starting", "stopped", "stopping"] +export type WorkspaceStatus = + | "canceled" + | "canceling" + | "deleted" + | "deleting" + | "failed" + | "pending" + | "running" + | "starting" + | "stopped" + | "stopping"; +export const WorkspaceStatuses: WorkspaceStatus[] = [ + "canceled", + "canceling", + "deleted", + "deleting", + "failed", + "pending", + "running", + "starting", + "stopped", + "stopping", +]; // From codersdk/workspacebuilds.go -export type WorkspaceTransition = "delete" | "start" | "stop" -export const WorkspaceTransitions: WorkspaceTransition[] = ["delete", "start", "stop"] +export type WorkspaceTransition = "delete" | "start" | "stop"; +export const WorkspaceTransitions: WorkspaceTransition[] = [ + "delete", + "start", + "stop", +]; // From codersdk/workspaceproxy.go -export type RegionTypes = Region | WorkspaceProxy +export type RegionTypes = Region | WorkspaceProxy; // The code below is generated from codersdk/healthsdk. @@ -2310,8 +2678,21 @@ export interface WorkspaceProxyReport extends BaseReport { } // From healthsdk/healthsdk.go -export type HealthSection = "AccessURL" | "DERP" | "Database" | "ProvisionerDaemons" | "Websocket" | "WorkspaceProxy" -export const HealthSections: HealthSection[] = ["AccessURL", "DERP", "Database", "ProvisionerDaemons", "Websocket", "WorkspaceProxy"] +export type HealthSection = + | "AccessURL" + | "DERP" + | "Database" + | "ProvisionerDaemons" + | "Websocket" + | "WorkspaceProxy"; +export const HealthSections: HealthSection[] = [ + "AccessURL", + "DERP", + "Database", + "ProvisionerDaemons", + "Websocket", + "WorkspaceProxy", +]; // The code below is generated from coderd/healthcheck/health. @@ -2322,17 +2703,54 @@ export interface HealthMessage { } // From health/model.go -export type HealthCode = "EACS01" | "EACS02" | "EACS03" | "EACS04" | "EDB01" | "EDB02" | "EDERP01" | "EDERP02" | "EPD01" | "EPD02" | "EPD03" | "EUNKNOWN" | "EWP01" | "EWP02" | "EWP04" | "EWS01" | "EWS02" | "EWS03" -export const HealthCodes: HealthCode[] = ["EACS01", "EACS02", "EACS03", "EACS04", "EDB01", "EDB02", "EDERP01", "EDERP02", "EPD01", "EPD02", "EPD03", "EUNKNOWN", "EWP01", "EWP02", "EWP04", "EWS01", "EWS02", "EWS03"] +export type HealthCode = + | "EACS01" + | "EACS02" + | "EACS03" + | "EACS04" + | "EDB01" + | "EDB02" + | "EDERP01" + | "EDERP02" + | "EPD01" + | "EPD02" + | "EPD03" + | "EUNKNOWN" + | "EWP01" + | "EWP02" + | "EWP04" + | "EWS01" + | "EWS02" + | "EWS03"; +export const HealthCodes: HealthCode[] = [ + "EACS01", + "EACS02", + "EACS03", + "EACS04", + "EDB01", + "EDB02", + "EDERP01", + "EDERP02", + "EPD01", + "EPD02", + "EPD03", + "EUNKNOWN", + "EWP01", + "EWP02", + "EWP04", + "EWS01", + "EWS02", + "EWS03", +]; // From health/model.go -export type HealthSeverity = "error" | "ok" | "warning" -export const HealthSeveritys: HealthSeverity[] = ["error", "ok", "warning"] +export type HealthSeverity = "error" | "ok" | "warning"; +export const HealthSeveritys: HealthSeverity[] = ["error", "ok", "warning"]; // The code below is generated from github.com/coder/serpent. // From serpent/serpent.go -export type SerpentAnnotations = Record +export type SerpentAnnotations = Record; // From serpent/serpent.go export interface SerpentGroup { @@ -2362,9 +2780,14 @@ export interface SerpentOption { } // From serpent/option.go -export type SerpentOptionSet = Readonly> +export type SerpentOptionSet = Readonly>; // From serpent/option.go -export type SerpentValueSource = "" | "default" | "env" | "flag" | "yaml" -export const SerpentValueSources: SerpentValueSource[] = ["", "default", "env", "flag", "yaml"] - +export type SerpentValueSource = "" | "default" | "env" | "flag" | "yaml"; +export const SerpentValueSources: SerpentValueSource[] = [ + "", + "default", + "env", + "flag", + "yaml", +]; diff --git a/site/src/pages/GroupsPage/GroupsPage.tsx b/site/src/pages/GroupsPage/GroupsPage.tsx index ca637b0eb185f..6313b8e450c9e 100644 --- a/site/src/pages/GroupsPage/GroupsPage.tsx +++ b/site/src/pages/GroupsPage/GroupsPage.tsx @@ -1,5 +1,5 @@ import { getErrorMessage } from "api/errors"; -import { groups } from "api/queries/groups"; +import { groupsByOrganization } from "api/queries/groups"; import { displayError } from "components/GlobalSnackbar/utils"; import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; @@ -12,7 +12,7 @@ import GroupsPageView from "./GroupsPageView"; export const GroupsPage: FC = () => { const { permissions } = useAuthenticated(); const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility(); - const groupsQuery = useQuery(groups("default")); + const groupsQuery = useQuery(groupsByOrganization("default")); useEffect(() => { if (groupsQuery.error) { diff --git a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx index 3c3b3d0b44cdb..4777f289e73eb 100644 --- a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx +++ b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx @@ -1,7 +1,7 @@ import GroupAdd from "@mui/icons-material/GroupAddOutlined"; import Button from "@mui/material/Button"; import { getErrorMessage } from "api/errors"; -import { groups } from "api/queries/groups"; +import { groupsByOrganization } from "api/queries/groups"; import { organizationPermissions } from "api/queries/organizations"; import type { Organization } from "api/typesGenerated"; import { EmptyState } from "components/EmptyState/EmptyState"; @@ -21,11 +21,9 @@ import GroupsPageView from "./GroupsPageView"; export const GroupsPage: FC = () => { const feats = useFeatureVisibility(); const { organization: organizationName } = useParams() as { - organization?: string; + organization: string; }; - const groupsQuery = useQuery( - organizationName ? groups(organizationName) : { enabled: false }, - ); + const groupsQuery = useQuery(groupsByOrganization(organizationName)); const { organizations } = useOrganizationSettings(); const organization = organizations?.find((o) => o.name === organizationName); const permissionsQuery = useQuery(organizationPermissions(organization?.id)); diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx index 2a30b8b2a2ee8..34b0ef29b12e3 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx @@ -17,8 +17,7 @@ export const AccountPage: FC = () => { const hasGroupsFeature = entitlements.features.user_role_management.enabled; const groupsQuery = useQuery({ - // TODO: This should probably list all groups, not just default org groups - ...groupsForUser("default", me.id), + ...groupsForUser(me.id), enabled: hasGroupsFeature, }); diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx index dccb85b001f42..0096df0756263 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx @@ -55,12 +55,7 @@ export const AccountUserGroups: FC = ({ imgUrl={group.avatar_url} altText={group.display_name || group.name} header={group.display_name || group.name} - subtitle={ - <> - {group.total_member_count} member - {group.total_member_count !== 1 && "s"} - - } + subtitle={group.organization_display_name} /> ))} diff --git a/site/src/pages/UsersPage/UsersPage.tsx b/site/src/pages/UsersPage/UsersPage.tsx index bc73e919fd948..9696ebfdeb84f 100644 --- a/site/src/pages/UsersPage/UsersPage.tsx +++ b/site/src/pages/UsersPage/UsersPage.tsx @@ -43,7 +43,9 @@ const UsersPage: FC = () => { const { entitlements, experiments } = useDashboard(); const [searchParams] = searchParamsResult; - const groupsByUserIdQuery = useQuery(groupsByUserId("default")); + // TODO: DEAL WITH THIS BEFORE YOU MERGE IT KAYLA + // SERIOUSLY, PAY ATTENTION + const groupsByUserIdQuery = useQuery(groupsByUserId()); const authMethodsQuery = useQuery(authMethods()); const { permissions, user: me } = useAuthenticated(); diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index c620164bed754..26e8cf125aa72 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2496,6 +2496,7 @@ export const MockGroup: TypesGen.Group = { display_name: "Front-End", avatar_url: "https://example.com", organization_id: MockOrganization.id, + organization_display_name: MockOrganization.display_name, members: [MockUser, MockUser2], quota_allowance: 5, source: "user", @@ -2507,6 +2508,7 @@ const everyOneGroup = (organizationId: string): TypesGen.Group => ({ name: "Everyone", display_name: "", organization_id: organizationId, + organization_display_name: "", members: [], avatar_url: "", quota_allowance: 0, From 164f8d66ec83ebc0c96da48c195d43832c6f4239 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 19:16:58 +0000 Subject: [PATCH 02/15] sqlc.embed --- coderd/database/dbmem/dbmem.go | 8 +------- coderd/database/queries.sql.go | 24 +++++++++--------------- coderd/database/queries/groups.sql | 2 +- coderd/telemetry/telemetry.go | 16 ++++++++-------- 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index e9252cd05a580..4e34456cc2305 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2659,13 +2659,7 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) } filtered = append(filtered, database.GetGroupsRow{ - ID: group.ID, - Name: group.Name, - OrganizationID: group.OrganizationID, - AvatarURL: group.AvatarURL, - QuotaAllowance: group.QuotaAllowance, - DisplayName: group.DisplayName, - Source: group.Source, + Group: group, OrganizationDisplayName: orgDisplayName, }) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 751b60ca96fa0..b94651c1967a4 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1598,14 +1598,8 @@ type GetGroupsParams struct { } type GetGroupsRow struct { - ID uuid.UUID `db:"id" json:"id"` - Name string `db:"name" json:"name"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - AvatarURL string `db:"avatar_url" json:"avatar_url"` - QuotaAllowance int32 `db:"quota_allowance" json:"quota_allowance"` - DisplayName string `db:"display_name" json:"display_name"` - Source GroupSource `db:"source" json:"source"` - OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` + Group Group `db:"group" json:"group"` + OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` } func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetGroupsRow, error) { @@ -1618,13 +1612,13 @@ func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetG for rows.Next() { var i GetGroupsRow if err := rows.Scan( - &i.ID, - &i.Name, - &i.OrganizationID, - &i.AvatarURL, - &i.QuotaAllowance, - &i.DisplayName, - &i.Source, + &i.Group.ID, + &i.Group.Name, + &i.Group.OrganizationID, + &i.Group.AvatarURL, + &i.Group.QuotaAllowance, + &i.Group.DisplayName, + &i.Group.Source, &i.OrganizationDisplayName, ); err != nil { return nil, err diff --git a/coderd/database/queries/groups.sql b/coderd/database/queries/groups.sql index 467e55d3dee89..484beb01cda8b 100644 --- a/coderd/database/queries/groups.sql +++ b/coderd/database/queries/groups.sql @@ -22,7 +22,7 @@ LIMIT -- name: GetGroups :many SELECT - groups.*, organizations.display_name AS organization_display_name + sqlc.embed(groups), organizations.display_name AS organization_display_name FROM groups INNER JOIN diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index c6d35cd29b948..20d6162be568a 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -669,15 +669,15 @@ func ConvertUser(dbUser database.User) User { } } -func ConvertGroup(group database.Group) Group { +func ConvertGroup(row database.GetGroupsRow) Group { return Group{ - ID: group.ID, - Name: group.Name, - OrganizationID: group.OrganizationID, - AvatarURL: group.AvatarURL, - QuotaAllowance: group.QuotaAllowance, - DisplayName: group.DisplayName, - Source: group.Source, + ID: row.Group.ID, + Name: row.Group.Name, + OrganizationID: row.Group.OrganizationID, + AvatarURL: row.Group.AvatarURL, + QuotaAllowance: row.Group.QuotaAllowance, + DisplayName: row.Group.DisplayName, + Source: row.Group.Source, } } From 013b4223cd17d12e6b0d5910d5f1e6b704cebdd4 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 19:41:49 +0000 Subject: [PATCH 03/15] hmm --- coderd/database/db2sdk/db2sdk.go | 21 ++++++++++--------- coderd/database/modelmethods.go | 4 ++++ .../provisionerdserver/provisionerdserver.go | 2 +- enterprise/coderd/groups.go | 4 ++-- enterprise/coderd/templates.go | 2 +- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index 1d513e75aff47..082989af29b32 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -208,17 +208,18 @@ func Users(users []database.User, organizationIDs map[uuid.UUID][]uuid.UUID) []c }) } -func Group(group database.Group, members []database.GroupMember, totalMemberCount int) codersdk.Group { +func Group(group database.GetGroupsRow, members []database.GroupMember, totalMemberCount int) codersdk.Group { return codersdk.Group{ - ID: group.ID, - Name: group.Name, - DisplayName: group.DisplayName, - OrganizationID: group.OrganizationID, - AvatarURL: group.AvatarURL, - Members: ReducedUsersFromGroupMembers(members), - TotalMemberCount: totalMemberCount, - QuotaAllowance: int(group.QuotaAllowance), - Source: codersdk.GroupSource(group.Source), + ID: group.Group.ID, + Name: group.Group.Name, + DisplayName: group.Group.DisplayName, + OrganizationID: group.Group.OrganizationID, + OrganizationDisplayName: group.OrganizationDisplayName, + AvatarURL: group.Group.AvatarURL, + Members: ReducedUsersFromGroupMembers(members), + TotalMemberCount: totalMemberCount, + QuotaAllowance: int(group.Group.QuotaAllowance), + Source: codersdk.GroupSource(group.Group.Source), } } diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index ac6d7e656d4d6..816fc4c9214b0 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -183,6 +183,10 @@ func (g Group) RBACObject() rbac.Object { }) } +func (g GetGroupsRow) RBACObject() rbac.Object { + return g.Group.RBACObject() +} + func (gm GroupMember) RBACObject() rbac.Object { return rbac.ResourceGroupMember.WithID(gm.UserID).InOrg(gm.OrganizationID).WithOwner(gm.UserID.String()) } diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 9a1a1d45a5b85..027bc0964b3d5 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -490,7 +490,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo } ownerGroupNames := []string{} for _, group := range ownerGroups { - ownerGroupNames = append(ownerGroupNames, group.Name) + ownerGroupNames = append(ownerGroupNames, group.Group.Name) } err = s.Pubsub.Publish(codersdk.WorkspaceNotifyChannel(workspace.ID), []byte{}) if err != nil { diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index a6f8c56624eae..230e11af70089 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -456,12 +456,12 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { resp := make([]codersdk.Group, 0, len(groups)) for _, group := range groups { - members, err := api.Database.GetGroupMembersByGroupID(ctx, group.ID) + members, err := api.Database.GetGroupMembersByGroupID(ctx, group.Group.ID) if err != nil { httpapi.InternalServerError(rw, err) return } - memberCount, err := api.Database.GetGroupMembersCountByGroupID(ctx, group.ID) + memberCount, err := api.Database.GetGroupMembersCountByGroupID(ctx, group.Group.ID) if err != nil { httpapi.InternalServerError(rw, err) return diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index e9dc5ea638fff..5f67397696cbc 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -147,7 +147,7 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { return } groups = append(groups, codersdk.TemplateGroup{ - Group: db2sdk.Group(group.Group, members, int(memberCount)), + Group: db2sdk.Group(group, members, int(memberCount)), Role: convertToTemplateRole(group.Actions), }) } From b5424ad536c0362f1a5c91edcdbd20c4b433b3ed Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:06:00 +0000 Subject: [PATCH 04/15] tada --- coderd/apidoc/docs.go | 3 + coderd/apidoc/swagger.json | 3 + coderd/database/db2sdk/db2sdk.go | 29 +- codersdk/groups.go | 5 + docs/reference/api/enterprise.md | 153 ++++---- docs/reference/api/schemas.md | 25 +- enterprise/coderd/groups.go | 4 +- enterprise/coderd/templates.go | 8 +- site/src/api/typesGenerated.ts | 639 ++++++------------------------- 9 files changed, 242 insertions(+), 627 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 08447abb22c50..72ba8293242ce 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10343,6 +10343,9 @@ const docTemplate = `{ "name": { "type": "string" }, + "organization_display_name": { + "type": "string" + }, "organization_id": { "type": "string", "format": "uuid" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 11126e672609a..4042a443e592d 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9287,6 +9287,9 @@ "name": { "type": "string" }, + "organization_display_name": { + "type": "string" + }, "organization_id": { "type": "string", "format": "uuid" diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index 082989af29b32..e826ea2ec8e4b 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -208,18 +208,25 @@ func Users(users []database.User, organizationIDs map[uuid.UUID][]uuid.UUID) []c }) } -func Group(group database.GetGroupsRow, members []database.GroupMember, totalMemberCount int) codersdk.Group { +func Group(group database.Group, members []database.GroupMember, totalMemberCount int) codersdk.Group { return codersdk.Group{ - ID: group.Group.ID, - Name: group.Group.Name, - DisplayName: group.Group.DisplayName, - OrganizationID: group.Group.OrganizationID, - OrganizationDisplayName: group.OrganizationDisplayName, - AvatarURL: group.Group.AvatarURL, - Members: ReducedUsersFromGroupMembers(members), - TotalMemberCount: totalMemberCount, - QuotaAllowance: int(group.Group.QuotaAllowance), - Source: codersdk.GroupSource(group.Group.Source), + ID: group.ID, + Name: group.Name, + DisplayName: group.DisplayName, + OrganizationID: group.OrganizationID, + AvatarURL: group.AvatarURL, + Members: ReducedUsersFromGroupMembers(members), + TotalMemberCount: totalMemberCount, + QuotaAllowance: int(group.QuotaAllowance), + Source: codersdk.GroupSource(group.Source), + } +} + +func GroupWithOrganizationInfo(group database.Group, orgDisplayName string, members []database.GroupMember, totalMemberCount int) codersdk.GroupWithOrganizationInfo { + sdkGroup := Group(group, members, totalMemberCount) + return codersdk.GroupWithOrganizationInfo{ + Group: sdkGroup, + OrganizationDisplayName: orgDisplayName, } } diff --git a/codersdk/groups.go b/codersdk/groups.go index b3b6b4dda9b0d..74a56e8c558b8 100644 --- a/codersdk/groups.go +++ b/codersdk/groups.go @@ -41,6 +41,11 @@ type Group struct { Source GroupSource `json:"source"` } +type GroupWithOrganizationInfo struct { + Group + OrganizationDisplayName string `json:"organization_display_name"` +} + func (g Group) IsEveryone() bool { return g.ID == g.OrganizationID } diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index 18ae4e7de61d5..af26ecc5dad14 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -219,6 +219,7 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -237,29 +238,30 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_display_name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values @@ -322,6 +324,7 @@ curl -X GET http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -381,6 +384,7 @@ curl -X DELETE http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -455,6 +459,7 @@ curl -X PATCH http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1214,6 +1219,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1232,29 +1238,30 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_display_name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values @@ -1330,6 +1337,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1390,6 +1398,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2136,6 +2145,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2171,31 +2181,32 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» groups` | array | false | | | -| `»» avatar_url` | string | false | | | -| `»» display_name` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» members` | array | false | | | -| `»»» avatar_url` | string(uri) | false | | | -| `»»» created_at` | string(date-time) | true | | | -| `»»» email` | string(email) | true | | | -| `»»» id` | string(uuid) | true | | | -| `»»» last_seen_at` | string(date-time) | false | | | -| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»»» name` | string | false | | | -| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»»» theme_preference` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» username` | string | true | | | -| `»» name` | string | false | | | -| `»» organization_id` | string(uuid) | false | | | -| `»» quota_allowance` | integer | false | | | -| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | -| `» users` | array | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------ | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» groups` | array | false | | | +| `»» avatar_url` | string | false | | | +| `»» display_name` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» members` | array | false | | | +| `»»» avatar_url` | string(uri) | false | | | +| `»»» created_at` | string(date-time) | true | | | +| `»»» email` | string(email) | true | | | +| `»»» id` | string(uuid) | true | | | +| `»»» last_seen_at` | string(date-time) | false | | | +| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»»» name` | string | false | | | +| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»»» theme_preference` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» username` | string | true | | | +| `»» name` | string | false | | | +| `»» organization_display_name` | string | false | | | +| `»» organization_id` | string(uuid) | false | | | +| `»» quota_allowance` | integer | false | | | +| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| `» users` | array | false | | | #### Enumerated Values diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index ee229a8cc4914..a8cfc442f7180 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -244,6 +244,7 @@ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2847,6 +2848,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2856,17 +2858,18 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ### Properties -| Name | Type | Required | Restrictions | Description | -| -------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `avatar_url` | string | false | | | -| `display_name` | string | false | | | -| `id` | string | false | | | -| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | -| `name` | string | false | | | -| `organization_id` | string | false | | | -| `quota_allowance` | integer | false | | | -| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | -| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| --------------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `avatar_url` | string | false | | | +| `display_name` | string | false | | | +| `id` | string | false | | | +| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | +| `name` | string | false | | | +| `organization_display_name` | string | false | | | +| `organization_id` | string | false | | | +| `quota_allowance` | integer | false | | | +| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | +| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | ## codersdk.GroupSource diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 230e11af70089..43b48b7bfad08 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -454,7 +454,7 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { return } - resp := make([]codersdk.Group, 0, len(groups)) + resp := make([]codersdk.GroupWithOrganizationInfo, 0, len(groups)) for _, group := range groups { members, err := api.Database.GetGroupMembersByGroupID(ctx, group.Group.ID) if err != nil { @@ -467,7 +467,7 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { return } - resp = append(resp, db2sdk.Group(group, members, int(memberCount))) + resp = append(resp, db2sdk.GroupWithOrganizationInfo(group.Group, group.OrganizationDisplayName, members, int(memberCount))) } httpapi.Write(ctx, rw, http.StatusOK, resp) diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 5f67397696cbc..322846895a028 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -61,18 +61,18 @@ func (api *API) templateAvailablePermissions(rw http.ResponseWriter, r *http.Req sdkGroups := make([]codersdk.Group, 0, len(groups)) for _, group := range groups { // nolint:gocritic - members, err := api.Database.GetGroupMembersByGroupID(dbauthz.AsSystemRestricted(ctx), group.ID) + members, err := api.Database.GetGroupMembersByGroupID(dbauthz.AsSystemRestricted(ctx), group.Group.ID) if err != nil { httpapi.InternalServerError(rw, err) return } - memberCount, err := api.Database.GetGroupMembersCountByGroupID(ctx, group.ID) + memberCount, err := api.Database.GetGroupMembersCountByGroupID(ctx, group.Group.ID) if err != nil { httpapi.InternalServerError(rw, err) return } - sdkGroups = append(sdkGroups, db2sdk.Group(group, members, int(memberCount))) + sdkGroups = append(sdkGroups, db2sdk.Group(group.Group, members, int(memberCount))) } httpapi.Write(ctx, rw, http.StatusOK, codersdk.ACLAvailable{ @@ -147,7 +147,7 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { return } groups = append(groups, codersdk.TemplateGroup{ - Group: db2sdk.Group(group, members, int(memberCount)), + Group: db2sdk.Group(group.Group, members, int(memberCount)), Role: convertToTemplateRole(group.Actions), }) } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 7ffc4c82504a6..d3c02da8e806b 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -71,7 +71,7 @@ export interface AssignableRoles extends Role { } // From codersdk/audit.go -export type AuditDiff = Record; +export type AuditDiff = Record // From codersdk/audit.go export interface AuditDiffField { @@ -150,7 +150,7 @@ export interface AuthorizationRequest { } // From codersdk/authorization.go -export type AuthorizationResponse = Record; +export type AuthorizationResponse = Record // From codersdk/deployment.go export interface AvailableExperiments { @@ -501,7 +501,7 @@ export interface Entitlements { } // From codersdk/deployment.go -export type Experiments = Readonly>; +export type Experiments = Readonly> // From codersdk/externalauth.go export interface ExternalAuth { @@ -632,6 +632,11 @@ export interface GroupArguments { readonly HasMember: string; } +// From codersdk/groups.go +export interface GroupWithOrganizationInfo extends Group { + readonly organization_display_name: string; +} + // From codersdk/workspaceapps.go export interface Healthcheck { readonly url: string; @@ -1276,10 +1281,7 @@ export interface TemplateAutostopRequirement { } // From codersdk/templates.go -export type TemplateBuildTimeStats = Record< - WorkspaceTransition, - TransitionStats ->; +export type TemplateBuildTimeStats = Record // From codersdk/templates.go export interface TemplateExample { @@ -2009,551 +2011,187 @@ export interface WorkspacesResponse { } // From codersdk/apikey.go -export type APIKeyScope = "all" | "application_connect"; -export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"]; +export type APIKeyScope = "all" | "application_connect" +export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"] // From codersdk/workspaceagents.go -export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace"; -export const AgentSubsystems: AgentSubsystem[] = [ - "envbox", - "envbuilder", - "exectrace", -]; +export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace" +export const AgentSubsystems: AgentSubsystem[] = ["envbox", "envbuilder", "exectrace"] // From codersdk/audit.go -export type AuditAction = - | "create" - | "delete" - | "login" - | "logout" - | "register" - | "start" - | "stop" - | "write"; -export const AuditActions: AuditAction[] = [ - "create", - "delete", - "login", - "logout", - "register", - "start", - "stop", - "write", -]; +export type AuditAction = "create" | "delete" | "login" | "logout" | "register" | "start" | "stop" | "write" +export const AuditActions: AuditAction[] = ["create", "delete", "login", "logout", "register", "start", "stop", "write"] // From codersdk/workspaces.go -export type AutomaticUpdates = "always" | "never"; -export const AutomaticUpdateses: AutomaticUpdates[] = ["always", "never"]; +export type AutomaticUpdates = "always" | "never" +export const AutomaticUpdateses: AutomaticUpdates[] = ["always", "never"] // From codersdk/workspacebuilds.go -export type BuildReason = "autostart" | "autostop" | "initiator"; -export const BuildReasons: BuildReason[] = [ - "autostart", - "autostop", - "initiator", -]; +export type BuildReason = "autostart" | "autostop" | "initiator" +export const BuildReasons: BuildReason[] = ["autostart", "autostop", "initiator"] // From codersdk/workspaceagents.go -export type DisplayApp = - | "port_forwarding_helper" - | "ssh_helper" - | "vscode" - | "vscode_insiders" - | "web_terminal"; -export const DisplayApps: DisplayApp[] = [ - "port_forwarding_helper", - "ssh_helper", - "vscode", - "vscode_insiders", - "web_terminal", -]; +export type DisplayApp = "port_forwarding_helper" | "ssh_helper" | "vscode" | "vscode_insiders" | "web_terminal" +export const DisplayApps: DisplayApp[] = ["port_forwarding_helper", "ssh_helper", "vscode", "vscode_insiders", "web_terminal"] // From codersdk/externalauth.go -export type EnhancedExternalAuthProvider = - | "azure-devops" - | "azure-devops-entra" - | "bitbucket-cloud" - | "bitbucket-server" - | "gitea" - | "github" - | "gitlab" - | "jfrog" - | "slack"; -export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = [ - "azure-devops", - "azure-devops-entra", - "bitbucket-cloud", - "bitbucket-server", - "gitea", - "github", - "gitlab", - "jfrog", - "slack", -]; +export type EnhancedExternalAuthProvider = "azure-devops" | "azure-devops-entra" | "bitbucket-cloud" | "bitbucket-server" | "gitea" | "github" | "gitlab" | "jfrog" | "slack" +export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = ["azure-devops", "azure-devops-entra", "bitbucket-cloud", "bitbucket-server", "gitea", "github", "gitlab", "jfrog", "slack"] // From codersdk/deployment.go -export type Entitlement = "entitled" | "grace_period" | "not_entitled"; -export const Entitlements: Entitlement[] = [ - "entitled", - "grace_period", - "not_entitled", -]; +export type Entitlement = "entitled" | "grace_period" | "not_entitled" +export const Entitlements: Entitlement[] = ["entitled", "grace_period", "not_entitled"] // From codersdk/deployment.go -export type Experiment = - | "auto-fill-parameters" - | "custom-roles" - | "example" - | "multi-organization" - | "notifications" - | "workspace-usage"; -export const Experiments: Experiment[] = [ - "auto-fill-parameters", - "custom-roles", - "example", - "multi-organization", - "notifications", - "workspace-usage", -]; +export type Experiment = "auto-fill-parameters" | "custom-roles" | "example" | "multi-organization" | "notifications" | "workspace-usage" +export const Experiments: Experiment[] = ["auto-fill-parameters", "custom-roles", "example", "multi-organization", "notifications", "workspace-usage"] // From codersdk/deployment.go -export type FeatureName = - | "access_control" - | "advanced_template_scheduling" - | "appearance" - | "audit_log" - | "browser_only" - | "control_shared_ports" - | "custom_roles" - | "external_provisioner_daemons" - | "external_token_encryption" - | "high_availability" - | "multiple_external_auth" - | "multiple_organizations" - | "scim" - | "template_rbac" - | "user_limit" - | "user_role_management" - | "workspace_batch_actions" - | "workspace_proxy"; -export const FeatureNames: FeatureName[] = [ - "access_control", - "advanced_template_scheduling", - "appearance", - "audit_log", - "browser_only", - "control_shared_ports", - "custom_roles", - "external_provisioner_daemons", - "external_token_encryption", - "high_availability", - "multiple_external_auth", - "multiple_organizations", - "scim", - "template_rbac", - "user_limit", - "user_role_management", - "workspace_batch_actions", - "workspace_proxy", -]; +export type FeatureName = "access_control" | "advanced_template_scheduling" | "appearance" | "audit_log" | "browser_only" | "control_shared_ports" | "custom_roles" | "external_provisioner_daemons" | "external_token_encryption" | "high_availability" | "multiple_external_auth" | "multiple_organizations" | "scim" | "template_rbac" | "user_limit" | "user_role_management" | "workspace_batch_actions" | "workspace_proxy" +export const FeatureNames: FeatureName[] = ["access_control", "advanced_template_scheduling", "appearance", "audit_log", "browser_only", "control_shared_ports", "custom_roles", "external_provisioner_daemons", "external_token_encryption", "high_availability", "multiple_external_auth", "multiple_organizations", "scim", "template_rbac", "user_limit", "user_role_management", "workspace_batch_actions", "workspace_proxy"] // From codersdk/deployment.go -export type FeatureSet = "" | "enterprise" | "premium"; -export const FeatureSets: FeatureSet[] = ["", "enterprise", "premium"]; +export type FeatureSet = "" | "enterprise" | "premium" +export const FeatureSets: FeatureSet[] = ["", "enterprise", "premium"] // From codersdk/groups.go -export type GroupSource = "oidc" | "user"; -export const GroupSources: GroupSource[] = ["oidc", "user"]; +export type GroupSource = "oidc" | "user" +export const GroupSources: GroupSource[] = ["oidc", "user"] // From codersdk/insights.go -export type InsightsReportInterval = "day" | "week"; -export const InsightsReportIntervals: InsightsReportInterval[] = [ - "day", - "week", -]; +export type InsightsReportInterval = "day" | "week" +export const InsightsReportIntervals: InsightsReportInterval[] = ["day", "week"] // From codersdk/provisionerdaemons.go -export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES"; -export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"]; +export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" +export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"] // From codersdk/provisionerdaemons.go -export type LogLevel = "debug" | "error" | "info" | "trace" | "warn"; -export const LogLevels: LogLevel[] = [ - "debug", - "error", - "info", - "trace", - "warn", -]; +export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" +export const LogLevels: LogLevel[] = ["debug", "error", "info", "trace", "warn"] // From codersdk/provisionerdaemons.go -export type LogSource = "provisioner" | "provisioner_daemon"; -export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"]; +export type LogSource = "provisioner" | "provisioner_daemon" +export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"] // From codersdk/apikey.go -export type LoginType = "" | "github" | "none" | "oidc" | "password" | "token"; -export const LoginTypes: LoginType[] = [ - "", - "github", - "none", - "oidc", - "password", - "token", -]; +export type LoginType = "" | "github" | "none" | "oidc" | "password" | "token" +export const LoginTypes: LoginType[] = ["", "github", "none", "oidc", "password", "token"] // From codersdk/oauth2.go -export type OAuth2ProviderGrantType = "authorization_code" | "refresh_token"; -export const OAuth2ProviderGrantTypes: OAuth2ProviderGrantType[] = [ - "authorization_code", - "refresh_token", -]; +export type OAuth2ProviderGrantType = "authorization_code" | "refresh_token" +export const OAuth2ProviderGrantTypes: OAuth2ProviderGrantType[] = ["authorization_code", "refresh_token"] // From codersdk/oauth2.go -export type OAuth2ProviderResponseType = "code"; -export const OAuth2ProviderResponseTypes: OAuth2ProviderResponseType[] = [ - "code", -]; +export type OAuth2ProviderResponseType = "code" +export const OAuth2ProviderResponseTypes: OAuth2ProviderResponseType[] = ["code"] // From codersdk/deployment.go -export type PostgresAuth = "awsiamrds" | "password"; -export const PostgresAuths: PostgresAuth[] = ["awsiamrds", "password"]; +export type PostgresAuth = "awsiamrds" | "password" +export const PostgresAuths: PostgresAuth[] = ["awsiamrds", "password"] // From codersdk/provisionerdaemons.go -export type ProvisionerJobStatus = - | "canceled" - | "canceling" - | "failed" - | "pending" - | "running" - | "succeeded" - | "unknown"; -export const ProvisionerJobStatuses: ProvisionerJobStatus[] = [ - "canceled", - "canceling", - "failed", - "pending", - "running", - "succeeded", - "unknown", -]; +export type ProvisionerJobStatus = "canceled" | "canceling" | "failed" | "pending" | "running" | "succeeded" | "unknown" +export const ProvisionerJobStatuses: ProvisionerJobStatus[] = ["canceled", "canceling", "failed", "pending", "running", "succeeded", "unknown"] // From codersdk/workspaces.go -export type ProvisionerLogLevel = "debug"; -export const ProvisionerLogLevels: ProvisionerLogLevel[] = ["debug"]; +export type ProvisionerLogLevel = "debug" +export const ProvisionerLogLevels: ProvisionerLogLevel[] = ["debug"] // From codersdk/organizations.go -export type ProvisionerStorageMethod = "file"; -export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"]; +export type ProvisionerStorageMethod = "file" +export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"] // From codersdk/organizations.go -export type ProvisionerType = "echo" | "terraform"; -export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"]; +export type ProvisionerType = "echo" | "terraform" +export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"] // From codersdk/workspaceproxy.go -export type ProxyHealthStatus = - | "ok" - | "unhealthy" - | "unreachable" - | "unregistered"; -export const ProxyHealthStatuses: ProxyHealthStatus[] = [ - "ok", - "unhealthy", - "unreachable", - "unregistered", -]; +export type ProxyHealthStatus = "ok" | "unhealthy" | "unreachable" | "unregistered" +export const ProxyHealthStatuses: ProxyHealthStatus[] = ["ok", "unhealthy", "unreachable", "unregistered"] // From codersdk/rbacresources_gen.go -export type RBACAction = - | "application_connect" - | "assign" - | "create" - | "delete" - | "read" - | "read_personal" - | "ssh" - | "start" - | "stop" - | "update" - | "update_personal" - | "use" - | "view_insights"; -export const RBACActions: RBACAction[] = [ - "application_connect", - "assign", - "create", - "delete", - "read", - "read_personal", - "ssh", - "start", - "stop", - "update", - "update_personal", - "use", - "view_insights", -]; +export type RBACAction = "application_connect" | "assign" | "create" | "delete" | "read" | "read_personal" | "ssh" | "start" | "stop" | "update" | "update_personal" | "use" | "view_insights" +export const RBACActions: RBACAction[] = ["application_connect", "assign", "create", "delete", "read", "read_personal", "ssh", "start", "stop", "update", "update_personal", "use", "view_insights"] // From codersdk/rbacresources_gen.go -export type RBACResource = - | "*" - | "api_key" - | "assign_org_role" - | "assign_role" - | "audit_log" - | "debug_info" - | "deployment_config" - | "deployment_stats" - | "file" - | "group" - | "group_member" - | "license" - | "notification_preference" - | "notification_template" - | "oauth2_app" - | "oauth2_app_code_token" - | "oauth2_app_secret" - | "organization" - | "organization_member" - | "provisioner_daemon" - | "provisioner_keys" - | "replicas" - | "system" - | "tailnet_coordinator" - | "template" - | "user" - | "workspace" - | "workspace_dormant" - | "workspace_proxy"; -export const RBACResources: RBACResource[] = [ - "*", - "api_key", - "assign_org_role", - "assign_role", - "audit_log", - "debug_info", - "deployment_config", - "deployment_stats", - "file", - "group", - "group_member", - "license", - "notification_preference", - "notification_template", - "oauth2_app", - "oauth2_app_code_token", - "oauth2_app_secret", - "organization", - "organization_member", - "provisioner_daemon", - "provisioner_keys", - "replicas", - "system", - "tailnet_coordinator", - "template", - "user", - "workspace", - "workspace_dormant", - "workspace_proxy", -]; +export type RBACResource = "*" | "api_key" | "assign_org_role" | "assign_role" | "audit_log" | "debug_info" | "deployment_config" | "deployment_stats" | "file" | "group" | "group_member" | "license" | "notification_preference" | "notification_template" | "oauth2_app" | "oauth2_app_code_token" | "oauth2_app_secret" | "organization" | "organization_member" | "provisioner_daemon" | "provisioner_keys" | "replicas" | "system" | "tailnet_coordinator" | "template" | "user" | "workspace" | "workspace_dormant" | "workspace_proxy" +export const RBACResources: RBACResource[] = ["*", "api_key", "assign_org_role", "assign_role", "audit_log", "debug_info", "deployment_config", "deployment_stats", "file", "group", "group_member", "license", "notification_preference", "notification_template", "oauth2_app", "oauth2_app_code_token", "oauth2_app_secret", "organization", "organization_member", "provisioner_daemon", "provisioner_keys", "replicas", "system", "tailnet_coordinator", "template", "user", "workspace", "workspace_dormant", "workspace_proxy"] // From codersdk/audit.go -export type ResourceType = - | "api_key" - | "convert_login" - | "custom_role" - | "git_ssh_key" - | "group" - | "health_settings" - | "license" - | "notifications_settings" - | "oauth2_provider_app" - | "oauth2_provider_app_secret" - | "organization" - | "template" - | "template_version" - | "user" - | "workspace" - | "workspace_build" - | "workspace_proxy"; -export const ResourceTypes: ResourceType[] = [ - "api_key", - "convert_login", - "custom_role", - "git_ssh_key", - "group", - "health_settings", - "license", - "notifications_settings", - "oauth2_provider_app", - "oauth2_provider_app_secret", - "organization", - "template", - "template_version", - "user", - "workspace", - "workspace_build", - "workspace_proxy", -]; +export type ResourceType = "api_key" | "convert_login" | "custom_role" | "git_ssh_key" | "group" | "health_settings" | "license" | "notifications_settings" | "oauth2_provider_app" | "oauth2_provider_app_secret" | "organization" | "template" | "template_version" | "user" | "workspace" | "workspace_build" | "workspace_proxy" +export const ResourceTypes: ResourceType[] = ["api_key", "convert_login", "custom_role", "git_ssh_key", "group", "health_settings", "license", "notifications_settings", "oauth2_provider_app", "oauth2_provider_app_secret", "organization", "template", "template_version", "user", "workspace", "workspace_build", "workspace_proxy"] // From codersdk/serversentevents.go -export type ServerSentEventType = "data" | "error" | "ping"; -export const ServerSentEventTypes: ServerSentEventType[] = [ - "data", - "error", - "ping", -]; +export type ServerSentEventType = "data" | "error" | "ping" +export const ServerSentEventTypes: ServerSentEventType[] = ["data", "error", "ping"] // From codersdk/insights.go -export type TemplateAppsType = "app" | "builtin"; -export const TemplateAppsTypes: TemplateAppsType[] = ["app", "builtin"]; +export type TemplateAppsType = "app" | "builtin" +export const TemplateAppsTypes: TemplateAppsType[] = ["app", "builtin"] // From codersdk/insights.go -export type TemplateInsightsSection = "interval_reports" | "report"; -export const TemplateInsightsSections: TemplateInsightsSection[] = [ - "interval_reports", - "report", -]; +export type TemplateInsightsSection = "interval_reports" | "report" +export const TemplateInsightsSections: TemplateInsightsSection[] = ["interval_reports", "report"] // From codersdk/templates.go -export type TemplateRole = "" | "admin" | "use"; -export const TemplateRoles: TemplateRole[] = ["", "admin", "use"]; +export type TemplateRole = "" | "admin" | "use" +export const TemplateRoles: TemplateRole[] = ["", "admin", "use"] // From codersdk/templateversions.go -export type TemplateVersionWarning = "UNSUPPORTED_WORKSPACES"; -export const TemplateVersionWarnings: TemplateVersionWarning[] = [ - "UNSUPPORTED_WORKSPACES", -]; +export type TemplateVersionWarning = "UNSUPPORTED_WORKSPACES" +export const TemplateVersionWarnings: TemplateVersionWarning[] = ["UNSUPPORTED_WORKSPACES"] // From codersdk/workspaces.go -export type UsageAppName = "jetbrains" | "reconnecting-pty" | "ssh" | "vscode"; -export const UsageAppNames: UsageAppName[] = [ - "jetbrains", - "reconnecting-pty", - "ssh", - "vscode", -]; +export type UsageAppName = "jetbrains" | "reconnecting-pty" | "ssh" | "vscode" +export const UsageAppNames: UsageAppName[] = ["jetbrains", "reconnecting-pty", "ssh", "vscode"] // From codersdk/users.go -export type UserStatus = "active" | "dormant" | "suspended"; -export const UserStatuses: UserStatus[] = ["active", "dormant", "suspended"]; +export type UserStatus = "active" | "dormant" | "suspended" +export const UserStatuses: UserStatus[] = ["active", "dormant", "suspended"] // From codersdk/templateversions.go -export type ValidationMonotonicOrder = "decreasing" | "increasing"; -export const ValidationMonotonicOrders: ValidationMonotonicOrder[] = [ - "decreasing", - "increasing", -]; +export type ValidationMonotonicOrder = "decreasing" | "increasing" +export const ValidationMonotonicOrders: ValidationMonotonicOrder[] = ["decreasing", "increasing"] // From codersdk/workspaceagents.go -export type WorkspaceAgentLifecycle = - | "created" - | "off" - | "ready" - | "shutdown_error" - | "shutdown_timeout" - | "shutting_down" - | "start_error" - | "start_timeout" - | "starting"; -export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [ - "created", - "off", - "ready", - "shutdown_error", - "shutdown_timeout", - "shutting_down", - "start_error", - "start_timeout", - "starting", -]; +export type WorkspaceAgentLifecycle = "created" | "off" | "ready" | "shutdown_error" | "shutdown_timeout" | "shutting_down" | "start_error" | "start_timeout" | "starting" +export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = ["created", "off", "ready", "shutdown_error", "shutdown_timeout", "shutting_down", "start_error", "start_timeout", "starting"] // From codersdk/workspaceagentportshare.go -export type WorkspaceAgentPortShareLevel = "authenticated" | "owner" | "public"; -export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = [ - "authenticated", - "owner", - "public", -]; +export type WorkspaceAgentPortShareLevel = "authenticated" | "owner" | "public" +export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = ["authenticated", "owner", "public"] // From codersdk/workspaceagentportshare.go -export type WorkspaceAgentPortShareProtocol = "http" | "https"; -export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] = - ["http", "https"]; +export type WorkspaceAgentPortShareProtocol = "http" | "https" +export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] = ["http", "https"] // From codersdk/workspaceagents.go -export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking"; -export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = - ["blocking", "non-blocking"]; +export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking" +export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = ["blocking", "non-blocking"] // From codersdk/workspaceagents.go -export type WorkspaceAgentStatus = - | "connected" - | "connecting" - | "disconnected" - | "timeout"; -export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = [ - "connected", - "connecting", - "disconnected", - "timeout", -]; +export type WorkspaceAgentStatus = "connected" | "connecting" | "disconnected" | "timeout" +export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = ["connected", "connecting", "disconnected", "timeout"] // From codersdk/workspaceapps.go -export type WorkspaceAppHealth = - | "disabled" - | "healthy" - | "initializing" - | "unhealthy"; -export const WorkspaceAppHealths: WorkspaceAppHealth[] = [ - "disabled", - "healthy", - "initializing", - "unhealthy", -]; +export type WorkspaceAppHealth = "disabled" | "healthy" | "initializing" | "unhealthy" +export const WorkspaceAppHealths: WorkspaceAppHealth[] = ["disabled", "healthy", "initializing", "unhealthy"] // From codersdk/workspaceapps.go -export type WorkspaceAppSharingLevel = "authenticated" | "owner" | "public"; -export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = [ - "authenticated", - "owner", - "public", -]; +export type WorkspaceAppSharingLevel = "authenticated" | "owner" | "public" +export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = ["authenticated", "owner", "public"] // From codersdk/workspacebuilds.go -export type WorkspaceStatus = - | "canceled" - | "canceling" - | "deleted" - | "deleting" - | "failed" - | "pending" - | "running" - | "starting" - | "stopped" - | "stopping"; -export const WorkspaceStatuses: WorkspaceStatus[] = [ - "canceled", - "canceling", - "deleted", - "deleting", - "failed", - "pending", - "running", - "starting", - "stopped", - "stopping", -]; +export type WorkspaceStatus = "canceled" | "canceling" | "deleted" | "deleting" | "failed" | "pending" | "running" | "starting" | "stopped" | "stopping" +export const WorkspaceStatuses: WorkspaceStatus[] = ["canceled", "canceling", "deleted", "deleting", "failed", "pending", "running", "starting", "stopped", "stopping"] // From codersdk/workspacebuilds.go -export type WorkspaceTransition = "delete" | "start" | "stop"; -export const WorkspaceTransitions: WorkspaceTransition[] = [ - "delete", - "start", - "stop", -]; +export type WorkspaceTransition = "delete" | "start" | "stop" +export const WorkspaceTransitions: WorkspaceTransition[] = ["delete", "start", "stop"] // From codersdk/workspaceproxy.go -export type RegionTypes = Region | WorkspaceProxy; +export type RegionTypes = Region | WorkspaceProxy // The code below is generated from codersdk/healthsdk. @@ -2679,21 +2317,8 @@ export interface WorkspaceProxyReport extends BaseReport { } // From healthsdk/healthsdk.go -export type HealthSection = - | "AccessURL" - | "DERP" - | "Database" - | "ProvisionerDaemons" - | "Websocket" - | "WorkspaceProxy"; -export const HealthSections: HealthSection[] = [ - "AccessURL", - "DERP", - "Database", - "ProvisionerDaemons", - "Websocket", - "WorkspaceProxy", -]; +export type HealthSection = "AccessURL" | "DERP" | "Database" | "ProvisionerDaemons" | "Websocket" | "WorkspaceProxy" +export const HealthSections: HealthSection[] = ["AccessURL", "DERP", "Database", "ProvisionerDaemons", "Websocket", "WorkspaceProxy"] // The code below is generated from coderd/healthcheck/health. @@ -2704,54 +2329,17 @@ export interface HealthMessage { } // From health/model.go -export type HealthCode = - | "EACS01" - | "EACS02" - | "EACS03" - | "EACS04" - | "EDB01" - | "EDB02" - | "EDERP01" - | "EDERP02" - | "EPD01" - | "EPD02" - | "EPD03" - | "EUNKNOWN" - | "EWP01" - | "EWP02" - | "EWP04" - | "EWS01" - | "EWS02" - | "EWS03"; -export const HealthCodes: HealthCode[] = [ - "EACS01", - "EACS02", - "EACS03", - "EACS04", - "EDB01", - "EDB02", - "EDERP01", - "EDERP02", - "EPD01", - "EPD02", - "EPD03", - "EUNKNOWN", - "EWP01", - "EWP02", - "EWP04", - "EWS01", - "EWS02", - "EWS03", -]; +export type HealthCode = "EACS01" | "EACS02" | "EACS03" | "EACS04" | "EDB01" | "EDB02" | "EDERP01" | "EDERP02" | "EPD01" | "EPD02" | "EPD03" | "EUNKNOWN" | "EWP01" | "EWP02" | "EWP04" | "EWS01" | "EWS02" | "EWS03" +export const HealthCodes: HealthCode[] = ["EACS01", "EACS02", "EACS03", "EACS04", "EDB01", "EDB02", "EDERP01", "EDERP02", "EPD01", "EPD02", "EPD03", "EUNKNOWN", "EWP01", "EWP02", "EWP04", "EWS01", "EWS02", "EWS03"] // From health/model.go -export type HealthSeverity = "error" | "ok" | "warning"; -export const HealthSeveritys: HealthSeverity[] = ["error", "ok", "warning"]; +export type HealthSeverity = "error" | "ok" | "warning" +export const HealthSeveritys: HealthSeverity[] = ["error", "ok", "warning"] // The code below is generated from github.com/coder/serpent. // From serpent/serpent.go -export type SerpentAnnotations = Record; +export type SerpentAnnotations = Record // From serpent/serpent.go export interface SerpentGroup { @@ -2781,14 +2369,9 @@ export interface SerpentOption { } // From serpent/option.go -export type SerpentOptionSet = Readonly>; +export type SerpentOptionSet = Readonly> // From serpent/option.go -export type SerpentValueSource = "" | "default" | "env" | "flag" | "yaml"; -export const SerpentValueSources: SerpentValueSource[] = [ - "", - "default", - "env", - "flag", - "yaml", -]; +export type SerpentValueSource = "" | "default" | "env" | "flag" | "yaml" +export const SerpentValueSources: SerpentValueSource[] = ["", "default", "env", "flag", "yaml"] + From d0cf4540a38738bd7a48b3db70f4e40d31b8cc4e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:12:30 +0000 Subject: [PATCH 05/15] somethin' --- coderd/apidoc/docs.go | 3 - coderd/apidoc/swagger.json | 3 - coderd/database/db2sdk/db2sdk.go | 3 +- coderd/database/queries.sql.go | 6 +- coderd/database/queries/groups.sql | 4 +- coderd/telemetry/telemetry.go | 18 ++-- codersdk/groups.go | 12 +-- docs/reference/api/enterprise.md | 153 +++++++++++++---------------- docs/reference/api/schemas.md | 25 +++-- enterprise/coderd/groups.go | 6 +- site/src/api/typesGenerated.ts | 2 +- 11 files changed, 113 insertions(+), 122 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 72ba8293242ce..08447abb22c50 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10343,9 +10343,6 @@ const docTemplate = `{ "name": { "type": "string" }, - "organization_display_name": { - "type": "string" - }, "organization_id": { "type": "string", "format": "uuid" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4042a443e592d..11126e672609a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9287,9 +9287,6 @@ "name": { "type": "string" }, - "organization_display_name": { - "type": "string" - }, "organization_id": { "type": "string", "format": "uuid" diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index e826ea2ec8e4b..b66aaa6f07dc8 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -222,10 +222,11 @@ func Group(group database.Group, members []database.GroupMember, totalMemberCoun } } -func GroupWithOrganizationInfo(group database.Group, orgDisplayName string, members []database.GroupMember, totalMemberCount int) codersdk.GroupWithOrganizationInfo { +func GroupWithOrganizationInfo(group database.Group, orgName, orgDisplayName string, members []database.GroupMember, totalMemberCount int) codersdk.GroupWithOrganizationInfo { sdkGroup := Group(group, members, totalMemberCount) return codersdk.GroupWithOrganizationInfo{ Group: sdkGroup, + OrganizationName: orgName, OrganizationDisplayName: orgDisplayName, } } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index b94651c1967a4..85f6b854b3df2 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1562,7 +1562,9 @@ func (q *sqlQuerier) GetGroupByOrgAndName(ctx context.Context, arg GetGroupByOrg const getGroups = `-- name: GetGroups :many SELECT - groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source, organizations.display_name AS organization_display_name + groups.id, groups.name, groups.organization_id, groups.avatar_url, groups.quota_allowance, groups.display_name, groups.source, + organizations.name AS organization_name, + organizations.display_name AS organization_display_name FROM groups INNER JOIN @@ -1599,6 +1601,7 @@ type GetGroupsParams struct { type GetGroupsRow struct { Group Group `db:"group" json:"group"` + OrganizationName string `db:"organization_name" json:"organization_name"` OrganizationDisplayName string `db:"organization_display_name" json:"organization_display_name"` } @@ -1619,6 +1622,7 @@ func (q *sqlQuerier) GetGroups(ctx context.Context, arg GetGroupsParams) ([]GetG &i.Group.QuotaAllowance, &i.Group.DisplayName, &i.Group.Source, + &i.OrganizationName, &i.OrganizationDisplayName, ); err != nil { return nil, err diff --git a/coderd/database/queries/groups.sql b/coderd/database/queries/groups.sql index 484beb01cda8b..08a8da4d7645f 100644 --- a/coderd/database/queries/groups.sql +++ b/coderd/database/queries/groups.sql @@ -22,7 +22,9 @@ LIMIT -- name: GetGroups :many SELECT - sqlc.embed(groups), organizations.display_name AS organization_display_name + sqlc.embed(groups), + organizations.name AS organization_name, + organizations.display_name AS organization_display_name FROM groups INNER JOIN diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 20d6162be568a..7078ef07334f9 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -373,7 +373,7 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) { } snapshot.Groups = make([]Group, 0, len(groups)) for _, group := range groups { - snapshot.Groups = append(snapshot.Groups, ConvertGroup(group)) + snapshot.Groups = append(snapshot.Groups, ConvertGroup(group.Group)) } return nil }) @@ -669,15 +669,15 @@ func ConvertUser(dbUser database.User) User { } } -func ConvertGroup(row database.GetGroupsRow) Group { +func ConvertGroup(group database.Group) Group { return Group{ - ID: row.Group.ID, - Name: row.Group.Name, - OrganizationID: row.Group.OrganizationID, - AvatarURL: row.Group.AvatarURL, - QuotaAllowance: row.Group.QuotaAllowance, - DisplayName: row.Group.DisplayName, - Source: row.Group.Source, + ID: group.ID, + Name: group.Name, + OrganizationID: group.OrganizationID, + AvatarURL: group.AvatarURL, + QuotaAllowance: group.QuotaAllowance, + DisplayName: group.DisplayName, + Source: group.Source, } } diff --git a/codersdk/groups.go b/codersdk/groups.go index 74a56e8c558b8..ded8566e5de6d 100644 --- a/codersdk/groups.go +++ b/codersdk/groups.go @@ -26,12 +26,11 @@ type CreateGroupRequest struct { } type Group struct { - ID uuid.UUID `json:"id" format:"uuid"` - Name string `json:"name"` - DisplayName string `json:"display_name"` - OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` - OrganizationDisplayName string `json:"organization_display_name"` - Members []ReducedUser `json:"members"` + ID uuid.UUID `json:"id" format:"uuid"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + OrganizationID uuid.UUID `json:"organization_id" format:"uuid"` + Members []ReducedUser `json:"members"` // How many members are in this group. Shows the total count, // even if the user is not authorized to read group member details. // May be greater than `len(Group.Members)`. @@ -43,6 +42,7 @@ type Group struct { type GroupWithOrganizationInfo struct { Group + OrganizationName string `json:"organization_name"` OrganizationDisplayName string `json:"organization_display_name"` } diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index af26ecc5dad14..18ae4e7de61d5 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -219,7 +219,6 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -238,30 +237,29 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_display_name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values @@ -324,7 +322,6 @@ curl -X GET http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -384,7 +381,6 @@ curl -X DELETE http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -459,7 +455,6 @@ curl -X PATCH http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1219,7 +1214,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1238,30 +1232,29 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_display_name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values @@ -1337,7 +1330,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -1398,7 +1390,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2145,7 +2136,6 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2181,32 +2171,31 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------ | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» groups` | array | false | | | -| `»» avatar_url` | string | false | | | -| `»» display_name` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» members` | array | false | | | -| `»»» avatar_url` | string(uri) | false | | | -| `»»» created_at` | string(date-time) | true | | | -| `»»» email` | string(email) | true | | | -| `»»» id` | string(uuid) | true | | | -| `»»» last_seen_at` | string(date-time) | false | | | -| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»»» name` | string | false | | | -| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»»» theme_preference` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» username` | string | true | | | -| `»» name` | string | false | | | -| `»» organization_display_name` | string | false | | | -| `»» organization_id` | string(uuid) | false | | | -| `»» quota_allowance` | integer | false | | | -| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | -| `» users` | array | false | | | +| Name | Type | Required | Restrictions | Description | +| ----------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» groups` | array | false | | | +| `»» avatar_url` | string | false | | | +| `»» display_name` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» members` | array | false | | | +| `»»» avatar_url` | string(uri) | false | | | +| `»»» created_at` | string(date-time) | true | | | +| `»»» email` | string(email) | true | | | +| `»»» id` | string(uuid) | true | | | +| `»»» last_seen_at` | string(date-time) | false | | | +| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»»» name` | string | false | | | +| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»»» theme_preference` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» username` | string | true | | | +| `»» name` | string | false | | | +| `»» organization_id` | string(uuid) | false | | | +| `»» quota_allowance` | integer | false | | | +| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| `» users` | array | false | | | #### Enumerated Values diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index a8cfc442f7180..ee229a8cc4914 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -244,7 +244,6 @@ } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2848,7 +2847,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o } ], "name": "string", - "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "quota_allowance": 0, "source": "user", @@ -2858,18 +2856,17 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ### Properties -| Name | Type | Required | Restrictions | Description | -| --------------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `avatar_url` | string | false | | | -| `display_name` | string | false | | | -| `id` | string | false | | | -| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | -| `name` | string | false | | | -| `organization_display_name` | string | false | | | -| `organization_id` | string | false | | | -| `quota_allowance` | integer | false | | | -| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | -| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `avatar_url` | string | false | | | +| `display_name` | string | false | | | +| `id` | string | false | | | +| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | +| `name` | string | false | | | +| `organization_id` | string | false | | | +| `quota_allowance` | integer | false | | | +| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | +| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | ## codersdk.GroupSource diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 43b48b7bfad08..5097cd227e255 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -467,7 +467,11 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { return } - resp = append(resp, db2sdk.GroupWithOrganizationInfo(group.Group, group.OrganizationDisplayName, members, int(memberCount))) + resp = append(resp, db2sdk.GroupWithOrganizationInfo( + group.Group, + group.OrganizationName, group.OrganizationDisplayName, + members, int(memberCount), + )) } httpapi.Write(ctx, rw, http.StatusOK, resp) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index d3c02da8e806b..f530242d6f6ed 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -618,7 +618,6 @@ export interface Group { readonly name: string; readonly display_name: string; readonly organization_id: string; - readonly organization_display_name: string; readonly members: Readonly>; readonly total_member_count: number; readonly avatar_url: string; @@ -634,6 +633,7 @@ export interface GroupArguments { // From codersdk/groups.go export interface GroupWithOrganizationInfo extends Group { + readonly organization_name: string; readonly organization_display_name: string; } From 90643b591fa31a171162fbe6907e0693db2346b0 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:18:11 +0000 Subject: [PATCH 06/15] fix entities --- site/src/testHelpers/entities.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 93ab4c87f4210..1b37cf71eec95 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2491,12 +2491,13 @@ export const MockWorkspaceQuota: TypesGen.WorkspaceQuota = { budget: 100, }; -export const MockGroup: TypesGen.Group = { +export const MockGroup: TypesGen.GroupWithOrganizationInfo = { id: "fbd2116a-8961-4954-87ae-e4575bd29ce0", name: "Front-End", display_name: "Front-End", avatar_url: "https://example.com", organization_id: MockOrganization.id, + organization_name: MockOrganization.name, organization_display_name: MockOrganization.display_name, members: [MockUser, MockUser2], quota_allowance: 5, @@ -2504,22 +2505,21 @@ export const MockGroup: TypesGen.Group = { total_member_count: 2, }; -const everyOneGroup = (organizationId: string): TypesGen.Group => ({ - id: organizationId, +const MockEveryoneGroup: TypesGen.Group = { + id: `${MockOrganization.id}-everyone`, name: "Everyone", display_name: "", - organization_id: organizationId, - organization_display_name: "", + organization_id: MockOrganization.id, members: [], avatar_url: "", quota_allowance: 0, source: "user", total_member_count: 0, -}); +}; export const MockTemplateACL: TypesGen.TemplateACL = { group: [ - { ...everyOneGroup(MockOrganization.id), role: "use" }, + { ...MockEveryoneGroup, role: "use" }, { ...MockGroup, role: "admin" }, ], users: [{ ...MockUser, role: "use" }], From ab2658f1b87b1f4dfc9b02ed584e72389b280eb5 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:28:14 +0000 Subject: [PATCH 07/15] bruh just do it then --- coderd/database/dbmem/dbmem.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 4e34456cc2305..efdc5f6ea8c52 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2632,7 +2632,6 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) groupIDs[orgMember.OrganizationID] = struct{}{} } } - } organizationDisplayNames := make(map[uuid.UUID]string) From 2b231a58dab80c31e4a8680f0c3d35a566e3db3b Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:35:07 +0000 Subject: [PATCH 08/15] what --- coderd/database/dbauthz/dbauthz_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index a270e4d2cb0d1..e7ad525b6bedb 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -607,7 +607,10 @@ func (s *MethodTestSuite) TestOrganization() { check.Args(database.GetGroupsParams{ OrganizationID: o.ID, }).Asserts(rbac.ResourceSystem, policy.ActionRead, a, policy.ActionRead, b, policy.ActionRead). - Returns([]database.Group{a, b}). + Returns([]database.GetGroupsRow{ + {Group: a, OrganizationName: o.Name, OrganizationDisplayName: o.DisplayName}, + {Group: b, OrganizationName: o.Name, OrganizationDisplayName: o.DisplayName}, + }). // Fail the system check shortcut FailSystemObjectChecks() })) From 7f147fc25e3fd45ddc76d5631f3071c51800cdc5 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:39:03 +0000 Subject: [PATCH 09/15] fix dbmem impl --- coderd/database/dbmem/dbmem.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index efdc5f6ea8c52..df72bfcec25af 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2609,6 +2609,10 @@ func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, groupID return int64(len(users)), nil } +type GetGroupsCachedOrganizationDetails struct { + name, displayName string +} + func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) { err := validateDatabaseType(arg) if err != nil { @@ -2634,7 +2638,7 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) } } - organizationDisplayNames := make(map[uuid.UUID]string) + organizationDisplayNames := make(map[uuid.UUID]struct{ name, displayName string }) filtered := make([]database.GetGroupsRow, 0) for _, group := range q.groups { if arg.OrganizationID != uuid.Nil && group.OrganizationID != arg.OrganizationID { @@ -2646,20 +2650,23 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) continue } - orgDisplayName, ok := organizationDisplayNames[group.ID] + orgDetails, ok := organizationDisplayNames[group.ID] if !ok { for _, org := range q.organizations { if group.OrganizationID == org.ID { - orgDisplayName = org.DisplayName + orgDetails = struct{ name, displayName string }{ + name: org.Name, displayName: org.DisplayName, + } break } } - organizationDisplayNames[group.ID] = orgDisplayName + organizationDisplayNames[group.ID] = orgDetails } filtered = append(filtered, database.GetGroupsRow{ Group: group, - OrganizationDisplayName: orgDisplayName, + OrganizationName: orgDetails.name, + OrganizationDisplayName: orgDetails.displayName, }) } From 98e249df4cd2f362c88e5acb768cddba1cbad79d Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 27 Aug 2024 23:48:31 +0000 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coderd/apidoc/docs.go | 46 ++++++++++++++- coderd/apidoc/swagger.json | 46 ++++++++++++++- coderd/database/dbmem/dbmem.go | 4 -- docs/reference/api/enterprise.md | 56 ++++++++++--------- docs/reference/api/schemas.md | 48 ++++++++++++++++ enterprise/coderd/groups.go | 2 +- site/src/api/api.ts | 2 +- site/src/api/queries/groups.ts | 13 +++-- .../AccountPage/AccountUserGroups.stories.tsx | 3 +- .../AccountPage/AccountUserGroups.tsx | 4 +- 10 files changed, 182 insertions(+), 42 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 08447abb22c50..3dc04dbc0dd92 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1070,7 +1070,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" } } } @@ -10370,6 +10370,50 @@ const docTemplate = `{ "GroupSourceOIDC" ] }, + "codersdk.GroupWithOrganizationInfo": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ReducedUser" + } + }, + "name": { + "type": "string" + }, + "organization_display_name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "organization_name": { + "type": "string" + }, + "quota_allowance": { + "type": "integer" + }, + "source": { + "$ref": "#/definitions/codersdk.GroupSource" + }, + "total_member_count": { + "description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than ` + "`" + `len(Group.Members)` + "`" + `.", + "type": "integer" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 11126e672609a..2318edbca4840 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -924,7 +924,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" } } } @@ -9308,6 +9308,50 @@ "enum": ["user", "oidc"], "x-enum-varnames": ["GroupSourceUser", "GroupSourceOIDC"] }, + "codersdk.GroupWithOrganizationInfo": { + "type": "object", + "properties": { + "avatar_url": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.ReducedUser" + } + }, + "name": { + "type": "string" + }, + "organization_display_name": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "organization_name": { + "type": "string" + }, + "quota_allowance": { + "type": "integer" + }, + "source": { + "$ref": "#/definitions/codersdk.GroupSource" + }, + "total_member_count": { + "description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than `len(Group.Members)`.", + "type": "integer" + } + } + }, "codersdk.Healthcheck": { "type": "object", "properties": { diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index df72bfcec25af..06dff2da6f8b2 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2609,10 +2609,6 @@ func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, groupID return int64(len(users)), nil } -type GetGroupsCachedOrganizationDetails struct { - name, displayName string -} - func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) { err := validateDatabaseType(arg) if err != nil { diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index 18ae4e7de61d5..bc0b7f06d7f53 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -219,7 +219,9 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -229,37 +231,39 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.GroupWithOrganizationInfo](schemas.md#codersdkgroupwithorganizationinfo) |

Response Schema

Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_display_name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» organization_name` | string | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index ee229a8cc4914..5bbc7bf9854ca 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -2883,6 +2883,54 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `user` | | `oidc` | +## codersdk.GroupWithOrganizationInfo + +```json +{ + "avatar_url": "string", + "display_name": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "members": [ + { + "avatar_url": "http://example.com", + "created_at": "2019-08-24T14:15:22Z", + "email": "user@example.com", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "last_seen_at": "2019-08-24T14:15:22Z", + "login_type": "", + "name": "string", + "status": "active", + "theme_preference": "string", + "updated_at": "2019-08-24T14:15:22Z", + "username": "string" + } + ], + "name": "string", + "organization_display_name": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", + "quota_allowance": 0, + "source": "user", + "total_member_count": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| --------------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `avatar_url` | string | false | | | +| `display_name` | string | false | | | +| `id` | string | false | | | +| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | +| `name` | string | false | | | +| `organization_display_name` | string | false | | | +| `organization_id` | string | false | | | +| `organization_name` | string | false | | | +| `quota_allowance` | integer | false | | | +| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | +| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | + ## codersdk.Healthcheck ```json diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 5097cd227e255..d1d62219f89ac 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -408,7 +408,7 @@ func (api *API) groupsByOrganization(rw http.ResponseWriter, r *http.Request) { // @Tags Enterprise // @Param organization query string true "Organization ID or name" // @Param has_member query string true "User ID or name" -// @Success 200 {array} codersdk.Group +// @Success 200 {array} codersdk.GroupWithOrganizationInfo // @Router /groups [get] func (api *API) groups(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 0acb88fe5650f..8a9336adfd27c 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1603,7 +1603,7 @@ class ApiMethods { return response.data; }; - getGroups = async (): Promise => { + getGroups = async (): Promise => { const response = await this.axios.get("/api/v2/groups"); return response.data; }; diff --git a/site/src/api/queries/groups.ts b/site/src/api/queries/groups.ts index f2d858d4fe57e..6e3f9797874df 100644 --- a/site/src/api/queries/groups.ts +++ b/site/src/api/queries/groups.ts @@ -2,6 +2,7 @@ import { API } from "api/api"; import type { CreateGroupRequest, Group, + GroupWithOrganizationInfo, PatchGroupRequest, } from "api/typesGenerated"; import type { QueryClient, UseQueryOptions } from "react-query"; @@ -14,7 +15,7 @@ export const groups = () => { return { queryKey: groupsQueryKey, queryFn: () => API.getGroups(), - } satisfies UseQueryOptions; + } satisfies UseQueryOptions; }; export const groupsByOrganization = (organization: string) => { @@ -76,7 +77,11 @@ export function groupsForUser(userId: string) { return sortGroupsByName(groupsForUser, "asc"); }, - } as const satisfies UseQueryOptions; + } as const satisfies UseQueryOptions< + GroupWithOrganizationInfo[], + unknown, + readonly GroupWithOrganizationInfo[] + >; } export const groupPermissionsKey = (groupId: string) => [ @@ -163,8 +168,8 @@ export const invalidateGroup = ( queryClient.invalidateQueries(getGroupQueryKey(organization, groupId)), ]); -export function sortGroupsByName( - groups: readonly Group[], +export function sortGroupsByName( + groups: readonly T[], order: GroupSortOrder, ) { return [...groups].sort((g1, g2) => { diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.stories.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.stories.tsx index 3a198836ab7a4..a11c4b2a108bb 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.stories.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from "@storybook/react"; -import type { Group } from "api/typesGenerated"; import { MockGroup as MockGroup1, MockUser, @@ -7,7 +6,7 @@ import { } from "testHelpers/entities"; import { AccountUserGroups } from "./AccountUserGroups"; -const MockGroup2: Group = { +const MockGroup2 = { ...MockGroup1, avatar_url: "", display_name: "Goofy Goobers", diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx index 0096df0756263..76b2d0a1c4e91 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx @@ -1,7 +1,7 @@ import { useTheme } from "@emotion/react"; import Grid from "@mui/material/Grid"; import { isApiError } from "api/errors"; -import type { Group } from "api/typesGenerated"; +import type { GroupWithOrganizationInfo } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { AvatarCard } from "components/AvatarCard/AvatarCard"; import { Loader } from "components/Loader/Loader"; @@ -9,7 +9,7 @@ import type { FC } from "react"; import { Section } from "../Section"; type AccountGroupsProps = { - groups: readonly Group[] | undefined; + groups: readonly GroupWithOrganizationInfo[] | undefined; error: unknown; loading: boolean; }; From 181a120f59a4c2b5a18b483679470ad6eea5d0cd Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Aug 2024 17:16:17 +0000 Subject: [PATCH 11/15] mhm --- coderd/database/dbmem/dbmem.go | 6 +++--- coderd/database/queries.sql.go | 2 +- coderd/database/queries/groups.sql | 2 +- site/src/api/api.ts | 2 +- site/src/api/queries/groups.ts | 16 +++++++++++++--- site/src/pages/UsersPage/UsersPage.tsx | 2 -- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 06dff2da6f8b2..efae3150eadef 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -2634,7 +2634,7 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) } } - organizationDisplayNames := make(map[uuid.UUID]struct{ name, displayName string }) + orgDetailsCache := make(map[uuid.UUID]struct{ name, displayName string }) filtered := make([]database.GetGroupsRow, 0) for _, group := range q.groups { if arg.OrganizationID != uuid.Nil && group.OrganizationID != arg.OrganizationID { @@ -2646,7 +2646,7 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) continue } - orgDetails, ok := organizationDisplayNames[group.ID] + orgDetails, ok := orgDetailsCache[group.ID] if !ok { for _, org := range q.organizations { if group.OrganizationID == org.ID { @@ -2656,7 +2656,7 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) break } } - organizationDisplayNames[group.ID] = orgDetails + orgDetailsCache[group.ID] = orgDetails } filtered = append(filtered, database.GetGroupsRow{ diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 85f6b854b3df2..5ca7b4d1c6894 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1568,7 +1568,7 @@ SELECT FROM groups INNER JOIN - organizations ON ((groups.organization_id = organizations.id)) + organizations ON groups.organization_id = organizations.id WHERE true AND CASE diff --git a/coderd/database/queries/groups.sql b/coderd/database/queries/groups.sql index 08a8da4d7645f..1752ccd112ea7 100644 --- a/coderd/database/queries/groups.sql +++ b/coderd/database/queries/groups.sql @@ -28,7 +28,7 @@ SELECT FROM groups INNER JOIN - organizations ON ((groups.organization_id = organizations.id)) + organizations ON groups.organization_id = organizations.id WHERE true AND CASE diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 8a9336adfd27c..11846fc569db1 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1615,7 +1615,7 @@ class ApiMethods { organization: string, ): Promise => { const response = await this.axios.get( - `/api/v2/organization/${organization}/groups`, + `/api/v2/organizations/${organization}/groups`, ); return response.data; }; diff --git a/site/src/api/queries/groups.ts b/site/src/api/queries/groups.ts index 6e3f9797874df..a7b2bb786c2f9 100644 --- a/site/src/api/queries/groups.ts +++ b/site/src/api/queries/groups.ts @@ -18,9 +18,15 @@ export const groups = () => { } satisfies UseQueryOptions; }; +const getGroupsByOrganizationQueryKey = (organization: string) => [ + "organization", + organization, + "groups", +]; + export const groupsByOrganization = (organization: string) => { return { - queryKey: [organization, ...groupsQueryKey], + queryKey: getGroupsByOrganizationQueryKey(organization), queryFn: () => API.getGroupsByOrganization(organization), } satisfies UseQueryOptions; }; @@ -114,7 +120,9 @@ export const createGroup = (queryClient: QueryClient, organization: string) => { API.createGroup(organization, request), onSuccess: async () => { await queryClient.invalidateQueries(groupsQueryKey); - await queryClient.invalidateQueries([organization, ...groupsQueryKey]); + await queryClient.invalidateQueries( + getGroupsByOrganizationQueryKey(organization), + ); }, }; }; @@ -164,7 +172,9 @@ export const invalidateGroup = ( ) => Promise.all([ queryClient.invalidateQueries(groupsQueryKey), - queryClient.invalidateQueries([organization, ...groupsQueryKey]), + queryClient.invalidateQueries( + getGroupsByOrganizationQueryKey(organization), + ), queryClient.invalidateQueries(getGroupQueryKey(organization, groupId)), ]); diff --git a/site/src/pages/UsersPage/UsersPage.tsx b/site/src/pages/UsersPage/UsersPage.tsx index 9696ebfdeb84f..363bc72627794 100644 --- a/site/src/pages/UsersPage/UsersPage.tsx +++ b/site/src/pages/UsersPage/UsersPage.tsx @@ -43,8 +43,6 @@ const UsersPage: FC = () => { const { entitlements, experiments } = useDashboard(); const [searchParams] = searchParamsResult; - // TODO: DEAL WITH THIS BEFORE YOU MERGE IT KAYLA - // SERIOUSLY, PAY ATTENTION const groupsByUserIdQuery = useQuery(groupsByUserId()); const authMethodsQuery = useQuery(authMethods()); From 4447c8139ff6ad621e0287a18393eacf30f9e0ad Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Aug 2024 17:28:58 +0000 Subject: [PATCH 12/15] update groupsByOrganization endpoint --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- docs/reference/api/enterprise.md | 56 +++++++++++++++++--------------- enterprise/coderd/groups.go | 2 +- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 3dc04dbc0dd92..75f1755d39780 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2377,7 +2377,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" } } } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 2318edbca4840..fa266d6da7993 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2075,7 +2075,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.Group" + "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" } } } diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index bc0b7f06d7f53..8221a517bfd45 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -1218,7 +1218,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -1228,37 +1230,39 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.GroupWithOrganizationInfo](schemas.md#codersdkgroupwithorganizationinfo) |

Response Schema

Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ---------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» avatar_url` | string | false | | | -| `» display_name` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» members` | array | false | | | -| `»» avatar_url` | string(uri) | false | | | -| `»» created_at` | string(date-time) | true | | | -| `»» email` | string(email) | true | | | -| `»» id` | string(uuid) | true | | | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»» name` | string | false | | | -| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»» theme_preference` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» username` | string | true | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» quota_allowance` | integer | false | | | -| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| Name | Type | Required | Restrictions | Description | +| ----------------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» avatar_url` | string | false | | | +| `» display_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» members` | array | false | | | +| `»» avatar_url` | string(uri) | false | | | +| `»» created_at` | string(date-time) | true | | | +| `»» email` | string(email) | true | | | +| `»» id` | string(uuid) | true | | | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»» name` | string | false | | | +| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»» theme_preference` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» username` | string | true | | | +| `» name` | string | false | | | +| `» organization_display_name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» organization_name` | string | false | | | +| `» quota_allowance` | integer | false | | | +| `» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | #### Enumerated Values diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index d1d62219f89ac..e3a0839a97044 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -389,7 +389,7 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Tags Enterprise // @Param organization path string true "Organization ID" format(uuid) -// @Success 200 {array} codersdk.Group +// @Success 200 {array} codersdk.GroupWithOrganizationInfo // @Router /organizations/{organization}/groups [get] func (api *API) groupsByOrganization(rw http.ResponseWriter, r *http.Request) { org := httpmw.OrganizationParam(r) From d46092257e28f7c4c11d2663b2d198c1f5218d96 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Aug 2024 18:09:19 +0000 Subject: [PATCH 13/15] get rid of `GroupWithOrganizationInfo` type --- coderd/apidoc/docs.go | 54 ++----------- coderd/apidoc/swagger.json | 52 ++----------- coderd/database/db2sdk/db2sdk.go | 31 +++----- codersdk/groups.go | 16 ++-- docs/reference/api/enterprise.md | 76 +++++++++++-------- docs/reference/api/schemas.md | 76 +++++-------------- enterprise/coderd/groups.go | 40 +++++++--- enterprise/coderd/templates.go | 10 ++- site/src/api/api.ts | 2 +- site/src/api/queries/groups.ts | 9 +-- site/src/api/typesGenerated.ts | 8 +- .../AccountPage/AccountUserGroups.tsx | 4 +- site/src/testHelpers/entities.ts | 2 +- 13 files changed, 139 insertions(+), 241 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 75f1755d39780..5ee74385f4eb8 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1070,7 +1070,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" + "$ref": "#/definitions/codersdk.Group" } } } @@ -2377,7 +2377,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" + "$ref": "#/definitions/codersdk.Group" } } } @@ -10343,10 +10343,16 @@ const docTemplate = `{ "name": { "type": "string" }, + "organization_display_name": { + "type": "string" + }, "organization_id": { "type": "string", "format": "uuid" }, + "organization_name": { + "type": "string" + }, "quota_allowance": { "type": "integer" }, @@ -10370,50 +10376,6 @@ const docTemplate = `{ "GroupSourceOIDC" ] }, - "codersdk.GroupWithOrganizationInfo": { - "type": "object", - "properties": { - "avatar_url": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "id": { - "type": "string", - "format": "uuid" - }, - "members": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.ReducedUser" - } - }, - "name": { - "type": "string" - }, - "organization_display_name": { - "type": "string" - }, - "organization_id": { - "type": "string", - "format": "uuid" - }, - "organization_name": { - "type": "string" - }, - "quota_allowance": { - "type": "integer" - }, - "source": { - "$ref": "#/definitions/codersdk.GroupSource" - }, - "total_member_count": { - "description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than ` + "`" + `len(Group.Members)` + "`" + `.", - "type": "integer" - } - } - }, "codersdk.Healthcheck": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index fa266d6da7993..26aea27d1b4f4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -924,7 +924,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" + "$ref": "#/definitions/codersdk.Group" } } } @@ -2075,7 +2075,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/codersdk.GroupWithOrganizationInfo" + "$ref": "#/definitions/codersdk.Group" } } } @@ -9266,49 +9266,6 @@ } }, "codersdk.Group": { - "type": "object", - "properties": { - "avatar_url": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "id": { - "type": "string", - "format": "uuid" - }, - "members": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.ReducedUser" - } - }, - "name": { - "type": "string" - }, - "organization_id": { - "type": "string", - "format": "uuid" - }, - "quota_allowance": { - "type": "integer" - }, - "source": { - "$ref": "#/definitions/codersdk.GroupSource" - }, - "total_member_count": { - "description": "How many members are in this group. Shows the total count,\neven if the user is not authorized to read group member details.\nMay be greater than `len(Group.Members)`.", - "type": "integer" - } - } - }, - "codersdk.GroupSource": { - "type": "string", - "enum": ["user", "oidc"], - "x-enum-varnames": ["GroupSourceUser", "GroupSourceOIDC"] - }, - "codersdk.GroupWithOrganizationInfo": { "type": "object", "properties": { "avatar_url": { @@ -9352,6 +9309,11 @@ } } }, + "codersdk.GroupSource": { + "type": "string", + "enum": ["user", "oidc"], + "x-enum-varnames": ["GroupSourceUser", "GroupSourceOIDC"] + }, "codersdk.Healthcheck": { "type": "object", "properties": { diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index b66aaa6f07dc8..541004b2bff28 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -208,26 +208,19 @@ func Users(users []database.User, organizationIDs map[uuid.UUID][]uuid.UUID) []c }) } -func Group(group database.Group, members []database.GroupMember, totalMemberCount int) codersdk.Group { +func Group(row database.GetGroupsRow, members []database.GroupMember, totalMemberCount int) codersdk.Group { return codersdk.Group{ - ID: group.ID, - Name: group.Name, - DisplayName: group.DisplayName, - OrganizationID: group.OrganizationID, - AvatarURL: group.AvatarURL, - Members: ReducedUsersFromGroupMembers(members), - TotalMemberCount: totalMemberCount, - QuotaAllowance: int(group.QuotaAllowance), - Source: codersdk.GroupSource(group.Source), - } -} - -func GroupWithOrganizationInfo(group database.Group, orgName, orgDisplayName string, members []database.GroupMember, totalMemberCount int) codersdk.GroupWithOrganizationInfo { - sdkGroup := Group(group, members, totalMemberCount) - return codersdk.GroupWithOrganizationInfo{ - Group: sdkGroup, - OrganizationName: orgName, - OrganizationDisplayName: orgDisplayName, + ID: row.Group.ID, + Name: row.Group.Name, + DisplayName: row.Group.DisplayName, + OrganizationID: row.Group.OrganizationID, + AvatarURL: row.Group.AvatarURL, + Members: ReducedUsersFromGroupMembers(members), + TotalMemberCount: totalMemberCount, + QuotaAllowance: int(row.Group.QuotaAllowance), + Source: codersdk.GroupSource(row.Group.Source), + OrganizationName: row.OrganizationName, + OrganizationDisplayName: row.OrganizationDisplayName, } } diff --git a/codersdk/groups.go b/codersdk/groups.go index ded8566e5de6d..9b0e487822063 100644 --- a/codersdk/groups.go +++ b/codersdk/groups.go @@ -34,16 +34,12 @@ type Group struct { // How many members are in this group. Shows the total count, // even if the user is not authorized to read group member details. // May be greater than `len(Group.Members)`. - TotalMemberCount int `json:"total_member_count"` - AvatarURL string `json:"avatar_url"` - QuotaAllowance int `json:"quota_allowance"` - Source GroupSource `json:"source"` -} - -type GroupWithOrganizationInfo struct { - Group - OrganizationName string `json:"organization_name"` - OrganizationDisplayName string `json:"organization_display_name"` + TotalMemberCount int `json:"total_member_count"` + AvatarURL string `json:"avatar_url"` + QuotaAllowance int `json:"quota_allowance"` + Source GroupSource `json:"source"` + OrganizationName string `json:"organization_name"` + OrganizationDisplayName string `json:"organization_display_name"` } func (g Group) IsEveryone() bool { diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index 8221a517bfd45..684329814edc1 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -231,9 +231,9 @@ curl -X GET http://coder-server:8080/api/v2/groups?organization=string&has_membe ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.GroupWithOrganizationInfo](schemas.md#codersdkgroupwithorganizationinfo) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) |

Response Schema

@@ -326,7 +326,9 @@ curl -X GET http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -385,7 +387,9 @@ curl -X DELETE http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -459,7 +463,9 @@ curl -X PATCH http://coder-server:8080/api/v2/groups/{group} \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -1230,9 +1236,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.GroupWithOrganizationInfo](schemas.md#codersdkgroupwithorganizationinfo) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Group](schemas.md#codersdkgroup) |

Response Schema

@@ -1338,7 +1344,9 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/groups } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -1398,7 +1406,9 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/groups/ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -2144,7 +2154,9 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -2179,31 +2191,33 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/acl/available \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------------- | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» groups` | array | false | | | -| `»» avatar_url` | string | false | | | -| `»» display_name` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» members` | array | false | | | -| `»»» avatar_url` | string(uri) | false | | | -| `»»» created_at` | string(date-time) | true | | | -| `»»» email` | string(email) | true | | | -| `»»» id` | string(uuid) | true | | | -| `»»» last_seen_at` | string(date-time) | false | | | -| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | -| `»»» name` | string | false | | | -| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | -| `»»» theme_preference` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» username` | string | true | | | -| `»» name` | string | false | | | -| `»» organization_id` | string(uuid) | false | | | -| `»» quota_allowance` | integer | false | | | -| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | -| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | -| `» users` | array | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------ | ------------------------------------------------------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» groups` | array | false | | | +| `»» avatar_url` | string | false | | | +| `»» display_name` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» members` | array | false | | | +| `»»» avatar_url` | string(uri) | false | | | +| `»»» created_at` | string(date-time) | true | | | +| `»»» email` | string(email) | true | | | +| `»»» id` | string(uuid) | true | | | +| `»»» last_seen_at` | string(date-time) | false | | | +| `»»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | | +| `»»» name` | string | false | | | +| `»»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | | +| `»»» theme_preference` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» username` | string | true | | | +| `»» name` | string | false | | | +| `»» organization_display_name` | string | false | | | +| `»» organization_id` | string(uuid) | false | | | +| `»» organization_name` | string | false | | | +| `»» quota_allowance` | integer | false | | | +| `»» source` | [codersdk.GroupSource](schemas.md#codersdkgroupsource) | false | | | +| `»» total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +| `» users` | array | false | | | #### Enumerated Values diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 5bbc7bf9854ca..e195a00167951 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -244,7 +244,9 @@ } ], "name": "string", + "organization_display_name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "organization_name": "string", "quota_allowance": 0, "source": "user", "total_member_count": 0 @@ -2826,65 +2828,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o ## codersdk.Group -```json -{ - "avatar_url": "string", - "display_name": "string", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "members": [ - { - "avatar_url": "http://example.com", - "created_at": "2019-08-24T14:15:22Z", - "email": "user@example.com", - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "last_seen_at": "2019-08-24T14:15:22Z", - "login_type": "", - "name": "string", - "status": "active", - "theme_preference": "string", - "updated_at": "2019-08-24T14:15:22Z", - "username": "string" - } - ], - "name": "string", - "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", - "quota_allowance": 0, - "source": "user", - "total_member_count": 0 -} -``` - -### Properties - -| Name | Type | Required | Restrictions | Description | -| -------------------- | ----------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `avatar_url` | string | false | | | -| `display_name` | string | false | | | -| `id` | string | false | | | -| `members` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | -| `name` | string | false | | | -| `organization_id` | string | false | | | -| `quota_allowance` | integer | false | | | -| `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | -| `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | - -## codersdk.GroupSource - -```json -"user" -``` - -### Properties - -#### Enumerated Values - -| Value | -| ------ | -| `user` | -| `oidc` | - -## codersdk.GroupWithOrganizationInfo - ```json { "avatar_url": "string", @@ -2931,6 +2874,21 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `source` | [codersdk.GroupSource](#codersdkgroupsource) | false | | | | `total_member_count` | integer | false | | How many members are in this group. Shows the total count, even if the user is not authorized to read group member details. May be greater than `len(Group.Members)`. | +## codersdk.GroupSource + +```json +"user" +``` + +### Properties + +#### Enumerated Values + +| Value | +| ------ | +| `user` | +| `oidc` | + ## codersdk.Healthcheck ```json diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index e3a0839a97044..4f58fb429b147 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -78,7 +78,11 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) var emptyMembers []database.GroupMember aReq.New = group.Auditable(emptyMembers) - httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.Group(group, nil, 0)) + httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.Group(database.GetGroupsRow{ + Group: group, + OrganizationName: org.Name, + OrganizationDisplayName: org.DisplayName, + }, nil, 0)) } // @Summary Update group by name @@ -275,6 +279,11 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { return } + org, err := api.Database.GetOrganizationByID(ctx, group.OrganizationID) + if err != nil { + httpapi.InternalServerError(rw, err) + } + patchedMembers, err := api.Database.GetGroupMembersByGroupID(ctx, group.ID) if err != nil { httpapi.InternalServerError(rw, err) @@ -289,7 +298,11 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(group, patchedMembers, int(memberCount))) + httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(database.GetGroupsRow{ + Group: group, + OrganizationName: org.Name, + OrganizationDisplayName: org.DisplayName, + }, patchedMembers, int(memberCount))) } // @Summary Delete group by name @@ -368,6 +381,11 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) { group = httpmw.GroupParam(r) ) + org, err := api.Database.GetOrganizationByID(ctx, group.OrganizationID) + if err != nil { + httpapi.InternalServerError(rw, err) + } + users, err := api.Database.GetGroupMembersByGroupID(ctx, group.ID) if err != nil && !errors.Is(err, sql.ErrNoRows) { httpapi.InternalServerError(rw, err) @@ -380,7 +398,11 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(group, users, int(memberCount))) + httpapi.Write(ctx, rw, http.StatusOK, db2sdk.Group(database.GetGroupsRow{ + Group: group, + OrganizationName: org.Name, + OrganizationDisplayName: org.DisplayName, + }, users, int(memberCount))) } // @Summary Get groups by organization @@ -389,7 +411,7 @@ func (api *API) group(rw http.ResponseWriter, r *http.Request) { // @Produce json // @Tags Enterprise // @Param organization path string true "Organization ID" format(uuid) -// @Success 200 {array} codersdk.GroupWithOrganizationInfo +// @Success 200 {array} codersdk.Group // @Router /organizations/{organization}/groups [get] func (api *API) groupsByOrganization(rw http.ResponseWriter, r *http.Request) { org := httpmw.OrganizationParam(r) @@ -408,7 +430,7 @@ func (api *API) groupsByOrganization(rw http.ResponseWriter, r *http.Request) { // @Tags Enterprise // @Param organization query string true "Organization ID or name" // @Param has_member query string true "User ID or name" -// @Success 200 {array} codersdk.GroupWithOrganizationInfo +// @Success 200 {array} codersdk.Group // @Router /groups [get] func (api *API) groups(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -454,7 +476,7 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { return } - resp := make([]codersdk.GroupWithOrganizationInfo, 0, len(groups)) + resp := make([]codersdk.Group, 0, len(groups)) for _, group := range groups { members, err := api.Database.GetGroupMembersByGroupID(ctx, group.Group.ID) if err != nil { @@ -467,11 +489,7 @@ func (api *API) groups(rw http.ResponseWriter, r *http.Request) { return } - resp = append(resp, db2sdk.GroupWithOrganizationInfo( - group.Group, - group.OrganizationName, group.OrganizationDisplayName, - members, int(memberCount), - )) + resp = append(resp, db2sdk.Group(group, members, int(memberCount))) } httpapi.Write(ctx, rw, http.StatusOK, resp) diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 322846895a028..d795e57c61a71 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -72,7 +72,7 @@ func (api *API) templateAvailablePermissions(rw http.ResponseWriter, r *http.Req return } - sdkGroups = append(sdkGroups, db2sdk.Group(group.Group, members, int(memberCount))) + sdkGroups = append(sdkGroups, db2sdk.Group(group, members, int(memberCount))) } httpapi.Write(ctx, rw, http.StatusOK, codersdk.ACLAvailable{ @@ -147,8 +147,12 @@ func (api *API) templateACL(rw http.ResponseWriter, r *http.Request) { return } groups = append(groups, codersdk.TemplateGroup{ - Group: db2sdk.Group(group.Group, members, int(memberCount)), - Role: convertToTemplateRole(group.Actions), + Group: db2sdk.Group(database.GetGroupsRow{ + Group: group.Group, + OrganizationName: template.OrganizationName, + OrganizationDisplayName: template.OrganizationDisplayName, + }, members, int(memberCount)), + Role: convertToTemplateRole(group.Actions), }) } diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 11846fc569db1..e22f121837f43 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1603,7 +1603,7 @@ class ApiMethods { return response.data; }; - getGroups = async (): Promise => { + getGroups = async (): Promise => { const response = await this.axios.get("/api/v2/groups"); return response.data; }; diff --git a/site/src/api/queries/groups.ts b/site/src/api/queries/groups.ts index a7b2bb786c2f9..96e04a9fb3782 100644 --- a/site/src/api/queries/groups.ts +++ b/site/src/api/queries/groups.ts @@ -2,7 +2,6 @@ import { API } from "api/api"; import type { CreateGroupRequest, Group, - GroupWithOrganizationInfo, PatchGroupRequest, } from "api/typesGenerated"; import type { QueryClient, UseQueryOptions } from "react-query"; @@ -15,7 +14,7 @@ export const groups = () => { return { queryKey: groupsQueryKey, queryFn: () => API.getGroups(), - } satisfies UseQueryOptions; + } satisfies UseQueryOptions; }; const getGroupsByOrganizationQueryKey = (organization: string) => [ @@ -83,11 +82,7 @@ export function groupsForUser(userId: string) { return sortGroupsByName(groupsForUser, "asc"); }, - } as const satisfies UseQueryOptions< - GroupWithOrganizationInfo[], - unknown, - readonly GroupWithOrganizationInfo[] - >; + } as const satisfies UseQueryOptions; } export const groupPermissionsKey = (groupId: string) => [ diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index f530242d6f6ed..4b2988adf6257 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -623,6 +623,8 @@ export interface Group { readonly avatar_url: string; readonly quota_allowance: number; readonly source: GroupSource; + readonly organization_name: string; + readonly organization_display_name: string; } // From codersdk/groups.go @@ -631,12 +633,6 @@ export interface GroupArguments { readonly HasMember: string; } -// From codersdk/groups.go -export interface GroupWithOrganizationInfo extends Group { - readonly organization_name: string; - readonly organization_display_name: string; -} - // From codersdk/workspaceapps.go export interface Healthcheck { readonly url: string; diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx index 76b2d0a1c4e91..0096df0756263 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx @@ -1,7 +1,7 @@ import { useTheme } from "@emotion/react"; import Grid from "@mui/material/Grid"; import { isApiError } from "api/errors"; -import type { GroupWithOrganizationInfo } from "api/typesGenerated"; +import type { Group } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { AvatarCard } from "components/AvatarCard/AvatarCard"; import { Loader } from "components/Loader/Loader"; @@ -9,7 +9,7 @@ import type { FC } from "react"; import { Section } from "../Section"; type AccountGroupsProps = { - groups: readonly GroupWithOrganizationInfo[] | undefined; + groups: readonly Group[] | undefined; error: unknown; loading: boolean; }; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 1b37cf71eec95..0ec22198acef0 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2491,7 +2491,7 @@ export const MockWorkspaceQuota: TypesGen.WorkspaceQuota = { budget: 100, }; -export const MockGroup: TypesGen.GroupWithOrganizationInfo = { +export const MockGroup: TypesGen.Group = { id: "fbd2116a-8961-4954-87ae-e4575bd29ce0", name: "Front-End", display_name: "Front-End", From c7a6ce810008e1b865489cf6730d32952675ad86 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Aug 2024 18:12:06 +0000 Subject: [PATCH 14/15] fix mom --- site/src/testHelpers/entities.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 0ec22198acef0..c183ab139b334 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2510,6 +2510,8 @@ const MockEveryoneGroup: TypesGen.Group = { name: "Everyone", display_name: "", organization_id: MockOrganization.id, + organization_name: MockOrganization.name, + organization_display_name: MockOrganization.display_name, members: [], avatar_url: "", quota_allowance: 0, From 5422157421e32886cc9e87d98e974744d29e2741 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Aug 2024 18:24:51 +0000 Subject: [PATCH 15/15] fix id --- site/src/testHelpers/entities.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index c183ab139b334..55e50a30fe0c4 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2506,7 +2506,9 @@ export const MockGroup: TypesGen.Group = { }; const MockEveryoneGroup: TypesGen.Group = { - id: `${MockOrganization.id}-everyone`, + // The "Everyone" group must have the same ID as a the organization it belongs + // to. + id: MockOrganization.id, name: "Everyone", display_name: "", organization_id: MockOrganization.id,