@@ -340,6 +340,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
340
340
// @Description specify either the Template ID or the Template Version ID,
341
341
// @Description not both. If the Template ID is specified, the active version
342
342
// @Description of the template will be used.
343
+ // @Deprecated
343
344
// @ID create-user-workspace-by-organization
344
345
// @Security CoderSessionToken
345
346
// @Accept json
@@ -353,9 +354,9 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
353
354
func (api * API ) postWorkspacesByOrganization (rw http.ResponseWriter , r * http.Request ) {
354
355
var (
355
356
ctx = r .Context ()
356
- organization = httpmw .OrganizationParam (r )
357
357
apiKey = httpmw .APIKey (r )
358
358
auditor = api .Auditor .Load ()
359
+ organization = httpmw .OrganizationParam (r )
359
360
member = httpmw .OrganizationMemberParam (r )
360
361
workspaceResourceInfo = audit.AdditionalFields {
361
362
WorkspaceOwner : member .Username ,
@@ -380,15 +381,78 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
380
381
return
381
382
}
382
383
383
- var createWorkspace codersdk.CreateWorkspaceRequest
384
- if ! httpapi .Read (ctx , rw , r , & createWorkspace ) {
384
+ var req codersdk.CreateWorkspaceRequest
385
+ if ! httpapi .Read (ctx , rw , r , & req ) {
385
386
return
386
387
}
387
388
389
+ user := database.User {
390
+ ID : member .UserID ,
391
+ Username : member .Username ,
392
+ AvatarURL : member .AvatarURL ,
393
+ }
394
+
395
+ createWorkspace (ctx , aReq , apiKey .UserID , api , user , req , rw , r )
396
+ }
397
+
398
+ // Create a new workspace for the currently authenticated user.
399
+ //
400
+ // @Summary Create user workspace by organization
401
+ // @Description Create a new workspace using a template. The request must
402
+ // @Description specify either the Template ID or the Template Version ID,
403
+ // @Description not both. If the Template ID is specified, the active version
404
+ // @Description of the template will be used.
405
+ // @ID create-user-workspace
406
+ // @Security CoderSessionToken
407
+ // @Accept json
408
+ // @Produce json
409
+ // @Tags Workspaces
410
+ // @Param user path string true "Username, UUID, or me"
411
+ // @Param request body codersdk.CreateWorkspaceRequest true "Create workspace request"
412
+ // @Success 200 {object} codersdk.Workspace
413
+ // @Router /users/{user}/workspaces [post]
414
+ func (api * API ) postUserWorkspaces (rw http.ResponseWriter , r * http.Request ) {
415
+ var (
416
+ ctx = r .Context ()
417
+ apiKey = httpmw .APIKey (r )
418
+ auditor = api .Auditor .Load ()
419
+ user = httpmw .UserParam (r )
420
+ )
421
+
422
+ aReq , commitAudit := audit .InitRequest [database.Workspace ](rw , & audit.RequestParams {
423
+ Audit : * auditor ,
424
+ Log : api .Logger ,
425
+ Request : r ,
426
+ Action : database .AuditActionCreate ,
427
+ AdditionalFields : audit.AdditionalFields {
428
+ WorkspaceOwner : user .Username ,
429
+ },
430
+ })
431
+
432
+ defer commitAudit ()
433
+
434
+ var req codersdk.CreateWorkspaceRequest
435
+ if ! httpapi .Read (ctx , rw , r , & req ) {
436
+ return
437
+ }
438
+
439
+ createWorkspace (ctx , aReq , apiKey .UserID , api , user , req , rw , r )
440
+ }
441
+
442
+ func createWorkspace (
443
+ ctx context.Context ,
444
+ auditReq * audit.Request [database.Workspace ],
445
+ initiatorID uuid.UUID ,
446
+ api * API ,
447
+ user database.User ,
448
+ req codersdk.CreateWorkspaceRequest ,
449
+ rw http.ResponseWriter ,
450
+ r * http.Request ,
451
+ ) {
388
452
// If we were given a `TemplateVersionID`, we need to determine the `TemplateID` from it.
389
- templateID := createWorkspace .TemplateID
453
+ templateID := req .TemplateID
390
454
if templateID == uuid .Nil {
391
- templateVersion , err := api .Database .GetTemplateVersionByID (ctx , createWorkspace .TemplateVersionID )
455
+ templateVersion , err := api .Database .GetTemplateVersionByID (ctx , req .TemplateVersionID )
392
456
if errors .Is (err , sql .ErrNoRows ) {
393
457
httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
394
458
Message : fmt .Sprintf ("Template version %q doesn't exist." , templateID .String ()),
@@ -446,6 +510,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
446
510
})
447
511
return
448
512
}
513
+ auditReq .UpdateOrganizationID (template .OrganizationID )
449
514
450
515
templateAccessControl := (* (api .AccessControlStore .Load ())).GetTemplateAccessControl (template )
451
516
if templateAccessControl .IsDeprecated () {
@@ -458,14 +523,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
458
523
return
459
524
}
460
525
461
- if organization .ID != template .OrganizationID {
462
- httpapi .Write (ctx , rw , http .StatusForbidden , codersdk.Response {
463
- Message : fmt .Sprintf ("Template is not in organization %q." , organization .Name ),
464
- })
465
- return
466
- }
467
-
468
- dbAutostartSchedule , err := validWorkspaceSchedule (createWorkspace .AutostartSchedule )
526
+ dbAutostartSchedule , err := validWorkspaceSchedule (req .AutostartSchedule )
469
527
if err != nil {
470
528
httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
471
529
Message : "Invalid Autostart Schedule." ,
@@ -483,7 +541,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
483
541
return
484
542
}
485
543
486
- dbTTL , err := validWorkspaceTTLMillis (createWorkspace .TTLMillis , templateSchedule .DefaultTTL )
544
+ dbTTL , err := validWorkspaceTTLMillis (req .TTLMillis , templateSchedule .DefaultTTL )
487
545
if err != nil {
488
546
httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
489
547
Message : "Invalid Workspace Time to Shutdown." ,
@@ -494,8 +552,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
494
552
495
553
// back-compatibility: default to "never" if not included.
496
554
dbAU := database .AutomaticUpdatesNever
497
- if createWorkspace .AutomaticUpdates != "" {
498
- dbAU , err = validWorkspaceAutomaticUpdates (createWorkspace .AutomaticUpdates )
555
+ if req .AutomaticUpdates != "" {
556
+ dbAU , err = validWorkspaceAutomaticUpdates (req .AutomaticUpdates )
499
557
if err != nil {
500
558
httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
501
559
Message : "Invalid Workspace Automatic Updates setting." ,
@@ -509,13 +567,13 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
509
567
// read other workspaces. Ideally we check the error on create and look for
510
568
// a postgres conflict error.
511
569
workspace , err := api .Database .GetWorkspaceByOwnerIDAndName (ctx , database.GetWorkspaceByOwnerIDAndNameParams {
512
- OwnerID : member . UserID ,
513
- Name : createWorkspace .Name ,
570
+ OwnerID : user . ID ,
571
+ Name : req .Name ,
514
572
})
515
573
if err == nil {
516
574
// If the workspace already exists, don't allow creation.
517
575
httpapi .Write (ctx , rw , http .StatusConflict , codersdk.Response {
518
- Message : fmt .Sprintf ("Workspace %q already exists." , createWorkspace .Name ),
576
+ Message : fmt .Sprintf ("Workspace %q already exists." , req .Name ),
519
577
Validations : []codersdk.ValidationError {{
520
578
Field : "name" ,
521
579
Detail : "This value is already in use and should be unique." ,
@@ -525,7 +583,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
525
583
}
526
584
if err != nil && ! errors .Is (err , sql .ErrNoRows ) {
527
585
httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
528
- Message : fmt .Sprintf ("Internal error fetching workspace by name %q." , createWorkspace .Name ),
586
+ Message : fmt .Sprintf ("Internal error fetching workspace by name %q." , req .Name ),
529
587
Detail : err .Error (),
530
588
})
531
589
return
@@ -542,10 +600,10 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
542
600
ID : uuid .New (),
543
601
CreatedAt : now ,
544
602
UpdatedAt : now ,
545
- OwnerID : member . UserID ,
603
+ OwnerID : user . ID ,
546
604
OrganizationID : template .OrganizationID ,
547
605
TemplateID : template .ID ,
548
- Name : createWorkspace .Name ,
606
+ Name : req .Name ,
549
607
AutostartSchedule : dbAutostartSchedule ,
550
608
Ttl : dbTTL ,
551
609
// The workspaces page will sort by last used at, and it's useful to
@@ -559,11 +617,11 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
559
617
560
618
builder := wsbuilder .New (workspace , database .WorkspaceTransitionStart ).
561
619
Reason (database .BuildReasonInitiator ).
562
- Initiator (apiKey . UserID ).
620
+ Initiator (initiatorID ).
563
621
ActiveVersion ().
564
- RichParameterValues (createWorkspace .RichParameterValues )
565
- if createWorkspace .TemplateVersionID != uuid .Nil {
566
- builder = builder .VersionID (createWorkspace .TemplateVersionID )
622
+ RichParameterValues (req .RichParameterValues )
623
+ if req .TemplateVersionID != uuid .Nil {
624
+ builder = builder .VersionID (req .TemplateVersionID )
567
625
}
568
626
569
627
workspaceBuild , provisionerJob , err = builder .Build (
@@ -596,7 +654,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
596
654
// Client probably doesn't care about this error, so just log it.
597
655
api .Logger .Error (ctx , "failed to post provisioner job to pubsub" , slog .Error (err ))
598
656
}
599
- aReq .New = workspace
657
+ auditReq .New = workspace
600
658
601
659
api .Telemetry .Report (& telemetry.Snapshot {
602
660
Workspaces : []telemetry.Workspace {telemetry .ConvertWorkspace (workspace )},
@@ -610,8 +668,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
610
668
ProvisionerJob : * provisionerJob ,
611
669
QueuePosition : 0 ,
612
670
},
613
- member .Username ,
614
- member .AvatarURL ,
671
+ user .Username ,
672
+ user .AvatarURL ,
615
673
[]database.WorkspaceResource {},
616
674
[]database.WorkspaceResourceMetadatum {},
617
675
[]database.WorkspaceAgent {},
@@ -629,12 +687,12 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
629
687
}
630
688
631
689
w , err := convertWorkspace (
632
- apiKey . UserID ,
690
+ initiatorID ,
633
691
workspace ,
634
692
apiBuild ,
635
693
template ,
636
- member .Username ,
637
- member .AvatarURL ,
694
+ user .Username ,
695
+ user .AvatarURL ,
638
696
api .Options .AllowWorkspaceRenames ,
639
697
)
640
698
if err != nil {
0 commit comments