github.com/devcamcar/cli@v0.0.0-20181107134215-706a05759d18/objects/trigger/triggers.go (about)

     1  package trigger
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  	"text/tabwriter"
    11  
    12  	"github.com/fnproject/fn_go/clientv2/fns"
    13  
    14  	"github.com/jmoiron/jsonq"
    15  
    16  	"github.com/fnproject/cli/common"
    17  	"github.com/fnproject/cli/objects/app"
    18  	"github.com/fnproject/cli/objects/fn"
    19  	"github.com/fnproject/fn_go/clientv2"
    20  	apiTriggers "github.com/fnproject/fn_go/clientv2/triggers"
    21  	models "github.com/fnproject/fn_go/modelsv2"
    22  	"github.com/fnproject/fn_go/provider"
    23  	"github.com/urfave/cli"
    24  )
    25  
    26  type triggersCmd struct {
    27  	provider provider.Provider
    28  	client   *clientv2.Fn
    29  }
    30  
    31  // TriggerFlags used to create/update triggers
    32  var TriggerFlags = []cli.Flag{
    33  	cli.StringFlag{
    34  		Name:  "source,s",
    35  		Usage: "trigger source",
    36  	},
    37  	cli.StringFlag{
    38  		Name:  "type, t",
    39  		Usage: "Todo",
    40  	},
    41  	cli.StringSliceFlag{
    42  		Name:  "annotation",
    43  		Usage: "fn annotation (can be specified multiple times)",
    44  	},
    45  }
    46  
    47  func (t *triggersCmd) create(c *cli.Context) error {
    48  	appName := c.Args().Get(0)
    49  	fnName := c.Args().Get(1)
    50  	triggerName := c.Args().Get(2)
    51  
    52  	app, err := app.GetAppByName(t.client, appName)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	fn, err := fn.GetFnByName(t.client, app.ID, fnName)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	trigger := &models.Trigger{
    63  		AppID: app.ID,
    64  		FnID:  fn.ID,
    65  	}
    66  
    67  	trigger.Name = triggerName
    68  
    69  	if triggerType := c.String("type"); triggerType != "" {
    70  		trigger.Type = triggerType
    71  	}
    72  
    73  	if triggerSource := c.String("source"); triggerSource != "" {
    74  		trigger.Source = validateTriggerSource(triggerSource)
    75  	}
    76  
    77  	WithFlags(c, trigger)
    78  
    79  	if trigger.Name == "" {
    80  		return errors.New("triggerName path is missing")
    81  	}
    82  
    83  	return CreateTrigger(t.client, trigger)
    84  }
    85  
    86  func validateTriggerSource(ts string) string {
    87  	if !strings.HasPrefix(ts, "/") {
    88  		ts = "/" + ts
    89  	}
    90  	return ts
    91  }
    92  
    93  // CreateTrigger request
    94  func CreateTrigger(client *clientv2.Fn, trigger *models.Trigger) error {
    95  	resp, err := client.Triggers.CreateTrigger(&apiTriggers.CreateTriggerParams{
    96  		Context: context.Background(),
    97  		Body:    trigger,
    98  	})
    99  
   100  	if err != nil {
   101  		switch e := err.(type) {
   102  		case *apiTriggers.CreateTriggerBadRequest:
   103  			fmt.Println(e)
   104  			return fmt.Errorf("%s", e.Payload.Message)
   105  		case *apiTriggers.CreateTriggerConflict:
   106  			return fmt.Errorf("%s", e.Payload.Message)
   107  		default:
   108  			return err
   109  		}
   110  	}
   111  
   112  	fmt.Println("Successfully created trigger:", resp.Payload.Name)
   113  	endpoint := resp.Payload.Annotations["fnproject.io/trigger/httpEndpoint"]
   114  	fmt.Println("Trigger Endpoint:", endpoint)
   115  
   116  	return nil
   117  }
   118  
   119  func (t *triggersCmd) list(c *cli.Context) error {
   120  	appName := c.Args().Get(0)
   121  	fnName := c.Args().Get(1)
   122  	var params *apiTriggers.ListTriggersParams
   123  
   124  	app, err := app.GetAppByName(t.client, appName)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	if len(fnName) == 0 {
   130  		params = &apiTriggers.ListTriggersParams{
   131  			Context: context.Background(),
   132  			AppID:   &app.ID,
   133  		}
   134  
   135  	} else {
   136  
   137  		fn, err := fn.GetFnByName(t.client, app.ID, fnName)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		params = &apiTriggers.ListTriggersParams{
   142  			Context: context.Background(),
   143  			AppID:   &app.ID,
   144  			FnID:    &fn.ID,
   145  		}
   146  	}
   147  
   148  	var resTriggers []*models.Trigger
   149  	for {
   150  
   151  		resp, err := t.client.Triggers.ListTriggers(params)
   152  		if err != nil {
   153  			return err
   154  		}
   155  		n := c.Int64("n")
   156  		if n < 0 {
   157  			return errors.New("number of calls: negative value not allowed")
   158  		}
   159  
   160  		resTriggers = append(resTriggers, resp.Payload.Items...)
   161  		howManyMore := n - int64(len(resTriggers)+len(resp.Payload.Items))
   162  		if howManyMore <= 0 || resp.Payload.NextCursor == "" {
   163  			break
   164  		}
   165  
   166  		params.Cursor = &resp.Payload.NextCursor
   167  	}
   168  
   169  	if len(resTriggers) == 0 {
   170  		if len(fnName) == 0 {
   171  			fmt.Fprintf(os.Stderr, "No triggers found for app: %s\n", appName)
   172  		} else {
   173  			fmt.Fprintf(os.Stderr, "No triggers found for function: %s\n", fnName)
   174  		}
   175  		return nil
   176  	}
   177  
   178  	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
   179  	if len(fnName) != 0 {
   180  
   181  		fmt.Fprint(w, "NAME", "\t", "ID", "\t", "TYPE", "\t", "SOURCE", "\t", "ENDPOINT", "\n")
   182  		for _, trigger := range resTriggers {
   183  			endpoint := trigger.Annotations["fnproject.io/trigger/httpEndpoint"]
   184  			fmt.Fprint(w, trigger.Name, "\t", trigger.ID, "\t", trigger.Type, "\t", trigger.Source, "\t", endpoint, "\n")
   185  		}
   186  	} else {
   187  		fmt.Fprint(w, "FUNCTION", "\t", "NAME", "\t", "ID", "\t", "TYPE", "\t", "SOURCE", "\t", "ENDPOINT", "\n")
   188  		for _, trigger := range resTriggers {
   189  			endpoint := trigger.Annotations["fnproject.io/trigger/httpEndpoint"]
   190  
   191  			resp, err := t.client.Fns.GetFn(&fns.GetFnParams{
   192  				FnID:    trigger.FnID,
   193  				Context: context.Background(),
   194  			})
   195  			if err != nil {
   196  				return err
   197  			}
   198  			fnName = resp.Payload.Name
   199  			fmt.Fprint(w, fnName, "\t", trigger.Name, "\t", trigger.ID, "\t", trigger.Type, "\t", trigger.Source, "\t", endpoint, "\n")
   200  		}
   201  	}
   202  	w.Flush()
   203  	return nil
   204  }
   205  
   206  func (t *triggersCmd) update(c *cli.Context) error {
   207  	appName := c.Args().Get(0)
   208  	fnName := c.Args().Get(1)
   209  	triggerName := c.Args().Get(2)
   210  
   211  	trigger, err := GetTrigger(t.client, appName, fnName, triggerName)
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	WithFlags(c, trigger)
   217  
   218  	err = PutTrigger(t.client, trigger)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	fmt.Println(appName, fnName, triggerName, "updated")
   224  	return nil
   225  }
   226  
   227  // PutTrigger updates the provided trigger with new values
   228  func PutTrigger(t *clientv2.Fn, trigger *models.Trigger) error {
   229  	_, err := t.Triggers.UpdateTrigger(&apiTriggers.UpdateTriggerParams{
   230  		Context:   context.Background(),
   231  		TriggerID: trigger.ID,
   232  		Body:      trigger,
   233  	})
   234  
   235  	if err != nil {
   236  		switch e := err.(type) {
   237  		case *apiTriggers.UpdateTriggerBadRequest:
   238  			return fmt.Errorf("%s", e.Payload.Message)
   239  		default:
   240  			return err
   241  		}
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func (t *triggersCmd) inspect(c *cli.Context) error {
   248  	appName := c.Args().Get(0)
   249  	fnName := c.Args().Get(1)
   250  	triggerName := c.Args().Get(2)
   251  	prop := c.Args().Get(3)
   252  
   253  	trigger, err := GetTrigger(t.client, appName, fnName, triggerName)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	if c.Bool("endpoint") {
   259  		endpoint, ok := trigger.Annotations["fnproject.io/trigger/httpEndpoint"].(string)
   260  		if !ok {
   261  			return errors.New("missing or invalid http endpoint on trigger")
   262  		}
   263  		fmt.Println(endpoint)
   264  		return nil
   265  	}
   266  
   267  	enc := json.NewEncoder(os.Stdout)
   268  	enc.SetIndent("", "\t")
   269  
   270  	if prop == "" {
   271  		enc.Encode(trigger)
   272  		return nil
   273  	}
   274  
   275  	data, err := json.Marshal(trigger)
   276  	if err != nil {
   277  		return fmt.Errorf("failed to inspect %s: %s", triggerName, err)
   278  	}
   279  
   280  	var inspect map[string]interface{}
   281  	err = json.Unmarshal(data, &inspect)
   282  	if err != nil {
   283  		return fmt.Errorf("failed to inspect %s: %s", triggerName, err)
   284  	}
   285  
   286  	jq := jsonq.NewQuery(inspect)
   287  	field, err := jq.Interface(strings.Split(prop, ".")...)
   288  	if err != nil {
   289  		return errors.New("failed to inspect %s field names")
   290  	}
   291  	enc.Encode(field)
   292  
   293  	return nil
   294  }
   295  
   296  func (t *triggersCmd) delete(c *cli.Context) error {
   297  	appName := c.Args().Get(0)
   298  	fnName := c.Args().Get(1)
   299  	triggerName := c.Args().Get(2)
   300  
   301  	trigger, err := GetTrigger(t.client, appName, fnName, triggerName)
   302  	if err != nil {
   303  		return err
   304  	}
   305  
   306  	params := apiTriggers.NewDeleteTriggerParams()
   307  	params.TriggerID = trigger.ID
   308  
   309  	_, err = t.client.Triggers.DeleteTrigger(params)
   310  	if err != nil {
   311  		return err
   312  	}
   313  
   314  	fmt.Println(appName, fnName, triggerName, "deleted")
   315  	return nil
   316  }
   317  
   318  // GetTrigger looks up a trigger using the provided client by app, function and trigger name
   319  func GetTrigger(client *clientv2.Fn, appName, fnName, triggerName string) (*models.Trigger, error) {
   320  	app, err := app.GetAppByName(client, appName)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  
   325  	fn, err := fn.GetFnByName(client, app.ID, fnName)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	trigger, err := GetTriggerByName(client, app.ID, fn.ID, triggerName)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	return trigger, nil
   336  }
   337  
   338  // GetTriggerByName looks up a trigger using the provided client by app and function ID and trigger name
   339  func GetTriggerByName(client *clientv2.Fn, appID string, fnID string, triggerName string) (*models.Trigger, error) {
   340  	triggerList, err := client.Triggers.ListTriggers(&apiTriggers.ListTriggersParams{
   341  		Context: context.Background(),
   342  		AppID:   &appID,
   343  		FnID:    &fnID,
   344  		Name:    &triggerName,
   345  	})
   346  
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	if len(triggerList.Payload.Items) == 0 {
   351  		return nil, fmt.Errorf("Trigger %s not found", triggerName)
   352  	}
   353  
   354  	return triggerList.Payload.Items[0], nil
   355  }
   356  
   357  // WithFlags returns a trigger with the specified flags
   358  func WithFlags(c *cli.Context, t *models.Trigger) {
   359  	if len(c.StringSlice("annotation")) > 0 {
   360  		t.Annotations = common.ExtractAnnotations(c)
   361  	}
   362  }