From 88dc545477a1937690fa4b5258c07a7ba71a5ad6 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Thu, 7 Jul 2016 01:25:09 +0200 Subject: [PATCH] add GitHub downloads integration --- metric.go | 17 ++++++++ metric_test.go | 20 +++++++++ service_github.go | 108 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 metric.go create mode 100644 metric_test.go diff --git a/metric.go b/metric.go new file mode 100644 index 0000000..b677602 --- /dev/null +++ b/metric.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "math" +) + +func metricFormat(in int64) string { + siUnits := []string{"k", "M", "G", "T", "P", "E"} + for i := len(siUnits) - 1; i >= 0; i-- { + p := int64(math.Pow(1000, float64(i+1))) + if in >= p { + return fmt.Sprintf("%d%s", in/p, siUnits[i]) + } + } + return fmt.Sprintf("%d", in) +} diff --git a/metric_test.go b/metric_test.go new file mode 100644 index 0000000..7fcf859 --- /dev/null +++ b/metric_test.go @@ -0,0 +1,20 @@ +package main + +import "testing" + +func TestMetricFormat(t *testing.T) { + cases := map[int64]string{ + 1000: "1k", + 1234: "1k", + 10023: "10k", + 100000: "100k", + 1000000: "1M", + 1023555: "1M", + 6012355: "6M", + } + for v, r := range cases { + if cr := metricFormat(v); cr != r { + t.Errorf("Metric format of number %d did not match '%s': '%s'", v, r, cr) + } + } +} diff --git a/service_github.go b/service_github.go index 383b2ea..52fb2fb 100644 --- a/service_github.go +++ b/service_github.go @@ -16,6 +16,16 @@ func init() { registerServiceHandler("github", githubServiceHandler{}) } +type githubRelease struct { + TagName string `json:"tag_name"` + Assets []githubAsset `json:"assets"` +} + +type githubAsset struct { + Name string `json:"name"` + Downloads int64 `json:"download_count"` +} + type githubServiceHandler struct{} func (g githubServiceHandler) GetDocumentation() serviceHandlerDocumentationList { @@ -35,6 +45,21 @@ func (g githubServiceHandler) GetDocumentation() serviceHandlerDocumentationList DemoPath: "/github/latest-release/lastpass/lastpass-cli", Arguments: []string{"latest-release", "", ""}, }, + { + ServiceName: "GitHub downloads by repo", + DemoPath: "/github/downloads/atom/atom", + Arguments: []string{"downloads", "", ""}, + }, + { + ServiceName: "GitHub downloads by release", + DemoPath: "/github/downloads/atom/atom/latest", + Arguments: []string{"downloads", "", "", ""}, + }, + { + ServiceName: "GitHub downloads by release and asset", + DemoPath: "/github/downloads/atom/atom/v1.8.0/atom-amd64.deb", + Arguments: []string{"downloads", "", "", "", ""}, + }, } } @@ -51,6 +76,8 @@ func (g githubServiceHandler) Handle(ctx context.Context, params []string) (titl title, text, color, err = g.handleLatestTag(ctx, params[1:]) case "latest-release": title, text, color, err = g.handleLatestRelease(ctx, params[1:]) + case "downloads": + title, text, color, err = g.handleDownloads(ctx, params[1:]) default: err = errors.New("An unknown service command was called") } @@ -58,15 +85,90 @@ func (g githubServiceHandler) Handle(ctx context.Context, params []string) (titl return } +func (g githubServiceHandler) handleDownloads(ctx context.Context, params []string) (title, text, color string, err error) { + switch len(params) { + case 2: + title, text, color, err = g.handleRepoDownloads(ctx, params) + case 3: + params = append(params, "total") + fallthrough + case 4: + title, text, color, err = g.handleReleaseDownloads(ctx, params) + default: + err = errors.New("Unsupported number of arguments") + } + return +} + +func (g githubServiceHandler) handleReleaseDownloads(ctx context.Context, params []string) (title, text, color string, err error) { + path := strings.Join([]string{"repos", params[0], params[1], "releases", "tags", params[2]}, "/") + if params[2] == "latest" { + path = strings.Join([]string{"repos", params[0], params[1], "releases", params[2]}, "/") + } + + text, err = cacheStore.Get("github_release_downloads", path) + + if err != nil { + r := githubRelease{} + + if err = g.fetchAPI(ctx, path, nil, &r); err != nil { + return + } + + var sum int64 + + for _, rel := range r.Assets { + if params[3] == "total" || rel.Name == params[3] { + sum = sum + rel.Downloads + } + } + + text = metricFormat(sum) + cacheStore.Set("github_release_downloads", path, text, 10*time.Minute) + } + + title = "downloads" + color = "brightgreen" + return +} + +func (g githubServiceHandler) handleRepoDownloads(ctx context.Context, params []string) (title, text, color string, err error) { + path := strings.Join([]string{"repos", params[0], params[1], "releases"}, "/") + + text, err = cacheStore.Get("github_repo_downloads", path) + + if err != nil { + r := []githubRelease{} + + // TODO: This does not respect pagination! + if err = g.fetchAPI(ctx, path, nil, &r); err != nil { + return + } + + var sum int64 + + for _, rel := range r { + for _, rea := range rel.Assets { + sum = sum + rea.Downloads + } + } + + text = metricFormat(sum) + cacheStore.Set("github_repo_downloads", path, text, 10*time.Minute) + } + + title = "downloads" + color = "brightgreen" + return +} + func (g githubServiceHandler) handleLatestRelease(ctx context.Context, params []string) (title, text, color string, err error) { path := strings.Join([]string{"repos", params[0], params[1], "releases", "latest"}, "/") text, err = cacheStore.Get("github_latest_release", path) if err != nil { - r := struct { - TagName string `json:"tag_name"` - }{} + r := githubRelease{} if err = g.fetchAPI(ctx, path, nil, &r); err != nil { return