Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
211 changes: 211 additions & 0 deletions pkg/cmd/create/API_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package create

Comment thread
vivshankar marked this conversation as resolved.
import (
"encoding/json"
"io"
"os"

"github.ibm.com/sec-ci/devops-experiments/pkg/cmd/resource"
"github.ibm.com/sec-ci/devops-experiments/pkg/config"
"github.ibm.com/sec-ci/devops-experiments/pkg/module"
"github.ibm.com/sec-ci/devops-experiments/pkg/module/directory"
cmdutil "github.ibm.com/sec-ci/devops-experiments/pkg/util/cmd"
"github.ibm.com/sec-ci/devops-experiments/pkg/util/templates"
"gopkg.in/yaml.v3"

"github.com/spf13/cobra"
)

const (
apiClientUsage = "apiclient [options]"
apiClientMessagePrefix = "CreateApiClient"
apiClientEntitlements = "Manage API Clients"
apiClientResourceName = "apiclient"
)

var (
apiClientShortDesc = cmdutil.TranslateShortDesc(apiClientMessagePrefix, "Options to create an API client.")

apiClientLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(apiClientMessagePrefix, `
Options to create an API client.

API clients on Verify require specific entitlements, so ensure that the application or API client used
with the 'auth' command has the required entitlements.

An empty resource file can be generated using:

verifyctl create apiclient --boilerplate

You can check required entitlements by running:

verifyctl create apiclient --entitlements`))

apiClientExamples = templates.Examples(cmdutil.TranslateExamples(apiClientMessagePrefix, `
# Create an empty API client resource.
verifyctl create apiclient --boilerplate

# Create an API client using a JSON file.
verifyctl create apiclient -f=./apiclient.json`))
)

type apiClientOptions struct {
options
config *config.CLIConfig
}

func newApiClientCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command {
o := &apiClientOptions{
config: config,
}

cmd := &cobra.Command{
Use: apiClientUsage,
Short: apiClientShortDesc,
Long: apiClientLongDesc,
Example: apiClientExamples,
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.ExitOnError(cmd, o.Complete(cmd, args))
cmdutil.ExitOnError(cmd, o.Validate(cmd, args))
cmdutil.ExitOnError(cmd, o.Run(cmd, args))
},
}

cmd.SetOut(streams)
cmd.SetErr(streams)
cmd.SetIn(streams)

o.AddFlags(cmd)

return cmd
}

func (o *apiClientOptions) AddFlags(cmd *cobra.Command) {
o.addCommonFlags(cmd, apiClientResourceName)
cmd.Flags().StringVarP(&o.file, "file", "f", "", "Path to the yaml file containing API client data.")
}

func (o *apiClientOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}

func (o *apiClientOptions) Validate(cmd *cobra.Command, args []string) error {
if o.entitlements || o.boilerplate {
return nil
}

if len(o.file) == 0 {
return module.MakeSimpleError("The 'file' option is required if no other options are used.")
}
return nil
}

func (o *apiClientOptions) Run(cmd *cobra.Command, args []string) error {
if o.entitlements {
cmdutil.WriteString(cmd, entitlementsMessage+" "+apiClientEntitlements)
return nil
}

if o.boilerplate {
resourceObj := &resource.ResourceObject{
Kind: resource.ResourceTypePrefix + "ApiClient",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use APIClient and not ApiClient. Acronyms, such as API, should be in caps.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

APIVersion: "1.0",
Data: &directory.Client{},
}

cmdutil.WriteAsYAML(cmd, resourceObj, cmd.OutOrStdout())
return nil
}

auth, err := o.config.GetCurrentAuth()
if err != nil {
return err
}

return o.createApiClient(cmd, auth)
}

func (o *apiClientOptions) createApiClient(cmd *cobra.Command, auth *config.AuthConfig) error {
Comment thread
vivshankar marked this conversation as resolved.
Outdated
ctx := cmd.Context()
vc := config.GetVerifyContext(ctx)

// Read the contents of the file
b, err := os.ReadFile(o.file)
if err != nil {
vc.Logger.Errorf("unable to read file; filename=%s, err=%v", o.file, err)
return err
}

// Create API client with data
return o.createApiClientWithData(cmd, auth, b)
}

func (o *apiClientOptions) createApiClientWithData(cmd *cobra.Command, auth *config.AuthConfig, data []byte) error {
ctx := cmd.Context()
vc := config.GetVerifyContext(ctx)

// Unmarshal yaml into API client struct
apiclient := &directory.Client{}
if err := yaml.Unmarshal(data, &apiclient); err != nil {
vc.Logger.Errorf("unable to unmarshal API client; err=%v", err)
return err
}

// Validate required fields
if apiclient.ClientName == "" {
return module.MakeSimpleError("clientName is required")
}
if len(apiclient.Entitlements) == 0 {
return module.MakeSimpleError("entitlements list is required")
}

// Create API client
client := directory.NewApiClient()
resourceURI, err := client.CreateApiClient(ctx, auth, apiclient)
if err != nil {
vc.Logger.Errorf("failed to create API client; err=%v", err)
return err
}

// Directly return the created resource URI
cmdutil.WriteString(cmd, "Resource created: "+resourceURI)
return nil
}

func (o *apiClientOptions) createApiClientFromDataMap(cmd *cobra.Command, auth *config.AuthConfig, data map[string]interface{}) error {
ctx := cmd.Context()
vc := config.GetVerifyContext(ctx)

// Convert map data to JSON
apiclient := &directory.Client{}
b, err := json.Marshal(data)
if err != nil {
vc.Logger.Errorf("failed to marshal data; err=%v", err)
return err
}

if err := json.Unmarshal(b, apiclient); err != nil {
vc.Logger.Errorf("unable to unmarshal data to API client; err=%v", err)
return err
}

// Validate required fields
if apiclient.ClientName == "" {
return module.MakeSimpleError("clientName is required")
}
if len(apiclient.Entitlements) == 0 {
return module.MakeSimpleError("entitlements list is required")
}

// Create API client
client := directory.NewApiClient()
resourceURI, err := client.CreateApiClient(ctx, auth, apiclient)
if err != nil {
vc.Logger.Errorf("failed to create API client; err=%v", err)
return err
}

// Directly return the created resource URI
cmdutil.WriteString(cmd, "Resource created: "+resourceURI)
return nil
}
5 changes: 5 additions & 0 deletions pkg/cmd/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string)
cmd.AddCommand(newAttributeCommand(config, streams))
cmd.AddCommand(newUserCommand(config, streams))
cmd.AddCommand(newGroupCommand(config, streams))
cmd.AddCommand(newApiClientCommand(config, streams))

return cmd
}
Expand Down Expand Up @@ -143,6 +144,10 @@ func (o *options) Run(cmd *cobra.Command, args []string) error {
case resource.ResourceTypePrefix + "Group":
options := &groupOptions{}
err = options.createGroupFromDataMap(cmd, auth, resourceObject.Data.(map[string]interface{}))

case resource.ResourceTypePrefix + "Apiclient":
options := &apiClientOptions{}
err = options.createApiClientFromDataMap(cmd, auth, resourceObject.Data.(map[string]interface{}))
}

return err
Expand Down
121 changes: 121 additions & 0 deletions pkg/cmd/delete/API_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package delete

import (
"io"

"github.com/spf13/cobra"
"github.ibm.com/sec-ci/devops-experiments/pkg/config"
"github.ibm.com/sec-ci/devops-experiments/pkg/i18n"
"github.ibm.com/sec-ci/devops-experiments/pkg/module"
"github.ibm.com/sec-ci/devops-experiments/pkg/module/directory"
cmdutil "github.ibm.com/sec-ci/devops-experiments/pkg/util/cmd"
"github.ibm.com/sec-ci/devops-experiments/pkg/util/templates"
)

const (
apiclientUsage = `apiclient [flags]`
apiclientMessagePrefix = "DeleteApiclient"
apiclientEntitlements = "Manage apiclients"
apiclientResourceName = "apiclient"
)

var (
apiclientLongDesc = templates.LongDesc(cmdutil.TranslateLongDesc(apiclientMessagePrefix, `
Delete Verify API client based on clientName.

Resources managed on Verify have specific entitlements, so ensure that the application or API client used
with the 'auth' command is configured with the appropriate entitlements.

You can identify the entitlement required by running:

verifyctl delete apiclient --entitlements`))

apiclientExamples = templates.Examples(cmdutil.TranslateExamples(messagePrefix, `
# Delete an API client
verifyctl delete apiclient --clientName="clientName"`,
))
)

type apiclientsOptions struct {
options

config *config.CLIConfig
}

func NewAPIclientCommand(config *config.CLIConfig, streams io.ReadWriter) *cobra.Command {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please correct the naming of this function. It should be NewAPIClientCommand.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

o := &apiclientsOptions{
config: config,
}

cmd := &cobra.Command{
Use: apiclientUsage,
Short: cmdutil.TranslateShortDesc(apiclientMessagePrefix, "Delete Verify API client based on an id."),
Comment thread
vivshankar marked this conversation as resolved.
Outdated
Long: apiclientLongDesc,
Example: apiclientExamples,
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.ExitOnError(cmd, o.Complete(cmd, args))
cmdutil.ExitOnError(cmd, o.Validate(cmd, args))
cmdutil.ExitOnError(cmd, o.Run(cmd, args))
},
}

cmd.SetOut(streams)
cmd.SetErr(streams)
cmd.SetIn(streams)

o.AddFlags(cmd)

return cmd
}

func (o *apiclientsOptions) AddFlags(cmd *cobra.Command) {
o.addCommonFlags(cmd)
cmd.Flags().StringVar(&o.name, "clientName", o.name, i18n.Translate("clientName to be deleted"))
}

func (o *apiclientsOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}

func (o *apiclientsOptions) Validate(cmd *cobra.Command, args []string) error {
if o.entitlements {
return nil
}

calledAs := cmd.CalledAs()
if calledAs == "apiclient" && o.name == "" {
return module.MakeSimpleError(i18n.Translate("'clientName' flag is required"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not also accepting clientID? In fact that is more natural for customers than the name, in this case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
return nil
}

func (o *apiclientsOptions) Run(cmd *cobra.Command, args []string) error {
if o.entitlements {
cmdutil.WriteString(cmd, entitlementsMessage+" "+apiclientEntitlements)
return nil
}

auth, err := o.config.GetCurrentAuth()
if err != nil {
return err
}

// invoke the operation
if cmd.CalledAs() == "apiclient" || len(o.name) > 0 {
// deal with single API client
return o.handleSingleApiClient(cmd, auth, args)
}
return nil
}

func (o *apiclientsOptions) handleSingleApiClient(cmd *cobra.Command, auth *config.AuthConfig, _ []string) error {

c := directory.NewApiClient()
err := c.DeleteApiclient(cmd.Context(), auth, o.name)
if err != nil {
return err
}
cmdutil.WriteString(cmd, "Resource deleted: "+o.name)
return nil
}
2 changes: 2 additions & 0 deletions pkg/cmd/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func NewCommand(config *config.CLIConfig, streams io.ReadWriter, groupID string)
// add sub commands
cmd.AddCommand(NewUserCommand(config, streams))
cmd.AddCommand(NewGroupCommand(config, streams))
cmd.AddCommand(NewAPIclientCommand(config, streams))

return cmd
}

Expand Down
Loading