feat: show organization name for groups on user profile (#14448)

This commit is contained in:
Kayla Washburn-Love
2024-08-29 10:55:00 -06:00
committed by GitHub
parent 4b5c45d6df
commit 49afab12d5
29 changed files with 357 additions and 229 deletions
+6
View File
@@ -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"
},
+6
View File
@@ -9287,10 +9287,16 @@
"name": {
"type": "string"
},
"organization_display_name": {
"type": "string"
},
"organization_id": {
"type": "string",
"format": "uuid"
},
"organization_name": {
"type": "string"
},
"quota_allowance": {
"type": "integer"
},
+12 -10
View File
@@ -208,17 +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),
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,
}
}
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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()
}))
+21 -3
View File
@@ -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
@@ -2634,7 +2634,8 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams)
}
}
filtered := make([]database.Group, 0)
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 {
continue
@@ -2645,7 +2646,24 @@ func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams)
continue
}
filtered = append(filtered, group)
orgDetails, ok := orgDetailsCache[group.ID]
if !ok {
for _, org := range q.organizations {
if group.OrganizationID == org.ID {
orgDetails = struct{ name, displayName string }{
name: org.Name, displayName: org.DisplayName,
}
break
}
}
orgDetailsCache[group.ID] = orgDetails
}
filtered = append(filtered, database.GetGroupsRow{
Group: group,
OrganizationName: orgDetails.name,
OrganizationDisplayName: orgDetails.displayName,
})
}
return filtered, nil
+1 -1
View File
@@ -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())
+2 -2
View File
@@ -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
}
+4
View File
@@ -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())
}
+1 -1
View File
@@ -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)
+52 -40
View File
@@ -1562,32 +1562,36 @@ 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.name AS organization_name,
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,23 +1599,31 @@ 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 {
Group Group `db:"group" json:"group"`
OrganizationName string `db:"organization_name" json:"organization_name"`
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,
&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.OrganizationName,
&i.OrganizationDisplayName,
); err != nil {
return nil, err
}
@@ -1703,15 +1715,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
`
+34 -32
View File
@@ -22,32 +22,36 @@ LIMIT
-- name: GetGroups :many
SELECT
*
sqlc.embed(groups),
organizations.name AS organization_name,
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 +74,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 +117,3 @@ DELETE FROM
groups
WHERE
id = $1;
@@ -491,7 +491,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 {
+1 -1
View File
@@ -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
})