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

Commit e83f69b

Browse files
author
Noah Lee
authored
Add the field parameter for the dynamic payload in CLI (#411)
* Add the `DynamicPayload` param * Add the 'field' for dynamic payload. * Add validation for required field * Add unit tests
1 parent 4590f46 commit e83f69b

File tree

4 files changed

+171
-7
lines changed

4 files changed

+171
-7
lines changed

Diff for: cmd/cli/deployment_create.go

+104-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package main
22

33
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
48
"github.com/urfave/cli/v2"
59

10+
"github.com/gitploy-io/gitploy/model/extent"
611
"github.com/gitploy-io/gitploy/pkg/api"
712
)
813

@@ -26,6 +31,11 @@ var deploymentCreateCommand = &cli.Command{
2631
Usage: "The specific ref. It can be any named branch, tag, or SHA.",
2732
Required: true,
2833
},
34+
&cli.StringSliceFlag{
35+
Name: "field",
36+
Aliases: []string{"f"},
37+
Usage: "The pair of key and value to add to the payload. The format must be <key>=<value>.",
38+
},
2939
},
3040
Action: func(cli *cli.Context) error {
3141
ns, n, err := splitFullName(cli.Args().First())
@@ -34,10 +44,26 @@ var deploymentCreateCommand = &cli.Command{
3444
}
3545

3646
c := buildClient(cli)
47+
48+
config, err := c.Config.Get(cli.Context, ns, n)
49+
if err != nil {
50+
return err
51+
}
52+
53+
// If the 'dynamic_payload' field is enabled,
54+
// it creates a payload and pass it as a parameter.
55+
var payload map[string]interface{}
56+
if env := config.GetEnv(cli.String("env")); env.IsDynamicPayloadEnabled() {
57+
if payload, err = buildDyanmicPayload(cli.StringSlice("field"), env); err != nil {
58+
return err
59+
}
60+
}
61+
3762
d, err := c.Deployment.Create(cli.Context, ns, n, &api.DeploymentCreateRequest{
38-
Type: cli.String("type"),
39-
Ref: cli.String("ref"),
40-
Env: cli.String("env"),
63+
Type: cli.String("type"),
64+
Ref: cli.String("ref"),
65+
Env: cli.String("env"),
66+
DynamicPayload: payload,
4167
})
4268
if err != nil {
4369
return err
@@ -46,3 +72,78 @@ var deploymentCreateCommand = &cli.Command{
4672
return printJson(cli, d)
4773
},
4874
}
75+
76+
func buildDyanmicPayload(fields []string, env *extent.Env) (map[string]interface{}, error) {
77+
values := make(map[string]string)
78+
79+
for _, f := range fields {
80+
keyAndValue := strings.SplitN(f, "=", 2)
81+
if len(keyAndValue) != 2 {
82+
return nil, fmt.Errorf("The field must be <key>=<value> format")
83+
}
84+
85+
values[keyAndValue[0]] = keyAndValue[1]
86+
}
87+
88+
payload := make(map[string]interface{})
89+
90+
// Build the payload, and use the default value if there is no value.
91+
for key, input := range env.DynamicPayload.Inputs {
92+
val, ok := values[key]
93+
// Set the default value if the value doesn't exist.
94+
if !ok {
95+
if input.Default != nil {
96+
payload[key] = *input.Default
97+
}
98+
99+
continue
100+
}
101+
102+
parsed, err := parseValue(input, val)
103+
if err != nil {
104+
return nil, fmt.Errorf("The value of the '%s' field is not %s", key, input.Type)
105+
}
106+
107+
payload[key] = parsed
108+
}
109+
110+
// Check that the required values are present.
111+
for key, input := range env.DynamicPayload.Inputs {
112+
if !(input.Required != nil && *input.Required) {
113+
continue
114+
}
115+
116+
if _, ok := payload[key]; !ok {
117+
return nil, fmt.Errorf("The value of the '%s' field is required", key)
118+
}
119+
}
120+
121+
return payload, nil
122+
}
123+
124+
func parseValue(input extent.Input, s string) (interface{}, error) {
125+
switch input.Type {
126+
case extent.InputTypeSelect:
127+
return s, nil
128+
129+
case extent.InputTypeString:
130+
return s, nil
131+
132+
case extent.InputTypeNumber:
133+
if val, err := strconv.ParseFloat(s, 64); err != nil {
134+
return nil, err
135+
} else {
136+
return val, nil
137+
}
138+
139+
case extent.InputTypeBoolean:
140+
if val, err := strconv.ParseBool(s); err != nil {
141+
return nil, err
142+
} else {
143+
return val, nil
144+
}
145+
146+
default:
147+
return nil, fmt.Errorf("%s is unsupported type.", input.Type)
148+
}
149+
}

Diff for: cmd/cli/deployment_create_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/gitploy-io/gitploy/model/extent"
8+
)
9+
10+
func Test_buildDyanmicPayload(t *testing.T) {
11+
t.Run("Return an error when syntax is invalid.", func(t *testing.T) {
12+
_, err := buildDyanmicPayload([]string{
13+
"foo",
14+
}, &extent.Env{
15+
DynamicPayload: &extent.DynamicPayload{
16+
Enabled: true,
17+
},
18+
})
19+
20+
if err == nil {
21+
t.Fatalf("buildDyanmicPayload dosen't return an error")
22+
}
23+
})
24+
25+
t.Run("Return a payload with default values.", func(t *testing.T) {
26+
var qux interface{} = "qux"
27+
28+
payload, err := buildDyanmicPayload([]string{}, &extent.Env{
29+
DynamicPayload: &extent.DynamicPayload{
30+
Enabled: true,
31+
Inputs: map[string]extent.Input{
32+
"foo": {
33+
Type: extent.InputTypeString,
34+
},
35+
"baz": {
36+
Type: extent.InputTypeString,
37+
Default: &qux,
38+
},
39+
},
40+
},
41+
})
42+
43+
if err != nil {
44+
t.Fatalf("buildDyanmicPayload returns an error")
45+
}
46+
47+
if expected := map[string]interface{}{
48+
"baz": "qux",
49+
}; !reflect.DeepEqual(payload, expected) {
50+
t.Fatalf("buildDyanmicPayload = %v, wanted %v", payload, expected)
51+
}
52+
})
53+
}

