Skip to content

Commit 9b5d499

Browse files
authored
chore: refactor dynamic parameters into dedicated package (#18420)
This PR extracts dynamic parameter rendering logic from coderd/parameters.go into a new coderd/dynamicparameters package. Partly for organization and maintainability, but primarily to be reused in `wsbuilder` to be leveraged as validation.
1 parent 72f7d70 commit 9b5d499

File tree

10 files changed

+941
-409
lines changed

10 files changed

+941
-409
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package coderdtest
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/google/uuid"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/coder/coder/v2/coderd/util/ptr"
12+
"github.com/coder/coder/v2/coderd/util/slice"
13+
"github.com/coder/coder/v2/codersdk"
14+
"github.com/coder/coder/v2/provisioner/echo"
15+
"github.com/coder/coder/v2/provisionersdk/proto"
16+
)
17+
18+
type DynamicParameterTemplateParams struct {
19+
MainTF string
20+
Plan json.RawMessage
21+
ModulesArchive []byte
22+
23+
// StaticParams is used if the provisioner daemon version does not support dynamic parameters.
24+
StaticParams []*proto.RichParameter
25+
}
26+
27+
func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UUID, args DynamicParameterTemplateParams) (codersdk.Template, codersdk.TemplateVersion) {
28+
t.Helper()
29+
30+
files := echo.WithExtraFiles(map[string][]byte{
31+
"main.tf": []byte(args.MainTF),
32+
})
33+
files.ProvisionPlan = []*proto.Response{{
34+
Type: &proto.Response_Plan{
35+
Plan: &proto.PlanComplete{
36+
Plan: args.Plan,
37+
ModuleFiles: args.ModulesArchive,
38+
Parameters: args.StaticParams,
39+
},
40+
},
41+
}}
42+
43+
version := CreateTemplateVersion(t, client, org, files)
44+
AwaitTemplateVersionJobCompleted(t, client, version.ID)
45+
tpl := CreateTemplate(t, client, org, version.ID)
46+
47+
var err error
48+
tpl, err = client.UpdateTemplateMeta(t.Context(), tpl.ID, codersdk.UpdateTemplateMeta{
49+
UseClassicParameterFlow: ptr.Ref(false),
50+
})
51+
require.NoError(t, err)
52+
53+
return tpl, version
54+
}
55+
56+
type ParameterAsserter struct {
57+
Name string
58+
Params []codersdk.PreviewParameter
59+
t *testing.T
60+
}
61+
62+
func AssertParameter(t *testing.T, name string, params []codersdk.PreviewParameter) *ParameterAsserter {
63+
return &ParameterAsserter{
64+
Name: name,
65+
Params: params,
66+
t: t,
67+
}
68+
}
69+
70+
func (a *ParameterAsserter) find(name string) *codersdk.PreviewParameter {
71+
a.t.Helper()
72+
for _, p := range a.Params {
73+
if p.Name == name {
74+
return &p
75+
}
76+
}
77+
78+
assert.Fail(a.t, "parameter not found", "expected parameter %q to exist", a.Name)
79+
return nil
80+
}
81+
82+
func (a *ParameterAsserter) NotExists() *ParameterAsserter {
83+
a.t.Helper()
84+
85+
names := slice.Convert(a.Params, func(p codersdk.PreviewParameter) string {
86+
return p.Name
87+
})
88+
89+
assert.NotContains(a.t, names, a.Name)
90+
return a
91+
}
92+
93+
func (a *ParameterAsserter) Exists() *ParameterAsserter {
94+
a.t.Helper()
95+
96+
names := slice.Convert(a.Params, func(p codersdk.PreviewParameter) string {
97+
return p.Name
98+
})
99+
100+
assert.Contains(a.t, names, a.Name)
101+
return a
102+
}
103+
104+
func (a *ParameterAsserter) Value(expected string) *ParameterAsserter {
105+
a.t.Helper()
106+
107+
p := a.find(a.Name)
108+
if p == nil {
109+
return a
110+
}
111+
112+
assert.Equal(a.t, expected, p.Value.Value)
113+
return a
114+
}
115+
116+
func (a *ParameterAsserter) Options(expected ...string) *ParameterAsserter {
117+
a.t.Helper()
118+
119+
p := a.find(a.Name)
120+
if p == nil {
121+
return a
122+
}
123+
124+
optValues := slice.Convert(p.Options, func(p codersdk.PreviewParameterOption) string {
125+
return p.Value.Value
126+
})
127+
assert.ElementsMatch(a.t, expected, optValues, "parameter %q options", a.Name)
128+
return a
129+
}

coderd/coderdtest/stream.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package coderdtest
2+
3+
import "github.com/coder/coder/v2/codersdk/wsjson"
4+
5+
// SynchronousStream returns a function that assumes the stream is synchronous.
6+
// Meaning each request sent assumes exactly one response will be received.
7+
// The function will block until the response is received or an error occurs.
8+
//
9+
// This should not be used in production code, as it does not handle edge cases.
10+
// The second function `pop` can be used to retrieve the next response from the
11+
// stream without sending a new request. This is useful for dynamic parameters
12+
func SynchronousStream[R any, W any](stream *wsjson.Stream[R, W]) (do func(W) (R, error), pop func() R) {
13+
rec := stream.Chan()
14+
15+
return func(req W) (R, error) {
16+
err := stream.Send(req)
17+
if err != nil {
18+
return *new(R), err
19+
}
20+
21+
return <-rec, nil
22+
}, func() R {
23+
return <-rec
24+
}
25+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy