<commit_before>package command

import (
	"flag"
	"fmt"
)

const (
	descCreateProjectId   = "Project ID this trigger belongs to (defaults to active project)."
	descCreateTriggerName = "Name of the new trigger."
	descCreateDataExpiry  = "Time (in milliseconds) after which data is considered too old to fire trigger (0 = never too old)."
	descCreateMinDelay    = "Minimum time (in milliseconds) between successive trigger firings (used to rate limit trigger events)."
)

// NewTriggersCommand returns the base 'trigger' command.
func NewTriggersCommand(ctx *Context) *Command {
	cmd := &Command{
		Name:  "trigger",
		Usage: "Commands for managing triggers.",
		SubCommands: Mux{
			"create": newCreateTriggerCommand(ctx),
		},
	}
	cmd.NewFlagSet("iobeam trigger")

	return cmd
}

//
// Common data structures used for all kinds of triggers.
//

// triggerData is the main meta data for all triggers.
type triggerData struct {
	TriggerId   uint64 `json:"trigger_id",omitempty`
	ProjectId   uint64 `json:"project_id"`
	TriggerName string `json:"name"`
	DataExpiry  uint64 `json:"data_expiry",omitempty`
}

func (d *triggerData) isTriggerMetaValid() bool {
	return d.ProjectId > 0 && len(d.TriggerName) > 0 && d.DataExpiry >= 0
}

// triggerAction is the data for a trigger action
type triggerAction struct {
	Type     string      `json:"type"`
	MinDelay uint64      `json:"min_delay"`
	Args     interface{} `json:"args"`
}

// fullTrigger is the data structure used when sending/receiving a trigger.
type fullTrigger struct {
	triggerData
	Actions []triggerAction `json:"actions"`
}

func newCreateTriggerCommand(ctx *Context) *Command {
	cmd := &Command{
		Name:  "create",
		Usage: "Commands for adding new triggers",
		SubCommands: Mux{
			"http": newHTTPTriggerCommand(ctx),
			"mqtt": newMQTTTriggerCommand(ctx),
			"sms":  newSMSTriggerCommand(ctx),
		},
	}
	cmd.NewFlagSet("config")

	return cmd
}

