1
1
package coderd
2
2
3
3
import (
4
- "database/sql"
5
- "errors"
6
- "fmt"
7
4
"net/http"
8
5
9
- "github.com/google/uuid"
10
- "golang.org/x/xerrors"
11
-
12
- "github.com/coder/coder/v2/coderd/audit"
13
- "github.com/coder/coder/v2/coderd/database"
14
6
"github.com/coder/coder/v2/coderd/database/db2sdk"
15
- "github.com/coder/coder/v2/coderd/database/dbtime"
16
7
"github.com/coder/coder/v2/coderd/httpapi"
17
8
"github.com/coder/coder/v2/coderd/httpmw"
18
9
"github.com/coder/coder/v2/codersdk"
@@ -40,7 +31,7 @@ func (api *API) organizations(rw http.ResponseWriter, r *http.Request) {
40
31
return
41
32
}
42
33
43
- httpapi .Write (ctx , rw , http .StatusOK , db2sdk .List (organizations , convertOrganization ))
34
+ httpapi .Write (ctx , rw , http .StatusOK , db2sdk .List (organizations , db2sdk . Organization ))
44
35
}
45
36
46
37
// @Summary Get organization by ID
@@ -55,275 +46,5 @@ func (*API) organization(rw http.ResponseWriter, r *http.Request) {
55
46
ctx := r .Context ()
56
47
organization := httpmw .OrganizationParam (r )
57
48
58
- httpapi .Write (ctx , rw , http .StatusOK , convertOrganization (organization ))
59
- }
60
-
61
- // @Summary Create organization
62
- // @ID create-organization
63
- // @Security CoderSessionToken
64
- // @Accept json
65
- // @Produce json
66
- // @Tags Organizations
67
- // @Param request body codersdk.CreateOrganizationRequest true "Create organization request"
68
- // @Success 201 {object} codersdk.Organization
69
- // @Router /organizations [post]
70
- func (api * API ) postOrganizations (rw http.ResponseWriter , r * http.Request ) {
71
- var (
72
- // organizationID is required before the audit log entry is created.
73
- organizationID = uuid .New ()
74
- ctx = r .Context ()
75
- apiKey = httpmw .APIKey (r )
76
- auditor = api .Auditor .Load ()
77
- aReq , commitAudit = audit .InitRequest [database.Organization ](rw , & audit.RequestParams {
78
- Audit : * auditor ,
79
- Log : api .Logger ,
80
- Request : r ,
81
- Action : database .AuditActionCreate ,
82
- OrganizationID : organizationID ,
83
- })
84
- )
85
- aReq .Old = database.Organization {}
86
- defer commitAudit ()
87
-
88
- var req codersdk.CreateOrganizationRequest
89
- if ! httpapi .Read (ctx , rw , r , & req ) {
90
- return
91
- }
92
-
93
- if req .Name == codersdk .DefaultOrganization {
94
- httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
95
- Message : fmt .Sprintf ("Organization name %q is reserved." , codersdk .DefaultOrganization ),
96
- })
97
- return
98
- }
99
-
100
- _ , err := api .Database .GetOrganizationByName (ctx , req .Name )
101
- if err == nil {
102
- httpapi .Write (ctx , rw , http .StatusConflict , codersdk.Response {
103
- Message : "Organization already exists with that name." ,
104
- })
105
- return
106
- }
107
- if ! errors .Is (err , sql .ErrNoRows ) {
108
- httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
109
- Message : fmt .Sprintf ("Internal error fetching organization %q." , req .Name ),
110
- Detail : err .Error (),
111
- })
112
- return
113
- }
114
-
115
- var organization database.Organization
116
- err = api .Database .InTx (func (tx database.Store ) error {
117
- if req .DisplayName == "" {
118
- req .DisplayName = req .Name
119
- }
120
-
121
- organization , err = tx .InsertOrganization (ctx , database.InsertOrganizationParams {
122
- ID : organizationID ,
123
- Name : req .Name ,
124
- DisplayName : req .DisplayName ,
125
- Description : req .Description ,
126
- Icon : req .Icon ,
127
- CreatedAt : dbtime .Now (),
128
- UpdatedAt : dbtime .Now (),
129
- })
130
- if err != nil {
131
- return xerrors .Errorf ("create organization: %w" , err )
132
- }
133
- _ , err = tx .InsertOrganizationMember (ctx , database.InsertOrganizationMemberParams {
134
- OrganizationID : organization .ID ,
135
- UserID : apiKey .UserID ,
136
- CreatedAt : dbtime .Now (),
137
- UpdatedAt : dbtime .Now (),
138
- Roles : []string {
139
- // TODO: When organizations are allowed to be created, we should
140
- // come back to determining the default role of the person who
141
- // creates the org. Until that happens, all users in an organization
142
- // should be just regular members.
143
- },
144
- })
145
- if err != nil {
146
- return xerrors .Errorf ("create organization admin: %w" , err )
147
- }
148
-
149
- _ , err = tx .InsertAllUsersGroup (ctx , organization .ID )
150
- if err != nil {
151
- return xerrors .Errorf ("create %q group: %w" , database .EveryoneGroup , err )
152
- }
153
- return nil
154
- }, nil )
155
- if err != nil {
156
- httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
157
- Message : "Internal error inserting organization member." ,
158
- Detail : err .Error (),
159
- })
160
- return
161
- }
162
-
163
- aReq .New = organization
164
- httpapi .Write (ctx , rw , http .StatusCreated , convertOrganization (organization ))
165
- }
166
-
167
- // @Summary Update organization
168
- // @ID update-organization
169
- // @Security CoderSessionToken
170
- // @Accept json
171
- // @Produce json
172
- // @Tags Organizations
173
- // @Param organization path string true "Organization ID or name"
174
- // @Param request body codersdk.UpdateOrganizationRequest true "Patch organization request"
175
- // @Success 200 {object} codersdk.Organization
176
- // @Router /organizations/{organization} [patch]
177
- func (api * API ) patchOrganization (rw http.ResponseWriter , r * http.Request ) {
178
- var (
179
- ctx = r .Context ()
180
- organization = httpmw .OrganizationParam (r )
181
- auditor = api .Auditor .Load ()
182
- aReq , commitAudit = audit .InitRequest [database.Organization ](rw , & audit.RequestParams {
183
- Audit : * auditor ,
184
- Log : api .Logger ,
185
- Request : r ,
186
- Action : database .AuditActionWrite ,
187
- OrganizationID : organization .ID ,
188
- })
189
- )
190
- aReq .Old = organization
191
- defer commitAudit ()
192
-
193
- var req codersdk.UpdateOrganizationRequest
194
- if ! httpapi .Read (ctx , rw , r , & req ) {
195
- return
196
- }
197
-
198
- // "default" is a reserved name that always refers to the default org (much like the way we
199
- // use "me" for users).
200
- if req .Name == codersdk .DefaultOrganization {
201
- httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
202
- Message : fmt .Sprintf ("Organization name %q is reserved." , codersdk .DefaultOrganization ),
203
- })
204
- return
205
- }
206
-
207
- err := database .ReadModifyUpdate (api .Database , func (tx database.Store ) error {
208
- var err error
209
- organization , err = tx .GetOrganizationByID (ctx , organization .ID )
210
- if err != nil {
211
- return err
212
- }
213
-
214
- updateOrgParams := database.UpdateOrganizationParams {
215
- UpdatedAt : dbtime .Now (),
216
- ID : organization .ID ,
217
- Name : organization .Name ,
218
- DisplayName : organization .DisplayName ,
219
- Description : organization .Description ,
220
- Icon : organization .Icon ,
221
- }
222
-
223
- if req .Name != "" {
224
- updateOrgParams .Name = req .Name
225
- }
226
- if req .DisplayName != "" {
227
- updateOrgParams .DisplayName = req .DisplayName
228
- }
229
- if req .Description != nil {
230
- updateOrgParams .Description = * req .Description
231
- }
232
- if req .Icon != nil {
233
- updateOrgParams .Icon = * req .Icon
234
- }
235
-
236
- organization , err = tx .UpdateOrganization (ctx , updateOrgParams )
237
- if err != nil {
238
- return err
239
- }
240
- return nil
241
- })
242
-
243
- if httpapi .Is404Error (err ) {
244
- httpapi .ResourceNotFound (rw )
245
- return
246
- }
247
- if database .IsUniqueViolation (err ) {
248
- httpapi .Write (ctx , rw , http .StatusConflict , codersdk.Response {
249
- Message : fmt .Sprintf ("Organization already exists with the name %q." , req .Name ),
250
- Validations : []codersdk.ValidationError {{
251
- Field : "name" ,
252
- Detail : "This value is already in use and should be unique." ,
253
- }},
254
- })
255
- return
256
- }
257
- if err != nil {
258
- httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
259
- Message : "Internal error updating organization." ,
260
- Detail : fmt .Sprintf ("update organization: %s" , err .Error ()),
261
- })
262
- return
263
- }
264
-
265
- aReq .New = organization
266
- httpapi .Write (ctx , rw , http .StatusOK , convertOrganization (organization ))
267
- }
268
-
269
- // @Summary Delete organization
270
- // @ID delete-organization
271
- // @Security CoderSessionToken
272
- // @Produce json
273
- // @Tags Organizations
274
- // @Param organization path string true "Organization ID or name"
275
- // @Success 200 {object} codersdk.Response
276
- // @Router /organizations/{organization} [delete]
277
- func (api * API ) deleteOrganization (rw http.ResponseWriter , r * http.Request ) {
278
- var (
279
- ctx = r .Context ()
280
- organization = httpmw .OrganizationParam (r )
281
- auditor = api .Auditor .Load ()
282
- aReq , commitAudit = audit .InitRequest [database.Organization ](rw , & audit.RequestParams {
283
- Audit : * auditor ,
284
- Log : api .Logger ,
285
- Request : r ,
286
- Action : database .AuditActionDelete ,
287
- OrganizationID : organization .ID ,
288
- })
289
- )
290
- aReq .Old = organization
291
- defer commitAudit ()
292
-
293
- if organization .IsDefault {
294
- httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
295
- Message : "Default organization cannot be deleted." ,
296
- })
297
- return
298
- }
299
-
300
- err := api .Database .DeleteOrganization (ctx , organization .ID )
301
- if err != nil {
302
- httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
303
- Message : "Internal error deleting organization." ,
304
- Detail : fmt .Sprintf ("delete organization: %s" , err .Error ()),
305
- })
306
- return
307
- }
308
-
309
- aReq .New = database.Organization {}
310
- httpapi .Write (ctx , rw , http .StatusOK , codersdk.Response {
311
- Message : "Organization has been deleted." ,
312
- })
313
- }
314
-
315
- // convertOrganization consumes the database representation and outputs an API friendly representation.
316
- func convertOrganization (organization database.Organization ) codersdk.Organization {
317
- return codersdk.Organization {
318
- MinimalOrganization : codersdk.MinimalOrganization {
319
- ID : organization .ID ,
320
- Name : organization .Name ,
321
- DisplayName : organization .DisplayName ,
322
- Icon : organization .Icon ,
323
- },
324
- Description : organization .Description ,
325
- CreatedAt : organization .CreatedAt ,
326
- UpdatedAt : organization .UpdatedAt ,
327
- IsDefault : organization .IsDefault ,
328
- }
49
+ httpapi .Write (ctx , rw , http .StatusOK , db2sdk .Organization (organization ))
329
50
}
0 commit comments