diff --git a/src/client_cli/cmdTemplate/helper.go b/src/client_cli/cmdTemplate/helper.go new file mode 100644 index 0000000000000000000000000000000000000000..5628b814966d00e8d1f8b27991f30344a5200bf9 --- /dev/null +++ b/src/client_cli/cmdTemplate/helper.go @@ -0,0 +1,128 @@ +package cmdTemplate + +import ( + "strings" + "regexp" + "encoding/json" + "nexus-client/cmd" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/google/uuid" + "github.com/go-resty/resty/v2" +) + +// Make sure these types MATCH their counterparts in nexus-server codebase! +type ( + Template struct { + ID uuid.UUID `json:"id" validate:"required"` + Name string `json:"name" validate:"required,min=2,max=256"` + Owner string `json:"owner" validate:"required,email"` + Access string `json:"access" validate:"required,min=4,max=16"` // private or public + } +) + +// Converts a Template structure into a pretty string. +func (tpl *Template)String() string { + output, err := json.MarshalIndent(tpl, "", " ") + if err != nil { + return err.Error() + } + return string(output) +} + +func printUsage(c cmd.Command) { + u.PrintlnErr(c.GetDesc()) + u.PrintlnErr("Usage: ",c.GetName()," [ID] [regex]") + u.PrintlnErr("Only templates matching either the specified ID or regex will be listed.") + u.PrintlnErr("Any number of ID or regex can be specified.") + u.PrintlnErr("The regex matches the template's name only and is case-insensitive.") + u.PrintlnErr("Regex examples:") + u.PrintlnErr(" \"\" -> matches any name") + u.PrintlnErr(" \".\" -> matches any name") + u.PrintlnErr(" \"bla\" -> matches any name containing \"bla\"") +} + +// Call the given route and filter the results based on ID and regex. +// Arguments (args) can either be: +// - any number of "template IDs" (UUID) +// - any number of "template names" +// - any combination of "template IDs" and "template names" +// - any regular expression (it applies to the template's name only) +// Remark: the matching is case-insensitive +// Regular expression examples: +// "" -> matches everything +// "." -> matches everything +// "bla" -> matches any template name containing "bla" +func getFilteredTemplates(c cmd.Command, args []string, route string) int { + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 1 { + c.PrintUsage() + return 1 + } + + var ids []string + var namePatterns []string + + for _, arg := range args { + _, err := uuid.Parse(arg) + if err != nil { + namePatterns = append(namePatterns, arg) + } else { + ids = append(ids, arg) + } + } + + resp, err := client.R().Get(host+route) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + if resp.IsSuccess() { + templates, err := getTemplates(resp) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + for _, template := range *templates { + found := false + for _, id := range ids { + if id == template.ID.String() { + u.Println(template.String()) + found = true + break + } + } + if found { + continue + } + for _, namePattern := range namePatterns { + match, err := regexp.MatchString(strings.ToLower(namePattern), strings.ToLower(template.Name)) + if err != nil { + u.PrintlnErr("Error matching \""+namePattern+"\": "+err.Error()) + } else { + if match { + u.Println(template.String()) + break + } + } + } + } + return 0 + } else { + u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) + return 1 + } +} + +func getTemplates(resp *resty.Response) (*[]Template, error) { + var templates []Template + if err := json.Unmarshal(resp.Body(), &templates); err != nil { + return nil, err + } + return &templates, nil +} \ No newline at end of file diff --git a/src/client_cli/cmdTemplate/templateList.go b/src/client_cli/cmdTemplate/templateList.go index 4d9a8a75e481768a41d43b4a362b71dd5523f810..bf9e5176e44c6ec4b0d82c566aaf793cbf9d0803 100644 --- a/src/client_cli/cmdTemplate/templateList.go +++ b/src/client_cli/cmdTemplate/templateList.go @@ -1,10 +1,5 @@ package cmdTemplate -import ( - u "nexus-client/utils" - g "nexus-client/globals" -) - type List struct { Name string } @@ -14,43 +9,13 @@ func (cmd *List)GetName() string { } func (cmd *List)GetDesc() string { - return "List one or more templates" + return "List templates that can be listed by the user" } func (cmd *List)PrintUsage() { - u.PrintlnErr(cmd.GetDesc()) - u.PrintlnErr("Usage: "+cmd.Name+" [id]") + printUsage(cmd) } func (cmd *List)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if !(argc == 0 || argc == 1) { - cmd.PrintUsage() - return 1 - } - - var url string - if argc == 0 { - url = host+"/templates" - } else { - id := args[0] - url = host+"/templates/"+id - } - - resp, err := client.R().Get(url) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - - if resp.IsSuccess() { - u.Println(resp) - return 0 - } else { - u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) - return 1 - } + return getFilteredTemplates(cmd, args, "/templates") } diff --git a/src/client_cli/cmdUser/helper.go b/src/client_cli/cmdUser/helper.go new file mode 100644 index 0000000000000000000000000000000000000000..ad853da5fd950fa70f5c19b2de64e38ff0b98958 --- /dev/null +++ b/src/client_cli/cmdUser/helper.go @@ -0,0 +1,103 @@ +package cmdUser + +import ( + "strings" + "regexp" + "encoding/json" + "nexus-client/cmd" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/go-resty/resty/v2" +) + +// Make sure these types MATCH their counterparts in nexus-server codebase! +type ( + Capabilities map[string]int + + User struct { + Email string `json:"email" validate:"required,email"` + FirstName string `json:"firstname" validate:"required,min=2,max=32"` + LastName string `json:"lastname" validate:"required,min=2,max=32"` + Pwd string `json:"pwd" validate:"required,min=8"` + Caps Capabilities `json:"caps" validate:"required"` + } +) + +// Converts a User structure into a pretty string. +func (user *User)String() string { + output, err := json.MarshalIndent(user, "", " ") + if err != nil { + return err.Error() + } + return string(output) +} + +func printUsage(c cmd.Command) { + u.PrintlnErr(c.GetDesc()) + u.PrintlnErr("Usage: ",c.GetName()," [regex]") + u.PrintlnErr("Only users matching the specified regex will be listed.") + u.PrintlnErr("Any number of regex can be specified.") + u.PrintlnErr("The regex matches the user email, first name and last name and is case-insensitive.") + u.PrintlnErr("Regex examples:") + u.PrintlnErr(" \"\" -> matches any user") + u.PrintlnErr(" \".\" -> matches any user") + u.PrintlnErr(" \"bla\" -> matches any user containing \"bla\"") +} + +// Call the given route and filter the results based on a regex. +// Remark: the regex matches the user email, first name and last name and is case-insensitive. +// Regular expression examples: +// "" -> matches everything +// "." -> matches everything +// "bla" -> matches any user containing "bla" +func getFilteredUsers(c cmd.Command, args []string, route string) int { + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 1 { + c.PrintUsage() + return 1 + } + + resp, err := client.R().Get(host+route) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + if resp.IsSuccess() { + users, err := getUsers(resp) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + for _, user := range *users { + for _, pattern := range args { + fieldsToMatch := user.Email+" "+user.FirstName+" "+user.LastName + match, err := regexp.MatchString(strings.ToLower(pattern), strings.ToLower(fieldsToMatch)) + if err != nil { + u.PrintlnErr("Error matching \""+pattern+"\": "+err.Error()) + } else { + if match { + u.Println(user.String()) + break + } + } + } + } + return 0 + } else { + u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) + return 1 + } +} + +func getUsers(resp *resty.Response) (*[]User, error) { + var users []User + if err := json.Unmarshal(resp.Body(), &users); err != nil { + return nil, err + } + return &users, nil +} \ No newline at end of file diff --git a/src/client_cli/cmdUser/userList.go b/src/client_cli/cmdUser/userList.go index f1cefedd5ebcc49e1f1eb7da17b0c141c936e554..8adbf62ab45038542038e4770da6219eaf417103 100644 --- a/src/client_cli/cmdUser/userList.go +++ b/src/client_cli/cmdUser/userList.go @@ -1,10 +1,5 @@ package cmdUser -import ( - u "nexus-client/utils" - g "nexus-client/globals" -) - type List struct { Name string } @@ -14,43 +9,13 @@ func (cmd *List)GetName() string { } func (cmd *List)GetDesc() string { - return "List one or all users" + return "List users" } func (cmd *List)PrintUsage() { - u.PrintlnErr(cmd.GetDesc()) - u.PrintlnErr("Usage: "+cmd.Name+" [email]") + printUsage(cmd) } func (cmd *List)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if !(argc == 0 || argc == 1) { - cmd.PrintUsage() - return 1 - } - - var url string - if argc == 0 { - url = host+"/users" - } else { - email := args[0] - url = host+"/users/"+email - } - - resp, err := client.R().Get(url) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - - if resp.IsSuccess() { - u.Println(resp) - return 0 - } else { - u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) - return 1 - } + return getFilteredUsers(cmd, args, "/users") } diff --git a/src/client_cli/cmdUser/userWhoami.go b/src/client_cli/cmdUser/userWhoami.go index ede314cd5a8d7049eb94f2412a94dae43ea24f4b..4efd4b668ad17e026108a60a028839f0e98f08d6 100644 --- a/src/client_cli/cmdUser/userWhoami.go +++ b/src/client_cli/cmdUser/userWhoami.go @@ -14,7 +14,7 @@ func (cmd *Whoami)GetName() string { } func (cmd *Whoami)GetDesc() string { - return "Display the currently authenticated user's details" + return "Display the user's details" } func (cmd *Whoami)PrintUsage() { diff --git a/src/client_cli/cmdVM/helper.go b/src/client_cli/cmdVM/helper.go index 2178733964bda3b92a9cf10ee59270b6f5f271ae..9a97aac6ccfa4a12e9675e14d702567786d8b54f 100644 --- a/src/client_cli/cmdVM/helper.go +++ b/src/client_cli/cmdVM/helper.go @@ -53,9 +53,9 @@ func (vm *VM)String() string { func printUsage(c cmd.Command) { u.PrintlnErr(c.GetDesc()) - u.PrintlnErr("Usage: ",c.GetName()," [vmID] [regex]") - u.PrintlnErr("Only VMs matching either the specified vmID or regex will be listed.") - u.PrintlnErr("Any number of vmID or regex can be specified.") + u.PrintlnErr("Usage: ",c.GetName()," [ID] [regex]") + u.PrintlnErr("Only VMs matching either the specified ID or regex will be listed.") + u.PrintlnErr("Any number of ID or regex can be specified.") u.PrintlnErr("The regex matches the VM's name only and is case-insensitive.") u.PrintlnErr("Regex examples:") u.PrintlnErr(" \"\" -> matches any name") @@ -63,7 +63,7 @@ func printUsage(c cmd.Command) { u.PrintlnErr(" \"bla\" -> matches any name containing \"bla\"") } -// Call the given route and filter the results based on vmID and regex. +// Call the given route and filter the results based on ID and regex. // Arguments (args) can either be: // - any number of "VM IDs" (UUID) // - any number of "VM names" diff --git a/src/client_cli/cmdVM/vmList.go b/src/client_cli/cmdVM/vmList.go index f5e13d4f8f2858543702ff59d66ddf7b0915494a..6aed8213d48021da984b16072fe6b05b49f61afa 100644 --- a/src/client_cli/cmdVM/vmList.go +++ b/src/client_cli/cmdVM/vmList.go @@ -9,7 +9,7 @@ func (cmd *List)GetName() string { } func (cmd *List)GetDesc() string { - return "List all VMs that can be listed" + return "List VMs that can be listed by the user" } func (cmd *List)PrintUsage() { @@ -17,5 +17,5 @@ func (cmd *List)PrintUsage() { } func (cmd *List)Run(args []string) int { - return getFilteredVMs(cmd, args, "/vms/list") + return getFilteredVMs(cmd, args, "/vms") } diff --git a/src/client_cli/cmdVM/vmListAttach.go b/src/client_cli/cmdVM/vmListAttach.go index c10ccfcb1a9cffe5aa0bf80b6344a9c26e73b42d..8567df47d6c539310123ab97ef4265b267c2af4c 100644 --- a/src/client_cli/cmdVM/vmListAttach.go +++ b/src/client_cli/cmdVM/vmListAttach.go @@ -9,7 +9,7 @@ func (cmd *ListAttach)GetName() string { } func (cmd *ListAttach)GetDesc() string { - return "List VMs that can be attached to" + return "List VMs the user can attach to" } func (cmd *ListAttach)PrintUsage() { diff --git a/src/client_cli/cmdVM/vmListDel.go b/src/client_cli/cmdVM/vmListDel.go index 7701bac75a73e548d525762cd0aa2c77846db7dd..3d27107d38f3cee54597074cff82eb5943daa56a 100644 --- a/src/client_cli/cmdVM/vmListDel.go +++ b/src/client_cli/cmdVM/vmListDel.go @@ -9,7 +9,7 @@ func (cmd *ListDel)GetName() string { } func (cmd *ListDel)GetDesc() string { - return "List VMs that can be deleted" + return "List VMs the user can delete" } func (cmd *ListDel)PrintUsage() { diff --git a/src/client_cli/cmdVM/vmListEdit.go b/src/client_cli/cmdVM/vmListEdit.go index 47b3356692cc41919cc6a95c656602b40e8f7a67..2fe522bb9923621891a9341743de3b881587cb0c 100644 --- a/src/client_cli/cmdVM/vmListEdit.go +++ b/src/client_cli/cmdVM/vmListEdit.go @@ -9,7 +9,7 @@ func (cmd *ListEdit)GetName() string { } func (cmd *ListEdit)GetDesc() string { - return "List VMs that can be edited" + return "List VMs the user can edit" } func (cmd *ListEdit)PrintUsage() { diff --git a/src/client_cli/cmdVM/vmListEditAccess.go b/src/client_cli/cmdVM/vmListEditAccess.go index d8fa17daecf020ecb230ef5585e66c124c4c1b11..c7db4da3d781713708c35f34ad84c2e369de7630 100644 --- a/src/client_cli/cmdVM/vmListEditAccess.go +++ b/src/client_cli/cmdVM/vmListEditAccess.go @@ -9,7 +9,7 @@ func (cmd *ListEditAccess)GetName() string { } func (cmd *ListEditAccess)GetDesc() string { - return "List VMs that can have their VM access edited" + return "List VMs the user can edit the VM access for" } func (cmd *ListEditAccess)PrintUsage() { diff --git a/src/client_cli/cmdVM/vmListStart.go b/src/client_cli/cmdVM/vmListStart.go index 66a486b7194b72c2eced633d69546b04517a0690..96a34bfc17ddeecb243effe5e751f696751d54ff 100644 --- a/src/client_cli/cmdVM/vmListStart.go +++ b/src/client_cli/cmdVM/vmListStart.go @@ -9,7 +9,7 @@ func (cmd *ListStart)GetName() string { } func (cmd *ListStart)GetDesc() string { - return "List VMs that can be started" + return "List VMs the user can start" } func (cmd *ListStart)PrintUsage() { diff --git a/src/client_cli/cmdVM/vmListStop.go b/src/client_cli/cmdVM/vmListStop.go index e7e8a2e1b172037d39cabd29807aa78bec1c1c52..534be4070f91123530f6d58246babf2d6cca19b8 100644 --- a/src/client_cli/cmdVM/vmListStop.go +++ b/src/client_cli/cmdVM/vmListStop.go @@ -9,7 +9,7 @@ func (cmd *ListStop)GetName() string { } func (cmd *ListStop)GetDesc() string { - return "List VMs that can be stopped" + return "List VMs the user can stop" } func (cmd *ListStop)PrintUsage() { diff --git a/src/client_cli/globals/Globals.go b/src/client_cli/globals/Globals.go index 4985794b399583152969b664bc9e03674e72844b..582c9a9a4e5affe2f302261ed3dbb0941c89d8e2 100644 --- a/src/client_cli/globals/Globals.go +++ b/src/client_cli/globals/Globals.go @@ -5,8 +5,8 @@ import ( ) type Globals struct { - Host string Hostname string + Host string PubCert string Client *resty.Client } @@ -17,7 +17,7 @@ func GetInstance() *Globals { return globals } -func Init(hostname string, host string, pubCert string, client *resty.Client) *Globals { - return &Globals{hostname, host, pubCert, client} +func Init(hostname string, host string, pubCert string, client *resty.Client) { + globals = &Globals{hostname, host, pubCert, client} }