diff --git a/cmd/generateConfig.go b/cmd/generateConfig.go index 702a70c..f9170c9 100644 --- a/cmd/generateConfig.go +++ b/cmd/generateConfig.go @@ -29,6 +29,17 @@ type GenerateConfigOptions struct { Out string } +type Response struct { + Available bool `json:"available"` + Suggestion Suggestion `json:"suggestion"` +} + +type Suggestion struct { + ServiceName string `json:"serviceName"` + Names []string `json:"names"` + Hosts []string `json:"hosts"` +} + func (o *GenerateConfigOptions) IsEmpty() bool { return o.Template == "" && o.Service == "" && o.Upstream == "" } @@ -40,11 +51,37 @@ func (o *GenerateConfigOptions) ValidateTemplate() error { return fmt.Errorf("%s is not a valid template", o.Template) } -func (o *GenerateConfigOptions) Exec() error { +func (o *GenerateConfigOptions) ValidateService(ctx *pkg.AppContext, service string) error { + path := fmt.Sprintf("/ds/api/v3/routes/availability?gatewayId=%s&serviceName=%s", ctx.Gateway, service) + URL, _ := ctx.CreateUrl(path, nil) + decodedURL, err := url.QueryUnescape(URL) + if err != nil { + return err + } + request, err := pkg.NewApiGet[Response](ctx, decodedURL) + if err != nil { + return err + } + response, err := request.Do() + if err != nil { + return err + } + + if !response.Data.Available { + return fmt.Errorf("Service %s is already in use. Suggestion: %s", service, response.Data.Suggestion.ServiceName) + } + return nil +} + +func (o *GenerateConfigOptions) Exec(ctx *pkg.AppContext) error { err := o.ValidateTemplate() if err != nil { return err } + err = o.ValidateService(ctx, o.Service) + if err != nil { + return err + } err = o.ParseUpstream() if err != nil { return err @@ -126,7 +163,7 @@ $ gwa generate-config --template client-credentials-shared-idp \ return err } } - err := opts.Exec() + err := opts.Exec(ctx) if err != nil { return err } @@ -199,6 +236,13 @@ func initGenerateModel(ctx *pkg.AppContext, opts *GenerateConfigOptions) pkg.Gen prompts[service] = pkg.NewTextInput("Service", "", true) prompts[service].TextInput.Focus() + prompts[service].Validator = func(input string) error { + err := opts.ValidateService(ctx, input) + if err != nil { + return err + } + return nil + } prompts[template] = pkg.NewList("Template", []string{ "client-credentials-shared-idp", diff --git a/cmd/generateConfig_test.go b/cmd/generateConfig_test.go index 875dfcb..d6a6f88 100644 --- a/cmd/generateConfig_test.go +++ b/cmd/generateConfig_test.go @@ -1,12 +1,14 @@ package cmd import ( + "fmt" "net/url" "os" "path" "testing" "github.com/bcgov/gwa-cli/pkg" + "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" ) @@ -133,6 +135,55 @@ func TestClientCredentialsGenerator(t *testing.T) { assert.Contains(t, compare, "allowed_aud: ap-cc-sampler-default-dev") } +func TestValidateService_Available(t *testing.T) { + ctx := &pkg.AppContext{ + Gateway: "test-gateway", + ApiHost: "api.gov.ca", + } + serviceName := "available-service" + + // Activate httpmock and register the mock response + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + URL := fmt.Sprintf("https://%s/ds/api/v3/routes/availability?gatewayId=%s&serviceName=%s", ctx.ApiHost, ctx.Gateway, serviceName) + httpmock.RegisterResponder("GET", URL, + httpmock.NewJsonResponderOrPanic(200, Response{ + Available: true, + }), + ) + + opts := &GenerateConfigOptions{} + err := opts.ValidateService(ctx, serviceName) + assert.NoError(t, err, "expected no error when service name is available") +} + +func TestValidateService_NotAvailable(t *testing.T) { + ctx := &pkg.AppContext{ + Gateway: "test-gateway", + ApiHost: "api.gov.ca", + } + serviceName := "unavailable-service" + + // Activate httpmock and register the mock response + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + URL := fmt.Sprintf("https://%s/ds/api/v3/routes/availability?gatewayId=%s&serviceName=%s", ctx.ApiHost, ctx.Gateway, serviceName) + httpmock.RegisterResponder("GET", URL, + httpmock.NewJsonResponderOrPanic(200, Response{ + Available: false, + Suggestion: Suggestion{ + ServiceName: "suggested-service-name", + }, + }), + ) + + opts := &GenerateConfigOptions{} + err := opts.ValidateService(ctx, serviceName) + assert.Error(t, err, "expected error when service name is not available") + assert.Contains(t, err.Error(), "Service unavailable-service is already in use. Suggestion: suggested-service-name") +======= func TestQuickStartGenerator(t *testing.T) { dir := t.TempDir() ctx := &pkg.AppContext{ @@ -166,4 +217,3 @@ func TestQuickStartGenerator(t *testing.T) { assert.Contains(t, compare, "url: https://httpbin.org/post") assert.Contains(t, compare, "- my-service.dev.api.gov.bc.ca") assert.Contains(t, compare, "paths: [/post]") -}