Diff for: pkg/api/deployment.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ type (
2121
}
2222

2323
DeploymentCreateRequest struct {
24-
Type string `json:"type"`
25-
Ref string `json:"ref"`
26-
Env string `json:"env"`
24+
Type string `json:"type"`
25+
Ref string `json:"ref"`
26+
Env string `json:"env"`
27+
DynamicPayload map[string]interface{} `json:"dynamic_payload"`
2728
}
2829
)
2930

Diff for: pkg/api/deployment_test.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"io/ioutil"
77
"net/http"
8+
"reflect"
89
"testing"
910

1011
"github.com/gitploy-io/gitploy/model/ent"
@@ -61,7 +62,12 @@ func TestDeploymentService_Create(t *testing.T) {
6162
}
6263

6364
// Verify the fields of the body.
64-
return b.Type == "branch" && b.Env == "production" && b.Ref == "main", nil
65+
return b.Type == "branch" &&
66+
b.Env == "production" &&
67+
b.Ref == "main" &&
68+
reflect.DeepEqual(b.DynamicPayload, map[string]interface{}{
69+
"foo": "bar",
70+
}), nil
6571
}).
6672
Reply(201).
6773
JSON(d)
@@ -72,6 +78,9 @@ func TestDeploymentService_Create(t *testing.T) {
7278
Type: "branch",
7379
Env: "production",
7480
Ref: "main",
81+
DynamicPayload: map[string]interface{}{
82+
"foo": "bar",
83+
},
7584
})
7685
if err != nil {
7786
t.Fatalf("Create returns an error: %s", err)

0 commit comments

Comments
 (0)