Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit b9329e6

Browse files
author
noah
committed
Add DynamicPayload field to env
1 parent 1b00790 commit b9329e6

File tree

2 files changed

+131
-33
lines changed

2 files changed

+131
-33
lines changed

Diff for: model/extent/env.go

+130-32
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,55 @@
11
package extent
22

33
import (
4+
"fmt"
45
"regexp"
56
"strings"
67
"time"
78

89
"github.com/gitploy-io/cronexpr"
10+
"github.com/gitploy-io/gitploy/pkg/e"
911
eutil "github.com/gitploy-io/gitploy/pkg/e"
1012
)
1113

12-
type (
13-
Env struct {
14-
Name string `json:"name" yaml:"name"`
14+
type Env struct {
15+
Name string `json:"name" yaml:"name"`
1516

16-
// GitHub parameters of deployment.
17-
Task *string `json:"task" yaml:"task"`
18-
Description *string `json:"description" yaml:"description"`
19-
AutoMerge *bool `json:"auto_merge" yaml:"auto_merge"`
20-
RequiredContexts *[]string `json:"required_contexts,omitempty" yaml:"required_contexts"`
21-
Payload interface{} `json:"payload" yaml:"payload"`
22-
ProductionEnvironment *bool `json:"production_environment" yaml:"production_environment"`
17+
// GitHub parameters of deployment.
18+
Task *string `json:"task" yaml:"task"`
19+
Description *string `json:"description" yaml:"description"`
20+
AutoMerge *bool `json:"auto_merge" yaml:"auto_merge"`
21+
RequiredContexts *[]string `json:"required_contexts,omitempty" yaml:"required_contexts"`
22+
Payload interface{} `json:"payload" yaml:"payload"`
23+
DynamicPayload *DynamicPayload `json:"dynamic_payload" yaml:"dynamic_payload"`
24+
ProductionEnvironment *bool `json:"production_environment" yaml:"production_environment"`
2325

24-
// DeployableRef validates the ref is deployable or not.
25-
DeployableRef *string `json:"deployable_ref" yaml:"deployable_ref"`
26+
// DeployableRef validates the ref is deployable or not.
27+
DeployableRef *string `json:"deployable_ref" yaml:"deployable_ref"`
2628

27-
// AutoDeployOn deploys automatically when the pattern is matched.
28-
AutoDeployOn *string `json:"auto_deploy_on" yaml:"auto_deploy_on"`
29+
// AutoDeployOn deploys automatically when the pattern is matched.
30+
AutoDeployOn *string `json:"auto_deploy_on" yaml:"auto_deploy_on"`
2931

30-
// Serialization verify if there is a running deployment.
31-
Serialization *bool `json:"serialization" yaml:"serialization"`
32+
// Serialization verify if there is a running deployment.
33+
Serialization *bool `json:"serialization" yaml:"serialization"`
3234

33-
// Review is the configuration of Review,
34-
// It is disabled when it is empty.
35-
Review *Review `json:"review,omitempty" yaml:"review"`
35+
// Review is the configuration of Review,
36+
// It is disabled when it is empty.
37+
Review *Review `json:"review,omitempty" yaml:"review"`
3638

37-
// FrozenWindows is the list of windows to freeze deployments.
38-
FrozenWindows []FrozenWindow `json:"frozen_windows" yaml:"frozen_windows"`
39-
}
39+
// FrozenWindows is the list of windows to freeze deployments.
40+
FrozenWindows []FrozenWindow `json:"frozen_windows" yaml:"frozen_windows"`
41+
}
4042

41-
Review struct {
42-
Enabled bool `json:"enabled" yaml:"enabled"`
43-
Reviewers []string `json:"reviewers" yaml:"reviewers"`
44-
}
43+
type Review struct {
44+
Enabled bool `json:"enabled" yaml:"enabled"`
45+
Reviewers []string `json:"reviewers" yaml:"reviewers"`
46+
}
4547

46-
FrozenWindow struct {
47-
Start string `json:"start" yaml:"start"`
48-
Duration string `json:"duration" yaml:"duration"`
49-
Location string `json:"location" yaml:"location"`
50-
}
51-
)
48+
type FrozenWindow struct {
49+
Start string `json:"start" yaml:"start"`
50+
Duration string `json:"duration" yaml:"duration"`
51+
Location string `json:"location" yaml:"location"`
52+
}
5253