// newConfig generates and sends a new trigger configuration given a body
// that is pre-made by each individual handler (http, mqtt, etc).
func newConfig(body *fullTrigger, c *Command, ctx *Context) error {
	_, err := ctx.Client.Post(c.ApiPath).Expect(201).
		ProjectToken(ctx.Profile, body.ProjectId).<commit_msg>command: 'iobeam trigger' learns to list, get, and delete triggers.<commit_after>package command

import (
	"flag"
	"fmt"
	"strconv"

	"github.com/iobeam/iobeam/client"
)

const (
	descCreateProjectId   = "Project ID this trigger belongs to (defaults to active project)."
	descCreateTriggerName = "Name of the new trigger."
	descCreateDataExpiry  = "Time (in milliseconds) after which data is considered too old to fire trigger (0 = never too old)."
	descCreateMinDelay    = "Minimum time (in milliseconds) between successive trigger firings (used to rate limit trigger events)."
)

// NewTriggersCommand returns the base 'trigger' command.
func NewTriggersCommand(ctx *Context) *Command {
	cmd := &Command{
		Name:  "trigger",
		Usage: "Commands for managing triggers.",
		SubCommands: Mux{
			"create": newCreateTriggerCommand(ctx),
			"delete": newDeleteTriggerCommand(ctx),
			"get":    newGetTriggerCommand(ctx),
			"list":   newListTriggersCommand(ctx),
		},
	}
	cmd.NewFlagSet("iobeam trigger")

	return cmd
}

//
// Common data structures used for all kinds of triggers.
//

// triggerData is the main meta data for all triggers.
type triggerData struct {
	TriggerId   uint64 `json:"trigger_id",omitempty`
	ProjectId   uint64 `json:"project_id"`
	TriggerName string `json:"name"`
	DataExpiry  uint64 `json:"data_expiry",omitempty`
}

func (d *triggerData) isTriggerMetaValid() bool {
	return d.ProjectId > 0 && len(d.TriggerName) > 0 && d.DataExpiry >= 0
}

// triggerAction is the data for a trigger action
type triggerAction struct {
	Type     string      `json:"type"`
	MinDelay uint64      `json:"min_delay"`
	Args     interface{} `json:"args"`
}

// fullTrigger is the data structure used when sending/receiving a trigger.
type fullTrigger struct {
	triggerData
	Actions []triggerAction `json:"actions"`
}

func (t *fullTrigger) Print() {
	fmt.Println("Trigger ID  :", t.TriggerId)
	fmt.Println("Trigger name:", t.TriggerName)
	fmt.Println("Project ID  :", t.ProjectId)
	fmt.Println("Data expiry :", t.DataExpiry)
	fmt.Println("Actions:")
	i := 1
	for _, a := range t.Actions {
		if i != 1 {
			fmt.Println()
		}
		fmt.Printf("  %d) Action type: %s\n", i, a.Type)
		fmt.Println("     Min delay  :", a.MinDelay)
		fmt.Printf("     Args: %v\n", a.Args)
	}
	fmt.Println()
}

// List command data and functions

type triggerListArgs struct {
	projectId uint64
}

func (a *triggerListArgs) IsValid() bool {
	return a.projectId > 0
}

func newListTriggersCommand(ctx *Context) *Command {
	a := new(triggerListArgs)
	cmd := &Command{
		Name:    "list",
		ApiPath: "/v1/triggers",
		Usage:   "Get all triggers for a project",
		Data:    a,
		Action:  getAllTriggers,
	}

	flags := cmd.NewFlagSet("list")
	flags.Uint64Var(&a.projectId, "projectId", ctx.Profile.ActiveProject, "Project ID to get triggers from.")

	return cmd
}

func getAllTriggers(c *Command, ctx *Context) error {
	args := c.Data.(*triggerListArgs)
	type triggersResult struct {
		Triggers []fullTrigger `json:"results"`
	}

	_, err := ctx.Client.Get(c.ApiPath).Expect(200).
		ProjectToken(ctx.Profile, args.projectId).
		ResponseBody(new(triggersResult)).
		ResponseBodyHandler(func(resp interface{}) error {
		results := resp.(*triggersResult)
		for _, t := range results.Triggers {
			t.Print()
		}
		return nil
	}).Execute()

	return err
}

// Single get data and functions

type triggerGetArgs struct {
	projectId   uint64
	triggerId   uint64
	triggerName string
}

func (a *triggerGetArgs) IsValid() bool {
	return a.projectId > 0 && (a.triggerId > 0 || len(a.triggerName) > 0)
}

func newGetTriggerCommand(ctx *Context) *Command {
	a := new(triggerGetArgs)
	cmd := &Command{
		Name:    "get",
		ApiPath: "/v1/triggers",
		Usage:   "Get trigger matching a name or id",
		Data:    a,
		Action:  getTrigger,
	}

	flags := cmd.NewFlagSet("get")
	flags.Uint64Var(&a.projectId, "projectId", ctx.Profile.ActiveProject, "Project ID to get trigger from.")
	flags.Uint64Var(&a.triggerId, "id", 0, "Trigger ID to get (either this or -name must be set).")
	flags.StringVar(&a.triggerName, "name", "", "Trigger name to get (either this or -id must be set).")

	return cmd
}

func getTrigger(c *Command, ctx *Context) error {
	args := c.Data.(*triggerGetArgs)
	var req *client.Request
	if args.triggerId > 0 {
		req = ctx.Client.Get(c.ApiPath + "/" + strconv.FormatUint(args.triggerId, 10))
	} else {
		req = ctx.Client.Get(c.ApiPath).Param("name", args.triggerName)
	}

	_, err := req.Expect(200).
		ProjectToken(ctx.Profile, args.projectId).
		ResponseBody(new(fullTrigger)).
		ResponseBodyHandler(func(resp interface{}) error {
		t := resp.(*fullTrigger)
		t.Print()
		return nil
	}).Execute()

	return err
}

// Delete data and functions

type triggerDeleteArgs struct {
	projectId   uint64
	triggerId   uint64
	triggerName string
}

func (a *triggerDeleteArgs) IsValid() bool {
	return a.projectId > 0 && (a.triggerId > 0 || len(a.triggerName) > 0)
}

func newDeleteTriggerCommand(ctx *Context) *Command {
	a := new(triggerDeleteArgs)
	cmd := &Command{
		Name:    "delete",
		ApiPath: "/v1/triggers",
		Usage:   "Delete trigger by id",
		Data:    a,
		Action:  deleteTrigger,
	}

	flags := cmd.NewFlagSet("delete")
	flags.Uint64Var(&a.projectId, "projectId", ctx.Profile.ActiveProject, "Project ID to delete trigger from.")
	flags.Uint64Var(&a.triggerId, "id", 0, "Trigger ID to delete.")
	// TODO: Support delete by name eventually
	//flags.StringVar(&a.triggerName, "name", "", "Trigger name to get (either this or -id must be set).")

	return cmd
}

func deleteTrigger(c *Command, ctx *Context) error {
	args := c.Data.(*triggerDeleteArgs)
	var req *client.Request
	if args.triggerId > 0 {
		req = ctx.Client.Delete(c.ApiPath + "/" + strconv.FormatUint(args.triggerId, 10))
	} else {
		req = ctx.Client.Delete(c.ApiPath).Param("name", args.triggerName)
	}

	_, err := req.Expect(204).
		ProjectToken(ctx.Profile, args.projectId).
		Execute()

	if err == nil {
		fmt.Println("Device successfully deleted")
	}

	return err
}

// Create data and functions

func newCreateTriggerCommand(ctx *Context) *Command {
	cmd := &Command{
		Name:  "create",
		Usage: "Commands for adding new triggers",
		SubCommands: Mux{
			"http": newHTTPTriggerCommand(ctx),
			"mqtt": newMQTTTriggerCommand(ctx),
			"sms":  newSMSTriggerCommand(ctx),
		},
	}
	cmd.NewFlagSet("config")

	return cmd
}

// newConfig generates and sends a new trigger configuration given a body
// that is pre-made by each individual handler (http, mqtt, etc).
func newConfig(body *fullTrigger, c *Command, ctx *Context) error {
	_, err := ctx.Client.Post(c.ApiPath).Expect(201).
		ProjectToken(ctx.Profile, body.ProjectId).
{'fraction_non_alphanumeric': 0.13053770713561041, 'fraction_numerical': 0.008905422162101228, 'mean_word_length': 4.1671520093185785, 'pattern_counts': {'":': 11, '<': 3, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4582686', 'n_tokens_mistral': 3072, 'n_tokens_neox': 2896, 'n_words': 845}