Skip to content

Commit

Permalink
feat(resources): Add environments (#219)
Browse files Browse the repository at this point in the history
* feat(resources): Add environments

* feat(resources): Add environments to list

* test: Add tests

* feat: Omit environment when empty

* chore: Use latest meroxa-go
  • Loading branch information
raulb authored Nov 29, 2021
1 parent 725bf81 commit 5206737
Show file tree
Hide file tree
Showing 29 changed files with 1,375 additions and 14 deletions.
28 changes: 25 additions & 3 deletions cmd/meroxa/root/resources/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
"encoding/json"
"fmt"

"github.com/meroxa/cli/log"

"github.com/google/uuid"
"github.com/meroxa/cli/cmd/meroxa/builder"
"github.com/meroxa/cli/log"
"github.com/meroxa/meroxa-go/pkg/meroxa"
)

Expand All @@ -44,6 +44,9 @@ type Create struct {
URL string `long:"url" short:"u" usage:"resource url" required:"true"`
Metadata string `long:"metadata" short:"m" usage:"resource metadata"`

// TODO: Add support to builder to create flags with an alias (--env | --environment)
Environment string `long:"env" usage:"environment (name or UUID) where resource will be created" hidden:"true"`

// credentials
Username string `long:"username" short:"" usage:"username"`
Password string `long:"password" short:"" usage:"password"`
Expand Down Expand Up @@ -74,6 +77,8 @@ func (c *Create) Docs() builder.Docs {
return builder.Docs{
Short: "Add a resource to your Meroxa resource catalog",
Long: `Use the create command to add resources to your Meroxa resource catalog.`,

// TODO: Provide example with `--env` once it's not behind a feature flag
Example: `
meroxa resources create store --type postgres -u $DATABASE_URL --metadata '{"logical_replication":true}'
meroxa resources create datalake --type s3 -u "s3://$AWS_ACCESS_KEY_ID:$AWS_ACCESS_KEY_SECRET@us-east-1/meroxa-demos"
Expand Down Expand Up @@ -107,13 +112,30 @@ func (c *Create) ParseArgs(args []string) error {
}

func (c *Create) Execute(ctx context.Context) error {
var env string

input := meroxa.CreateResourceInput{
Type: meroxa.ResourceType(c.flags.Type),
Name: c.args.Name,
URL: c.flags.URL,
Metadata: nil,
}

if c.flags.Environment != "" {
input.Environment = &meroxa.ResourceEnvironment{}
env = c.flags.Environment

_, err := uuid.Parse(c.flags.Environment)

if err == nil {
input.Environment.UUID = c.flags.Environment
} else {
input.Environment.Name = c.flags.Environment
}
} else {
env = string(meroxa.EnvironmentTypeCommon)
}

if c.hasCredentials() {
input.Credentials = &meroxa.Credentials{
Username: c.flags.Username,
Expand All @@ -139,7 +161,7 @@ func (c *Create) Execute(ctx context.Context) error {
}
}

c.logger.Infof(ctx, "Creating %q resource...", input.Type)
c.logger.Infof(ctx, "Creating %q resource in %q environment...", input.Type, env)

res, err := c.client.CreateResource(ctx, &input)
if err != nil {
Expand Down
138 changes: 136 additions & 2 deletions cmd/meroxa/root/resources/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestCreateResourceFlags(t *testing.T) {
name string
required bool
shorthand string
hidden bool
}{
{name: "type", required: true, shorthand: ""},
{name: "url", required: true, shorthand: "u"},
Expand All @@ -55,6 +56,7 @@ func TestCreateResourceFlags(t *testing.T) {
{name: "client-key", required: false, shorthand: ""},
{name: "ssl", required: false, shorthand: ""},
{name: "metadata", required: false, shorthand: "m"},
{name: "env", required: false, hidden: true},
}

c := builder.BuildCobraCommand(&Create{})
Expand All @@ -71,6 +73,14 @@ func TestCreateResourceFlags(t *testing.T) {
if f.required && !utils.IsFlagRequired(cf) {
t.Fatalf("expected flag \"%s\" to be required", f.name)
}

if cf.Hidden != f.hidden {
if cf.Hidden {
t.Fatalf("expected flag \"%s\" not to be hidden", f.name)
} else {
t.Fatalf("expected flag \"%s\" to be hidden", f.name)
}
}
}
}
}
Expand Down Expand Up @@ -112,9 +122,133 @@ func TestCreateResourceExecution(t *testing.T) {
}

gotLeveledOutput := logger.LeveledOutput()
wantLeveledOutput := fmt.Sprintf(`Creating %q resource...
wantLeveledOutput := fmt.Sprintf(`Creating %q resource in %q environment...
Resource %q is successfully created!
`, cr.Type, meroxa.EnvironmentTypeCommon, cr.Name)

if gotLeveledOutput != wantLeveledOutput {
t.Fatalf("expected output:\n%s\ngot:\n%s", wantLeveledOutput, gotLeveledOutput)
}

gotJSONOutput := logger.JSONOutput()
var gotResource meroxa.Resource
err = json.Unmarshal([]byte(gotJSONOutput), &gotResource)
if err != nil {
t.Fatalf("not expected error, got %q", err.Error())
}

if !reflect.DeepEqual(gotResource, cr) {
t.Fatalf("expected \"%v\", got \"%v\"", cr, gotResource)
}
}

func TestCreateResourceExecutionWithEnvironmentName(t *testing.T) {
ctx := context.Background()
ctrl := gomock.NewController(t)
client := mock.NewMockClient(ctrl)
logger := log.NewTestLogger()

cr := utils.GenerateResourceWithEnvironment()

r := meroxa.CreateResourceInput{
Type: "postgres",
Name: "",
URL: "https://foo.url",
Credentials: nil,
Metadata: nil,
Environment: &meroxa.ResourceEnvironment{
Name: cr.Environment.Name,
},
}

client.
EXPECT().
CreateResource(
ctx,
&r,
).
Return(&cr, nil)

c := &Create{
client: client,
logger: logger,
}
c.args.Name = r.Name
c.flags.Type = string(r.Type)
c.flags.URL = r.URL
c.flags.Environment = r.Environment.Name

err := c.Execute(ctx)
if err != nil {
t.Fatalf("not expected error, got %q", err.Error())
}

gotLeveledOutput := logger.LeveledOutput()
wantLeveledOutput := fmt.Sprintf(`Creating %q resource in %q environment...
Resource %q is successfully created!
`, cr.Type, cr.Environment.Name, cr.Name)

if gotLeveledOutput != wantLeveledOutput {
t.Fatalf("expected output:\n%s\ngot:\n%s", wantLeveledOutput, gotLeveledOutput)
}

gotJSONOutput := logger.JSONOutput()
var gotResource meroxa.Resource
err = json.Unmarshal([]byte(gotJSONOutput), &gotResource)
if err != nil {
t.Fatalf("not expected error, got %q", err.Error())
}

if !reflect.DeepEqual(gotResource, cr) {
t.Fatalf("expected \"%v\", got \"%v\"", cr, gotResource)
}
}

func TestCreateResourceExecutionWithEnvironmentUUID(t *testing.T) {
ctx := context.Background()
ctrl := gomock.NewController(t)
client := mock.NewMockClient(ctrl)
logger := log.NewTestLogger()

cr := utils.GenerateResourceWithEnvironment()

r := meroxa.CreateResourceInput{
Type: "postgres",
Name: "",
URL: "https://foo.url",
Credentials: nil,
Metadata: nil,
Environment: &meroxa.ResourceEnvironment{
UUID: cr.Environment.UUID,
},
}

client.
EXPECT().
CreateResource(
ctx,
&r,
).
Return(&cr, nil)

c := &Create{
client: client,
logger: logger,
}
c.args.Name = r.Name
c.flags.Type = string(r.Type)
c.flags.URL = r.URL
c.flags.Environment = r.Environment.UUID

err := c.Execute(ctx)
if err != nil {
t.Fatalf("not expected error, got %q", err.Error())
}

gotLeveledOutput := logger.LeveledOutput()
wantLeveledOutput := fmt.Sprintf(`Creating %q resource in %q environment...
Resource %q is successfully created!
`, cr.Type, cr.Name)
`, cr.Type, cr.Environment.UUID, cr.Name)

if gotLeveledOutput != wantLeveledOutput {
t.Fatalf("expected output:\n%s\ngot:\n%s", wantLeveledOutput, gotLeveledOutput)
Expand Down
8 changes: 7 additions & 1 deletion cmd/meroxa/root/resources/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ func getResources() []*meroxa.Resource {
return append(resources, &r)
}

func getResourcesWithEnvironment() []*meroxa.Resource {
var resources []*meroxa.Resource
r := utils.GenerateResourceWithEnvironment()
return append(resources, &r)
}

func TestListResourcesFlags(t *testing.T) {
expectedFlags := []struct {
name string
Expand Down Expand Up @@ -71,7 +77,7 @@ func TestListResourcesExecution(t *testing.T) {
client := mock.NewMockClient(ctrl)
logger := log.NewTestLogger()

resources := getResources()
resources := append(getResources(), getResourcesWithEnvironment()...)

client.
EXPECT().
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ require (
github.com/fatih/color v1.9.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.6
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.7.3
github.com/manifoldco/promptui v0.8.0
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/meroxa/meroxa-go v0.0.0-20211119112217-54866585cbe9
github.com/meroxa/meroxa-go v0.0.0-20211129114423-a076cb0bbfbd
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
github.com/rivo/uniseg v0.2.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
Expand Down Expand Up @@ -221,8 +223,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/meroxa/meroxa-go v0.0.0-20211119112217-54866585cbe9 h1:INJ4aSBxCFqF40/b+9jxOyxZX07asDPrpFKdYIrd5go=
github.com/meroxa/meroxa-go v0.0.0-20211119112217-54866585cbe9/go.mod h1:HDFszURCM1cOpKE699o5Hs0T2tEIXqY+vFcsur3RiwY=
github.com/meroxa/meroxa-go v0.0.0-20211129114423-a076cb0bbfbd h1:NtbUYqnzKWSg8Kg/KStwaKOkhZRvYsIqgBz4DeZg1xs=
github.com/meroxa/meroxa-go v0.0.0-20211129114423-a076cb0bbfbd/go.mod h1:HDFszURCM1cOpKE699o5Hs0T2tEIXqY+vFcsur3RiwY=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
Expand Down
27 changes: 27 additions & 0 deletions utils/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ func ResourceTable(res *meroxa.Resource) string {
{Text: strings.Title(d)},
})
}

if res.Environment != nil {
if e := res.Environment.UUID; e != "" {
mainTable.Body.Cells = append(mainTable.Body.Cells, []*simpletable.Cell{
{Align: simpletable.AlignRight, Text: "Environment UUID:"},
{Text: e},
})
}

if e := res.Environment.Name; e != "" {
mainTable.Body.Cells = append(mainTable.Body.Cells, []*simpletable.Cell{
{Align: simpletable.AlignRight, Text: "Environment Name:"},
{Text: e},
})
}
}

mainTable.SetStyle(simpletable.StyleCompact)

return mainTable.String()
Expand All @@ -115,6 +132,7 @@ func ResourcesTable(resources []*meroxa.Resource, hideHeaders bool) string {
{Align: simpletable.AlignCenter, Text: "ID"},
{Align: simpletable.AlignCenter, Text: "NAME"},
{Align: simpletable.AlignCenter, Text: "TYPE"},
{Align: simpletable.AlignCenter, Text: "ENVIRONMENT"},
{Align: simpletable.AlignCenter, Text: "URL"},
{Align: simpletable.AlignCenter, Text: "TUNNEL"},
{Align: simpletable.AlignCenter, Text: "STATE"},
Expand All @@ -128,10 +146,19 @@ func ResourcesTable(resources []*meroxa.Resource, hideHeaders bool) string {
tunnel = "SSH"
}

var env string

if res.Environment != nil && res.Environment.Name != "" {
env = res.Environment.Name
} else {
env = string(meroxa.EnvironmentTypeCommon)
}

r := []*simpletable.Cell{
{Align: simpletable.AlignRight, Text: fmt.Sprintf("%d", res.ID)},
{Text: res.Name},
{Text: string(res.Type)},
{Text: env},
{Text: res.URL},
{Align: simpletable.AlignCenter, Text: tunnel},
{Align: simpletable.AlignCenter, Text: strings.Title(string(res.Status.State))},
Expand Down
2 changes: 1 addition & 1 deletion utils/display_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestResourcesTable(t *testing.T) {
"ID_Alignment": {resource, resIDAlign},
}

tableHeaders := []string{"ID", "NAME", "TYPE", "URL", "TUNNEL", "STATE"}
tableHeaders := []string{"ID", "NAME", "TYPE", "ENVIRONMENT", "URL", "TUNNEL", "STATE"}

for name, resources := range tests {
t.Run(name, func(t *testing.T) {
Expand Down
10 changes: 10 additions & 0 deletions utils/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ func GenerateResource() meroxa.Resource {
}
}

func GenerateResourceWithEnvironment() meroxa.Resource {
r := GenerateResource()

r.Environment = &meroxa.ResourceEnvironment{
UUID: "424ec647-9f0f-45a5-8e4b-3e0441f12444",
Name: "my-environment",
}
return r
}

func GenerateConnector(pipelineID int, connectorName string) meroxa.Connector {
if pipelineID == 0 {
pipelineID = rand.Intn(10000)
Expand Down
9 changes: 9 additions & 0 deletions vendor/github.com/google/uuid/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions vendor/github.com/google/uuid/CONTRIBUTING.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5206737

Please sign in to comment.