Skip to content

Commit c633d23

Browse files
authored
Add application management (#217)
1 parent 234f477 commit c633d23

7 files changed

Lines changed: 672 additions & 3 deletions

File tree

cmd/account/account.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func AccountRootCmd(f factory.ClientFactory) *cobra.Command {
1616
cmd.AddCommand(accountContactRootCmd(f))
1717
cmd.AddCommand(accountDetailsRootCmd(f))
1818
cmd.AddCommand(accountCreditRootCmd(f))
19+
cmd.AddCommand(accountApplicationRootCmd(f))
1920

2021
return cmd
2122
}

cmd/account/account_application.go

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
package account
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
8+
"github.com/ans-group/cli/internal/pkg/factory"
9+
"github.com/ans-group/cli/internal/pkg/helper"
10+
"github.com/ans-group/cli/internal/pkg/output"
11+
"github.com/ans-group/sdk-go/pkg/service/account"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func accountApplicationRootCmd(f factory.ClientFactory) *cobra.Command {
16+
cmd := &cobra.Command{
17+
Use: "application",
18+
Short: "sub-commands relating to applications",
19+
}
20+
21+
// Child commands
22+
cmd.AddCommand(accountApplicationListCmd(f))
23+
cmd.AddCommand(accountApplicationShowCmd(f))
24+
cmd.AddCommand(accountApplicationCreateCmd(f))
25+
cmd.AddCommand(accountApplicationUpdateCmd(f))
26+
cmd.AddCommand(accountApplicationDeleteCmd(f))
27+
cmd.AddCommand(accountApplicationRestrictionsRootCmd(f))
28+
29+
return cmd
30+
}
31+
32+
func accountApplicationListCmd(f factory.ClientFactory) *cobra.Command {
33+
return &cobra.Command{
34+
Use: "list",
35+
Short: "Lists applications",
36+
Long: "This command lists applications",
37+
Example: "ans account application list",
38+
RunE: func(cmd *cobra.Command, args []string) error {
39+
c, err := f.NewClient()
40+
if err != nil {
41+
return err
42+
}
43+
44+
return accountApplicationList(c.AccountService(), cmd, args)
45+
},
46+
}
47+
}
48+
49+
func accountApplicationList(service account.AccountService, cmd *cobra.Command, args []string) error {
50+
params, err := helper.GetAPIRequestParametersFromFlags(cmd)
51+
if err != nil {
52+
return err
53+
}
54+
55+
applications, err := service.GetApplications(params)
56+
if err != nil {
57+
return fmt.Errorf("account: error retrieving applications: %s", err)
58+
}
59+
60+
return output.CommandOutput(cmd, ApplicationCollection(applications))
61+
}
62+
63+
func accountApplicationShowCmd(f factory.ClientFactory) *cobra.Command {
64+
return &cobra.Command{
65+
Use: "show <application: id>...",
66+
Short: "Shows an application",
67+
Long: "This command shows one or more applications",
68+
Example: "ans account application show 550e8400-e29b-41d4-a716-446655440000",
69+
Args: func(cmd *cobra.Command, args []string) error {
70+
if len(args) < 1 {
71+
return errors.New("missing application")
72+
}
73+
74+
return nil
75+
},
76+
RunE: func(cmd *cobra.Command, args []string) error {
77+
c, err := f.NewClient()
78+
if err != nil {
79+
return err
80+
}
81+
82+
return accountApplicationShow(c.AccountService(), cmd, args)
83+
},
84+
}
85+
}
86+
87+
func accountApplicationShow(service account.AccountService, cmd *cobra.Command, args []string) error {
88+
var applications []account.Application
89+
for _, arg := range args {
90+
application, err := service.GetApplication(arg)
91+
if err != nil {
92+
output.OutputWithErrorLevelf("Error retrieving application [%s]: %s", arg, err)
93+
continue
94+
}
95+
96+
applications = append(applications, application)
97+
}
98+
99+
return output.CommandOutput(cmd, ApplicationCollection(applications))
100+
}
101+
102+
func accountApplicationCreateCmd(f factory.ClientFactory) *cobra.Command {
103+
cmd := &cobra.Command{
104+
Use: "create",
105+
Short: "Creates an application",
106+
Long: "This command creates an application",
107+
Example: "ans account application create --name \"My App\" --description \"My application description\"",
108+
RunE: func(cmd *cobra.Command, args []string) error {
109+
c, err := f.NewClient()
110+
if err != nil {
111+
return err
112+
}
113+
114+
return accountApplicationCreate(c.AccountService(), cmd, args)
115+
},
116+
}
117+
118+
// Setup flags
119+
cmd.Flags().String("name", "", "Name of application")
120+
cmd.MarkFlagRequired("name")
121+
cmd.Flags().String("description", "", "Description of application")
122+
cmd.Flags().StringSlice("allow-ip", []string{}, "IP addresses/ranges to allow (sets allowlist)")
123+
cmd.Flags().StringSlice("deny-ip", []string{}, "IP addresses/ranges to deny (sets denylist)")
124+
125+
return cmd
126+
}
127+
128+
func accountApplicationCreate(service account.AccountService, cmd *cobra.Command, args []string) error {
129+
createRequest := account.CreateApplicationRequest{}
130+
createRequest.Name, _ = cmd.Flags().GetString("name")
131+
createRequest.Description, _ = cmd.Flags().GetString("description")
132+
133+
// Validate IP restriction flags are not both specified
134+
allowIPs, _ := cmd.Flags().GetStringSlice("allow-ip")
135+
denyIPs, _ := cmd.Flags().GetStringSlice("deny-ip")
136+
137+
if len(allowIPs) > 0 && len(denyIPs) > 0 {
138+
return fmt.Errorf("account: cannot specify both --allow-ip and --deny-ip")
139+
}
140+
141+
// Validate IP addresses/ranges
142+
if err := validateIPRanges(allowIPs); err != nil {
143+
return fmt.Errorf("account: invalid IP in --allow-ip: %s", err)
144+
}
145+
if err := validateIPRanges(denyIPs); err != nil {
146+
return fmt.Errorf("account: invalid IP in --deny-ip: %s", err)
147+
}
148+
149+
response, err := service.CreateApplication(createRequest)
150+
if err != nil {
151+
return fmt.Errorf("account: error creating application: %s", err)
152+
}
153+
154+
// Set IP restrictions if specified
155+
if len(allowIPs) > 0 || len(denyIPs) > 0 {
156+
restrictionRequest := account.SetRestrictionRequest{}
157+
if len(allowIPs) > 0 {
158+
restrictionRequest.IPRestrictionType = "allowlist"
159+
restrictionRequest.IPRanges = allowIPs
160+
} else {
161+
restrictionRequest.IPRestrictionType = "denylist"
162+
restrictionRequest.IPRanges = denyIPs
163+
}
164+
165+
err = service.SetApplicationRestrictions(response.ID, restrictionRequest)
166+
if err != nil {
167+
output.OutputWithErrorLevelf("Warning: Application created but failed to set IP restrictions: %s", err)
168+
}
169+
}
170+
171+
// Get the full application details to display
172+
application, err := service.GetApplication(response.ID)
173+
if err != nil {
174+
return fmt.Errorf("account: error retrieving new application: %s", err)
175+
}
176+
177+
return output.CommandOutput(cmd, ApplicationCollection([]account.Application{application}))
178+
}
179+
180+
func accountApplicationUpdateCmd(f factory.ClientFactory) *cobra.Command {
181+
cmd := &cobra.Command{
182+
Use: "update <application: id>...",
183+
Short: "Updates an application",
184+
Long: "This command updates one or more applications",
185+
Example: "ans account application update 550e8400-e29b-41d4-a716-446655440000 --name \"Updated App\"",
186+
Args: func(cmd *cobra.Command, args []string) error {
187+
if len(args) < 1 {
188+
return errors.New("missing application")
189+
}
190+
191+
return nil
192+
},
193+
RunE: func(cmd *cobra.Command, args []string) error {
194+
c, err := f.NewClient()
195+
if err != nil {
196+
return err
197+
}
198+
199+
return accountApplicationUpdate(c.AccountService(), cmd, args)
200+
},
201+
}
202+
203+
// Setup flags
204+
cmd.Flags().String("name", "", "Name of application")
205+
cmd.Flags().String("description", "", "Description of application")
206+
207+
return cmd
208+
}
209+
210+
func accountApplicationUpdate(service account.AccountService, cmd *cobra.Command, args []string) error {
211+
updateRequest := account.UpdateApplicationRequest{}
212+
updateRequest.Name, _ = cmd.Flags().GetString("name")
213+
updateRequest.Description, _ = cmd.Flags().GetString("description")
214+
215+
var applications []account.Application
216+
217+
for _, arg := range args {
218+
err := service.UpdateApplication(arg, updateRequest)
219+
if err != nil {
220+
output.OutputWithErrorLevelf("Error updating application [%s]: %s", arg, err.Error())
221+
continue
222+
}
223+
224+
application, err := service.GetApplication(arg)
225+
if err != nil {
226+
output.OutputWithErrorLevelf("Error retrieving updated application [%s]: %s", arg, err.Error())
227+
continue
228+
}
229+
230+
applications = append(applications, application)
231+
}
232+
233+
return output.CommandOutput(cmd, ApplicationCollection(applications))
234+
}
235+
236+
func accountApplicationDeleteCmd(f factory.ClientFactory) *cobra.Command {
237+
return &cobra.Command{
238+
Use: "delete <application: id>...",
239+
Short: "Removes an application",
240+
Long: "This command removes one or more applications",
241+
Example: "ans account application delete 550e8400-e29b-41d4-a716-446655440000",
242+
Args: func(cmd *cobra.Command, args []string) error {
243+
if len(args) < 1 {
244+
return errors.New("missing application")
245+
}
246+
247+
return nil
248+
},
249+
RunE: func(cmd *cobra.Command, args []string) error {
250+
c, err := f.NewClient()
251+
if err != nil {
252+
return err
253+
}
254+
255+
return accountApplicationDelete(c.AccountService(), cmd, args)
256+
},
257+
}
258+
}
259+
260+
func accountApplicationDelete(service account.AccountService, cmd *cobra.Command, args []string) error {
261+
var hasErrors bool
262+
for _, arg := range args {
263+
err := service.DeleteApplication(arg)
264+
if err != nil {
265+
output.OutputWithErrorLevelf("Error removing application [%s]: %s", arg, err)
266+
hasErrors = true
267+
continue
268+
}
269+
}
270+
271+
if hasErrors {
272+
return fmt.Errorf("account: failed to delete one or more applications")
273+
}
274+
return nil
275+
}
276+
277+
// validateIPRanges validates that all provided strings are valid IP addresses or CIDR ranges
278+
func validateIPRanges(ipRanges []string) error {
279+
for _, ipRange := range ipRanges {
280+
// Try parsing as CIDR first
281+
_, _, err := net.ParseCIDR(ipRange)
282+
if err != nil {
283+
// If not CIDR, try parsing as IP
284+
ip := net.ParseIP(ipRange)
285+
if ip == nil {
286+
return fmt.Errorf("invalid IP address or CIDR range: %s", ipRange)
287+
}
288+
}
289+
}
290+
return nil
291+
}

0 commit comments

Comments
 (0)