Skip to content

Commit 5d02c32

Browse files
Add the ability to show generated VCL for a service version (#1498)
All Submissions: * [x] Have you followed the guidelines in our Contributing document? * [x] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/fastly/cli/pulls) for the same update/change? <!-- You can erase any parts of this template not applicable to your Pull Request. --> ### New Feature Submissions: * [x] Does your submission pass tests? ### Changes to Core Features: * [x] Have you added an explanation of what your changes do and why you'd like us to include them? * [x] Have you written new tests for your core changes, as applicable? * [x] Have you successfully run tests with your changes locally? ### User Impact * [x] What is the user impact of this change? - _Adds a new command to show the generated VCL for a service-version_ ### Are there any considerations that need to be addressed for release? <!-- Any breaking changes, etc --> Co-authored-by: Kevin P. Fleming <kpfleming@users.noreply.github.com>
1 parent f6bb91f commit 5d02c32

6 files changed

Lines changed: 225 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Breaking:
66

77
### Enhancements:
8+
- feat(vcl): Allow showing of generated VCL for a service version [#1498](https://github.com/fastly/cli/pull/1498)
89
- Add experimental "enable Pushpin" mode ([#1509](https://github.com/fastly/cli/pull/1509))
910

1011
### Bug fixes:

pkg/api/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ type Interface interface {
239239

240240
CreateManagedLogging(*fastly.CreateManagedLoggingInput) (*fastly.ManagedLogging, error)
241241

242+
GetGeneratedVCL(i *fastly.GetGeneratedVCLInput) (*fastly.VCL, error)
243+
242244
CreateVCL(*fastly.CreateVCLInput) (*fastly.VCL, error)
243245
ListVCLs(*fastly.ListVCLsInput) ([]*fastly.VCL, error)
244246
GetVCL(*fastly.GetVCLInput) (*fastly.VCL, error)

pkg/commands/commands.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ func Define( // nolint:revive // function-length
500500
userList := user.NewListCommand(userCmdRoot.CmdClause, data)
501501
userUpdate := user.NewUpdateCommand(userCmdRoot.CmdClause, data)
502502
vclCmdRoot := vcl.NewRootCommand(app, data)
503+
vclDescribe := vcl.NewDescribeCommand(vclCmdRoot.CmdClause, data)
503504
vclConditionCmdRoot := condition.NewRootCommand(vclCmdRoot.CmdClause, data)
504505
vclConditionCreate := condition.NewCreateCommand(vclConditionCmdRoot.CmdClause, data)
505506
vclConditionDelete := condition.NewDeleteCommand(vclConditionCmdRoot.CmdClause, data)
@@ -913,6 +914,7 @@ func Define( // nolint:revive // function-length
913914
userList,
914915
userUpdate,
915916
vclCmdRoot,
917+
vclDescribe,
916918
vclConditionCmdRoot,
917919
vclConditionCreate,
918920
vclConditionDelete,

pkg/commands/vcl/describe.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package vcl
2+
3+
import (
4+
"fmt"
5+
"io"
6+
7+
"github.com/fastly/go-fastly/v10/fastly"
8+
9+
"github.com/fastly/cli/pkg/argparser"
10+
fsterr "github.com/fastly/cli/pkg/errors"
11+
"github.com/fastly/cli/pkg/global"
12+
)
13+
14+
// NewDescribeCommand returns a usable command registered under the parent.
15+
func NewDescribeCommand(parent argparser.Registerer, g *global.Data) *DescribeCommand {
16+
c := DescribeCommand{
17+
Base: argparser.Base{
18+
Globals: g,
19+
},
20+
}
21+
c.CmdClause = parent.Command("describe", "Get the generated VCL for a particular service and version").Alias("get")
22+
23+
// Required.
24+
c.RegisterFlag(argparser.StringFlagOpts{
25+
Name: argparser.FlagVersionName,
26+
Description: argparser.FlagVersionDesc,
27+
Dst: &c.serviceVersion.Value,
28+
Required: true,
29+
})
30+
31+
// Optional.
32+
c.RegisterFlagBool(c.JSONFlag()) // --json
33+
c.RegisterFlag(argparser.StringFlagOpts{
34+
Name: argparser.FlagServiceIDName,
35+
Description: argparser.FlagServiceIDDesc,
36+
Dst: &g.Manifest.Flag.ServiceID,
37+
Short: 's',
38+
})
39+
c.RegisterFlag(argparser.StringFlagOpts{
40+
Action: c.serviceName.Set,
41+
Name: argparser.FlagServiceName,
42+
Description: argparser.FlagServiceNameDesc,
43+
Dst: &c.serviceName.Value,
44+
})
45+
46+
return &c
47+
}
48+
49+
// DescribeCommand calls the Fastly API to list appropriate resources.
50+
type DescribeCommand struct {
51+
argparser.Base
52+
argparser.JSONOutput
53+
54+
serviceName argparser.OptionalServiceNameID
55+
serviceVersion argparser.OptionalServiceVersion
56+
}
57+
58+
// Exec invokes the application logic for the command.
59+
func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error {
60+
if c.Globals.Verbose() && c.JSONOutput.Enabled {
61+
return fsterr.ErrInvalidVerboseJSONCombo
62+
}
63+
64+
serviceID, serviceVersion, err := argparser.ServiceDetails(argparser.ServiceDetailsOpts{
65+
APIClient: c.Globals.APIClient,
66+
Manifest: *c.Globals.Manifest,
67+
Out: out,
68+
ServiceNameFlag: c.serviceName,
69+
ServiceVersionFlag: c.serviceVersion,
70+
VerboseMode: c.Globals.Flags.Verbose,
71+
})
72+
if err != nil {
73+
c.Globals.ErrLog.AddWithContext(err, map[string]any{
74+
"Service ID": serviceID,
75+
"Service Version": fsterr.ServiceVersion(serviceVersion),
76+
})
77+
return err
78+
}
79+
80+
input := c.constructInput(serviceID, fastly.ToValue(serviceVersion.Number))
81+
82+
o, err := c.Globals.APIClient.GetGeneratedVCL(input)
83+
if err != nil {
84+
c.Globals.ErrLog.AddWithContext(err, map[string]any{
85+
"Service ID": serviceID,
86+
"Service Version": fastly.ToValue(serviceVersion.Number),
87+
})
88+
return err
89+
}
90+
91+
if ok, err := c.WriteJSON(out, o); ok {
92+
return err
93+
}
94+
95+
if c.Globals.Verbose() {
96+
c.printVerbose(out, fastly.ToValue(serviceVersion.Number), o)
97+
} else {
98+
err = c.print(out, o)
99+
if err != nil {
100+
return err
101+
}
102+
}
103+
return nil
104+
}
105+
106+
// constructInput transforms values parsed from CLI flags into an object to be used by the API client library.
107+
func (c *DescribeCommand) constructInput(serviceID string, serviceVersion int) *fastly.GetGeneratedVCLInput {
108+
var input fastly.GetGeneratedVCLInput
109+
110+
input.ServiceID = serviceID
111+
input.ServiceVersion = serviceVersion
112+
113+
return &input
114+
}
115+
116+
// printVerbose displays the information returned from the API in a verbose
117+
// format.
118+
func (c *DescribeCommand) printVerbose(out io.Writer, serviceVersion int, v *fastly.VCL) {
119+
fmt.Fprintf(out, "Service Version: %d\n", serviceVersion)
120+
121+
fmt.Fprintf(out, "\n")
122+
fmt.Fprintf(out, "Name: %s\n", fastly.ToValue(v.Name))
123+
fmt.Fprintf(out, "Main: %t\n", fastly.ToValue(v.Main))
124+
125+
if v.CreatedAt != nil {
126+
fmt.Fprintf(out, "Created at: %s\n", v.CreatedAt)
127+
}
128+
if v.UpdatedAt != nil {
129+
fmt.Fprintf(out, "Updated at: %s\n", v.UpdatedAt)
130+
}
131+
if v.DeletedAt != nil {
132+
fmt.Fprintf(out, "Deleted at: %s\n", v.DeletedAt)
133+
}
134+
135+
fmt.Fprintf(out, "Content: \n%s\n", fastly.ToValue(v.Content))
136+
}
137+
138+
// print the generated VCL.
139+
func (c *DescribeCommand) print(out io.Writer, v *fastly.VCL) error {
140+
fmt.Fprintf(out, "%s\n", fastly.ToValue(v.Content))
141+
return nil
142+
}

pkg/commands/vcl/vcl_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package vcl_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/fastly/go-fastly/v10/fastly"
7+
8+
root "github.com/fastly/cli/pkg/commands/vcl"
9+
"github.com/fastly/cli/pkg/mock"
10+
"github.com/fastly/cli/pkg/testutil"
11+
)
12+
13+
func TestVCLDescribe(t *testing.T) {
14+
scenarios := []testutil.CLIScenario{
15+
{
16+
Name: "validate missing --version flag",
17+
WantError: "error parsing arguments: required flag --version not provided",
18+
},
19+
{
20+
Name: "validate missing --service-id flag",
21+
Args: "--version 3",
22+
WantError: "error reading service: no service ID found",
23+
},
24+
{
25+
Name: "validate DescribeVCL API error",
26+
API: mock.API{
27+
ListVersionsFn: testutil.ListVersions,
28+
GetGeneratedVCLFn: func(_ *fastly.GetGeneratedVCLInput) (*fastly.VCL, error) {
29+
return nil, testutil.Err
30+
},
31+
},
32+
Args: "--service-id 123 --version 3",
33+
WantError: testutil.Err.Error(),
34+
},
35+
{
36+
Name: "validate DescribeVCL API success",
37+
API: mock.API{
38+
ListVersionsFn: testutil.ListVersions,
39+
GetGeneratedVCLFn: getVCL,
40+
},
41+
Args: "--service-id 123 --version 3",
42+
WantOutput: "# some vcl content\n",
43+
},
44+
{
45+
Name: "validate missing --verbose flag",
46+
API: mock.API{
47+
ListVersionsFn: testutil.ListVersions,
48+
GetGeneratedVCLFn: getVCL,
49+
},
50+
Args: "--service-id 123 --verbose --version 1",
51+
WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: false\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\nContent: \n# some vcl content\n",
52+
},
53+
}
54+
55+
testutil.RunCLIScenarios(t, []string{root.CommandName, "describe"}, scenarios)
56+
}
57+
58+
func getVCL(i *fastly.GetGeneratedVCLInput) (*fastly.VCL, error) {
59+
t := testutil.Date
60+
61+
return &fastly.VCL{
62+
Content: fastly.ToPointer("# some vcl content"),
63+
Main: fastly.ToPointer(false),
64+
Name: fastly.ToPointer("foo"),
65+
ServiceID: fastly.ToPointer(i.ServiceID),
66+
ServiceVersion: fastly.ToPointer(i.ServiceVersion),
67+
CreatedAt: &t,
68+
DeletedAt: &t,
69+
UpdatedAt: &t,
70+
}, nil
71+
}

pkg/mock/api.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ type API struct {
231231

232232
CreateManagedLoggingFn func(*fastly.CreateManagedLoggingInput) (*fastly.ManagedLogging, error)
233233

234+
GetGeneratedVCLFn func(*fastly.GetGeneratedVCLInput) (*fastly.VCL, error)
235+
234236
CreateVCLFn func(*fastly.CreateVCLInput) (*fastly.VCL, error)
235237
ListVCLsFn func(*fastly.ListVCLsInput) ([]*fastly.VCL, error)
236238
GetVCLFn func(*fastly.GetVCLInput) (*fastly.VCL, error)
@@ -1321,6 +1323,11 @@ func (m API) CreateManagedLogging(i *fastly.CreateManagedLoggingInput) (*fastly.
13211323
return m.CreateManagedLoggingFn(i)
13221324
}
13231325

1326+
// GetGeneratedVCL implements Interface.
1327+
func (m API) GetGeneratedVCL(i *fastly.GetGeneratedVCLInput) (*fastly.VCL, error) {
1328+
return m.GetGeneratedVCLFn(i)
1329+
}
1330+
13241331
// CreateVCL implements Interface.
13251332
func (m API) CreateVCL(i *fastly.CreateVCLInput) (*fastly.VCL, error) {
13261333
return m.CreateVCLFn(i)

0 commit comments

Comments
 (0)