8
8
"encoding/json"
9
9
"errors"
10
10
"fmt"
11
+ "math"
11
12
"net/http"
12
13
"os"
13
14
@@ -35,6 +36,7 @@ import (
35
36
"github.com/coder/coder/v2/coderd/tracing"
36
37
"github.com/coder/coder/v2/coderd/util/ptr"
37
38
"github.com/coder/coder/v2/codersdk"
39
+ "github.com/coder/coder/v2/codersdk/wsjson"
38
40
"github.com/coder/coder/v2/examples"
39
41
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
40
42
"github.com/coder/coder/v2/provisionersdk"
@@ -43,7 +45,6 @@ import (
43
45
previewtypes "github.com/coder/preview/types"
44
46
previewweb "github.com/coder/preview/web"
45
47
"github.com/coder/websocket"
46
- "github.com/coder/websocket/wsjson"
47
48
)
48
49
49
50
// @Summary Get template version by ID
@@ -299,7 +300,23 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
299
300
return
300
301
}
301
302
302
- fileID , err := api .Database .GetFileIDByTemplateVersionID (ctx , templateVersion .ID )
303
+ // Having the Terraform plan available for the evaluation engine is helpful
304
+ // for populating values from data blocks, but isn't strictly required. If
305
+ // we don't have a cached plan available, we just use an empty one instead.
306
+ var plan json.RawMessage = []byte ("{}" )
307
+ tf , err := api .Database .GetTemplateVersionTerraformValues (ctx , templateVersion .ID )
308
+ if err == nil {
309
+ plan = tf .CachedPlan
310
+ }
311
+
312
+ input := preview.Input {
313
+ PlanJSON : plan ,
314
+ ParameterValues : map [string ]string {},
315
+ Owner : previewtypes.WorkspaceOwner {},
316
+ }
317
+
318
+ fileCtx := dbauthz .AsProvisionerd (ctx )
319
+ fileID , err := api .Database .GetFileIDByTemplateVersionID (fileCtx , templateVersion .ID )
303
320
if err != nil {
304
321
httpapi .Write (ctx , rw , http .StatusInternalServerError , codersdk.Response {
305
322
Message : "Internal error finding template version Terraform." ,
@@ -308,7 +325,8 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
308
325
return
309
326
}
310
327
311
- fs , err := api .FileCache .Acquire (fileID )
328
+ fs , err := api .FileCache .Acquire (fileCtx , fileID )
329
+ defer api .FileCache .Release (fileID )
312
330
if err != nil {
313
331
httpapi .Write (ctx , rw , http .StatusNotFound , codersdk.Response {
314
332
Message : "Internal error fetching template version Terraform." ,
@@ -317,37 +335,45 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
317
335
return
318
336
}
319
337
320
- // Having the Terraform plan available for the evaluation engine is helpful
321
- // for populating values from data blocks, but isn't strictly required. If
322
- // we don't have a cached plan available, we just use an empty one instead.
323
- var plan json.RawMessage = []byte ("{}" )
324
- tf , err := api .Database .GetTemplateVersionTerraformValues (ctx , templateVersion .ID )
325
- if err == nil {
326
- plan = tf .CachedPlan
327
- }
328
-
329
- // TODO: idk if I need to set any options here. it doesn't seem like anyone
330
- // else does, but steven had to set a * domain in the prototype. do we need
331
- // to specify some host or anything?
332
- conn , err := websocket .Accept (rw , r , & websocket.AcceptOptions {})
338
+ conn , err := websocket .Accept (rw , r , nil )
333
339
if err != nil {
334
- httpapi .Write (ctx , rw , http .StatusBadGateway , codersdk.Response {
335
- Message : "devour feculence mr drummond" ,
340
+ httpapi .Write (ctx , rw , http .StatusUpgradeRequired , codersdk.Response {
341
+ Message : "Failed to accept WebSocket." ,
342
+ Detail : err .Error (),
336
343
})
337
344
return
338
345
}
339
346
340
- input := preview.Input {
341
- PlanJSON : plan ,
342
- ParameterValues : map [string ]string {},
343
- Owner : previewtypes.WorkspaceOwner {},
344
- }
347
+ stream := wsjson .NewStream [previewweb.Request , previewweb.Response ](conn , websocket .MessageText , websocket .MessageText , api .Logger )
345
348
349
+ // Send an initial form state, computed without any user input.
346
350
result , diagnostics := preview .Preview (ctx , input , fs )
347
- wsjson .Write (ctx , conn , previewweb.Response {
351
+ stream .Send (previewweb.Response {
352
+ // or maybe it could be -1 or something? it just has to be unique from
353
+ // anything a client could reasonably send.
354
+ ID : math .MaxInt32 ,
348
355
Parameters : result .Parameters ,
349
356
Diagnostics : previewtypes .Diagnostics (diagnostics ),
350
357
})
358
+
359
+ // As the user types into the form, reprocess the state using their input,
360
+ // and respond with updates.
361
+ updates := stream .Chan ()
362
+ for {
363
+ select {
364
+ case <- ctx .Done ():
365
+ return
366
+ case update := <- updates :
367
+ newInput := input
368
+ newInput .ParameterValues = update .Inputs
369
+ result , diagnostics := preview .Preview (ctx , input , fs )
370
+ stream .Send (previewweb.Response {
371
+ ID : update .ID ,
372
+ Parameters : result .Parameters ,
373
+ Diagnostics : previewtypes .Diagnostics (diagnostics ),
374
+ })
375
+ }
376
+ }
351
377
}
352
378
353
379
// @Summary Get rich parameters by template version
0 commit comments