Skip to content

Commit 65380db

Browse files
committed
Refactor parameter to allow for hiding redisplay
1 parent 7167fa0 commit 65380db

File tree

2 files changed

+414
-0
lines changed

2 files changed

+414
-0
lines changed

coderd/parameter/compute.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package parameter
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"errors"
7+
8+
"github.com/google/uuid"
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/database"
12+
)
13+
14+
// ComputeScope targets identifiers to pull parameters from.
15+
type ComputeScope struct {
16+
ProjectImportJobID uuid.UUID
17+
OrganizationID string
18+
UserID string
19+
ProjectID uuid.NullUUID
20+
WorkspaceID uuid.NullUUID
21+
}
22+
23+
type ComputeOptions struct {
24+
// HideRedisplayValues removes the value from parameters that
25+
// come from schemas with RedisplayValue set to false.
26+
HideRedisplayValues bool
27+
}
28+
29+
// ComputedValue represents a computed parameter value.
30+
type ComputedValue struct {
31+
database.ParameterValue
32+
SchemaID uuid.UUID `json:"schema_id"`
33+
DefaultSourceValue bool `json:"default_source_value"`
34+
}
35+
36+
// Compute accepts a scope in which parameter values are sourced.
37+
// These sources are iterated in a hierarchical fashion to determine
38+
// the runtime parameter values for schemas provided.
39+
func Compute(ctx context.Context, db database.Store, scope ComputeScope, options *ComputeOptions) ([]ComputedValue, error) {
40+
if options == nil {
41+
options = &ComputeOptions{}
42+
}
43+
compute := &compute{
44+
options: options,
45+
db: db,
46+
computedParameterByName: map[string]ComputedValue{},
47+
parameterSchemasByName: map[string]database.ParameterSchema{},
48+
}
49+
50+
// All parameters for the import job ID!
51+
parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ProjectImportJobID)
52+
if errors.Is(err, sql.ErrNoRows) {
53+
err = nil
54+
}
55+
if err != nil {
56+
return nil, xerrors.Errorf("get project parameters: %w", err)
57+
}
58+
for _, parameterSchema := range parameterSchemas {
59+
compute.parameterSchemasByName[parameterSchema.Name] = parameterSchema
60+
}
61+
62+
// Organization parameters come first!
63+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
64+
Scope: database.ParameterScopeOrganization,
65+
ScopeID: scope.OrganizationID,
66+
})
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
// Job parameters come second!
72+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
73+
Scope: database.ParameterScopeImportJob,
74+
ScopeID: scope.ProjectImportJobID.String(),
75+
})
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
// Default project parameter values come second!
81+
for _, parameterSchema := range parameterSchemas {
82+
if parameterSchema.DefaultSourceScheme == database.ParameterSourceSchemeNone {
83+
continue
84+
}
85+
if _, ok := compute.computedParameterByName[parameterSchema.Name]; ok {
86+
// We already have a value! No need to use th default.
87+
continue
88+
}
89+
90+
switch parameterSchema.DefaultSourceScheme {
91+
case database.ParameterSourceSchemeData:
92+
// Inject a default value scoped to the import job ID.
93+
// This doesn't need to be inserted into the database,
94+
// because it's a dynamic value associated with the schema.
95+
err = compute.injectSingle(database.ParameterValue{
96+
ID: uuid.New(),
97+
CreatedAt: database.Now(),
98+
UpdatedAt: database.Now(),
99+
SourceScheme: database.ParameterSourceSchemeData,
100+
Name: parameterSchema.Name,
101+
DestinationScheme: parameterSchema.DefaultDestinationScheme,
102+
SourceValue: parameterSchema.DefaultSourceValue,
103+
Scope: database.ParameterScopeImportJob,
104+
ScopeID: scope.ProjectImportJobID.String(),
105+
}, true)
106+
if err != nil {
107+
return nil, xerrors.Errorf("insert default value: %w", err)
108+
}
109+
default:
110+
return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", parameterSchema.Name, string(parameterSchema.DefaultSourceScheme))
111+
}
112+
}
113+
114+
if scope.ProjectID.Valid {
115+
// Project parameters come third!
116+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
117+
Scope: database.ParameterScopeProject,
118+
ScopeID: scope.ProjectID.UUID.String(),
119+
})
120+
if err != nil {
121+
return nil, err
122+
}
123+
}
124+
125+
// User parameters come fourth!
126+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
127+
Scope: database.ParameterScopeUser,
128+
ScopeID: scope.UserID,
129+
})
130+
if err != nil {
131+
return nil, err
132+
}
133+
134+
if scope.WorkspaceID.Valid {
135+
// Workspace parameters come last!
136+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
137+
Scope: database.ParameterScopeWorkspace,
138+
ScopeID: scope.WorkspaceID.UUID.String(),
139+
})
140+
if err != nil {
141+
return nil, err
142+
}
143+
}
144+
145+
values := make([]ComputedValue, 0, len(compute.computedParameterByName))
146+
for _, value := range compute.computedParameterByName {
147+
values = append(values, value)
148+
}
149+
return values, nil
150+
}
151+
152+
type compute struct {
153+
options *ComputeOptions
154+
db database.Store
155+
computedParameterByName map[string]ComputedValue
156+
parameterSchemasByName map[string]database.ParameterSchema
157+
}
158+
159+
// Validates and computes the value for parameters; setting the value on "parameterByName".
160+
func (c *compute) injectScope(ctx context.Context, scopeParams database.GetParameterValuesByScopeParams) error {
161+
scopedParameters, err := c.db.GetParameterValuesByScope(ctx, scopeParams)
162+
if errors.Is(err, sql.ErrNoRows) {
163+
err = nil
164+
}
165+
if err != nil {
166+
return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err)
167+
}
168+
169+
for _, scopedParameter := range scopedParameters {
170+
err = c.injectSingle(scopedParameter, false)
171+
if err != nil {
172+
return xerrors.Errorf("inject single %q: %w", scopedParameter.Name, err)
173+
}
174+
}
175+
return nil
176+
}
177+
178+
func (c *compute) injectSingle(scopedParameter database.ParameterValue, defaultValue bool) error {
179+
parameterSchema, hasParameterSchema := c.parameterSchemasByName[scopedParameter.Name]
180+
if !hasParameterSchema {
181+
// Don't inject parameters that aren't defined by the project.
182+
return nil
183+
}
184+
185+
_, hasParameterValue := c.computedParameterByName[scopedParameter.Name]
186+
if hasParameterValue {
187+
if !parameterSchema.AllowOverrideSource &&
188+
// Users and workspaces cannot override anything on a project!
189+
(scopedParameter.Scope == database.ParameterScopeUser ||
190+
scopedParameter.Scope == database.ParameterScopeWorkspace) {
191+
return nil
192+
}
193+
}
194+
195+
switch scopedParameter.SourceScheme {
196+
case database.ParameterSourceSchemeData:
197+
value := ComputedValue{
198+
ParameterValue: scopedParameter,
199+
SchemaID: parameterSchema.ID,
200+
DefaultSourceValue: defaultValue,
201+
}
202+
if c.options.HideRedisplayValues && !parameterSchema.RedisplayValue {
203+
value.SourceValue = ""
204+
}
205+
c.computedParameterByName[scopedParameter.Name] = value
206+
default:
207+
return xerrors.Errorf("unsupported source scheme: %q", string(parameterSchema.DefaultSourceScheme))
208+
}
209+
return nil
210+
}

0 commit comments

Comments
 (0)