From 67cad9bccadcc83c12081224416509db948c4a4b Mon Sep 17 00:00:00 2001 From: Tom Curtis <105217023+dinosaursrarr@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:35:45 +0100 Subject: [PATCH] Add commands for managing apps on device (#667) * Add command for listing apps installed on a given tidbyt Example: ``` $ ./pixlet list coarsely-sunny-refreshed-blowfish-f9f boris-bikes MKBGQqUqZfRIAvQDADC9 tartan EpxRBkyVx3XgOpXJrGlK tube-status tVGDbkTLIpeBGMEhNUCM amazing IxOnNFhmbyr5H3BjUuPp tube PE4KU0h3k9Jit7Dfj7GS busy-tube UAZrgEqv58XV27dIXGbW tube 2RTeEpncRe9T7T5EtCfS busy-tube vEGu50hl59fpxQYaiNUE tube d0eHVa3g1NaTDz7EG91l tube WxH2uHMmME7CT6KBvCUJ tube EdXutAKiSGryn9W0DJ7O busy-tube XoYkwjDbyOc7yoSpPEGN mind-the-gap mTbYjHAtBwHC2GMICacF london-bus-stop engEBag2Nini9nm7Fwet london-bus-stop UUQUbjEUI5W1Uh0KqaGg london-bus-stop FcUt1ZgEYvlE8DIwp9WJ london-bus-stop KsF57VpF70M0610WI8zf london-bus-stop 5hFDlBKIigd1xYqAojmm sunrise-sunset cR47CGrB2yHsuTBYlf8T weather 8BxjDROVyZ2BE3Z8J1eG clock-by-henry MCWwnWVcQflVZge3usXC jwst dUxcwMJvBBNpxGepbCNH life kIs0z9DRVt80AhiQLU1g random-cats 2Tf77dm191pxSiokKP9h national-rail BxXPJYzjgeQRMcE41Icd earthquake-map zOl1GofQfeAkxohCcmZC yule-log DUDIQ1figTTK3mqlvGyd superbowl NNKsGirwYUlBdEpuAj6r ``` * Add command for deleting scripts from a Tidbyt push.go has a comment saying installation-id should be a flag. I think it makes sense as an argument here, since it's required. You can push without an installation ID but you can't delete one. Feel free to let me know if you'd prefer a flag though. This let me get rid of the broken app that I couldn't delete from my phone. So I'm happy. --- cmd/delete.go | 68 ++++++++++++++++++++++++++++++++++++++ cmd/list.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 2 ++ 3 files changed, 160 insertions(+) create mode 100644 cmd/delete.go create mode 100644 cmd/list.go diff --git a/cmd/delete.go b/cmd/delete.go new file mode 100644 index 0000000000..272db4330c --- /dev/null +++ b/cmd/delete.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + + "github.com/spf13/cobra" +) + +const ( + TidbytAPIDelete = "https://api.tidbyt.com/v0/devices/%s/installations/%s" +) + +func init() { + DeleteCmd.Flags().StringVarP(&apiToken, "api-token", "t", "", "Tidbyt API token") +} + +var DeleteCmd = &cobra.Command{ + Use: "delete [device ID] [installation ID]", + Short: "Delete a pixlet script from a Tidbyt", + Args: cobra.MinimumNArgs(2), + RunE: delete, +} + +func delete(cmd *cobra.Command, args []string) error { + deviceID := args[0] + installationID := args[1] + + if apiToken == "" { + apiToken = os.Getenv(APITokenEnv) + } + + if apiToken == "" { + apiToken = oauthTokenFromConfig(cmd.Context()) + } + + if apiToken == "" { + return fmt.Errorf("blank Tidbyt API token (use `pixlet login`, set $%s or pass with --api-token)", APITokenEnv) + } + + client := &http.Client{} + req, err := http.NewRequest( + "DELETE", + fmt.Sprintf(TidbytAPIDelete, deviceID, installationID), + nil, + ) + if err != nil { + return fmt.Errorf("creating DELETE request: %w", err) + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", apiToken)) + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("deleting via API: %w", err) + } + + if resp.StatusCode != 200 { + fmt.Printf("Tidbyt API returned status %s\n", resp.Status) + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(string(body)) + return fmt.Errorf("Tidbyt API returned status: %s", resp.Status) + } + + return nil +} diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 0000000000..6ff9f4ad65 --- /dev/null +++ b/cmd/list.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "text/tabwriter" + + "github.com/spf13/cobra" +) + +const ( + TidbytAPIList = "https://api.tidbyt.com/v0/devices/%s/installations" +) + +type TidbytInstallationJSON struct { + Id string `json:"id"` + AppId string `json:"appID"` +} + +type TidbytInstallationListJSON struct { + Installations []TidbytInstallationJSON `json:"installations"` +} + +func init() { + ListCmd.Flags().StringVarP(&apiToken, "api-token", "t", "", "Tidbyt API token") +} + +var ListCmd = &cobra.Command{ + Use: "list [device ID]", + Short: "Lists all apps installed on a Tidbyt", + Args: cobra.MinimumNArgs(1), + RunE: listInstallations, +} + +func listInstallations(cmd *cobra.Command, args []string) error { + deviceID := args[0] + + if apiToken == "" { + apiToken = os.Getenv(APITokenEnv) + } + + if apiToken == "" { + apiToken = oauthTokenFromConfig(cmd.Context()) + } + + if apiToken == "" { + return fmt.Errorf("blank Tidbyt API token (use `pixlet login`, set $%s or pass with --api-token)", APITokenEnv) + } + + client := &http.Client{} + req, err := http.NewRequest( + "GET", + fmt.Sprintf(TidbytAPIList, deviceID), nil) + if err != nil { + return fmt.Errorf("creating GET request: %w", err) + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", apiToken)) + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("listing installations from API: %w", err) + } + + body, _ := ioutil.ReadAll(resp.Body) + if resp.StatusCode != 200 { + fmt.Printf("Tidbyt API returned status %s\n", resp.Status) + fmt.Println(string(body)) + return fmt.Errorf("Tidbyt API returned status: %s", resp.Status) + } + + var installations TidbytInstallationListJSON + err = json.Unmarshal(body, &installations) + if err != nil { + return fmt.Errorf("failed to unmarshal json: %w", err) + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 22, 8, 0, '\t', 0) + defer w.Flush() + + for _, inst := range installations.Installations { + fmt.Fprintf(w, "%s\t%s\n", inst.AppId, inst.Id) + } + + return nil +} diff --git a/main.go b/main.go index a3c8b6d003..620174151d 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,8 @@ func init() { rootCmd.AddCommand(cmd.ProfileCmd) rootCmd.AddCommand(cmd.LoginCmd) rootCmd.AddCommand(cmd.DevicesCmd) + rootCmd.AddCommand(cmd.ListCmd) + rootCmd.AddCommand(cmd.DeleteCmd) rootCmd.AddCommand(cmd.FormatCmd) rootCmd.AddCommand(cmd.LintCmd) rootCmd.AddCommand(cmd.CheckCmd)