From 059b3be604465902cdf1629d60da56e26c12f14b Mon Sep 17 00:00:00 2001 From: Gabriel Chaves Date: Tue, 24 Aug 2021 21:26:33 -0300 Subject: [PATCH] version v0.2.0 implemented --- .gitignore | 1 + .goreleaser.yml | 10 +++ cmd/lab01s02/main.go | 90 ++++++++++++++++++--- cmd/lab01s02/structures.go | 68 +++++++++++----- go.mod | 5 ++ go.sum | 21 +++++ internal/pkg/configuration/configuration.go | 9 ++- pkg/graphql/providers/github/client.go | 2 +- pkg/graphql/request.go | 1 + 9 files changed, 172 insertions(+), 35 deletions(-) create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 66f8fb5..e0c65ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ .vscode/ +*.csv diff --git a/.goreleaser.yml b/.goreleaser.yml index 64bc3a4..e1e91be 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -15,3 +15,13 @@ builds: - linux - darwin - windows + + - id: lab01s02 + main: ./cmd/lab01s02 + binary: bin/lab01s02 + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows diff --git a/cmd/lab01s02/main.go b/cmd/lab01s02/main.go index b7e1539..fcf3cc2 100644 --- a/cmd/lab01s02/main.go +++ b/cmd/lab01s02/main.go @@ -2,13 +2,19 @@ package main import ( "context" - "encoding/json" + "fmt" + "math" "os" + "path/filepath" + "time" "github.com/gamoch/popular_repos/internal/pkg/configuration" "github.com/gamoch/popular_repos/pkg/graphql" "github.com/gamoch/popular_repos/pkg/graphql/providers/github" "github.com/gamoch/popular_repos/pkg/logs" + + "github.com/cheggaaa/pb/v3" + "github.com/gocarina/gocsv" ) const query = `query PopularRepos($cursor: String) { @@ -23,7 +29,7 @@ const query = `query PopularRepos($cursor: String) { stargazerCount createdAt updatedAt - pullRequests(states: MERGED) { + mergedPullRequests: pullRequests(states: MERGED) { totalCount } releases { @@ -32,7 +38,7 @@ const query = `query PopularRepos($cursor: String) { primaryLanguage { name } - openIssues: issues(states: OPEN) { + issues { totalCount } closedIssues: issues(states: CLOSED) { @@ -50,16 +56,82 @@ func main() { githubClient := github.NewClient(config.Token) req := graphql.NewRequest(query) - graphQLData := new(GraphQLData) - if err := githubClient.Run(ctx, req, graphQLData); err != nil { + var nodes []GraphQLNode + var cursor *string + + barTemplate := `Getting repositories... {{counters . }}` + bar := pb.ProgressBarTemplate(barTemplate).Start(10) + + for i := 0; i < 10; i++ { + req.SetVariable("cursor", cursor) + bar.Increment() + + graphQLData := new(GraphQLData) + if err := githubClient.Run(ctx, req, graphQLData); err != nil { + logs.Error.Fatal(err) + } + + for _, node := range graphQLData.Search.Nodes { + nodes = append(nodes, node) + } + + if !graphQLData.Search.PageInfo.HasNextPage { + break + } + + cursor = &graphQLData.Search.PageInfo.EndCursor + } + bar.Finish() + + var repositories []Repository + for _, node := range nodes { + repositories = append(repositories, graphQLNodeToRepository(node)) + } + + if err := writeRepositoriesToCSV(config, repositories); err != nil { logs.Error.Fatal(err) } +} + +func graphQLNodeToRepository(node GraphQLNode) Repository { + percentageClosedIssues := 1.0 + if node.Issues.TotalCount != 0 { + percentageClosedIssues = float64(node.ClosedIssues.TotalCount) / float64(node.Issues.TotalCount) + percentageClosedIssues = math.Round(percentageClosedIssues*100) / 100 + } + + return Repository{ + NameWithOwner: node.NameWithOwner, + StargazerCount: node.StargazerCount, + Age: RepositoryTime{time.Since(node.CreatedAt).Truncate(time.Second)}, + TimeUntilLastUpdate: RepositoryTime{time.Since(node.UpdatedAt).Truncate(time.Second)}, + TotalAcceptedPullRequests: node.MergedPullRequests.TotalCount, + TotalReleases: node.Releases.TotalCount, + PrimaryLanguage: node.PrimaryLanguage.Name, + PercentageClosedIssues: percentageClosedIssues, + } +} + +func writeRepositoriesToCSV(config *configuration.Config, repositories []Repository) error { + filePath := "repositories.csv" + if config.File != "" { + filePath = config.File + + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + return err + } + } - graphqlJSON, err := json.MarshalIndent(graphQLData, "", " ") + file, err := os.Create(filePath) if err != nil { - logs.Error.Fatal(err) + return err } - graphqlJSON = append(graphqlJSON, '\n') - os.Stdout.Write(graphqlJSON) + if err = gocsv.MarshalFile(&repositories, file); err != nil { + return err + } + + fmt.Println("Repositories saved at " + file.Name()) + + return nil } diff --git a/cmd/lab01s02/structures.go b/cmd/lab01s02/structures.go index 0dae944..7e0e858 100644 --- a/cmd/lab01s02/structures.go +++ b/cmd/lab01s02/structures.go @@ -1,6 +1,8 @@ package main -import "time" +import ( + "time" +) type GraphQLData struct { Search struct { @@ -8,26 +10,48 @@ type GraphQLData struct { HasNextPage bool `json:"hasNextPage"` EndCursor string `json:"endCursor"` } `json:"pageInfo"` - Nodes []struct { - NameWithOwner string `json:"nameWithOwner"` - StargazerCount int `json:"stargazerCount"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - PullRequests struct { - TotalCount int `json:"totalCount"` - } `json:"pullRequests"` - Releases struct { - TotalCount int `json:"totalCount"` - } `json:"releases"` - PrimaryLanguage *struct { - Name string `json:"name"` - } `json:"primaryLanguage"` - OpenIssues struct { - TotalCount int `json:"totalCount"` - } `json:"openIssues"` - ClosedIssues struct { - TotalCount int `json:"totalCount"` - } `json:"closedIssues"` - } `json:"nodes"` + Nodes []GraphQLNode `json:"nodes"` } `json:"search"` } + +type GraphQLNode struct { + NameWithOwner string `json:"nameWithOwner"` + StargazerCount int `json:"stargazerCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + MergedPullRequests struct { + TotalCount int `json:"totalCount"` + } `json:"mergedPullRequests"` + Releases struct { + TotalCount int `json:"totalCount"` + } `json:"releases"` + PrimaryLanguage struct { + Name string `json:"name"` + } `json:"primaryLanguage"` + Issues struct { + TotalCount int `json:"totalCount"` + } `json:"issues"` + ClosedIssues struct { + TotalCount int `json:"totalCount"` + } `json:"closedIssues"` +} + +type Repository struct { + NameWithOwner string `csv:"nameWithOwner"` + StargazerCount int `csv:"stargazerCount"` + Age RepositoryTime `csv:"age"` + TimeUntilLastUpdate RepositoryTime `csv:"timeUntilLastUpdate"` + TotalAcceptedPullRequests int `csv:"totalAcceptedPullRequests"` + TotalReleases int `csv:"totalReleases"` + PrimaryLanguage string `csv:"primaryLanguage"` + PercentageClosedIssues float64 `csv:"percentageClosedIssues"` +} + +type RepositoryTime struct { + time.Duration +} + +func (t *RepositoryTime) UnmarshalCSV(value string) (err error) { + t.Duration, err = time.ParseDuration(value) + return err +} diff --git a/go.mod b/go.mod index 29970a5..a4cb785 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/gamoch/popular_repos go 1.16 + +require ( + github.com/cheggaaa/pb/v3 v3.0.8 + github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..008e93f --- /dev/null +++ b/go.sum @@ -0,0 +1,21 @@ +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= +github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8 h1:hp1oqdzmv37vPLYFGjuM/RmUgUMfD9vQfMszc54l55Y= +github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/pkg/configuration/configuration.go b/internal/pkg/configuration/configuration.go index 497df74..34c121a 100644 --- a/internal/pkg/configuration/configuration.go +++ b/internal/pkg/configuration/configuration.go @@ -5,15 +5,18 @@ import ( "os" ) -type config struct { +type Config struct { Token string + File string } -func Get() *config { +func Get() *Config { token := flag.String("token", os.Getenv("GITHUB_TOKEN"), "GitHub Token (env: GITHUB_TOKEN)") + file := flag.String("file", os.Getenv("CSV_FILE"), "CSV File Location (env: CSV_FILE)") flag.Parse() - return &config{ + return &Config{ Token: *token, + File: *file, } } diff --git a/pkg/graphql/providers/github/client.go b/pkg/graphql/providers/github/client.go index 054dffe..af1020f 100644 --- a/pkg/graphql/providers/github/client.go +++ b/pkg/graphql/providers/github/client.go @@ -29,7 +29,7 @@ func NewClient(token string, options ...graphql.ClientOption) *client { func (c *client) Run(ctx context.Context, req *graphql.Request, res interface{}) error { if req != nil { - req.Header.Add("Authorization", "bearer "+c.token) + req.Header.Set("Authorization", "bearer "+c.token) } err := c.client.Run(ctx, req, res) diff --git a/pkg/graphql/request.go b/pkg/graphql/request.go index 8fcdd23..165dc3a 100644 --- a/pkg/graphql/request.go +++ b/pkg/graphql/request.go @@ -38,6 +38,7 @@ func NewRequest(query string, options ...RequestOption) *Request { func WithHTTPHeader(httpHeader http.Header) RequestOption { return func(request *Request) { for key, values := range httpHeader { + request.Header.Del(key) for _, value := range values { request.Header.Add(key, value) }