diff --git a/fn/README.md b/fn/README.md index e9e03f019..658a05705 100644 --- a/fn/README.md +++ b/fn/README.md @@ -56,20 +56,20 @@ fn push You can operate IronFunctions from the command line. ```sh -$ fn apps # list apps +$ fn apps list # list apps myapp $ fn apps create otherapp # create new app otherapp created -$ fn apps config otherapp # show app-specific configuration -this application has no configurations +$ fn apps inspect otherapp config # show app-specific configuration +{ ... } $ fn apps myapp otherapp -$ fn routes myapp # list routes of an app +$ fn routes list myapp # list routes of an app path image /hello iron/hello @@ -191,6 +191,47 @@ $ fn routes create myapp $ fn test --remote myapp ``` +## Other examples of usage + +### Creating a new function from source +``` +fn init iron/hello --runtime ruby +fn deploy myapp /hello +``` + +### Updating function +``` +fn deploy myapp (discover route path if available in func.yaml) +``` + +### Testing function locally +``` +fn run iron/hello +``` + +### Testing route +``` +fn call myapp /hello +``` + +### App management +``` +fn apps create myapp +fn apps update myapp --headers "content-type=application/json" +fn apps config set log_level info +fn apps inspect myapp +fn apps delete myapp +``` + +### Route management +``` +fn routes create myapp /hello iron/hello +fn routes update myapp /hello --timeout 30 --type async +fn routes config set myapp /hello log_level info +fn routes inspect myapp /hello +fn routes delete myapp /hello +``` + ## Contributing Ensure you have Go configured and installed in your environment. Once it is diff --git a/fn/apps.go b/fn/apps.go index 8513da47d..ac645ad8b 100644 --- a/fn/apps.go +++ b/fn/apps.go @@ -5,13 +5,15 @@ import ( "errors" "fmt" "os" - "text/tabwriter" "context" + "github.com/iron-io/functions_go" fnclient "github.com/iron-io/functions_go/client" apiapps "github.com/iron-io/functions_go/client/apps" "github.com/iron-io/functions_go/models" + "github.com/jmoiron/jsonq" "github.com/urfave/cli" + "strings" ) type appsCmd struct { @@ -40,32 +42,29 @@ func apps() cli.Command { }, }, { - Name: "list", - Aliases: []string{"l"}, - Usage: "list all apps", - Action: a.list, + Name: "inspect", + Aliases: []string{"i"}, + Usage: "retrieve one or all apps properties", + ArgsUsage: "`app` [property.[key]]", + Action: a.inspect, + }, + { + Name: "update", + Aliases: []string{"u"}, + Usage: "update an `app`", + ArgsUsage: "`app`", + Action: a.update, + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "config,c", + Usage: "route configuration", + }, + }, }, { Name: "config", - Usage: "operate an application configuration set", + Usage: "manage your apps's function configs", Subcommands: []cli.Command{ - { - Name: "view", - Aliases: []string{"v"}, - Usage: "view all configuration keys for this app", - ArgsUsage: "`app`", - Action: a.configList, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "shell,s", - Usage: "output in shell format", - }, - cli.BoolFlag{ - Name: "json,j", - Usage: "output in JSON format", - }, - }, - }, { Name: "set", Aliases: []string{"s"}, @@ -82,6 +81,12 @@ func apps() cli.Command { }, }, }, + { + Name: "list", + Aliases: []string{"l"}, + Usage: "list all apps", + Action: a.list, + }, { Name: "delete", Usage: "delete an app", @@ -120,7 +125,7 @@ func (a *appsCmd) list(c *cli.Context) error { func (a *appsCmd) create(c *cli.Context) error { if c.Args().First() == "" { - return errors.New("error: app creating takes one argument, an app name") + return errors.New("error: missing app name after create command") } body := &models.AppWrapper{App: &models.App{ @@ -149,50 +154,23 @@ func (a *appsCmd) create(c *cli.Context) error { return nil } -func (a *appsCmd) configList(c *cli.Context) error { +func (a *appsCmd) update(c *cli.Context) error { if c.Args().First() == "" { - return errors.New("error: app description takes one argument, an app name") + return errors.New("error: missing app name after update command") } - appName := c.Args().Get(0) + appName := c.Args().First() - resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{ - Context: context.Background(), - App: appName, - }) + patchedApp := &functions.App{ + Config: extractEnvConfig(c.StringSlice("config")), + } + err := a.patchApp(appName, patchedApp) if err != nil { - switch err.(type) { - case *apiapps.GetAppsAppNotFound: - return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message) - case *apiapps.GetAppsAppDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message) - } - return fmt.Errorf("unexpected error: %v", err) + return err } - config := resp.Payload.App.Config - if len(config) == 0 { - return errors.New("this application has no configurations") - } - - if c.Bool("json") { - if err := json.NewEncoder(os.Stdout).Encode(config); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } else if c.Bool("shell") { - for k, v := range resp.Payload.App.Config { - fmt.Print("export ", k, "=", v, "\n") - } - } else { - fmt.Println(resp.Payload.App.Name, "configuration:") - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0) - for k, v := range config { - fmt.Fprint(w, k, ":\t", v, "\n") - } - w.Flush() - } + fmt.Println("app", appName, "updated") return nil } @@ -205,34 +183,17 @@ func (a *appsCmd) configSet(c *cli.Context) error { key := c.Args().Get(1) value := c.Args().Get(2) - resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{ - Context: context.Background(), - App: appName, - }) - - if err != nil { - switch err.(type) { - case *apiapps.GetAppsAppNotFound: - return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message) - case *apiapps.GetAppsAppDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message) - } - return fmt.Errorf("unexpected error: %v", err) + app := &functions.App{ + Config: make(map[string]string), } - config := resp.Payload.App.Config + app.Config[key] = value - if config == nil { - config = make(map[string]string) - } - - config[key] = value - - if err := a.patchApp(appName, config); err != nil { + if err := a.patchApp(appName, app); err != nil { return fmt.Errorf("error updating app configuration: %v", err) } - fmt.Println(resp.Payload.App.Name, "updated", key, "with", value) + fmt.Println(appName, "updated", key, "with", value) return nil } @@ -244,6 +205,21 @@ func (a *appsCmd) configUnset(c *cli.Context) error { appName := c.Args().Get(0) key := c.Args().Get(1) + app := &functions.App{ + Config: make(map[string]string), + } + + app.Config["-"+key] = "" + + if err := a.patchApp(appName, app); err != nil { + return fmt.Errorf("error updating app configuration: %v", err) + } + + fmt.Printf("removed key '%s' from app '%s' \n", key, appName) + return nil +} + +func (a *appsCmd) patchApp(appName string, app *functions.App) error { resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{ Context: context.Background(), App: appName, @@ -259,32 +235,26 @@ func (a *appsCmd) configUnset(c *cli.Context) error { return fmt.Errorf("unexpected error: %v", err) } - config := resp.Payload.App.Config - - if config == nil { - config = make(map[string]string) + if resp.Payload.App.Config == nil { + resp.Payload.App.Config = map[string]string{} } - if _, ok := config[key]; !ok { - return fmt.Errorf("configuration key %s not found", key) + resp.Payload.App.Name = "" + if app != nil { + if app.Config != nil { + for k, v := range app.Config { + if string(k[0]) == "-" { + delete(resp.Payload.App.Config, string(k[1:])) + continue + } + resp.Payload.App.Config[k] = v + } + } } - delete(config, key) + body := &models.AppWrapper{App: resp.Payload.App} - if err := a.patchApp(appName, config); err != nil { - return fmt.Errorf("error updating app configuration: %v", err) - } - - fmt.Println(resp.Payload.App.Name, "removed", key) - return nil -} - -func (a *appsCmd) patchApp(appName string, config map[string]string) error { - body := &models.AppWrapper{App: &models.App{ - Config: config, - }} - - _, err := a.client.Apps.PatchAppsApp(&apiapps.PatchAppsAppParams{ + _, err = a.client.Apps.PatchAppsApp(&apiapps.PatchAppsAppParams{ Context: context.Background(), App: appName, Body: body, @@ -305,6 +275,59 @@ func (a *appsCmd) patchApp(appName string, config map[string]string) error { return nil } +func (a *appsCmd) inspect(c *cli.Context) error { + if c.Args().Get(0) == "" { + return errors.New("error: missing app name after the inspect command") + } + + appName := c.Args().First() + prop := c.Args().Get(1) + + resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{ + Context: context.Background(), + App: appName, + }) + + if err != nil { + switch err.(type) { + case *apiapps.GetAppsAppNotFound: + return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message) + case *apiapps.GetAppsAppDefault: + return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message) + } + return fmt.Errorf("unexpected error: %v", err) + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", "\t") + + if prop == "" { + enc.Encode(resp.Payload.App) + return nil + } + + // TODO: we really need to marshal it here just to + // unmarshal as map[string]interface{}? + data, err := json.Marshal(resp.Payload.App) + if err != nil { + return fmt.Errorf("error inspect app: %v", err) + } + var inspect map[string]interface{} + err = json.Unmarshal(data, &inspect) + if err != nil { + return fmt.Errorf("error inspect app: %v", err) + } + + jq := jsonq.NewQuery(inspect) + field, err := jq.Interface(strings.Split(prop, ".")...) + if err != nil { + return errors.New("failed to inspect that apps's field") + } + enc.Encode(field) + + return nil +} + func (a *appsCmd) delete(c *cli.Context) error { appName := c.Args().First() if appName == "" { @@ -326,6 +349,6 @@ func (a *appsCmd) delete(c *cli.Context) error { return fmt.Errorf("unexpected error: %v", err) } - fmt.Println(appName, "deleted") + fmt.Println("app", appName, "deleted") return nil } diff --git a/fn/common.go b/fn/common.go index 3de02401f..db7ef286e 100644 --- a/fn/common.go +++ b/fn/common.go @@ -185,7 +185,9 @@ func extractEnvConfig(configs []string) map[string]string { c := make(map[string]string) for _, v := range configs { kv := strings.SplitN(v, "=", 2) - c[kv[0]] = os.ExpandEnv(kv[1]) + if len(kv) == 2 { + c[kv[0]] = os.ExpandEnv(kv[1]) + } } return c } diff --git a/fn/routes.go b/fn/routes.go index 27ff3f5b6..0e9069fe9 100644 --- a/fn/routes.go +++ b/fn/routes.go @@ -17,6 +17,7 @@ import ( fnclient "github.com/iron-io/functions_go/client" apiroutes "github.com/iron-io/functions_go/client/routes" "github.com/iron-io/functions_go/models" + fnmodels "github.com/iron-io/functions_go/models" "github.com/jmoiron/jsonq" "github.com/urfave/cli" ) @@ -100,12 +101,10 @@ func routes() cli.Command { cli.Int64Flag{ Name: "memory,m", Usage: "memory in MiB", - Value: 128, }, cli.StringFlag{ Name: "type,t", Usage: "route type - sync or async", - Value: "sync", }, cli.StringSliceFlag{ Name: "config,c", @@ -118,17 +117,14 @@ func routes() cli.Command { cli.StringFlag{ Name: "format,f", Usage: "hot container IO format - json or http", - Value: "", }, cli.IntFlag{ - Name: "max-concurrency", + Name: "max-concurrency,mc", Usage: "maximum concurrency for hot container", - Value: 1, }, cli.DurationFlag{ Name: "timeout", - Usage: "route timeout", - Value: 30 * time.Second, + Usage: "route timeout (eg. 30s)", }, }, }, @@ -273,8 +269,8 @@ func envAsHeader(req *http.Request, selectedEnv []string) { } func (a *routesCmd) create(c *cli.Context) error { - if c.Args().Get(0) == "" { - return errors.New("error: routes creation takes at least one argument: an app name") + if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" { + return errors.New("error: routes creation takes at least three arguments: app name, route path and image name") } appName := c.Args().Get(0) @@ -361,167 +357,75 @@ func (a *routesCmd) create(c *cli.Context) error { return nil } -func (a *routesCmd) delete(c *cli.Context) error { - if c.Args().Get(0) == "" || c.Args().Get(1) == "" { - return errors.New("error: routes listing takes three arguments: an app name and a path") - } - - appName := c.Args().Get(0) - route := c.Args().Get(1) - - _, err := a.client.Routes.DeleteAppsAppRoutesRoute(&apiroutes.DeleteAppsAppRoutesRouteParams{ - Context: context.Background(), - App: appName, - Route: route, - }) - if err != nil { - switch err.(type) { - case *apiroutes.DeleteAppsAppRoutesRouteNotFound: - return fmt.Errorf("error: %v", err.(*apiroutes.DeleteAppsAppRoutesRouteNotFound).Payload.Error.Message) - case *apiroutes.DeleteAppsAppRoutesRouteDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiroutes.DeleteAppsAppRoutesRouteDefault).Payload.Error.Message) - } - return fmt.Errorf("unexpected error: %v", err) - } - - fmt.Println(route, "deleted") - return nil -} - -// _, err = a.client.Routes.PatchAppsAppRoutesRoute(&apiroutes.PatchAppsAppRoutesRouteParams{ -// Context: context.Background(), -// App: appName, -// Route: route, -// Body: resp.Payload, -// }) - -// if err != nil { -// switch err.(type) { -// case *apiroutes.PatchAppsAppRoutesRouteBadRequest: -// return fmt.Errorf("error: %v", err.(*apiroutes.PatchAppsAppRoutesRouteBadRequest).Payload.Error.Message) -// case *apiroutes.PatchAppsAppRoutesRouteNotFound: -// return fmt.Errorf("error: %v", err.(*apiroutes.PatchAppsAppRoutesRouteNotFound).Payload.Error.Message) -// case *apiroutes.PatchAppsAppRoutesRouteDefault: -// return fmt.Errorf("unexpected error: %v", err.(*apiroutes.PatchAppsAppRoutesRouteDefault).Payload.Error.Message) -// } -// return fmt.Errorf("unexpected error: %v", err) -// } - -func (a *routesCmd) update(c *cli.Context) error { - if c.Args().Get(0) == "" || c.Args().Get(1) == "" { - return errors.New("error: route configuration description takes two arguments: an app name and a route") - } - - appName := c.Args().Get(0) - route := c.Args().Get(1) - +func (a *routesCmd) patchRoute(appName, routePath string, r *fnmodels.Route) error { resp, err := a.client.Routes.GetAppsAppRoutesRoute(&apiroutes.GetAppsAppRoutesRouteParams{ Context: context.Background(), App: appName, - Route: route, - }) - if err != nil { - switch err.(type) { - case *apiroutes.GetAppsAppRoutesRouteNotFound: - return fmt.Errorf("error: %v", err.(*apiroutes.GetAppsAppRoutesRouteNotFound).Payload.Error.Message) - case *apiroutes.GetAppsAppRoutesRouteDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiroutes.GetAppsAppRoutesRouteDefault).Payload.Error.Message) - } - return fmt.Errorf("unexpected error: %v", err) - } - - config := resp.Payload.Route.Config - if len(config) == 0 { - return errors.New("this route has no configurations") - } - - if c.Bool("json") { - if err := json.NewEncoder(os.Stdout).Encode(config); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - } else if c.Bool("shell") { - for k, v := range config { - fmt.Print("export ", k, "=", v, "\n") - } - } else { - fmt.Println(appName, resp.Payload.Route.Path, "configuration:") - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0) - for k, v := range config { - fmt.Fprint(w, k, ":\t", v, "\n") - } - w.Flush() - } - - // if route == "" { - // return errors.New("error: route path is missing") - // } - - // headers := map[string][]string{} - // for _, header := range c.StringSlice("headers") { - // parts := strings.Split(header, "=") - // headers[parts[0]] = strings.Split(parts[1], ";") - // } - - // patchRoute := &functions.Route{ - // Image: c.String("image"), - // Memory: c.Int64("memory"), - // Type_: c.String("type"), - // Config: extractEnvConfig(c.StringSlice("config")), - // Headers: headers, - // Format: c.String("format"), - // MaxConcurrency: int32(c.Int64("max-concurrency")), - // Timeout: int32(c.Int64("timeout")), - // } - - // err := a.patchRoute(appName, route, patchRoute) - // if err != nil { - // return err - // } - - fmt.Println(appName, route, "updated") - return nil -} - -func (a *routesCmd) configSet(c *cli.Context) error { - if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" { - return errors.New("error: route configuration setting takes four arguments: an app name, a route, a key and a value") - } - - appName := c.Args().Get(0) - route := c.Args().Get(1) - key := c.Args().Get(2) - value := c.Args().Get(3) - - resp, err := a.client.Routes.GetAppsAppRoutesRoute(&apiroutes.GetAppsAppRoutesRouteParams{ - Context: context.Background(), - App: appName, - Route: route, + Route: routePath, }) if err != nil { switch err.(type) { case *apiroutes.GetAppsAppRoutesRouteNotFound: return fmt.Errorf("error: %v", err.(*apiroutes.GetAppsAppRoutesRouteNotFound).Payload.Error.Message) - case *apiroutes.GetAppsAppRoutesRouteDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiroutes.GetAppsAppRoutesRouteDefault).Payload.Error.Message) + case *apiroutes.GetAppsAppRoutesDefault: + return fmt.Errorf("unexpected error: %v", err.(*apiroutes.GetAppsAppRoutesDefault).Payload.Error.Message) } return fmt.Errorf("unexpected error: %v", err) } - config := resp.Payload.Route.Config - - if config == nil { - config = make(map[string]string) + if resp.Payload.Route.Config == nil { + resp.Payload.Route.Config = map[string]string{} } - config[key] = value - resp.Payload.Route.Config = config + if resp.Payload.Route.Headers == nil { + resp.Payload.Route.Headers = map[string][]string{} + } + + resp.Payload.Route.Path = "" + if r != nil { + if r.Config != nil { + for k, v := range r.Config { + if string(k[0]) == "-" { + delete(resp.Payload.Route.Config, string(k[1:])) + continue + } + resp.Payload.Route.Config[k] = v + } + } + if r.Headers != nil { + for k, v := range r.Headers { + if string(k[0]) == "-" { + delete(resp.Payload.Route.Headers, k) + continue + } + resp.Payload.Route.Headers[k] = v + } + } + if r.Image != "" { + resp.Payload.Route.Image = r.Image + } + if r.Format != "" { + resp.Payload.Route.Format = r.Format + } + if r.Type != "" { + resp.Payload.Route.Type = r.Type + } + if r.MaxConcurrency > 0 { + resp.Payload.Route.MaxConcurrency = r.MaxConcurrency + } + if r.Memory > 0 { + resp.Payload.Route.Memory = r.Memory + } + if r.Timeout != nil { + resp.Payload.Route.Timeout = r.Timeout + } + } _, err = a.client.Routes.PatchAppsAppRoutesRoute(&apiroutes.PatchAppsAppRoutesRouteParams{ Context: context.Background(), App: appName, - Route: route, + Route: routePath, Body: resp.Payload, }) @@ -537,20 +441,82 @@ func (a *routesCmd) configSet(c *cli.Context) error { return fmt.Errorf("unexpected error: %v", err) } - fmt.Println(appName, resp.Payload.Route.Path, "updated", key, "with", value) + return nil +} - // patchRoute := functions.Route{ - // Config: make(map[string]string), - // } +func (a *routesCmd) update(c *cli.Context) error { + if c.Args().Get(0) == "" || c.Args().Get(1) == "" { + return errors.New("error: route configuration description takes two arguments: an app name and a route") + } - // patchRoute.Config[key] = value + appName := c.Args().Get(0) + route := c.Args().Get(1) - // err := a.patchRoute(appName, route, &patchRoute) - // if err != nil { - // return err - // } + var ( + format string + maxC int + timeout time.Duration + ) - // fmt.Println(appName, route, "updated", key, "with", value) + if f := c.String("format"); f != "" { + format = f + } + if m := c.Int("max-concurrency"); m > 0 { + maxC = m + } + if t := c.Duration("timeout"); t > 0 { + timeout = t + } + + headers := map[string][]string{} + for _, header := range c.StringSlice("headers") { + parts := strings.Split(header, "=") + headers[parts[0]] = strings.Split(parts[1], ";") + } + + to := int64(timeout.Seconds()) + patchRoute := &fnmodels.Route{ + Image: c.String("image"), + Memory: c.Int64("memory"), + Type: c.String("type"), + Config: extractEnvConfig(c.StringSlice("config")), + Headers: headers, + Format: format, + MaxConcurrency: int32(maxC), + Timeout: &to, + } + + err := a.patchRoute(appName, route, patchRoute) + if err != nil { + return err + } + + fmt.Println(appName, route, "updated") + return nil +} + +func (a *routesCmd) configSet(c *cli.Context) error { + if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" { + return errors.New("error: route configuration setting takes four arguments: an app name, a route, a key and a value") + } + + appName := c.Args().Get(0) + route := c.Args().Get(1) + key := c.Args().Get(2) + value := c.Args().Get(3) + + patchRoute := fnmodels.Route{ + Config: make(map[string]string), + } + + patchRoute.Config[key] = value + + err := a.patchRoute(appName, route, &patchRoute) + if err != nil { + return err + } + + fmt.Println(appName, route, "updated", key, "with", value) return nil } @@ -563,36 +529,18 @@ func (a *routesCmd) configUnset(c *cli.Context) error { route := c.Args().Get(1) key := c.Args().Get(2) - resp, err := a.client.Routes.GetAppsAppRoutesRoute(&apiroutes.GetAppsAppRoutesRouteParams{ - Context: context.Background(), - App: appName, - Route: route, - }) + patchRoute := fnmodels.Route{ + Config: make(map[string]string), + } + patchRoute.Config["-"+key] = "" + + err := a.patchRoute(appName, route, &patchRoute) if err != nil { - switch err.(type) { - case *apiroutes.GetAppsAppRoutesRouteNotFound: - return fmt.Errorf("error: %v", err.(*apiroutes.GetAppsAppRoutesRouteNotFound).Payload.Error.Message) - case *apiroutes.GetAppsAppRoutesRouteDefault: - return fmt.Errorf("unexpected error: %v", err.(*apiroutes.GetAppsAppRoutesRouteDefault).Payload.Error.Message) - } - return fmt.Errorf("unexpected error: %v", err) + return err } - config := resp.Payload.Route.Config - - if config == nil { - config = make(map[string]string) - } - - if _, ok := config[key]; !ok { - return fmt.Errorf("configuration key %s not found", key) - } - - delete(config, key) - resp.Payload.Route.Config = config - - fmt.Println(appName, resp.Payload.Route.Path, "removed", key) + fmt.Printf("removed key '%s' from the route '%s%s'", key, appName, key) return nil } @@ -629,12 +577,49 @@ func (a *routesCmd) inspect(c *cli.Context) error { return nil } - jq := jsonq.NewQuery(resp.Payload.Route) + data, err := json.Marshal(resp.Payload.Route) + if err != nil { + return fmt.Errorf("failed to inspect route: %v", err) + } + var inspect map[string]interface{} + err = json.Unmarshal(data, &inspect) + if err != nil { + return fmt.Errorf("failed to inspect route: %v", err) + } + + jq := jsonq.NewQuery(inspect) field, err := jq.Interface(strings.Split(prop, ".")...) if err != nil { - return errors.New("failed to inspect the property") + return errors.New("failed to inspect that route's field") } enc.Encode(field) return nil } + +func (a *routesCmd) delete(c *cli.Context) error { + if c.Args().Get(0) == "" || c.Args().Get(1) == "" { + return errors.New("error: routes listing takes three arguments: an app name and a path") + } + + appName := c.Args().Get(0) + route := c.Args().Get(1) + + _, err := a.client.Routes.DeleteAppsAppRoutesRoute(&apiroutes.DeleteAppsAppRoutesRouteParams{ + Context: context.Background(), + App: appName, + Route: route, + }) + if err != nil { + switch err.(type) { + case *apiroutes.DeleteAppsAppRoutesRouteNotFound: + return fmt.Errorf("error: %v", err.(*apiroutes.DeleteAppsAppRoutesRouteNotFound).Payload.Error.Message) + case *apiroutes.DeleteAppsAppRoutesRouteDefault: + return fmt.Errorf("unexpected error: %v", err.(*apiroutes.DeleteAppsAppRoutesRouteDefault).Payload.Error.Message) + } + return fmt.Errorf("unexpected error: %v", err) + } + + fmt.Println(appName, route, "deleted") + return nil +}