Skip to content

Commit ed660ea

Browse files
FarhanSajid1case-fastlykpfleming
authored
feat(domain discovery): add support for Domain Discovery commands (#1482)
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? This PR adds commands for `tools` and a `domain` subcommand. The `tools` command will be used for placing tools moving forward. Once a user has enabled Domain Discovery on their accounts, they should have access to the Domain Discovery endpoints. The commands make use of the `status` and `suggest` implementation which were added in fastly/go-fastly#672. The core changes: * Add a root command for tools, which can be reused * Add a subcommand for `domains`, which will be a child of `tools`. * Add `suggest` and `status` subcommands under tools * Tests for `suggest` and `status` subcommands ### Changes to Core Features: * [ ] Have you added an explanation of what your changes do and why you'd like us to include them? * [ ] Have you written new tests for your core changes, as applicable? * [ ] Have you successfully run tests with your changes locally? ### User Impact * [x] What is the user impact of this change? Users that have enabled domain discovery on their accounts can use the `suggest` and `status` commands. Usage: Fastly Tools ``` fastly tools --help USAGE fastly tools <command> [<args> ...] Tools for working with the Fastly platform ``` Fastly Domain Tools ``` fastly tools domain --help USAGE fastly tools domain <command> [<args> ...] Domain Discovery API tools ``` Status Usage: ``` fastly tools domain status --help USAGE fastly tools domain status [<flags>] <domain> ``` Status Commands ``` Check domain name availability fastly tools domain status fastly-testing.org --scope=estimate fastly tools domain status ``` Suggest Usage: ``` fastly tools domain suggest --help USAGE fastly tools domain suggest [<flags>] <query> ... Returns domain search results for a given query ``` Suggest Commands ``` fastly domain-v1 tools suggest apple fastly tools domain suggest --keywords food,kitchen --defaults=club who what ``` ### Are there any considerations that need to be addressed for release? No breaking changes --------- Co-authored-by: case-fastly <141883771+case-fastly@users.noreply.github.com> Co-authored-by: Kevin P. Fleming <kpfleming@users.noreply.github.com>
1 parent d284bbc commit ed660ea

11 files changed

Lines changed: 634 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
### Enhancements:
88
- feat(config-store): Allow for dynamic limits on Config Store entry lengths [#1485](https://github.com/fastly/cli/pull/1485)
9-
109
- feat(backend): Add support for 'prefer IPv6' attribute. ([#1487](https://github.com/fastly/cli/pull/1487))
10+
- feat(tools/domain): add `suggest` and `status` domain tools endpoints ([#1482](https://github.com/fastly/cli/pull/1482))
1111

1212
### Bug fixes:
1313

pkg/app/run_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ tls-config
9898
tls-custom
9999
tls-platform
100100
tls-subscription
101+
tools
101102
update
102103
user
103104
vcl

pkg/commands/commands.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ import (
7878
tlscustomprivatekey "github.com/fastly/cli/pkg/commands/tls/custom/privatekey"
7979
tlsplatform "github.com/fastly/cli/pkg/commands/tls/platform"
8080
tlssubscription "github.com/fastly/cli/pkg/commands/tls/subscription"
81+
"github.com/fastly/cli/pkg/commands/tools"
82+
domainTools "github.com/fastly/cli/pkg/commands/tools/domain"
8183
"github.com/fastly/cli/pkg/commands/update"
8284
"github.com/fastly/cli/pkg/commands/user"
8385
"github.com/fastly/cli/pkg/commands/vcl"
@@ -486,6 +488,10 @@ func Define( // nolint:revive // function-length
486488
tlsSubscriptionDescribe := tlssubscription.NewDescribeCommand(tlsSubscriptionCmdRoot.CmdClause, data)
487489
tlsSubscriptionList := tlssubscription.NewListCommand(tlsSubscriptionCmdRoot.CmdClause, data)
488490
tlsSubscriptionUpdate := tlssubscription.NewUpdateCommand(tlsSubscriptionCmdRoot.CmdClause, data)
491+
toolsCmdRoot := tools.NewRootCommand(app, data)
492+
toolsDomainCmdRoot := domainTools.NewRootCommand(toolsCmdRoot.CmdClause, data)
493+
toolsDomainStatus := domainTools.NewDomainStatusCommand(toolsDomainCmdRoot.CmdClause, data)
494+
toolsDomainSuggestions := domainTools.NewDomainSuggestionsCommand(toolsDomainCmdRoot.CmdClause, data)
489495
updateRoot := update.NewRootCommand(app, data)
490496
userCmdRoot := user.NewRootCommand(app, data)
491497
userCreate := user.NewCreateCommand(userCmdRoot.CmdClause, data)
@@ -895,6 +901,10 @@ func Define( // nolint:revive // function-length
895901
tlsSubscriptionDescribe,
896902
tlsSubscriptionList,
897903
tlsSubscriptionUpdate,
904+
toolsCmdRoot,
905+
toolsDomainCmdRoot,
906+
toolsDomainStatus,
907+
toolsDomainSuggestions,
898908
updateRoot,
899909
userCmdRoot,
900910
userCreate,

pkg/commands/tools/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package tools contains tools for working with the Fastly platform.
2+
package tools

pkg/commands/tools/domain/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package domain contains Domain Discovery API tools.
2+
package domain

pkg/commands/tools/domain/root.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package domain
2+
3+
import (
4+
"io"
5+
6+
"github.com/fastly/cli/pkg/argparser"
7+
"github.com/fastly/cli/pkg/global"
8+
)
9+
10+
// RootCommand is the parent command for all tool subcommands in this package.
11+
// It should be installed under the primary `tools` root command.
12+
type RootCommand struct {
13+
argparser.Base
14+
// no flags
15+
}
16+
17+
// CommandName is the string to be used to invoke this command.
18+
const CommandName = "domain"
19+
20+
// NewRootCommand returns a new tools command registered in the parent.
21+
func NewRootCommand(parent argparser.Registerer, g *global.Data) *RootCommand {
22+
var c RootCommand
23+
c.Globals = g
24+
c.CmdClause = parent.Command(CommandName, "Domain Discovery API tools")
25+
return &c
26+
}
27+
28+
// Exec implements the command interface.
29+
func (c *RootCommand) Exec(_ io.Reader, _ io.Writer) error {
30+
panic("unreachable")
31+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package domain
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
8+
"github.com/fastly/cli/pkg/argparser"
9+
fsterr "github.com/fastly/cli/pkg/errors"
10+
"github.com/fastly/cli/pkg/global"
11+
"github.com/fastly/go-fastly/v10/fastly"
12+
"github.com/fastly/go-fastly/v10/fastly/domains/v1/tools/status"
13+
)
14+
15+
// GetDomainStatusCommand calls the Fastly API to check the availability of a domain name.
16+
type GetDomainStatusCommand struct {
17+
argparser.Base
18+
argparser.JSONOutput
19+
// Required.
20+
domain string
21+
// Optional.
22+
scope argparser.OptionalString
23+
}
24+
25+
// NewDomainStatusCommand returns a usable DomainStatusCommand registered under the parent.
26+
func NewDomainStatusCommand(parent argparser.Registerer, g *global.Data) *GetDomainStatusCommand {
27+
cmd := GetDomainStatusCommand{
28+
Base: argparser.Base{
29+
Globals: g,
30+
},
31+
}
32+
33+
cmd.CmdClause = parent.Command("status", "Check domain name availability")
34+
// Required.
35+
cmd.CmdClause.Arg("domain", "Domain name to check").Required().StringVar(&cmd.domain)
36+
// Optional.
37+
cmd.RegisterFlagBool(cmd.JSONFlag())
38+
cmd.CmdClause.Flag("scope", "Specify `--scope=estimate` to perform an “estimated” availability check, which checks the DNS and domain aftermarkets, not domain registries").Action(cmd.scope.Set).StringVar(&cmd.scope.Value)
39+
40+
return &cmd
41+
}
42+
43+
// Exec invokes the application logic for the command.
44+
func (g *GetDomainStatusCommand) Exec(_ io.Reader, out io.Writer) error {
45+
if g.Globals.Verbose() && g.JSONOutput.Enabled {
46+
return fsterr.ErrInvalidVerboseJSONCombo
47+
}
48+
49+
input := &status.GetInput{
50+
Domain: g.domain,
51+
}
52+
53+
if g.scope.WasSet {
54+
scope := status.Scope(g.scope.Value)
55+
if scope != status.ScopeEstimate {
56+
return fsterr.RemediationError{
57+
Inner: errors.New("invalid scope provided"),
58+
Remediation: "Use `--scope=estimate` for an estimated status check",
59+
}
60+
}
61+
input.Scope = fastly.ToPointer(scope)
62+
}
63+
64+
fc, ok := g.Globals.APIClient.(*fastly.Client)
65+
if !ok {
66+
return errors.New("failed to acquire the Fastly API client")
67+
}
68+
69+
st, err := status.Get(fc, input)
70+
if err != nil {
71+
g.Globals.ErrLog.Add(err)
72+
return err
73+
}
74+
75+
if ok, err := g.WriteJSON(out, st); ok {
76+
return err
77+
}
78+
79+
printStatusSummary(out, st)
80+
return nil
81+
}
82+
83+
// printStatusSummary displays the information returned from the API in a summarized format.
84+
func printStatusSummary(w io.Writer, st *status.Status) {
85+
fmt.Fprintf(w, "Domain: %s\n", st.Domain)
86+
fmt.Fprintf(w, "Zone: %s\n", st.Zone)
87+
fmt.Fprintf(w, "Status: %s\n", st.Status)
88+
fmt.Fprintf(w, "Tags: %s\n", st.Tags)
89+
90+
if st.Scope != nil {
91+
fmt.Fprintf(w, "Scope: %s\n", *st.Scope)
92+
}
93+
94+
if len(st.Offers) > 0 {
95+
fmt.Fprintf(w, "Offers:\n")
96+
for _, o := range st.Offers {
97+
fmt.Fprintf(w, " - Vendor: %s\n", o.Vendor)
98+
fmt.Fprintf(w, " Currency: %s\n", o.Currency)
99+
fmt.Fprintf(w, " Price: %s\n", o.Price)
100+
}
101+
}
102+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package domain_test
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/fastly/cli/pkg/commands/tools"
10+
"github.com/fastly/cli/pkg/commands/tools/domain"
11+
"github.com/fastly/cli/pkg/testutil"
12+
"github.com/fastly/go-fastly/v10/fastly"
13+
"github.com/fastly/go-fastly/v10/fastly/domains/v1/tools/status"
14+
)
15+
16+
func TestNewDomainsV1ToolsStatusCommand(t *testing.T) {
17+
scenarios := []testutil.CLIScenario{
18+
{
19+
Args: "",
20+
WantError: "error parsing arguments: required argument 'domain' not provided",
21+
},
22+
{
23+
Args: "fastly-cli-testing.com --scope not-estimate",
24+
WantError: "invalid scope provided",
25+
},
26+
{
27+
Args: "fastly-cli-testing.com",
28+
Client: &http.Client{
29+
Transport: &testutil.MockRoundTripper{
30+
Response: &http.Response{
31+
StatusCode: http.StatusOK,
32+
Status: http.StatusText(http.StatusOK),
33+
Body: io.NopCloser(bytes.NewReader(testutil.GenJSON(status.Status{
34+
Domain: "fastly-cli-testing.com",
35+
Zone: "com",
36+
Status: "undelegated inactive",
37+
Tags: "generic",
38+
}))),
39+
},
40+
},
41+
},
42+
WantOutput: `Domain: fastly-cli-testing.com
43+
Zone: com
44+
Status: undelegated inactive
45+
Tags: generic
46+
`,
47+
},
48+
{
49+
Args: "--scope estimate fastly-cli-testing-offers.com",
50+
Client: &http.Client{
51+
Transport: &testutil.MockRoundTripper{
52+
Response: &http.Response{
53+
StatusCode: http.StatusOK,
54+
Status: http.StatusText(http.StatusOK),
55+
Body: io.NopCloser(bytes.NewReader(testutil.GenJSON(status.Status{
56+
Domain: "fastly-cli-testing-offers.com",
57+
Zone: "com",
58+
Status: "marketed priced transferable active",
59+
Tags: "generic",
60+
Scope: fastly.ToPointer(status.ScopeEstimate),
61+
Offers: []status.Offer{
62+
{
63+
Vendor: "example.com",
64+
Currency: "USD",
65+
Price: "20000.00",
66+
},
67+
},
68+
}))),
69+
},
70+
},
71+
},
72+
WantOutput: `Domain: fastly-cli-testing-offers.com
73+
Zone: com
74+
Status: marketed priced transferable active
75+
Tags: generic
76+
Scope: estimate
77+
Offers:
78+
- Vendor: example.com
79+
Currency: USD
80+
Price: 20000.00
81+
`,
82+
},
83+
{
84+
Args: "-j --scope estimate fastly-cli-testing-offers.com",
85+
Client: &http.Client{
86+
Transport: &testutil.MockRoundTripper{
87+
Response: &http.Response{
88+
StatusCode: http.StatusOK,
89+
Status: http.StatusText(http.StatusOK),
90+
Body: io.NopCloser(bytes.NewReader(testutil.GenJSON(status.Status{
91+
Domain: "fastly-cli-testing-offers.com",
92+
Zone: "com",
93+
Status: "marketed priced transferable active",
94+
Tags: "generic",
95+
Scope: fastly.ToPointer(status.ScopeEstimate),
96+
Offers: []status.Offer{
97+
{
98+
Vendor: "example.com",
99+
Currency: "USD",
100+
Price: "20000.00",
101+
},
102+
},
103+
}))),
104+
},
105+
},
106+
},
107+
WantOutput: `{
108+
"domain": "fastly-cli-testing-offers.com",
109+
"zone": "com",
110+
"status": "marketed priced transferable active",
111+
"scope": "estimate",
112+
"tags": "generic",
113+
"offers": [
114+
{
115+
"vendor": "example.com",
116+
"price": "20000.00",
117+
"currency": "USD"
118+
}
119+
]
120+
}
121+
`,
122+
},
123+
}
124+
testutil.RunCLIScenarios(t, []string{tools.CommandName, domain.CommandName, "status"}, scenarios)
125+
}

0 commit comments

Comments
 (0)