Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
cmd/tier: add share command
Browse files Browse the repository at this point in the history
  • Loading branch information
bmizerany committed Feb 1, 2023
1 parent 3d81f73 commit 27fcaab
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
10 changes: 10 additions & 0 deletions cmd/tier/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,16 @@ Examples:
; tier clean -switchaccounts 730h # remove all switch accounts older than 30 days
; tier clean -switchaccounts 0 # remove all switch accounts
; tier clean -switchaccounts -1 # nop
`,
"share": `Usage:
tier share <filename | url | - >
Share posts the provided pricing model to the Tier Model Builder and returns a
URL for sharing, collaborating, and deploying pricing models. See
https://model.tier.run for more information on the Model Builder.
Share does not make any changes or requests to your Stripe account.
`,
}

Expand Down
63 changes: 54 additions & 9 deletions cmd/tier/tier.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -188,7 +189,7 @@ func runTier(cmd string, args []string) (err error) {
pj = fs.Arg(0)
}

f, err := fileOrStdin(ctx, pj)
f, _, err := stdinRemoteOrFile(ctx, pj)
if err != nil {
return err
}
Expand Down Expand Up @@ -457,35 +458,79 @@ func runTier(cmd string, args []string) (err error) {
return cleanAccounts(*accountAge)
}
return errUsage
case "share":
if len(args) < 1 {
return errUsage
}

f, isURL, err := stdinRemoteOrFile(ctx, args[0])
if err != nil {
return err
}
defer f.Close()

req, err := http.NewRequestWithContext(ctx, "POST", "https://model.tier.run/upload", f)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
if isURL {
req.Header.Set("Tier-Origin", args[0])
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode/100 != 2 {
io.Copy(stderr, resp.Body)
return fmt.Errorf("error returned from server: %v", resp.Status)
}
var v struct {
URL string `json:"url"`
}
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
return err
}
fmt.Fprintln(stdout, v.URL)
return nil
default:
return errUsage
}
}

func fileOrStdin(ctx context.Context, fname string) (io.ReadCloser, error) {
// fileRemoteOrStdin returns the contents of the file for fname. If fname is
// ("-") then stdin is returned. If fname is a valid URL, a request with Accept
// set to ("application/json") is sent, and the body of the request is
// returned. If fname is not stdin, a valid URL, but a valid local filename,
// the file is returned; otherwise and error is returned.
func stdinRemoteOrFile(ctx context.Context, fname string) (r io.ReadCloser, isURL bool, err error) {
if fname == "" {
return nil, errUsage
return nil, false, errUsage
}
if fname == "-" {
return io.NopCloser(stdin), nil
return io.NopCloser(stdin), false, nil
}
u, err := url.Parse(fname)
if err == nil && (u.Scheme == "http" || u.Scheme == "https") {
r, err := http.NewRequestWithContext(ctx, "GET", fname, nil)
if err != nil {
return nil, err
return nil, false, err
}
r.Header.Set("Accept", "application/json")
res, err := http.DefaultClient.Do(r)
if err != nil {
return nil, err
return nil, false, err
}
if res.StatusCode/100 != 2 {
return nil, fmt.Errorf("http error fetching pricing.json: %s", res.Status)
return nil, false, fmt.Errorf("http error fetching pricing.json: %s", res.Status)
}
return res.Body, nil
return res.Body, true, nil
}
return os.Open(fname)
f, err := os.Open(fname)
return f, false, err
}

var debugLevel, _ = strconv.Atoi(os.Getenv("TIER_DEBUG"))
Expand Down

0 comments on commit 27fcaab

Please sign in to comment.