Skip to content

Commit

Permalink
refactor: Move from global state to functions
Browse files Browse the repository at this point in the history
This commit represents a few experiments of features I've used in Cobra

1. Uses cli.GenericFlag to encapsulate parsing and validation of flag
   values at parse time. This removes the burden from the individual
   CLI commands to parse and validate args and options.

2. Add flag.ID that may be used by any flag that requires an Influx ID.
   flag.ID parses and validates string value is a valid ID, removing
   this burden from individual commands and ensuring valid values before
   the command actions begins.

3. Adds a Flags() method to the Params structs to directly capture
   the values when parsing flags.

4. Moves from global state to local builder functions for the majority
   of the commands. This allows the commands to bind to flag variables
   reducing the repeated ctx.String(), ctx.Int(), etc

5. Leverages the BeforeFunc to create middleware and inject the CLI and
   API client into commands, saving the repeated boilerplate across
   all of the instantiated commands. This is extensible, so additional
   middleware can be appends using the middleware.WithBeforeFns
  • Loading branch information
stuartcarnie committed Apr 29, 2021
1 parent 81cf5d7 commit 4cfb5de
Show file tree
Hide file tree
Showing 16 changed files with 1,104 additions and 552 deletions.
302 changes: 90 additions & 212 deletions cmd/influx/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,223 +2,101 @@ package main

import (
"github.com/influxdata/influx-cli/v2/internal"
"github.com/influxdata/influx-cli/v2/pkg/cmd/middleware"
"github.com/urfave/cli/v2"
)

