Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func addRootCommand(cmd *cobra.Command, r handlers.Runtime, title string, f func
// therefore do not direclty perform any actions. The function accepts a
// single argument `rootCommands` which is an array of RootCommand instances.
func makeRootCommand(rootCommands []RootCommand) []*cobra.Command {
descriptors := loadDescriptors("descriptors")
descriptors := cmdutils.LoadDescriptorsFromContent("descriptors", &content)

var commands []*cobra.Command

Expand Down
41 changes: 0 additions & 41 deletions cmd/descriptors.go

This file was deleted.

4 changes: 2 additions & 2 deletions cmd/descriptors/asset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ clear:

import:
description: |
Import an asset
Import an asset from a file or repository
include_groups: true

export:
description: |
Export an asset
Export an asset to a file or repository
include_groups: true
16 changes: 14 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cmd

import (
"context"
"embed"
"os"
"path"
"strings"
Expand All @@ -21,6 +22,11 @@ import (
"github.com/spf13/cobra"
)

//go:embed descriptors/*.yaml
var content embed.FS

// Provides the general description of the application. This should be moved
// into descriptors.
const description = `Manage Itential Platform

Find more information at: https://docs.itential.com
Expand Down Expand Up @@ -55,7 +61,9 @@ func versionCommand() *cobra.Command {
return cmd
}

func Do(c client.Client, cfg *config.Config) *cobra.Command {
// runCli builds and runs the CLI command. It will create the command tree
// using cobra and execute the command.
func runCli(c client.Client, cfg *config.Config) *cobra.Command {
var cmd = &cobra.Command{
Use: "ipctl",
Short: strings.Split(description, "\n")[0],
Expand All @@ -81,6 +89,10 @@ func Do(c client.Client, cfg *config.Config) *cobra.Command {
return cmd
}

// Execute is the entrypoint to the CLI called from main. This fucntion will
// load the configuration file, initialize the logger, create the client and
// run the application. It will return an int that is to be used as the return
// code.
func Execute() int {
cfg := config.NewConfig(nil, nil, "", "", "")
logger.InitializeLogger(cfg)
Expand Down Expand Up @@ -108,7 +120,7 @@ func Execute() int {

c := client.New(ctx, profile)

if err := Do(c, cfg).Execute(); err != nil {
if err := runCli(c, cfg).Execute(); err != nil {
cmdutils.CheckError(err, cfg.TerminalNoColor)
}

Expand Down
68 changes: 67 additions & 1 deletion internal/cmdutils/cmdutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,55 @@
package cmdutils

import (
"embed"
"strings"

"github.com/itential/ipctl/internal/terminal"
"github.com/itential/ipctl/pkg/logger"
"gopkg.in/yaml.v2"
)

type Descriptors map[string]map[string]Descriptor

// Descriptors are used to describe a rcommand in the application.
type Descriptor struct {
// Use defines the command
Use string `yaml:"use"`

// Group name to put the command into
Group string `yaml:"group"`

// The full description of the commnand. This text is split into a short
// description and a long description when the command is created. The
// short description is the first line of the command ending with `\n`.
// The long form of the description is the entire description text.
Description string `yaml:"description"`

// Example provides examples of how to run the command.
Example string `yaml:"example"`

// IncludeGroups specifies whether or not to include the default groups as
// part of the command. This needs to be refactors out of the descriptor.
IncludeGroups bool `yaml:"include_groups"`

ExactArgs int `yaml:"exact_args"`

// Disables specifies whether or not the command is enabled or disable.
// When the command is disabled, it is not added to the command tree.
Disabled bool `yaml:"disabled"`

// Hidden cpecifies whether or not the command is seen when using the
// `--help` flag.
Hidden bool `yaml:"hidden"`
}

func (d Descriptor) Short() string {
return strings.Split(d.Description, "\n")[0]
}

// LoadDescriptor will unmarshal a byte stream into a map of descriptor
// entries. The `data` argument must be a YAML file that has string based keys
// with each key is a descriptor.
func LoadDescriptor(data []byte) map[string]Descriptor {
var s map[string]Descriptor
if err := yaml.Unmarshal(data, &s); err != nil {
Expand All @@ -42,11 +62,13 @@ func LoadDescriptor(data []byte) map[string]Descriptor {
return s
}

// LoadDescriptorFromString will unmarhsal a string into a map of descriptor
// entries.
func LoadDescriptorFromString(s string) map[string]Descriptor {
return LoadDescriptor([]byte(s))
}

// checkError functions very similarly to cobra's builtin cobra.checkErr
// CheckError functions very similarly to cobra's builtin cobra.checkErr
// function. Our function adds extra functionality such as colored outputs
// and logging the error to the loggers
func CheckError(err error, terminalNoColor bool) {
Expand All @@ -56,3 +78,47 @@ func CheckError(err error, terminalNoColor bool) {
logger.Fatal(err, "")
}
}

// LoadDescriptorsFromContent iterates over the files provided in `content`
// and unmarshales them into maps of descriptors. Those maps are then added to
// create the `Descriptors` object. The file map will look like the following:
//
// <filename>:
// <key>: <descriptor>
//
// Give a the following filename in the content folder:
// assets.yaml
// ---
// get:
// use: get
// description: this is for documentation
//
// The resulting descriptor object would be
//
// assets:
// get: <descriptor>

func LoadDescriptorsFromContent(path string, content *embed.FS) Descriptors {
logger.Trace()

descriptors := map[string]map[string]Descriptor{}

entries, err := content.ReadDir(path)
if err != nil {
logger.Fatal(err, "failed to read descriptors directory")
}

for _, ele := range entries {
name := strings.Split(ele.Name(), ".")[0]
fn := strings.Join([]string{path, ele.Name()}, "/")

data, err := content.ReadFile(fn)
if err != nil {
logger.Fatal(err, "failed to read descriptor")
}

descriptors[name] = LoadDescriptor(data)
}

return descriptors
}
54 changes: 54 additions & 0 deletions internal/cmdutils/cmdutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2024 Itential Inc. All Rights Reserved
// Unauthorized copying of this file, via any medium is strictly prohibited
// Proprietary and confidential

package cmdutils

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const testYAML = `
testcmd:
use: test
group: testing
description: |
Short description line.
This is the long description.
example: testcmd --flag
include_groups: true
exact_args: 1
disabled: false
hidden: false
`

func TestLoadDescriptorFromString(t *testing.T) {
descs := LoadDescriptorFromString(testYAML)

require.Len(t, descs, 1)
desc, ok := descs["testcmd"]
require.True(t, ok)

assert.Equal(t, "test", desc.Use)
assert.Equal(t, "testing", desc.Group)
assert.Equal(t, "testcmd --flag", desc.Example)
assert.Equal(t, true, desc.IncludeGroups)
assert.Equal(t, 1, desc.ExactArgs)
assert.Equal(t, false, desc.Disabled)
assert.Equal(t, false, desc.Hidden)
assert.Equal(t, "Short description line.", desc.Short())
}

func TestDescriptorShort(t *testing.T) {
desc := Descriptor{Description: "Hello there!\nLonger details here."}
assert.Equal(t, "Hello there!", desc.Short())
}

func TestCheckErrorNil(t *testing.T) {
assert.NotPanics(t, func() {
CheckError(nil, false)
})
}
29 changes: 29 additions & 0 deletions vendor/github.com/stretchr/testify/require/doc.go

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

16 changes: 16 additions & 0 deletions vendor/github.com/stretchr/testify/require/forward_requirements.go

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

Loading