5354
// IsProductionEnvironment verifies whether the environment is production or not.
5455
func (e *Env) IsProductionEnvironment() bool {
@@ -124,3 +125,100 @@ func (e *Env) IsFreezed(t time.Time) (bool, error) {
124125

125126
return false, nil
126127
}
128+
129+
func (e *Env) EvaluateDynamicPayload(values map[string]interface{}) (map[string]interface{}, error) {
130+
return e.DynamicPayload.Evaluate(values)
131+
}
132+
133+
// DynamicPayload can be set to dynamically fill in the payload.
134+
type DynamicPayload struct {
135+
Enabled bool `json:"enabled" yaml:"enabled"`
136+
Inputs map[string]Input `json:"inputs" yaml:"inputs"`
137+
}
138+
139+
// Evaluate validates the payload. After that,
140+
// an object containing only the value of the defined field is returned.
141+
func (dp *DynamicPayload) Evaluate(values map[string]interface{}) (output map[string]interface{}, err error) {
142+
for key, input := range dp.Inputs {
143+
// If it is a required field, check if the value exists.
144+
value, ok := values[key]
145+
if !ok {
146+
if optional := !(input.Required != nil && *input.Required); optional {
147+
continue
148+
}
149+
150+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, fmt.Sprintf("The '%s' field is required.", key), nil)
151+
}
152+
153+
eval, err := dp.validate(input, value)
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
output[key] = eval
159+
}
160+
161+
return output, nil
162+
}
163+
164+
func (dp *DynamicPayload) validate(input Input, value interface{}) (interface{}, error) {
165+
switch input.Type {
166+
case InputTypeSelect:
167+
// Checks if the selected value matches the option,
168+
// and returns the value if it is.
169+
sv, ok := value.(string)
170+
if !ok {
171+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, fmt.Sprintf("The '%v' is not string type.", value), nil)
172+
}
173+
174+
for _, option := range *input.Options {
175+
if sv == option {
176+
return sv, nil
177+
}
178+
}
179+
180+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, "The '%s' is not matched with the options.", nil)
181+
case InputTypeNumber:
182+
nv, ok := value.(float64)
183+
if !ok {
184+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, fmt.Sprintf("The '%v' is not string type.", value), nil)
185+
}
186+
187+
return nv, nil
188+
case InputTypeString:
189+
sv, ok := value.(string)
190+
if !ok {
191+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, fmt.Sprintf("The '%v' is not string type.", value), nil)
192+
}
193+
194+
return sv, nil
195+
case InputTypeBoolean:
196+
bv, ok := value.(bool)
197+
if !ok {
198+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, fmt.Sprintf("The '%v' is not string type.", value), nil)
199+
}
200+
201+
return bv, nil
202+
default:
203+
return nil, e.NewErrorWithMessage(e.ErrorCodeDeploymentInvalid, "The type must be 'select', 'number', 'string', or 'boolean'.", nil)
204+
}
205+
}
206+
207+
// Input defines specifications for input values.
208+
type Input struct {
209+
Type InputType `json:"type" yaml:"type"`
210+
Required *bool `json:"required" yaml:"required"`
211+
Default *interface{} `json:"default" yaml:"default"`
212+
Description *string `json:"description" yaml:"description"`
213+
Options *[]string `json:"options" yaml:"options"`
214+
}
215+
216+
// InputType is the type for input.
217+
type InputType string
218+
219+
const (
220+
InputTypeSelect InputType = "select"
221+
InputTypeNumber InputType = "number"
222+
InputTypeString InputType = "string"
223+
InputTypeBoolean InputType = "boolean"
224+
)

Diff for: pkg/e/code.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const (
1414

1515
// ErrorCodeDeploymentConflict is the deployment number is conflicted.
1616
ErrorCodeDeploymentConflict ErrorCode = "deployment_conflict"
17-
// ErrorCodeDeploymentInvalid is the payload is invalid when it posts a remote deployment.
17+
// ErrorCodeDeploymentInvalid is the payload is invalid.
1818
ErrorCodeDeploymentInvalid ErrorCode = "deployment_invalid"
1919
// ErrorCodeDeploymentLocked is when the environment is locked.
2020
ErrorCodeDeploymentLocked ErrorCode = "deployment_locked"

0 commit comments

Comments
 (0)