Skip to content

Commit d4d1eaa

Browse files
committed
chore: implement delete organization member
Side effects of removing an organization member will orphan their user resources. These side effects are not addressed here
1 parent 097776c commit d4d1eaa

File tree

8 files changed

+115
-0
lines changed

8 files changed

+115
-0
lines changed

coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ func New(options *Options) *API {
854854
httpmw.ExtractUserParam(options.Database),
855855
)
856856
r.Post("/", api.postOrganizationMember)
857+
r.Delete("/", api.deleteOrganizationMember)
857858
})
858859

859860
r.Group(func(r chi.Router) {

coderd/database/dbauthz/dbauthz.go

+13
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,19 @@ func (q *querier) DeleteOrganization(ctx context.Context, id uuid.UUID) error {
10351035
return deleteQ(q.log, q.auth, q.db.GetOrganizationByID, q.db.DeleteOrganization)(ctx, id)
10361036
}
10371037

1038+
func (q *querier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
1039+
return deleteQ[database.OrganizationMember](q.log, q.auth, func(ctx context.Context, arg database.DeleteOrganizationMemberParams) (database.OrganizationMember, error) {
1040+
member, err := database.ExpectOne(q.OrganizationMembers(ctx, database.OrganizationMembersParams{
1041+
OrganizationID: arg.OrganizationID,
1042+
UserID: arg.UserID,
1043+
}))
1044+
if err != nil {
1045+
return database.OrganizationMember{}, err
1046+
}
1047+
return member.OrganizationMember, nil
1048+
}, q.db.DeleteOrganizationMember)(ctx, arg)
1049+
}
1050+
10381051
func (q *querier) DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error {
10391052
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
10401053
return err

coderd/database/dbmem/dbmem.go

+18
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,24 @@ func (q *FakeQuerier) DeleteOrganization(_ context.Context, id uuid.UUID) error
16321632
return sql.ErrNoRows
16331633
}
16341634

1635+
func (q *FakeQuerier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
1636+
err := validateDatabaseType(arg)
1637+
if err != nil {
1638+
return err
1639+
}
1640+
1641+
q.mutex.Lock()
1642+
defer q.mutex.Unlock()
1643+
1644+
deleted := slices.DeleteFunc(q.data.organizationMembers, func(member database.OrganizationMember) bool {
1645+
return member.OrganizationID == arg.OrganizationID && member.UserID == arg.UserID
1646+
})
1647+
if len(deleted) == 0 {
1648+
return sql.ErrNoRows
1649+
}
1650+
return nil
1651+
}
1652+
16351653
func (q *FakeQuerier) DeleteReplicasUpdatedBefore(_ context.Context, before time.Time) error {
16361654
q.mutex.Lock()
16371655
defer q.mutex.Unlock()

coderd/database/dbmetrics/dbmetrics.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/organizationmembers.sql

+9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ INSERT INTO
3636
VALUES
3737
($1, $2, $3, $4, $5) RETURNING *;
3838

39+
-- name: DeleteOrganizationMember :exec
40+
DELETE
41+
FROM
42+
organization_members
43+
WHERE
44+
organization_id = @organization_id AND
45+
user_id = @user_id
46+
;
47+
3948

4049
-- name: GetOrganizationIDsByMemberIDs :many
4150
SELECT

coderd/members.go

+47
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,53 @@ func (api *API) postOrganizationMember(rw http.ResponseWriter, r *http.Request)
6262
httpapi.Write(ctx, rw, http.StatusOK, resp[0])
6363
}
6464

65+
// @Summary Remove organization member
66+
// @ID remove-organization-member
67+
// @Security CoderSessionToken
68+
// @Produce json
69+
// @Tags Members
70+
// @Param organization path string true "Organization ID"
71+
// @Param user path string true "User ID, name, or me"
72+
// @Success 200 {object} codersdk.OrganizationMember
73+
// @Router /organizations/{organization}/members/{user} [delete]
74+
func (api *API) deleteOrganizationMember(rw http.ResponseWriter, r *http.Request) {
75+
var (
76+
ctx = r.Context()
77+
organization = httpmw.OrganizationParam(r)
78+
user = httpmw.UserParam(r)
79+
)
80+
81+
api.Database.InsertOrganizationMember()
82+
member, err := api.Database.InsertOrganizationMember(ctx, database.InsertOrganizationMemberParams{
83+
OrganizationID: organization.ID,
84+
UserID: user.ID,
85+
CreatedAt: dbtime.Now(),
86+
UpdatedAt: dbtime.Now(),
87+
Roles: []string{},
88+
})
89+
if httpapi.Is404Error(err) {
90+
httpapi.ResourceNotFound(rw)
91+
return
92+
}
93+
if err != nil {
94+
httpapi.InternalServerError(rw, err)
95+
return
96+
}
97+
98+
resp, err := convertOrganizationMembers(ctx, api.Database, []database.OrganizationMember{member})
99+
if err != nil {
100+
httpapi.InternalServerError(rw, err)
101+
return
102+
}
103+
104+
if len(resp) == 0 {
105+
httpapi.InternalServerError(rw, xerrors.Errorf("marshal member"))
106+
return
107+
}
108+
109+
httpapi.Write(ctx, rw, http.StatusOK, resp[0])
110+
}
111+
65112
// @Summary List organization members
66113
// @ID list-organization-members
67114
// @Security CoderSessionToken

0 commit comments

Comments
 (0)