Skip to content

Commit

Permalink
feat: add cobra cmd to context (#9)
Browse files Browse the repository at this point in the history
* feat: add cobra cmd to context

* fix test

* refactor

* use getter method instead for ctx

* no lint

* fix readme
  • Loading branch information
raulb authored Dec 20, 2024
1 parent 516d4f2 commit 4a9a7e1
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func main() {
}
```

### Configuration
### CommandWithConfig

Ecdysis provides an automatic way to parse a configuration file, environment variables, and flags using the [`viper`](https://github.com/spf13/viper) library. To use it, you need to implement the `CommandWithConfig` interface.

Expand All @@ -122,7 +122,9 @@ The order of precedence for configuration values is:
```go
var (
_ ecdysis.CommandWithConfiguration = (*RootCommand)(nil)
_ ecdysis.CommandWithFlags = (*RootCommand)(nil)
_ ecdysis.CommandWithExecute = (*RootCommand)(nil)
_ ecdysis.CommandWithConfig = (*RootCommand)(nil)
)

type ConduitConfig struct {
Expand All @@ -144,12 +146,12 @@ type RootCommand struct {
cfg ConduitConfig
}

func (c *RootCommand) ParseConfig() ecdysis.Config {
func (c *RootCommand) Config() ecdysis.Config {
return ecdysis.Config{
EnvPrefix: "CONDUIT", // prefix for environment variables
ParsedCfg: &c.cfg, // where configuration will be parsed
ConfigPath: c.flags.ConduitCfgPath, // where to read the configuration file
DefaultCfg: c.cfg, // where to extract default values from
EnvPrefix: "CONDUIT",
Parsed: &c.Cfg,
Path: c.flags.ConduitCfgPath,
DefaultValues: conduit.DefaultConfigWithBasePath(path),
}
}

Expand All @@ -167,7 +169,21 @@ func (c *RootCommand) Flags() []ecdysis.Flag {

return flags
}
````
```

### Fetching `cobra.Command` from `CommandWithExecute`

If you need to access the `cobra.Command` instance from a `CommandWithExecute` implementation, you can utilize
the `ecdysis.CobraCmdFromContext` function to fetch it from the context:

```go
func (c *RootCommand) Execute(ctx context.Context) error {
if cmd := ecdysis.CobraCmdFromContext(ctx); cmd != nil {
return cmd.Help()
}
return nil
}
```

## Flags

Expand Down
38 changes: 38 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright © 2024 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ecdysis

import (
"context"

"github.com/spf13/cobra"
)

type cobraCmdCtxKey struct{}

// contextWithCobraCommand provides the cobra command to the context.
// This is useful for situations such as wanting to execute cmd.Help() directly from Execute().
func contextWithCobraCommand(ctx context.Context, cmd *cobra.Command) context.Context {
return context.WithValue(ctx, cobraCmdCtxKey{}, cmd)
}

// CobraCmdFromContext fetches the cobra command from the context. If the
// context does not contain a cobra command, it returns nil.
func CobraCmdFromContext(ctx context.Context) *cobra.Command {
if cobraCmd := ctx.Value(cobraCmdCtxKey{}); cobraCmd != nil {
return cobraCmd.(*cobra.Command) //nolint:forcetypeassert // only this package can set the value, it has to be a *cobra.Command
}
return nil
}
4 changes: 3 additions & 1 deletion decorators.go
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,9 @@ func (CommandWithExecuteDecorator) Decorate(_ *Ecdysis, cmd *cobra.Command, c Co
return err
}
}
return v.Execute(cmd.Context())

ctx := contextWithCobraCommand(cmd.Context(), cmd)
return v.Execute(ctx)
}

return nil
Expand Down
3 changes: 2 additions & 1 deletion ecdysis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func TestBuildCobraCommand_Behavioral(t *testing.T) {
ctx := context.Background()
ctrl := gomock.NewController(t)
cmd := NewMockBehavioralTestCommand(ctrl)
ctx = context.WithValue(ctx, cobraCmdCtxKey{}, cmd)

wantLogger := slog.New(slog.NewTextHandler(nil, nil))
ecdysis := New(WithDecorators(CommandWithLoggerDecorator{Logger: wantLogger}))
Expand All @@ -124,7 +125,7 @@ func TestBuildCobraCommand_Behavioral(t *testing.T) {

// Set up the remaining expectations before executing the command.
call = cmd.EXPECT().Args(gomock.Any()).Return(nil).After(call)
cmd.EXPECT().Execute(ctx).Return(nil).After(call)
cmd.EXPECT().Execute(gomock.AssignableToTypeOf(ctx)).Return(nil).After(call)

err := got.ExecuteContext(ctx)
if err != nil {
Expand Down

0 comments on commit 4a9a7e1

Please sign in to comment.