var bucketCmd = cli.Command{
Name: "bucket",
Usage: "Bucket management commands",
Subcommands: []*cli.Command{
{
Name: "create",
Usage: "Create bucket",
Flags: append(
commonFlags,
&cli.StringFlag{
Name: "name",
Usage: "New bucket name",
Aliases: []string{"n"},
EnvVars: []string{"INFLUX_BUCKET_NAME"},
Required: true,
},
&cli.StringFlag{
Name: "description",
Usage: "Description of the bucket that will be created",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "retention",
Usage: "Duration bucket will retain data, or 0 for infinite",
Aliases: []string{"r"},
DefaultText: "infinite",
},
&cli.StringFlag{
Name: "shard-group-duration",
Usage: "Shard group duration used internally by the storage engine",
DefaultText: "calculated from retention",
},
&cli.StringFlag{
Name: "org-id",
Usage: "The ID of the organization",
EnvVars: []string{"INFLUX_ORG_ID"},
},
&cli.StringFlag{
Name: "org",
Usage: "The name of the organization",
Aliases: []string{"o"},
EnvVars: []string{"INFLUX_ORG"},
},
),
Action: func(ctx *cli.Context) error {
cli, err := newCli(ctx)
if err != nil {
return err
}
client, err := newApiClient(ctx, cli, true)
if err != nil {
return err
}
clients := internal.BucketsClients{
BucketApi: client.BucketsApi,
OrgApi: client.OrganizationsApi,
}
return cli.BucketsCreate(standardCtx(ctx), &clients, &internal.BucketsCreateParams{
OrgID: ctx.String("org-id"),
OrgName: ctx.String("org"),
Name: ctx.String("name"),
Description: ctx.String("description"),
Retention: ctx.String("retention"),
ShardGroupDuration: ctx.String("shard-group-duration"),
})
},
func withBucketsClient() cli.BeforeFunc {
return middleware.WithBeforeFns(
withCli(),
withApi(),
func(ctx *cli.Context) error {
client := getAPI(ctx)
ctx.App.Metadata["bucketsClient"] = internal.BucketsClients{
BucketApi: client.BucketsApi,
OrgApi: client.OrganizationsApi,
}
return nil
},
{
Name: "delete",
Usage: "Delete bucket",
Flags: append(
commonFlags,
&cli.StringFlag{
Name: "id",
Usage: "The bucket ID, required if name isn't provided",
Aliases: []string{"i"},
},
&cli.StringFlag{
Name: "name",
Usage: "The bucket name, org or org-id will be required by choosing this",
Aliases: []string{"n"},
},
&cli.StringFlag{
Name: "org-id",
Usage: "The ID of the organization",
EnvVars: []string{"INFLUX_ORG_ID"},
},
&cli.StringFlag{
Name: "org",
Usage: "The name of the organization",
Aliases: []string{"o"},
EnvVars: []string{"INFLUX_ORG"},
},
),
Action: func(ctx *cli.Context) error {
cli, err := newCli(ctx)
if err != nil {
return err
}
client, err := newApiClient(ctx, cli, true)
if err != nil {
return err
}
clients := internal.BucketsClients{
BucketApi: client.BucketsApi,
OrgApi: client.OrganizationsApi,
}
return cli.BucketsDelete(standardCtx(ctx), &clients, &internal.BucketsDeleteParams{
ID: ctx.String("id"),
Name: ctx.String("name"),
OrgID: ctx.String("org-id"),
OrgName: ctx.String("org"),
})
},
)
}

func getBucketsClient(ctx *cli.Context) internal.BucketsClients {
i, ok := ctx.App.Metadata["bucketsClient"].(internal.BucketsClients)
if !ok {
panic("missing buckets client")
}
return i
}

func newBucketCmd() *cli.Command {
return &cli.Command{
Name: "bucket",
Usage: "Bucket management commands",
Subcommands: []*cli.Command{
newBucketCreateCmd(),
newBucketDeleteCmd(),
newBucketListCmd(),
newBucketUpdateCmd(),
},
{
Name: "list",
Usage: "List buckets",
Aliases: []string{"find", "ls"},
Flags: append(
commonFlags,
&cli.StringFlag{
Name: "name",
Usage: "The bucket name",
Aliases: []string{"n"},
EnvVars: []string{"INFLUX_BUCKET_NAME"},
},
&cli.StringFlag{
Name: "org-id",
Usage: "The ID of the organization",
EnvVars: []string{"INFLUX_ORG_ID"},
},
&cli.StringFlag{
Name: "org",
Usage: "The name of the organization",
Aliases: []string{"o"},
EnvVars: []string{"INFLUX_ORG"},
},
&cli.StringFlag{
Name: "id",
Usage: "The bucket ID",
Aliases: []string{"i"},
},
),
Action: func(ctx *cli.Context) error {
cli, err := newCli(ctx)
if err != nil {
return err
}
client, err := newApiClient(ctx, cli, true)
if err != nil {
return err
}
clients := internal.BucketsClients{
BucketApi: client.BucketsApi,
OrgApi: client.OrganizationsApi,
}
return cli.BucketsList(standardCtx(ctx), &clients, &internal.BucketsListParams{
ID: ctx.String("id"),
Name: ctx.String("name"),
OrgID: ctx.String("org-id"),
OrgName: ctx.String("org"),
})
},
}

}

func newBucketCreateCmd() *cli.Command {
var params internal.BucketsCreateParams
return &cli.Command{
Name: "create",
Usage: "Create bucket",
Before: withBucketsClient(),
Flags: append(commonFlags, params.Flags()...),
Action: func(ctx *cli.Context) error {
clients := getBucketsClient(ctx)
return getCLI(ctx).BucketsCreate(ctx.Context, &clients, &params)
},
{
Name: "update",
Usage: "Update bucket",
Flags: append(
commonFlags,
&cli.StringFlag{
Name: "name",
Usage: "New name to set on the bucket",
Aliases: []string{"n"},
EnvVars: []string{"INFLUX_BUCKET_NAME"},
},
&cli.StringFlag{
Name: "id",
Usage: "The bucket ID",
Aliases: []string{"i"},
Required: true,
},
&cli.StringFlag{
Name: "description",
Usage: "New description to set on the bucket",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "retention",
Usage: "New retention duration to set on the bucket, or 0 for infinite",
Aliases: []string{"r"},
},
&cli.StringFlag{
Name: "shard-group-duration",
Usage: "New shard group duration to set on the bucket, or 0 to have the server calculate a value",
},
),
Action: func(ctx *cli.Context) error {
cli, err := newCli(ctx)
if err != nil {
return err
}
client, err := newApiClient(ctx, cli, true)
if err != nil {
return err
}
return cli.BucketsUpdate(standardCtx(ctx), client.BucketsApi, &internal.BucketsUpdateParams{
ID: ctx.String("id"),
Name: ctx.String("name"),
Description: ctx.String("description"),
Retention: ctx.String("retention"),
ShardGroupDuration: ctx.String("shard-group-duration"),
})
},
}
}

func newBucketDeleteCmd() *cli.Command {
var params internal.BucketsDeleteParams
return &cli.Command{
Name: "delete",
Usage: "Delete bucket",
Before: withBucketsClient(),
Flags: append(commonFlags, params.Flags()...),
Action: func(ctx *cli.Context) error {
clients := getBucketsClient(ctx)
return getCLI(ctx).BucketsDelete(ctx.Context, &clients, &params)
},
}
}

func newBucketListCmd() *cli.Command {
var params internal.BucketsListParams
return &cli.Command{
Name: "list",
Usage: "List buckets",
Aliases: []string{"find", "ls"},
Before: withBucketsClient(),
Flags: append(commonFlags, params.Flags()...),
Action: func(ctx *cli.Context) error {
clients := getBucketsClient(ctx)
return getCLI(ctx).BucketsList(ctx.Context, &clients, &params)
},
}
}

func newBucketUpdateCmd() *cli.Command {
var params internal.BucketsUpdateParams
return &cli.Command{
Name: "update",
Usage: "Update bucket",
Aliases: []string{"find", "ls"},
Before: withBucketsClient(),
Flags: append(commonFlags, params.Flags()...),
Action: func(ctx *cli.Context) error {
clients := getBucketsClient(ctx)
return getCLI(ctx).BucketsUpdate(ctx.Context, clients.BucketApi, &params)
},
},
}
}
Loading

0 comments on commit 4cfb5de

Please sign in to comment.