├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── controllers └── UserController.go ├── env └── env.go ├── go.mod ├── go.sum ├── handler ├── handler.go └── handler_test.go ├── main.go ├── pkg ├── github.go └── github_test.go └── postman ├── GitHub API.postman_collection.json └── Stone Desafio API.postman_collection.json /.env: -------------------------------------------------------------------------------- 1 | APPLICATION_PORT=8080 2 | GITHUB_BASE_URL=https://api.github.com 3 | GITHUB_PERSONAL_ACCESS_TOKEN= 4 | CACHE_EXPIRATION_TIME_MINUTES=5 5 | CACHE_PURGE_TIME_MINUTES=8 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Enviroments 15 | *.env 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # IDE 21 | .idea 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine 2 | 3 | # Set necessary environmet variables needed for our image 4 | ENV GO111MODULE=on \ 5 | CGO_ENABLED=0 \ 6 | GOOS=linux \ 7 | GOARCH=amd64 8 | 9 | # Move to working directory /build 10 | WORKDIR /build 11 | 12 | # Copy and download dependency using go mod 13 | COPY .env . 14 | COPY go.mod . 15 | COPY go.sum . 16 | RUN go mod download 17 | 18 | # Copy the code into the container 19 | COPY . . 20 | 21 | # Build the application 22 | RUN go build -o main . 23 | 24 | # Move to /dist directory as the place for resulting binary folder 25 | WORKDIR /dist 26 | 27 | # Copy binary from build to main folder 28 | RUN cp /build/main . 29 | 30 | # Move .env to final workdir 31 | RUN cp /build/.env . 32 | 33 | # Export necessary port 34 | EXPOSE 8080 35 | 36 | # Command to run when starting the container 37 | CMD ["/dist/main"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stone_GitHub_API_Golang 2 | Teste prático para vaga de back-end Python / Go na Stone. 3 | 4 | 5 | `Nota 1: Utilizar a branch Release`
6 | `Nota 2: É necessário adicionar o TOKEN de acesso do GITHUB no .ENV da Aplicação.` 7 | 8 | 9 | ## 1 - Como subir a api? 10 | Após clonar o repositório, a estrutura de pastas deve ficar em algo como: 11 | 12 | `$GOPATH/src/Stone_GitHub_API_Golang` 13 | 14 | Feito isso, basta entrar na pasta raiz do projeto e executar o comando abaixo 15 | para criar a imagem da aplicação: 16 | 17 | `docker build . -t stone_backend_rennasccenth` 18 | 19 | Com nossa devida imagem criada, podemos executá-la através do comando: 20 | 21 | `docker run -d --name=rennasccenth_teste -p 8080:8080 stone_backend_rennasccenth:latest` 22 | 23 | Pronto, agora temos a aplicação rodando na porta 8080 local, então certifique-se de que essa porta esteja livre! 24 | 25 | ## 2 - E quanto aos testes?? 26 | Para executar os testes, podemos executar o seguinte comando, ainda na pasta raiz do projeto e com a aplicação rodando: 27 | 28 | `godotenv go test ./...` 29 | 30 | ## 3 - Facilitando os testes... 31 | Para facilitar os testes, deixarei a collection do postman com as requisições que foram utilizadas para validar as features juntamente com o environment usado, estão na pasta: 32 | 33 | `$GOPATH/src/Stone_GitHub_API_Golang/postman` 34 | 35 | ## Aos reviewers! 36 | Essa foi minha primeira experiência com Golang, conhecia a linguagem somente por cima e sabia que lembrava um pouco de C, então aceitei o desafio/sugestão da Lídia de fazer o teste na linguagem que eu menos tinha domínio (nesse caso era 0 mesmo uheuhuaheu). 37 | Dito isso, caso tenham algo a acrescentar ou apontar erros cometidos, sintam-se totalmente a vontade! 38 | 39 | ## O que foi feito? 40 | 41 | - [x] Dockerização 42 | - [x] Documentação 43 | - [x] Testes 44 | 45 | ### Tier 1 46 | - [x] Retornar Repositório com mais Stars. 47 | - [x] Retornar a Issue aberta que possui mais comentários. 48 | - [x] Retornar os Pull Requests abertos que ainda não foram interagidos. 49 | 50 | ### Tier 2 51 | - [x] Implementação de uma camada de cache, para as rotas do tier 1. 52 | 53 | -------------------------------------------------------------------------------- /controllers/UserController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/gorilla/mux" 6 | "github.com/rennasccenth/Stone_GitHub_API_Golang/handler" 7 | "log" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | func GetUserMostStarredRepo(w http.ResponseWriter, r *http.Request) { 13 | params := mux.Vars(r) 14 | userParam := params["user"] 15 | 16 | mostStarredRepo, _ := handler.GetUserMostStarredRepository(userParam) 17 | 18 | setDefaultHeader(w) 19 | 20 | setResponse(mostStarredRepo, w) 21 | } 22 | 23 | func GetUserMostCommentedOpenedIssue(w http.ResponseWriter, r *http.Request) { 24 | params := mux.Vars(r) 25 | user := params["user"] 26 | repository := params["repository"] 27 | 28 | mostCommentedOpenedIssue, _ := handler.GetMostCommentedIssue(user, repository) 29 | 30 | setDefaultHeader(w) 31 | 32 | setResponse(mostCommentedOpenedIssue, w) 33 | } 34 | 35 | func GetUserOpenedPullRequests(w http.ResponseWriter, r *http.Request) { 36 | params := mux.Vars(r) 37 | user := params["user"] 38 | repository := params["repository"] 39 | 40 | nonInteractedPullRequests, _ := handler.GetNonInteractedPullRequests(user, repository) 41 | 42 | setDefaultHeader(w) 43 | 44 | setResponse(nonInteractedPullRequests, w) 45 | 46 | } 47 | 48 | // setDefaultHeader set default Header of this Controller 49 | func setDefaultHeader(w http.ResponseWriter) { 50 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 51 | w.Header().Set("Date", time.Now().String()) 52 | } 53 | 54 | // setResponse set the commom response of this Controller 55 | func setResponse(data interface{}, w http.ResponseWriter) { 56 | marshalResp, _ := json.Marshal(data) 57 | 58 | _, err := w.Write(marshalResp) 59 | if err != nil { 60 | w.WriteHeader(http.StatusInternalServerError) 61 | log.Print(err) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /env/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/joho/godotenv" 5 | "log" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | // Get get the environment key at the .env file. 11 | func Get(key string) string { 12 | value := os.Getenv(key) 13 | if value != "" { 14 | return value 15 | } 16 | 17 | err := godotenv.Load(".env") 18 | 19 | if err != nil { 20 | log.Fatalf("Error loading .env file") 21 | } 22 | 23 | return os.Getenv(key) 24 | } 25 | 26 | // GetInt get the environment key as int at the .env file. 27 | func GetInt(key string) int { 28 | value := os.Getenv(key) 29 | if value != "" { 30 | i, err := strconv.Atoi(value) 31 | if err != nil { 32 | return 0 33 | } 34 | return i 35 | } 36 | 37 | err := godotenv.Load(".env") 38 | if err != nil { 39 | log.Fatalf("Error loading .env file") 40 | } 41 | 42 | i, err := strconv.Atoi(value) 43 | if err != nil { 44 | return 0 45 | } 46 | return i 47 | } 48 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rennasccenth/Stone_GitHub_API_Golang 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 7 | github.com/gorilla/mux v1.8.0 8 | github.com/joho/godotenv v1.3.0 9 | github.com/patrickmn/go-cache v2.1.0+incompatible 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs= 2 | github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= 3 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 4 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 5 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 6 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 7 | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 8 | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 9 | github.com/rennasccenth/Stone_GitHub_API_Golang v0.0.0-20210601122919-02fc1bef0bb2 h1:NBJBUDW4nz1JlTi7BCchDHQ6bZbGFlc0+HB25JbBNGY= 10 | github.com/rennasccenth/Stone_GitHub_API_Golang v0.0.0-20210601122919-02fc1bef0bb2/go.mod h1:RlQG92PzOT2DXx0UeCrBE2gM8Iy5QAsm/t5eW3xXjh8= 11 | -------------------------------------------------------------------------------- /handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "fmt" 5 | "github.com/patrickmn/go-cache" 6 | "github.com/rennasccenth/Stone_GitHub_API_Golang/pkg" 7 | "time" 8 | ) 9 | 10 | var cacheLayer = startCacheLayer() 11 | 12 | // startCacheLayer inicialize a Cache layer 13 | func startCacheLayer() *cache.Cache { 14 | c := cache.New(5*time.Minute, 10*time.Minute) 15 | return c 16 | } 17 | 18 | // GetUserMostStarredRepository tries to get the most starred pkg.Repository 19 | // on cache layer. If not found, fetches and updates the cache. 20 | func GetUserMostStarredRepository(userLogin string) (mostStarredRepo pkg.Repository, cacheKey string) { 21 | cacheKey = fmt.Sprintf("{Action_1}{%s}", userLogin) 22 | 23 | cachedResponse, found := cacheLayer.Get(cacheKey) 24 | if found { 25 | mostStarredRepo, _ = cachedResponse.(pkg.Repository) 26 | return mostStarredRepo, cacheKey 27 | } 28 | mostStarredRepo = pkg.GetUserMostStarredRepository(userLogin) 29 | cacheLayer.Set(cacheKey, mostStarredRepo, cache.DefaultExpiration) 30 | 31 | return mostStarredRepo, cacheKey 32 | } 33 | 34 | // GetMostCommentedIssue tries to get the most commented pkg.Issue 35 | // on cache layer. If not found , fetches and updates the cache 36 | func GetMostCommentedIssue(user string, repository string) (mostCommentedOpenedIssue pkg.Issue, cacheKey string) { 37 | cacheKey = fmt.Sprintf("{Action_2}{%s}{%s}", user, repository) 38 | 39 | cachedResponse, found := cacheLayer.Get(cacheKey) 40 | if found { 41 | mostCommentedOpenedIssue = cachedResponse.(pkg.Issue) 42 | return mostCommentedOpenedIssue, cacheKey 43 | } 44 | mostCommentedOpenedIssue = pkg.GetMostCommentedIssue(user, repository) 45 | cacheLayer.Set(cacheKey, mostCommentedOpenedIssue, cache.DefaultExpiration) 46 | 47 | return mostCommentedOpenedIssue, cacheKey 48 | } 49 | 50 | // GetNonInteractedPullRequests tries to get all non interacted pkg.PullRequest 51 | // of a pkg.Repository on cache layer. If not found , fetches and updates the cache. 52 | func GetNonInteractedPullRequests(user string, repository string) (nonInteractedPullRequests []pkg.PullRequest, cacheKey string) { 53 | cacheKey = fmt.Sprintf("{Action_3}{%s}{%s}", user, repository) 54 | 55 | cachedResponse, found := cacheLayer.Get(cacheKey) 56 | if found { 57 | nonInteractedPullRequests, _ = cachedResponse.([]pkg.PullRequest) 58 | return nonInteractedPullRequests, cacheKey 59 | } 60 | nonInteractedPullRequests = pkg.GetNonInteractedPullRequests(user, repository) 61 | cacheLayer.Set(cacheKey, nonInteractedPullRequests, cache.DefaultExpiration) 62 | 63 | return nonInteractedPullRequests, cacheKey 64 | } 65 | -------------------------------------------------------------------------------- /handler/handler_test.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/dchest/uniuri" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestCacheOnGetUserMostStarredRepository(t *testing.T) { 10 | mockRandomUsername := uniuri.NewLen(10) 11 | 12 | _, cacheKey := GetUserMostStarredRepository(mockRandomUsername) 13 | 14 | _, found := cacheLayer.Get(cacheKey) 15 | 16 | if reflect.DeepEqual(true, found) { 17 | t.Log("CacheOnGetUserMostStarredRepository PASSED") 18 | } else { 19 | t.Errorf("CacheOnGetUserMostStarredRepository FAILED, expected %v but got %v", 20 | true, found) 21 | } 22 | } 23 | 24 | func TestCacheGetMostCommentedIssue(t *testing.T) { 25 | mockRandomUsername := uniuri.NewLen(10) 26 | mockRandomRepository := uniuri.NewLen(10) 27 | 28 | _, cacheKey := GetMostCommentedIssue(mockRandomUsername, mockRandomRepository) 29 | 30 | _, found := cacheLayer.Get(cacheKey) 31 | 32 | if reflect.DeepEqual(true, found) { 33 | t.Log("TestCacheGetMostCommentedIssue PASSED") 34 | } else { 35 | t.Errorf("TestCacheGetMostCommentedIssue FAILED, expected %v but got %v", 36 | true, found) 37 | } 38 | } 39 | 40 | func TestCacheGetNonInteractedPullRequests(t *testing.T) { 41 | mockRandomUsername := uniuri.NewLen(10) 42 | mockRandomRepository := uniuri.NewLen(10) 43 | 44 | _, cacheKey := GetNonInteractedPullRequests(mockRandomUsername, mockRandomRepository) 45 | 46 | _, found := cacheLayer.Get(cacheKey) 47 | 48 | if reflect.DeepEqual(found, true) { 49 | t.Log("TestCacheGetNonInteractedPullRequests PASSED") 50 | } else { 51 | t.Errorf("TestCacheGetNonInteractedPullRequests FAILED, expected %v but got %v", 52 | true, found) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gorilla/mux" 6 | "github.com/rennasccenth/Stone_GitHub_API_Golang/controllers" 7 | "github.com/rennasccenth/Stone_GitHub_API_Golang/env" 8 | "log" 9 | "net/http" 10 | ) 11 | 12 | func main() { 13 | routerHandler := mux.NewRouter() 14 | 15 | routerHandler.HandleFunc("/users/{user}/popular-repository", 16 | controllers.GetUserMostStarredRepo).Methods(http.MethodGet) 17 | 18 | routerHandler.HandleFunc("/users/{user}/repositories/{repository}/popular-issue", 19 | controllers.GetUserMostCommentedOpenedIssue).Methods(http.MethodGet) 20 | 21 | routerHandler.HandleFunc("/users/{user}/repositories/{repository}/open-pull-requests", 22 | controllers.GetUserOpenedPullRequests).Methods(http.MethodGet) 23 | 24 | serveOnDefaultPort(routerHandler) 25 | } 26 | 27 | // serveOnDefaultPort serves the handler on the port 28 | // stored on .env as APPLICATION_PORT 29 | func serveOnDefaultPort(handler http.Handler) { 30 | applicationPort := env.Get("APPLICATION_PORT") 31 | if applicationPort == "" { 32 | applicationPort = "8080" 33 | } 34 | addr := fmt.Sprintf(":%s", applicationPort) 35 | log.Printf("Subindo API na porta %s.", addr) 36 | log.Fatal(http.ListenAndServe(addr, handler)) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/github.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/rennasccenth/Stone_GitHub_API_Golang/env" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "os" 12 | "time" 13 | ) 14 | 15 | type User struct { 16 | Login string `json:"login"` 17 | Id int `json:"id"` 18 | NodeId string `json:"node_id"` 19 | AvatarUrl string `json:"avatar_url"` 20 | GravatarId string `json:"gravatar_id"` 21 | Url string `json:"url"` 22 | HtmlUrl string `json:"html_url"` 23 | FollowersUrl string `json:"followers_url"` 24 | FollowingUrl string `json:"following_url"` 25 | GistsUrl string `json:"gists_url"` 26 | StarredUrl string `json:"starred_url"` 27 | SubscriptionsUrl string `json:"subscriptions_url"` 28 | OrganizationsUrl string `json:"organizations_url"` 29 | ReposUrl string `json:"repos_url"` 30 | EventsUrl string `json:"events_url"` 31 | ReceivedEventsUrl string `json:"received_events_url"` 32 | Type string `json:"type"` 33 | SiteAdmin bool `json:"site_admin"` 34 | Name string `json:"name"` 35 | Company interface{} `json:"company"` 36 | Blog string `json:"blog"` 37 | Location interface{} `json:"location"` 38 | Email interface{} `json:"email"` 39 | Hireable interface{} `json:"hireable"` 40 | Bio string `json:"bio"` 41 | TwitterUsername interface{} `json:"twitter_username"` 42 | PublicRepos int `json:"public_repos"` 43 | PublicGists int `json:"public_gists"` 44 | Followers int `json:"followers"` 45 | Following int `json:"following"` 46 | CreatedAt time.Time `json:"created_at"` 47 | UpdatedAt time.Time `json:"updated_at"` 48 | PrivateGists int `json:"private_gists"` 49 | TotalPrivateRepos int `json:"total_private_repos"` 50 | OwnedPrivateRepos int `json:"owned_private_repos"` 51 | DiskUsage int `json:"disk_usage"` 52 | Collaborators int `json:"collaborators"` 53 | TwoFactorAuthentication bool `json:"two_factor_authentication"` 54 | Plan struct { 55 | Name string `json:"name"` 56 | Space int `json:"space"` 57 | Collaborators int `json:"collaborators"` 58 | PrivateRepos int `json:"private_repos"` 59 | } `json:"plan"` 60 | } 61 | 62 | type Repository struct { 63 | Id int `json:"id"` 64 | NodeId string `json:"node_id"` 65 | Name string `json:"name"` 66 | FullName string `json:"full_name"` 67 | Private bool `json:"private"` 68 | Owner struct { 69 | Login string `json:"login"` 70 | Id int `json:"id"` 71 | NodeId string `json:"node_id"` 72 | AvatarUrl string `json:"avatar_url"` 73 | GravatarId string `json:"gravatar_id"` 74 | Url string `json:"url"` 75 | HtmlUrl string `json:"html_url"` 76 | FollowersUrl string `json:"followers_url"` 77 | FollowingUrl string `json:"following_url"` 78 | GistsUrl string `json:"gists_url"` 79 | StarredUrl string `json:"starred_url"` 80 | SubscriptionsUrl string `json:"subscriptions_url"` 81 | OrganizationsUrl string `json:"organizations_url"` 82 | ReposUrl string `json:"repos_url"` 83 | EventsUrl string `json:"events_url"` 84 | ReceivedEventsUrl string `json:"received_events_url"` 85 | Type string `json:"type"` 86 | SiteAdmin bool `json:"site_admin"` 87 | } `json:"owner"` 88 | HtmlUrl string `json:"html_url"` 89 | Description *string `json:"description"` 90 | Fork bool `json:"fork"` 91 | Url string `json:"url"` 92 | ForksUrl string `json:"forks_url"` 93 | KeysUrl string `json:"keys_url"` 94 | CollaboratorsUrl string `json:"collaborators_url"` 95 | TeamsUrl string `json:"teams_url"` 96 | HooksUrl string `json:"hooks_url"` 97 | IssueEventsUrl string `json:"issue_events_url"` 98 | EventsUrl string `json:"events_url"` 99 | AssigneesUrl string `json:"assignees_url"` 100 | BranchesUrl string `json:"branches_url"` 101 | TagsUrl string `json:"tags_url"` 102 | BlobsUrl string `json:"blobs_url"` 103 | GitTagsUrl string `json:"git_tags_url"` 104 | GitRefsUrl string `json:"git_refs_url"` 105 | TreesUrl string `json:"trees_url"` 106 | StatusesUrl string `json:"statuses_url"` 107 | LanguagesUrl string `json:"languages_url"` 108 | StargazersUrl string `json:"stargazers_url"` 109 | ContributorsUrl string `json:"contributors_url"` 110 | SubscribersUrl string `json:"subscribers_url"` 111 | SubscriptionUrl string `json:"subscription_url"` 112 | CommitsUrl string `json:"commits_url"` 113 | GitCommitsUrl string `json:"git_commits_url"` 114 | CommentsUrl string `json:"comments_url"` 115 | IssueCommentUrl string `json:"issue_comment_url"` 116 | ContentsUrl string `json:"contents_url"` 117 | CompareUrl string `json:"compare_url"` 118 | MergesUrl string `json:"merges_url"` 119 | ArchiveUrl string `json:"archive_url"` 120 | DownloadsUrl string `json:"downloads_url"` 121 | IssuesUrl string `json:"issues_url"` 122 | PullsUrl string `json:"pulls_url"` 123 | MilestonesUrl string `json:"milestones_url"` 124 | NotificationsUrl string `json:"notifications_url"` 125 | LabelsUrl string `json:"labels_url"` 126 | ReleasesUrl string `json:"releases_url"` 127 | DeploymentsUrl string `json:"deployments_url"` 128 | CreatedAt time.Time `json:"created_at"` 129 | UpdatedAt time.Time `json:"updated_at"` 130 | PushedAt time.Time `json:"pushed_at"` 131 | GitUrl string `json:"git_url"` 132 | SshUrl string `json:"ssh_url"` 133 | CloneUrl string `json:"clone_url"` 134 | SvnUrl string `json:"svn_url"` 135 | Homepage interface{} `json:"homepage"` 136 | Size int `json:"size"` 137 | StargazersCount int `json:"stargazers_count"` 138 | WatchersCount int `json:"watchers_count"` 139 | Language string `json:"language"` 140 | HasIssues bool `json:"has_issues"` 141 | HasProjects bool `json:"has_projects"` 142 | HasDownloads bool `json:"has_downloads"` 143 | HasWiki bool `json:"has_wiki"` 144 | HasPages bool `json:"has_pages"` 145 | ForksCount int `json:"forks_count"` 146 | MirrorUrl interface{} `json:"mirror_url"` 147 | Archived bool `json:"archived"` 148 | Disabled bool `json:"disabled"` 149 | OpenIssuesCount int `json:"open_issues_count"` 150 | License interface{} `json:"license"` 151 | Forks int `json:"forks"` 152 | OpenIssues int `json:"open_issues"` 153 | Watchers int `json:"watchers"` 154 | DefaultBranch string `json:"default_branch"` 155 | Permissions struct { 156 | Admin bool `json:"admin"` 157 | Push bool `json:"push"` 158 | Pull bool `json:"pull"` 159 | } `json:"permissions"` 160 | url string 161 | stars int 162 | } 163 | 164 | type Issue struct { 165 | Url string `json:"url"` 166 | RepositoryUrl string `json:"repository_url"` 167 | LabelsUrl string `json:"labels_url"` 168 | CommentsUrl string `json:"comments_url"` 169 | EventsUrl string `json:"events_url"` 170 | HtmlUrl string `json:"html_url"` 171 | Id int `json:"id"` 172 | NodeId string `json:"node_id"` 173 | Number int `json:"number"` 174 | Title string `json:"title"` 175 | User struct { 176 | Login string `json:"login"` 177 | Id int `json:"id"` 178 | NodeId string `json:"node_id"` 179 | AvatarUrl string `json:"avatar_url"` 180 | GravatarId string `json:"gravatar_id"` 181 | Url string `json:"url"` 182 | HtmlUrl string `json:"html_url"` 183 | FollowersUrl string `json:"followers_url"` 184 | FollowingUrl string `json:"following_url"` 185 | GistsUrl string `json:"gists_url"` 186 | StarredUrl string `json:"starred_url"` 187 | SubscriptionsUrl string `json:"subscriptions_url"` 188 | OrganizationsUrl string `json:"organizations_url"` 189 | ReposUrl string `json:"repos_url"` 190 | EventsUrl string `json:"events_url"` 191 | ReceivedEventsUrl string `json:"received_events_url"` 192 | Type string `json:"type"` 193 | SiteAdmin bool `json:"site_admin"` 194 | } `json:"user"` 195 | Labels []interface{} `json:"labels"` 196 | State string `json:"state"` 197 | Locked bool `json:"locked"` 198 | Assignee interface{} `json:"assignee"` 199 | Assignees []interface{} `json:"assignees"` 200 | Milestone interface{} `json:"milestone"` 201 | Comments int `json:"comments"` 202 | CreatedAt time.Time `json:"created_at"` 203 | UpdatedAt time.Time `json:"updated_at"` 204 | ClosedAt interface{} `json:"closed_at"` 205 | AuthorAssociation string `json:"author_association"` 206 | ActiveLockReason interface{} `json:"active_lock_reason"` 207 | Body string `json:"body"` 208 | PerformedViaGithubApp interface{} `json:"performed_via_github_app"` 209 | } 210 | 211 | type PullRequest struct { 212 | Url string `json:"url"` 213 | Id int `json:"id"` 214 | NodeId string `json:"node_id"` 215 | HtmlUrl string `json:"html_url"` 216 | DiffUrl string `json:"diff_url"` 217 | PatchUrl string `json:"patch_url"` 218 | IssueUrl string `json:"issue_url"` 219 | Number int `json:"number"` 220 | State string `json:"state"` 221 | Locked bool `json:"locked"` 222 | Title string `json:"title"` 223 | User struct { 224 | Login string `json:"login"` 225 | Id int `json:"id"` 226 | NodeId string `json:"node_id"` 227 | AvatarUrl string `json:"avatar_url"` 228 | GravatarId string `json:"gravatar_id"` 229 | Url string `json:"url"` 230 | HtmlUrl string `json:"html_url"` 231 | FollowersUrl string `json:"followers_url"` 232 | FollowingUrl string `json:"following_url"` 233 | GistsUrl string `json:"gists_url"` 234 | StarredUrl string `json:"starred_url"` 235 | SubscriptionsUrl string `json:"subscriptions_url"` 236 | OrganizationsUrl string `json:"organizations_url"` 237 | ReposUrl string `json:"repos_url"` 238 | EventsUrl string `json:"events_url"` 239 | ReceivedEventsUrl string `json:"received_events_url"` 240 | Type string `json:"type"` 241 | SiteAdmin bool `json:"site_admin"` 242 | } `json:"user"` 243 | Body string `json:"body"` 244 | CreatedAt time.Time `json:"created_at"` 245 | UpdatedAt time.Time `json:"updated_at"` 246 | ClosedAt interface{} `json:"closed_at"` 247 | MergedAt interface{} `json:"merged_at"` 248 | MergeCommitSha interface{} `json:"merge_commit_sha"` 249 | Assignee interface{} `json:"assignee"` 250 | Assignees []interface{} `json:"assignees"` 251 | RequestedReviewers []interface{} `json:"requested_reviewers"` 252 | RequestedTeams []interface{} `json:"requested_teams"` 253 | Labels []interface{} `json:"labels"` 254 | Milestone interface{} `json:"milestone"` 255 | Draft bool `json:"draft"` 256 | CommitsUrl string `json:"commits_url"` 257 | ReviewCommentsUrl string `json:"review_comments_url"` 258 | ReviewCommentUrl string `json:"review_comment_url"` 259 | CommentsUrl string `json:"comments_url"` 260 | StatusesUrl string `json:"statuses_url"` 261 | Head struct { 262 | Label string `json:"label"` 263 | Ref string `json:"ref"` 264 | Sha string `json:"sha"` 265 | User struct { 266 | Login string `json:"login"` 267 | Id int `json:"id"` 268 | NodeId string `json:"node_id"` 269 | AvatarUrl string `json:"avatar_url"` 270 | GravatarId string `json:"gravatar_id"` 271 | Url string `json:"url"` 272 | HtmlUrl string `json:"html_url"` 273 | FollowersUrl string `json:"followers_url"` 274 | FollowingUrl string `json:"following_url"` 275 | GistsUrl string `json:"gists_url"` 276 | StarredUrl string `json:"starred_url"` 277 | SubscriptionsUrl string `json:"subscriptions_url"` 278 | OrganizationsUrl string `json:"organizations_url"` 279 | ReposUrl string `json:"repos_url"` 280 | EventsUrl string `json:"events_url"` 281 | ReceivedEventsUrl string `json:"received_events_url"` 282 | Type string `json:"type"` 283 | SiteAdmin bool `json:"site_admin"` 284 | } `json:"user"` 285 | Repo struct { 286 | Id int `json:"id"` 287 | NodeId string `json:"node_id"` 288 | Name string `json:"name"` 289 | FullName string `json:"full_name"` 290 | Private bool `json:"private"` 291 | Owner struct { 292 | Login string `json:"login"` 293 | Id int `json:"id"` 294 | NodeId string `json:"node_id"` 295 | AvatarUrl string `json:"avatar_url"` 296 | GravatarId string `json:"gravatar_id"` 297 | Url string `json:"url"` 298 | HtmlUrl string `json:"html_url"` 299 | FollowersUrl string `json:"followers_url"` 300 | FollowingUrl string `json:"following_url"` 301 | GistsUrl string `json:"gists_url"` 302 | StarredUrl string `json:"starred_url"` 303 | SubscriptionsUrl string `json:"subscriptions_url"` 304 | OrganizationsUrl string `json:"organizations_url"` 305 | ReposUrl string `json:"repos_url"` 306 | EventsUrl string `json:"events_url"` 307 | ReceivedEventsUrl string `json:"received_events_url"` 308 | Type string `json:"type"` 309 | SiteAdmin bool `json:"site_admin"` 310 | } `json:"owner"` 311 | HtmlUrl string `json:"html_url"` 312 | Description string `json:"description"` 313 | Fork bool `json:"fork"` 314 | Url string `json:"url"` 315 | ForksUrl string `json:"forks_url"` 316 | KeysUrl string `json:"keys_url"` 317 | CollaboratorsUrl string `json:"collaborators_url"` 318 | TeamsUrl string `json:"teams_url"` 319 | HooksUrl string `json:"hooks_url"` 320 | IssueEventsUrl string `json:"issue_events_url"` 321 | EventsUrl string `json:"events_url"` 322 | AssigneesUrl string `json:"assignees_url"` 323 | BranchesUrl string `json:"branches_url"` 324 | TagsUrl string `json:"tags_url"` 325 | BlobsUrl string `json:"blobs_url"` 326 | GitTagsUrl string `json:"git_tags_url"` 327 | GitRefsUrl string `json:"git_refs_url"` 328 | TreesUrl string `json:"trees_url"` 329 | StatusesUrl string `json:"statuses_url"` 330 | LanguagesUrl string `json:"languages_url"` 331 | StargazersUrl string `json:"stargazers_url"` 332 | ContributorsUrl string `json:"contributors_url"` 333 | SubscribersUrl string `json:"subscribers_url"` 334 | SubscriptionUrl string `json:"subscription_url"` 335 | CommitsUrl string `json:"commits_url"` 336 | GitCommitsUrl string `json:"git_commits_url"` 337 | CommentsUrl string `json:"comments_url"` 338 | IssueCommentUrl string `json:"issue_comment_url"` 339 | ContentsUrl string `json:"contents_url"` 340 | CompareUrl string `json:"compare_url"` 341 | MergesUrl string `json:"merges_url"` 342 | ArchiveUrl string `json:"archive_url"` 343 | DownloadsUrl string `json:"downloads_url"` 344 | IssuesUrl string `json:"issues_url"` 345 | PullsUrl string `json:"pulls_url"` 346 | MilestonesUrl string `json:"milestones_url"` 347 | NotificationsUrl string `json:"notifications_url"` 348 | LabelsUrl string `json:"labels_url"` 349 | ReleasesUrl string `json:"releases_url"` 350 | DeploymentsUrl string `json:"deployments_url"` 351 | CreatedAt time.Time `json:"created_at"` 352 | UpdatedAt time.Time `json:"updated_at"` 353 | PushedAt time.Time `json:"pushed_at"` 354 | GitUrl string `json:"git_url"` 355 | SshUrl string `json:"ssh_url"` 356 | CloneUrl string `json:"clone_url"` 357 | SvnUrl string `json:"svn_url"` 358 | Homepage interface{} `json:"homepage"` 359 | Size int `json:"size"` 360 | StargazersCount int `json:"stargazers_count"` 361 | WatchersCount int `json:"watchers_count"` 362 | Language string `json:"language"` 363 | HasIssues bool `json:"has_issues"` 364 | HasProjects bool `json:"has_projects"` 365 | HasDownloads bool `json:"has_downloads"` 366 | HasWiki bool `json:"has_wiki"` 367 | HasPages bool `json:"has_pages"` 368 | ForksCount int `json:"forks_count"` 369 | MirrorUrl interface{} `json:"mirror_url"` 370 | Archived bool `json:"archived"` 371 | Disabled bool `json:"disabled"` 372 | OpenIssuesCount int `json:"open_issues_count"` 373 | License interface{} `json:"license"` 374 | Forks int `json:"forks"` 375 | OpenIssues int `json:"open_issues"` 376 | Watchers int `json:"watchers"` 377 | DefaultBranch string `json:"default_branch"` 378 | } `json:"repo"` 379 | } `json:"head"` 380 | Base struct { 381 | Label string `json:"label"` 382 | Ref string `json:"ref"` 383 | Sha string `json:"sha"` 384 | User struct { 385 | Login string `json:"login"` 386 | Id int `json:"id"` 387 | NodeId string `json:"node_id"` 388 | AvatarUrl string `json:"avatar_url"` 389 | GravatarId string `json:"gravatar_id"` 390 | Url string `json:"url"` 391 | HtmlUrl string `json:"html_url"` 392 | FollowersUrl string `json:"followers_url"` 393 | FollowingUrl string `json:"following_url"` 394 | GistsUrl string `json:"gists_url"` 395 | StarredUrl string `json:"starred_url"` 396 | SubscriptionsUrl string `json:"subscriptions_url"` 397 | OrganizationsUrl string `json:"organizations_url"` 398 | ReposUrl string `json:"repos_url"` 399 | EventsUrl string `json:"events_url"` 400 | ReceivedEventsUrl string `json:"received_events_url"` 401 | Type string `json:"type"` 402 | SiteAdmin bool `json:"site_admin"` 403 | } `json:"user"` 404 | Repo struct { 405 | Id int `json:"id"` 406 | NodeId string `json:"node_id"` 407 | Name string `json:"name"` 408 | FullName string `json:"full_name"` 409 | Private bool `json:"private"` 410 | Owner struct { 411 | Login string `json:"login"` 412 | Id int `json:"id"` 413 | NodeId string `json:"node_id"` 414 | AvatarUrl string `json:"avatar_url"` 415 | GravatarId string `json:"gravatar_id"` 416 | Url string `json:"url"` 417 | HtmlUrl string `json:"html_url"` 418 | FollowersUrl string `json:"followers_url"` 419 | FollowingUrl string `json:"following_url"` 420 | GistsUrl string `json:"gists_url"` 421 | StarredUrl string `json:"starred_url"` 422 | SubscriptionsUrl string `json:"subscriptions_url"` 423 | OrganizationsUrl string `json:"organizations_url"` 424 | ReposUrl string `json:"repos_url"` 425 | EventsUrl string `json:"events_url"` 426 | ReceivedEventsUrl string `json:"received_events_url"` 427 | Type string `json:"type"` 428 | SiteAdmin bool `json:"site_admin"` 429 | } `json:"owner"` 430 | HtmlUrl string `json:"html_url"` 431 | Description string `json:"description"` 432 | Fork bool `json:"fork"` 433 | Url string `json:"url"` 434 | ForksUrl string `json:"forks_url"` 435 | KeysUrl string `json:"keys_url"` 436 | CollaboratorsUrl string `json:"collaborators_url"` 437 | TeamsUrl string `json:"teams_url"` 438 | HooksUrl string `json:"hooks_url"` 439 | IssueEventsUrl string `json:"issue_events_url"` 440 | EventsUrl string `json:"events_url"` 441 | AssigneesUrl string `json:"assignees_url"` 442 | BranchesUrl string `json:"branches_url"` 443 | TagsUrl string `json:"tags_url"` 444 | BlobsUrl string `json:"blobs_url"` 445 | GitTagsUrl string `json:"git_tags_url"` 446 | GitRefsUrl string `json:"git_refs_url"` 447 | TreesUrl string `json:"trees_url"` 448 | StatusesUrl string `json:"statuses_url"` 449 | LanguagesUrl string `json:"languages_url"` 450 | StargazersUrl string `json:"stargazers_url"` 451 | ContributorsUrl string `json:"contributors_url"` 452 | SubscribersUrl string `json:"subscribers_url"` 453 | SubscriptionUrl string `json:"subscription_url"` 454 | CommitsUrl string `json:"commits_url"` 455 | GitCommitsUrl string `json:"git_commits_url"` 456 | CommentsUrl string `json:"comments_url"` 457 | IssueCommentUrl string `json:"issue_comment_url"` 458 | ContentsUrl string `json:"contents_url"` 459 | CompareUrl string `json:"compare_url"` 460 | MergesUrl string `json:"merges_url"` 461 | ArchiveUrl string `json:"archive_url"` 462 | DownloadsUrl string `json:"downloads_url"` 463 | IssuesUrl string `json:"issues_url"` 464 | PullsUrl string `json:"pulls_url"` 465 | MilestonesUrl string `json:"milestones_url"` 466 | NotificationsUrl string `json:"notifications_url"` 467 | LabelsUrl string `json:"labels_url"` 468 | ReleasesUrl string `json:"releases_url"` 469 | DeploymentsUrl string `json:"deployments_url"` 470 | CreatedAt time.Time `json:"created_at"` 471 | UpdatedAt time.Time `json:"updated_at"` 472 | PushedAt time.Time `json:"pushed_at"` 473 | GitUrl string `json:"git_url"` 474 | SshUrl string `json:"ssh_url"` 475 | CloneUrl string `json:"clone_url"` 476 | SvnUrl string `json:"svn_url"` 477 | Homepage interface{} `json:"homepage"` 478 | Size int `json:"size"` 479 | StargazersCount int `json:"stargazers_count"` 480 | WatchersCount int `json:"watchers_count"` 481 | Language string `json:"language"` 482 | HasIssues bool `json:"has_issues"` 483 | HasProjects bool `json:"has_projects"` 484 | HasDownloads bool `json:"has_downloads"` 485 | HasWiki bool `json:"has_wiki"` 486 | HasPages bool `json:"has_pages"` 487 | ForksCount int `json:"forks_count"` 488 | MirrorUrl interface{} `json:"mirror_url"` 489 | Archived bool `json:"archived"` 490 | Disabled bool `json:"disabled"` 491 | OpenIssuesCount int `json:"open_issues_count"` 492 | License interface{} `json:"license"` 493 | Forks int `json:"forks"` 494 | OpenIssues int `json:"open_issues"` 495 | Watchers int `json:"watchers"` 496 | DefaultBranch string `json:"default_branch"` 497 | } `json:"repo"` 498 | } `json:"base"` 499 | Links struct { 500 | Self struct { 501 | Href string `json:"href"` 502 | } `json:"self"` 503 | Html struct { 504 | Href string `json:"href"` 505 | } `json:"html"` 506 | Issue struct { 507 | Href string `json:"href"` 508 | } `json:"issue"` 509 | Comments struct { 510 | Href string `json:"href"` 511 | } `json:"comments"` 512 | ReviewComments struct { 513 | Href string `json:"href"` 514 | } `json:"review_comments"` 515 | ReviewComment struct { 516 | Href string `json:"href"` 517 | } `json:"review_comment"` 518 | Commits struct { 519 | Href string `json:"href"` 520 | } `json:"commits"` 521 | Statuses struct { 522 | Href string `json:"href"` 523 | } `json:"statuses"` 524 | } `json:"_links"` 525 | AuthorAssociation string `json:"author_association"` 526 | AutoMerge interface{} `json:"auto_merge"` 527 | ActiveLockReason interface{} `json:"active_lock_reason"` 528 | Merged bool `json:"merged"` 529 | Mergeable bool `json:"mergeable"` 530 | Rebaseable bool `json:"rebaseable"` 531 | MergeableState string `json:"mergeable_state"` 532 | MergedBy interface{} `json:"merged_by"` 533 | Comments int `json:"comments"` 534 | ReviewComments int `json:"review_comments"` 535 | MaintainerCanModify bool `json:"maintainer_can_modify"` 536 | Commits int `json:"commits"` 537 | Additions int `json:"additions"` 538 | Deletions int `json:"deletions"` 539 | ChangedFiles int `json:"changed_files"` 540 | } 541 | 542 | var gitHubBaseUrl = env.Get("GITHUB_BASE_URL") 543 | var gitHubCommonHeader = generateCommonHeader() 544 | 545 | // GetUserMostStarredRepository get the user's most starred repository 546 | // on the GitHub API 547 | func GetUserMostStarredRepository(userLogin string) Repository { 548 | repositories := getUserRepositories(userLogin) 549 | mostStarredRepo := findMostStarredRepository(repositories) 550 | 551 | return mostStarredRepo 552 | } 553 | 554 | // GetMostCommentedIssue get the most commented Issue of a user's 555 | // Repository on the GitHub API 556 | func GetMostCommentedIssue(userLogin string, repositoryName string) Issue { 557 | repositoryIssues := getRepositoryIssues(userLogin, repositoryName) 558 | mostCommentedIssue := findMostCommentedOpenedIssue(repositoryIssues) 559 | 560 | return mostCommentedIssue 561 | } 562 | 563 | // GetNonInteractedPullRequests get all non interacted PullRequest 564 | // of a user's Repository on the GitHub API 565 | func GetNonInteractedPullRequests(userLogin string, repositoryName string) []PullRequest { 566 | pullRequestsOnRepo := getPullRequests(userLogin, repositoryName) 567 | filledPullRequests := fillComments(pullRequestsOnRepo) 568 | nonInteractedPullRequests := filterNonInteractedPullRequests(filledPullRequests) 569 | 570 | return nonInteractedPullRequests 571 | } 572 | 573 | // getUserRepositories get all user repositories 574 | func getUserRepositories(userName string) []Repository { 575 | repositoriesEndpoint := fmt.Sprintf("/users/%s/repos", userName) 576 | url := gitHubBaseUrl + repositoriesEndpoint 577 | var repositories []Repository 578 | client := http.Client{} 579 | 580 | preparedRequest, _ := http.NewRequest(http.MethodGet, url, nil) 581 | preparedRequest.Header = gitHubCommonHeader 582 | 583 | repositoriesResponse, err := client.Do(preparedRequest) 584 | if err != nil { 585 | log.Printf("Error na requisição de repositórios.") 586 | return nil 587 | } 588 | 589 | if repositoriesResponse.StatusCode == http.StatusOK { 590 | repositories = makeRepositoriesFromBody(repositoriesResponse.Body) 591 | } 592 | 593 | return repositories 594 | } 595 | 596 | // getPullRequests get all pull requests on a repository 597 | func getPullRequests(userLogin string, repositoryName string) []PullRequest { 598 | pullRequestsEndpoint := 599 | fmt.Sprintf("/repos/%s/%s/pulls", userLogin, repositoryName) 600 | var pullRequests []PullRequest 601 | url := gitHubBaseUrl + pullRequestsEndpoint 602 | 603 | preparedRequest, _ := http.NewRequest(http.MethodGet, url, nil) 604 | preparedRequest.Header = gitHubCommonHeader 605 | 606 | client := http.Client{} 607 | 608 | pullRequestResponse, err := client.Do(preparedRequest) 609 | if err != nil { 610 | return nil 611 | } 612 | 613 | if pullRequestResponse.StatusCode == http.StatusOK { 614 | pullRequests = makePullRequestsFromBody(pullRequestResponse.Body) 615 | } 616 | 617 | return pullRequests 618 | } 619 | 620 | // getRepositoryIssues gets a array of Issue based on a userLogin and 621 | // a repository 622 | func getRepositoryIssues(userLogin string, repositoryName string) []Issue { 623 | issuesRepositoryEndpoint := 624 | fmt.Sprintf("/repos/%s/%s/issues", userLogin, repositoryName) 625 | var issues []Issue 626 | url := gitHubBaseUrl + issuesRepositoryEndpoint 627 | 628 | issuesRequest, _ := http.NewRequest(http.MethodGet, url, nil) 629 | issuesRequest.Header = gitHubCommonHeader 630 | 631 | client := http.Client{} 632 | 633 | issuesResponse, err := client.Do(issuesRequest) 634 | if err != nil { 635 | log.Printf("Error na requisição das issues do %s.", repositoryName) 636 | return nil 637 | } 638 | 639 | if issuesResponse.StatusCode == http.StatusOK { 640 | issues = generateIssuesFromBody(issuesResponse.Body) 641 | } 642 | 643 | return issues 644 | } 645 | 646 | // getGitHubUser get a User instance from GitHub API 647 | func getGitHubUser(user string) User { 648 | formatedEndpoint := fmt.Sprintf("/users/%s", user) 649 | buildedURL := gitHubBaseUrl + formatedEndpoint 650 | client := http.Client{} 651 | 652 | userRequest, _ := http.NewRequest(http.MethodGet, buildedURL, nil) 653 | userRequest.Header = gitHubCommonHeader 654 | 655 | userResponse, err := client.Do(userRequest) 656 | if err != nil { 657 | log.Print(err) 658 | } 659 | 660 | var gitHubUser = generateUserFromBody(userResponse.Body) 661 | 662 | return gitHubUser 663 | } 664 | 665 | // GenerateAuthenticationHeader generates the header kv used to authorize 666 | // our requests using the personal access token stored at .env 667 | func generateAuthenticationHeader() (key string, value string) { 668 | personalToken := env.Get("GITHUB_PERSONAL_ACCESS_TOKEN") 669 | key = "Authentication" 670 | 671 | header := fmt.Sprintf("token %s", personalToken) 672 | 673 | return key, header 674 | } 675 | 676 | // findMostStarredRepository finds the most starred 677 | // repository over a []Repository and returns it 678 | func findMostStarredRepository(repos []Repository) Repository { 679 | var mostStarred Repository 680 | starThreshold := 0 681 | 682 | for _, repo := range repos { 683 | if repo.StargazersCount >= starThreshold { 684 | mostStarred = repo 685 | starThreshold = repo.StargazersCount 686 | } 687 | } 688 | 689 | return mostStarred 690 | } 691 | 692 | // filterNonInteractedPullRequests get only non interacted PullRequest 693 | // in a array of PullRequest 694 | func filterNonInteractedPullRequests(pullRequests []PullRequest) []PullRequest { 695 | 696 | filteredPullRequests := removeInteractedPullRequests(pullRequests) 697 | 698 | return filteredPullRequests 699 | } 700 | 701 | // fillComments fill an new array of PullRequest with comments field 702 | func fillComments(pullRequests []PullRequest) []PullRequest { 703 | var buffer []PullRequest 704 | for _, pullRequest := range pullRequests { 705 | buffer = append(buffer, fetchPullRequestComments(&pullRequest)) 706 | } 707 | 708 | return buffer 709 | } 710 | 711 | // fetchPullRequestComments get a PullRequest with comments field 712 | func fetchPullRequestComments(pullRequest *PullRequest) PullRequest { 713 | client := http.Client{} 714 | var buffer PullRequest 715 | 716 | preparedRequest, _ := http.NewRequest(http.MethodGet, pullRequest.Url, nil) 717 | preparedRequest.Header = gitHubCommonHeader 718 | 719 | pullRequestResponse, _ := client.Do(preparedRequest) 720 | 721 | bodyBytes, _ := ioutil.ReadAll(pullRequestResponse.Body) 722 | 723 | if pullRequestResponse.StatusCode == http.StatusOK { 724 | buffer = PullRequest{} 725 | _ = json.Unmarshal(bodyBytes, &buffer) 726 | } 727 | 728 | return buffer 729 | } 730 | 731 | // removeInteractedPullRequests retuns a new PullRequest array with only 732 | // non interacted PullRequests 733 | func removeInteractedPullRequests(prs []PullRequest) []PullRequest { 734 | var buffer []PullRequest 735 | for _, pr := range prs { 736 | if pr.Comments == 0 && pr.State == "open" { 737 | buffer = append(buffer, pr) 738 | } 739 | } 740 | 741 | return buffer 742 | } 743 | 744 | // findMostCommentedOpenedIssue find the most commented and opened 745 | // Issue 746 | func findMostCommentedOpenedIssue(issues []Issue) Issue { 747 | var mostCommentedOpenedIssue Issue 748 | for _, issue := range issues { 749 | if issue.State == "open" && 750 | issue.Comments > mostCommentedOpenedIssue.Comments { 751 | mostCommentedOpenedIssue = issue 752 | } 753 | } 754 | 755 | return mostCommentedOpenedIssue 756 | } 757 | 758 | // generateCommonHeader generates a common http.Header used to 759 | // make a http.Request to GitHub API 760 | func generateCommonHeader() http.Header { 761 | hostname, _ := os.Hostname() 762 | header := http.Header{} 763 | header.Add(generateAuthenticationHeader()) 764 | header.Add("Accept", "application/vnd.github.v3+json") 765 | header.Add("Connection", "keep-alive") 766 | header.Add("Host", hostname) 767 | 768 | return header 769 | } 770 | 771 | // generateUserFromBody creates a User from a 772 | // io.Reader generally provided by a http.Request 773 | func generateUserFromBody(requestBody io.Reader) User { 774 | var user User 775 | bodyBytes, _ := ioutil.ReadAll(requestBody) 776 | err := json.Unmarshal(bodyBytes, &user) 777 | if err != nil { 778 | log.Print(err) 779 | } 780 | 781 | return user 782 | } 783 | 784 | // generateIssuesFromBody creates a array of 785 | // Issue from a io.Reader generally provided 786 | // by a http.Request 787 | func generateIssuesFromBody(requestBody io.Reader) []Issue { 788 | bodyBytes, _ := ioutil.ReadAll(requestBody) 789 | issues := make([]Issue, 0) 790 | err := json.Unmarshal(bodyBytes, &issues) 791 | if err != nil { 792 | log.Print(err) 793 | } 794 | 795 | return issues 796 | } 797 | 798 | // makeRepositoriesFromBody creates a array of 799 | // Repository from a io.Reader generally provided 800 | // by a http.Request 801 | func makeRepositoriesFromBody(requestBody io.Reader) []Repository { 802 | bodyBytes, _ := ioutil.ReadAll(requestBody) 803 | repos := make([]Repository, 0) 804 | err := json.Unmarshal(bodyBytes, &repos) 805 | if err != nil { 806 | log.Print(err) 807 | } 808 | 809 | return repos 810 | } 811 | 812 | // makePullRequestsFromBody creates a array of 813 | // PullRequest from a io.Reader generally provided 814 | // by a http.Request 815 | func makePullRequestsFromBody(requestBody io.Reader) []PullRequest { 816 | bodyBytes, _ := ioutil.ReadAll(requestBody) 817 | pullRequests := make([]PullRequest, 0) 818 | err := json.Unmarshal(bodyBytes, &pullRequests) 819 | if err != nil { 820 | log.Print(err) 821 | } 822 | 823 | return pullRequests 824 | } 825 | -------------------------------------------------------------------------------- /pkg/github_test.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFindMostStarredRepository(t *testing.T) { 9 | var mockedRepositories []Repository 10 | 11 | repo1 := Repository{ 12 | FullName: "Repo com menos estrelas.", 13 | StargazersCount: 1, 14 | } 15 | repo2 := Repository{ 16 | FullName: "Repo com um pouco mais de estrelas.", 17 | StargazersCount: 500, 18 | } 19 | repo3 := Repository{ 20 | FullName: "Repo vencedor com maior número de estrelas.", 21 | StargazersCount: 987, 22 | } 23 | 24 | mockedRepositories = append(mockedRepositories, repo1, repo2, repo3) 25 | 26 | mostStarredRepository := findMostStarredRepository(mockedRepositories) 27 | result := mostStarredRepository.StargazersCount 28 | expectedResult := repo3.StargazersCount 29 | 30 | if reflect.DeepEqual(result, expectedResult) { 31 | t.Log("findMostStarredRepository PASSED") 32 | } else { 33 | t.Errorf("findMostStarredRepository FAILED, expected %d stars but got %d instead.", 34 | expectedResult, result) 35 | } 36 | 37 | } 38 | 39 | func TestFindMostCommentedOpenedIssue(t *testing.T) { 40 | var mockedIssues []Issue 41 | 42 | issue1 := Issue{ 43 | Title: "Menos comentada", 44 | State: "open", 45 | Comments: 4, 46 | } 47 | issue2 := Issue{ 48 | Title: "Quantidade mediana de comentários", 49 | State: "open", 50 | Comments: 80, 51 | } 52 | issue3 := Issue{ 53 | Title: "Mais comentada com status aberto (famoso chat do UOL)", 54 | State: "open", 55 | Comments: 900, 56 | } 57 | issue4 := Issue{ 58 | Title: "Quantidade alta de comentários mas está fechada", 59 | State: "closed", 60 | Comments: 870, 61 | } 62 | issue5 := Issue{ 63 | Title: "Quantidade Altíssima de comentários porém bloqueada", 64 | State: "closed", 65 | Comments: 8001, 66 | } 67 | 68 | mockedIssues = append(mockedIssues, issue1, issue2, issue3, issue4, issue5) 69 | 70 | winnerIssue := findMostCommentedOpenedIssue(mockedIssues) 71 | result := winnerIssue.Comments 72 | expectedResult := issue3.Comments 73 | 74 | if reflect.DeepEqual(result, expectedResult) { 75 | t.Log("findMostCommentedOpenedIssue PASSED") 76 | } else { 77 | t.Errorf("findMostCommentedOpenedIssue FAILED, expected %d comments but got %d instead.", 78 | expectedResult, result) 79 | } 80 | } 81 | 82 | func TestFilterNonInteractedPullRequests(t *testing.T) { 83 | var mockedPullRequests []PullRequest 84 | 85 | pr1 := PullRequest{ 86 | Title: "Pull Request Avulso", 87 | State: "open", 88 | Comments: 10, 89 | } 90 | pr2 := PullRequest{ 91 | Title: "Pull Request com status fechado", 92 | State: "closed", 93 | Comments: 100, 94 | } 95 | pr3 := PullRequest{ 96 | Title: "Pull Request com status fechado e sem interação", 97 | State: "closed", 98 | Comments: 0, 99 | } 100 | pr4 := PullRequest{ 101 | Title: "Pull Request sem interação numero um", 102 | State: "open", 103 | Comments: 0, 104 | } 105 | pr5 := PullRequest{ 106 | Title: "Pull Request sem interação numero dois", 107 | State: "open", 108 | Comments: 0, 109 | } 110 | pr6 := PullRequest{ 111 | Title: "Pull Request sem interação numero três", 112 | State: "open", 113 | Comments: 0, 114 | } 115 | 116 | mockedPullRequests = append(mockedPullRequests, pr1, pr2, pr3, pr4, pr5, pr6) 117 | 118 | filteredPullRequests := filterNonInteractedPullRequests(mockedPullRequests) 119 | 120 | interactedPullRequests := 0 121 | 122 | for _, pr := range filteredPullRequests { 123 | if pr.Comments > 0 || pr.State != "open" { 124 | interactedPullRequests++ 125 | } 126 | } 127 | 128 | result := interactedPullRequests 129 | expectedResult := 0 130 | 131 | if reflect.DeepEqual(result, expectedResult) { 132 | t.Log("filterNonInteractedPullRequests PASSED") 133 | } else { 134 | t.Errorf("filterNonInteractedPullRequests FAILED, expected %d interacted Pull Requests but got %d instead.", 135 | expectedResult, result) 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /postman/GitHub API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "e090dc62-c273-4b75-965c-e31974a9d47a", 4 | "name": "GitHub API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Get User", 10 | "protocolProfileBehavior": { 11 | "disabledSystemHeaders": { 12 | "accept-encoding": true 13 | } 14 | }, 15 | "request": { 16 | "auth": { 17 | "type": "oauth2", 18 | "oauth2": [ 19 | { 20 | "key": "addTokenTo", 21 | "value": "header", 22 | "type": "string" 23 | } 24 | ] 25 | }, 26 | "method": "GET", 27 | "header": [ 28 | { 29 | "key": "Authorization", 30 | "value": "token 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", 31 | "type": "text" 32 | } 33 | ], 34 | "url": { 35 | "raw": "https://api.github.com/users/Rennasccenth", 36 | "protocol": "https", 37 | "host": [ 38 | "api", 39 | "github", 40 | "com" 41 | ], 42 | "path": [ 43 | "users", 44 | "Rennasccenth" 45 | ] 46 | } 47 | }, 48 | "response": [ 49 | { 50 | "name": "Get User", 51 | "originalRequest": { 52 | "method": "GET", 53 | "header": [ 54 | { 55 | "key": "Authorization", 56 | "value": "token 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", 57 | "type": "text" 58 | } 59 | ], 60 | "url": { 61 | "raw": "https://api.github.com/users/Rennasccenth", 62 | "protocol": "https", 63 | "host": [ 64 | "api", 65 | "github", 66 | "com" 67 | ], 68 | "path": [ 69 | "users", 70 | "Rennasccenth" 71 | ] 72 | } 73 | }, 74 | "status": "OK", 75 | "code": 200, 76 | "_postman_previewlanguage": "json", 77 | "header": [ 78 | { 79 | "key": "Server", 80 | "value": "GitHub.com" 81 | }, 82 | { 83 | "key": "Date", 84 | "value": "Mon, 17 May 2021 06:30:42 GMT" 85 | }, 86 | { 87 | "key": "Content-Type", 88 | "value": "application/json; charset=utf-8" 89 | }, 90 | { 91 | "key": "Content-Length", 92 | "value": "1510" 93 | }, 94 | { 95 | "key": "Cache-Control", 96 | "value": "private, max-age=60, s-maxage=60" 97 | }, 98 | { 99 | "key": "Vary", 100 | "value": "Accept, Authorization, Cookie, X-GitHub-OTP" 101 | }, 102 | { 103 | "key": "Vary", 104 | "value": "Accept-Encoding, Accept, X-Requested-With" 105 | }, 106 | { 107 | "key": "ETag", 108 | "value": "\"15584b9411a3e942a1ab3de10f1cc7587d18b2ba594919c7d30fc3055091acd6\"" 109 | }, 110 | { 111 | "key": "Last-Modified", 112 | "value": "Sun, 16 May 2021 09:06:00 GMT" 113 | }, 114 | { 115 | "key": "X-OAuth-Scopes", 116 | "value": "admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, repo, user, workflow, write:discussion, write:packages" 117 | }, 118 | { 119 | "key": "X-Accepted-OAuth-Scopes", 120 | "value": "" 121 | }, 122 | { 123 | "key": "X-GitHub-Media-Type", 124 | "value": "github.v3; format=json" 125 | }, 126 | { 127 | "key": "X-RateLimit-Limit", 128 | "value": "5000" 129 | }, 130 | { 131 | "key": "X-RateLimit-Remaining", 132 | "value": "4995" 133 | }, 134 | { 135 | "key": "X-RateLimit-Reset", 136 | "value": "1621236593" 137 | }, 138 | { 139 | "key": "X-RateLimit-Used", 140 | "value": "5" 141 | }, 142 | { 143 | "key": "X-RateLimit-Resource", 144 | "value": "core" 145 | }, 146 | { 147 | "key": "Access-Control-Expose-Headers", 148 | "value": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset" 149 | }, 150 | { 151 | "key": "Access-Control-Allow-Origin", 152 | "value": "*" 153 | }, 154 | { 155 | "key": "Strict-Transport-Security", 156 | "value": "max-age=31536000; includeSubdomains; preload" 157 | }, 158 | { 159 | "key": "X-Frame-Options", 160 | "value": "deny" 161 | }, 162 | { 163 | "key": "X-Content-Type-Options", 164 | "value": "nosniff" 165 | }, 166 | { 167 | "key": "X-XSS-Protection", 168 | "value": "0" 169 | }, 170 | { 171 | "key": "Referrer-Policy", 172 | "value": "origin-when-cross-origin, strict-origin-when-cross-origin" 173 | }, 174 | { 175 | "key": "Content-Security-Policy", 176 | "value": "default-src 'none'" 177 | }, 178 | { 179 | "key": "X-GitHub-Request-Id", 180 | "value": "E4A4:4DEA:34A2B4:DC8114:60A20D92" 181 | } 182 | ], 183 | "cookie": [], 184 | "body": "{\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false,\n \"name\": \"Felipe Nunes\",\n \"company\": null,\n \"blog\": \"\",\n \"location\": null,\n \"email\": null,\n \"hireable\": null,\n \"bio\": \"Graduando em Ciência da Computação pela Universidade Federal do Mato Grosso.\",\n \"twitter_username\": null,\n \"public_repos\": 5,\n \"public_gists\": 0,\n \"followers\": 30,\n \"following\": 45,\n \"created_at\": \"2018-09-03T00:13:06Z\",\n \"updated_at\": \"2021-05-16T09:06:00Z\",\n \"private_gists\": 1,\n \"total_private_repos\": 3,\n \"owned_private_repos\": 3,\n \"disk_usage\": 2356,\n \"collaborators\": 0,\n \"two_factor_authentication\": false,\n \"plan\": {\n \"name\": \"pro\",\n \"space\": 976562499,\n \"collaborators\": 0,\n \"private_repos\": 9999\n }\n}" 185 | } 186 | ] 187 | }, 188 | { 189 | "name": "Get User Repositories", 190 | "protocolProfileBehavior": { 191 | "disabledSystemHeaders": { 192 | "accept-encoding": true 193 | } 194 | }, 195 | "request": { 196 | "auth": { 197 | "type": "oauth2", 198 | "oauth2": [ 199 | { 200 | "key": "addTokenTo", 201 | "value": "header", 202 | "type": "string" 203 | } 204 | ] 205 | }, 206 | "method": "GET", 207 | "header": [ 208 | { 209 | "key": "Authorization", 210 | "value": "token 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", 211 | "type": "text" 212 | } 213 | ], 214 | "url": { 215 | "raw": "https://api.github.com/users/Rennasccenth/repos", 216 | "protocol": "https", 217 | "host": [ 218 | "api", 219 | "github", 220 | "com" 221 | ], 222 | "path": [ 223 | "users", 224 | "Rennasccenth", 225 | "repos" 226 | ] 227 | } 228 | }, 229 | "response": [ 230 | { 231 | "name": "Get User Repositories", 232 | "originalRequest": { 233 | "method": "GET", 234 | "header": [ 235 | { 236 | "key": "Authorization", 237 | "value": "token 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", 238 | "type": "text" 239 | } 240 | ], 241 | "url": { 242 | "raw": "https://api.github.com/users/Rennasccenth/repos", 243 | "protocol": "https", 244 | "host": [ 245 | "api", 246 | "github", 247 | "com" 248 | ], 249 | "path": [ 250 | "users", 251 | "Rennasccenth", 252 | "repos" 253 | ] 254 | } 255 | }, 256 | "status": "OK", 257 | "code": 200, 258 | "_postman_previewlanguage": "json", 259 | "header": [ 260 | { 261 | "key": "Server", 262 | "value": "GitHub.com" 263 | }, 264 | { 265 | "key": "Date", 266 | "value": "Mon, 17 May 2021 06:30:39 GMT" 267 | }, 268 | { 269 | "key": "Content-Type", 270 | "value": "application/json; charset=utf-8" 271 | }, 272 | { 273 | "key": "Content-Length", 274 | "value": "28536" 275 | }, 276 | { 277 | "key": "Cache-Control", 278 | "value": "private, max-age=60, s-maxage=60" 279 | }, 280 | { 281 | "key": "Vary", 282 | "value": "Accept, Authorization, Cookie, X-GitHub-OTP" 283 | }, 284 | { 285 | "key": "Vary", 286 | "value": "Accept-Encoding, Accept, X-Requested-With" 287 | }, 288 | { 289 | "key": "ETag", 290 | "value": "\"4e57bb616982b91eaacc189771598ff039ed167a0099d494cd06311d9a3c9f29\"" 291 | }, 292 | { 293 | "key": "X-OAuth-Scopes", 294 | "value": "admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, repo, user, workflow, write:discussion, write:packages" 295 | }, 296 | { 297 | "key": "X-Accepted-OAuth-Scopes", 298 | "value": "" 299 | }, 300 | { 301 | "key": "X-GitHub-Media-Type", 302 | "value": "github.v3; format=json" 303 | }, 304 | { 305 | "key": "X-RateLimit-Limit", 306 | "value": "5000" 307 | }, 308 | { 309 | "key": "X-RateLimit-Remaining", 310 | "value": "4996" 311 | }, 312 | { 313 | "key": "X-RateLimit-Reset", 314 | "value": "1621236593" 315 | }, 316 | { 317 | "key": "X-RateLimit-Used", 318 | "value": "4" 319 | }, 320 | { 321 | "key": "X-RateLimit-Resource", 322 | "value": "core" 323 | }, 324 | { 325 | "key": "Access-Control-Expose-Headers", 326 | "value": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset" 327 | }, 328 | { 329 | "key": "Access-Control-Allow-Origin", 330 | "value": "*" 331 | }, 332 | { 333 | "key": "Strict-Transport-Security", 334 | "value": "max-age=31536000; includeSubdomains; preload" 335 | }, 336 | { 337 | "key": "X-Frame-Options", 338 | "value": "deny" 339 | }, 340 | { 341 | "key": "X-Content-Type-Options", 342 | "value": "nosniff" 343 | }, 344 | { 345 | "key": "X-XSS-Protection", 346 | "value": "0" 347 | }, 348 | { 349 | "key": "Referrer-Policy", 350 | "value": "origin-when-cross-origin, strict-origin-when-cross-origin" 351 | }, 352 | { 353 | "key": "Content-Security-Policy", 354 | "value": "default-src 'none'" 355 | }, 356 | { 357 | "key": "X-GitHub-Request-Id", 358 | "value": "E4A4:4DEA:34A282:DC8038:60A20D8F" 359 | } 360 | ], 361 | "cookie": [], 362 | "body": "[\n {\n \"id\": 346067427,\n \"node_id\": \"MDEwOlJlcG9zaXRvcnkzNDYwNjc0Mjc=\",\n \"name\": \"Entity-Framework-Core-POC\",\n \"full_name\": \"Rennasccenth/Entity-Framework-Core-POC\",\n \"private\": false,\n \"owner\": {\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false\n },\n \"html_url\": \"https://github.com/Rennasccenth/Entity-Framework-Core-POC\",\n \"description\": \"POC of EF Core on .NET 5\",\n \"fork\": false,\n \"url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC\",\n \"forks_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/forks\",\n \"keys_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/keys{/key_id}\",\n \"collaborators_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/collaborators{/collaborator}\",\n \"teams_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/teams\",\n \"hooks_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/hooks\",\n \"issue_events_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/issues/events{/number}\",\n \"events_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/events\",\n \"assignees_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/assignees{/user}\",\n \"branches_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/branches{/branch}\",\n \"tags_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/tags\",\n \"blobs_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/git/blobs{/sha}\",\n \"git_tags_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/git/tags{/sha}\",\n \"git_refs_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/git/refs{/sha}\",\n \"trees_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/git/trees{/sha}\",\n \"statuses_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/statuses/{sha}\",\n \"languages_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/languages\",\n \"stargazers_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/stargazers\",\n \"contributors_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/contributors\",\n \"subscribers_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/subscribers\",\n \"subscription_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/subscription\",\n \"commits_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/commits{/sha}\",\n \"git_commits_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/git/commits{/sha}\",\n \"comments_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/comments{/number}\",\n \"issue_comment_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/issues/comments{/number}\",\n \"contents_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/contents/{+path}\",\n \"compare_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/compare/{base}...{head}\",\n \"merges_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/merges\",\n \"archive_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/{archive_format}{/ref}\",\n \"downloads_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/downloads\",\n \"issues_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/issues{/number}\",\n \"pulls_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/pulls{/number}\",\n \"milestones_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/milestones{/number}\",\n \"notifications_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/notifications{?since,all,participating}\",\n \"labels_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/labels{/name}\",\n \"releases_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/releases{/id}\",\n \"deployments_url\": \"https://api.github.com/repos/Rennasccenth/Entity-Framework-Core-POC/deployments\",\n \"created_at\": \"2021-03-09T16:15:26Z\",\n \"updated_at\": \"2021-05-02T07:53:20Z\",\n \"pushed_at\": \"2021-05-02T07:52:25Z\",\n \"git_url\": \"git://github.com/Rennasccenth/Entity-Framework-Core-POC.git\",\n \"ssh_url\": \"git@github.com:Rennasccenth/Entity-Framework-Core-POC.git\",\n \"clone_url\": \"https://github.com/Rennasccenth/Entity-Framework-Core-POC.git\",\n \"svn_url\": \"https://github.com/Rennasccenth/Entity-Framework-Core-POC\",\n \"homepage\": null,\n \"size\": 805,\n \"stargazers_count\": 0,\n \"watchers_count\": 0,\n \"language\": \"C#\",\n \"has_issues\": true,\n \"has_projects\": true,\n \"has_downloads\": true,\n \"has_wiki\": true,\n \"has_pages\": false,\n \"forks_count\": 0,\n \"mirror_url\": null,\n \"archived\": false,\n \"disabled\": false,\n \"open_issues_count\": 0,\n \"license\": null,\n \"forks\": 0,\n \"open_issues\": 0,\n \"watchers\": 0,\n \"default_branch\": \"main\",\n \"permissions\": {\n \"admin\": true,\n \"push\": true,\n \"pull\": true\n }\n },\n {\n \"id\": 279911973,\n \"node_id\": \"MDEwOlJlcG9zaXRvcnkyNzk5MTE5NzM=\",\n \"name\": \"GitTestCommands\",\n \"full_name\": \"Rennasccenth/GitTestCommands\",\n \"private\": false,\n \"owner\": {\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false\n },\n \"html_url\": \"https://github.com/Rennasccenth/GitTestCommands\",\n \"description\": \"A simple repository to ruin a git repo in awesome ways.\",\n \"fork\": false,\n \"url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands\",\n \"forks_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/forks\",\n \"keys_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/keys{/key_id}\",\n \"collaborators_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/collaborators{/collaborator}\",\n \"teams_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/teams\",\n \"hooks_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/hooks\",\n \"issue_events_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/issues/events{/number}\",\n \"events_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/events\",\n \"assignees_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/assignees{/user}\",\n \"branches_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/branches{/branch}\",\n \"tags_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/tags\",\n \"blobs_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/git/blobs{/sha}\",\n \"git_tags_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/git/tags{/sha}\",\n \"git_refs_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/git/refs{/sha}\",\n \"trees_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/git/trees{/sha}\",\n \"statuses_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/statuses/{sha}\",\n \"languages_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/languages\",\n \"stargazers_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/stargazers\",\n \"contributors_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/contributors\",\n \"subscribers_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/subscribers\",\n \"subscription_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/subscription\",\n \"commits_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/commits{/sha}\",\n \"git_commits_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/git/commits{/sha}\",\n \"comments_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/comments{/number}\",\n \"issue_comment_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/issues/comments{/number}\",\n \"contents_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/contents/{+path}\",\n \"compare_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/compare/{base}...{head}\",\n \"merges_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/merges\",\n \"archive_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/{archive_format}{/ref}\",\n \"downloads_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/downloads\",\n \"issues_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/issues{/number}\",\n \"pulls_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/pulls{/number}\",\n \"milestones_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/milestones{/number}\",\n \"notifications_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/notifications{?since,all,participating}\",\n \"labels_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/labels{/name}\",\n \"releases_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/releases{/id}\",\n \"deployments_url\": \"https://api.github.com/repos/Rennasccenth/GitTestCommands/deployments\",\n \"created_at\": \"2020-07-15T15:53:51Z\",\n \"updated_at\": \"2021-05-02T07:54:39Z\",\n \"pushed_at\": \"2020-07-15T16:55:22Z\",\n \"git_url\": \"git://github.com/Rennasccenth/GitTestCommands.git\",\n \"ssh_url\": \"git@github.com:Rennasccenth/GitTestCommands.git\",\n \"clone_url\": \"https://github.com/Rennasccenth/GitTestCommands.git\",\n \"svn_url\": \"https://github.com/Rennasccenth/GitTestCommands\",\n \"homepage\": null,\n \"size\": 0,\n \"stargazers_count\": 0,\n \"watchers_count\": 0,\n \"language\": \"Python\",\n \"has_issues\": true,\n \"has_projects\": true,\n \"has_downloads\": true,\n \"has_wiki\": true,\n \"has_pages\": false,\n \"forks_count\": 0,\n \"mirror_url\": null,\n \"archived\": false,\n \"disabled\": false,\n \"open_issues_count\": 0,\n \"license\": null,\n \"forks\": 0,\n \"open_issues\": 0,\n \"watchers\": 0,\n \"default_branch\": \"master\",\n \"permissions\": {\n \"admin\": true,\n \"push\": true,\n \"pull\": true\n }\n },\n {\n \"id\": 216449860,\n \"node_id\": \"MDEwOlJlcG9zaXRvcnkyMTY0NDk4NjA=\",\n \"name\": \"Laboratorio-de-Banco-de-Dados-2019.2\",\n \"full_name\": \"Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2\",\n \"private\": false,\n \"owner\": {\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false\n },\n \"html_url\": \"https://github.com/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2\",\n \"description\": null,\n \"fork\": false,\n \"url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2\",\n \"forks_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/forks\",\n \"keys_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/keys{/key_id}\",\n \"collaborators_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/collaborators{/collaborator}\",\n \"teams_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/teams\",\n \"hooks_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/hooks\",\n \"issue_events_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/issues/events{/number}\",\n \"events_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/events\",\n \"assignees_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/assignees{/user}\",\n \"branches_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/branches{/branch}\",\n \"tags_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/tags\",\n \"blobs_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/git/blobs{/sha}\",\n \"git_tags_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/git/tags{/sha}\",\n \"git_refs_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/git/refs{/sha}\",\n \"trees_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/git/trees{/sha}\",\n \"statuses_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/statuses/{sha}\",\n \"languages_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/languages\",\n \"stargazers_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/stargazers\",\n \"contributors_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/contributors\",\n \"subscribers_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/subscribers\",\n \"subscription_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/subscription\",\n \"commits_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/commits{/sha}\",\n \"git_commits_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/git/commits{/sha}\",\n \"comments_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/comments{/number}\",\n \"issue_comment_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/issues/comments{/number}\",\n \"contents_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/contents/{+path}\",\n \"compare_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/compare/{base}...{head}\",\n \"merges_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/merges\",\n \"archive_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/{archive_format}{/ref}\",\n \"downloads_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/downloads\",\n \"issues_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/issues{/number}\",\n \"pulls_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/pulls{/number}\",\n \"milestones_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/milestones{/number}\",\n \"notifications_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/notifications{?since,all,participating}\",\n \"labels_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/labels{/name}\",\n \"releases_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/releases{/id}\",\n \"deployments_url\": \"https://api.github.com/repos/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2/deployments\",\n \"created_at\": \"2019-10-21T01:01:08Z\",\n \"updated_at\": \"2021-03-21T07:21:54Z\",\n \"pushed_at\": \"2021-03-21T07:21:52Z\",\n \"git_url\": \"git://github.com/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2.git\",\n \"ssh_url\": \"git@github.com:Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2.git\",\n \"clone_url\": \"https://github.com/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2.git\",\n \"svn_url\": \"https://github.com/Rennasccenth/Laboratorio-de-Banco-de-Dados-2019.2\",\n \"homepage\": null,\n \"size\": 783,\n \"stargazers_count\": 0,\n \"watchers_count\": 0,\n \"language\": \"Java\",\n \"has_issues\": true,\n \"has_projects\": true,\n \"has_downloads\": true,\n \"has_wiki\": true,\n \"has_pages\": false,\n \"forks_count\": 0,\n \"mirror_url\": null,\n \"archived\": false,\n \"disabled\": false,\n \"open_issues_count\": 0,\n \"license\": null,\n \"forks\": 0,\n \"open_issues\": 0,\n \"watchers\": 0,\n \"default_branch\": \"master\",\n \"permissions\": {\n \"admin\": true,\n \"push\": true,\n \"pull\": true\n }\n },\n {\n \"id\": 204854327,\n \"node_id\": \"MDEwOlJlcG9zaXRvcnkyMDQ4NTQzMjc=\",\n \"name\": \"Metodos-Computacionais-UFMT-2019\",\n \"full_name\": \"Rennasccenth/Metodos-Computacionais-UFMT-2019\",\n \"private\": false,\n \"owner\": {\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false\n },\n \"html_url\": \"https://github.com/Rennasccenth/Metodos-Computacionais-UFMT-2019\",\n \"description\": \"Trabalho de métodos computacionais realizado individualmente durante o ano de 2019 na Universidade Federal do Mato Grosso.\",\n \"fork\": false,\n \"url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019\",\n \"forks_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/forks\",\n \"keys_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/keys{/key_id}\",\n \"collaborators_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/collaborators{/collaborator}\",\n \"teams_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/teams\",\n \"hooks_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/hooks\",\n \"issue_events_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/issues/events{/number}\",\n \"events_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/events\",\n \"assignees_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/assignees{/user}\",\n \"branches_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/branches{/branch}\",\n \"tags_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/tags\",\n \"blobs_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/git/blobs{/sha}\",\n \"git_tags_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/git/tags{/sha}\",\n \"git_refs_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/git/refs{/sha}\",\n \"trees_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/git/trees{/sha}\",\n \"statuses_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/statuses/{sha}\",\n \"languages_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/languages\",\n \"stargazers_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/stargazers\",\n \"contributors_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/contributors\",\n \"subscribers_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/subscribers\",\n \"subscription_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/subscription\",\n \"commits_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/commits{/sha}\",\n \"git_commits_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/git/commits{/sha}\",\n \"comments_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/comments{/number}\",\n \"issue_comment_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/issues/comments{/number}\",\n \"contents_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/contents/{+path}\",\n \"compare_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/compare/{base}...{head}\",\n \"merges_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/merges\",\n \"archive_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/{archive_format}{/ref}\",\n \"downloads_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/downloads\",\n \"issues_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/issues{/number}\",\n \"pulls_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/pulls{/number}\",\n \"milestones_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/milestones{/number}\",\n \"notifications_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/notifications{?since,all,participating}\",\n \"labels_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/labels{/name}\",\n \"releases_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/releases{/id}\",\n \"deployments_url\": \"https://api.github.com/repos/Rennasccenth/Metodos-Computacionais-UFMT-2019/deployments\",\n \"created_at\": \"2019-08-28T05:23:18Z\",\n \"updated_at\": \"2021-03-21T07:14:05Z\",\n \"pushed_at\": \"2019-08-28T05:48:04Z\",\n \"git_url\": \"git://github.com/Rennasccenth/Metodos-Computacionais-UFMT-2019.git\",\n \"ssh_url\": \"git@github.com:Rennasccenth/Metodos-Computacionais-UFMT-2019.git\",\n \"clone_url\": \"https://github.com/Rennasccenth/Metodos-Computacionais-UFMT-2019.git\",\n \"svn_url\": \"https://github.com/Rennasccenth/Metodos-Computacionais-UFMT-2019\",\n \"homepage\": null,\n \"size\": 22,\n \"stargazers_count\": 0,\n \"watchers_count\": 0,\n \"language\": \"Python\",\n \"has_issues\": true,\n \"has_projects\": true,\n \"has_downloads\": true,\n \"has_wiki\": true,\n \"has_pages\": false,\n \"forks_count\": 0,\n \"mirror_url\": null,\n \"archived\": false,\n \"disabled\": false,\n \"open_issues_count\": 0,\n \"license\": null,\n \"forks\": 0,\n \"open_issues\": 0,\n \"watchers\": 0,\n \"default_branch\": \"master\",\n \"permissions\": {\n \"admin\": true,\n \"push\": true,\n \"pull\": true\n }\n },\n {\n \"id\": 345485440,\n \"node_id\": \"MDEwOlJlcG9zaXRvcnkzNDU0ODU0NDA=\",\n \"name\": \"Sharpening\",\n \"full_name\": \"Rennasccenth/Sharpening\",\n \"private\": false,\n \"owner\": {\n \"login\": \"Rennasccenth\",\n \"id\": 42920088,\n \"node_id\": \"MDQ6VXNlcjQyOTIwMDg4\",\n \"avatar_url\": \"https://avatars.githubusercontent.com/u/42920088?v=4\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/Rennasccenth\",\n \"html_url\": \"https://github.com/Rennasccenth\",\n \"followers_url\": \"https://api.github.com/users/Rennasccenth/followers\",\n \"following_url\": \"https://api.github.com/users/Rennasccenth/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/Rennasccenth/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/Rennasccenth/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/Rennasccenth/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/Rennasccenth/orgs\",\n \"repos_url\": \"https://api.github.com/users/Rennasccenth/repos\",\n \"events_url\": \"https://api.github.com/users/Rennasccenth/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/Rennasccenth/received_events\",\n \"type\": \"User\",\n \"site_admin\": false\n },\n \"html_url\": \"https://github.com/Rennasccenth/Sharpening\",\n \"description\": \".NET features that i wanna try.\",\n \"fork\": false,\n \"url\": \"https://api.github.com/repos/Rennasccenth/Sharpening\",\n \"forks_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/forks\",\n \"keys_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/keys{/key_id}\",\n \"collaborators_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/collaborators{/collaborator}\",\n \"teams_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/teams\",\n \"hooks_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/hooks\",\n \"issue_events_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/issues/events{/number}\",\n \"events_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/events\",\n \"assignees_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/assignees{/user}\",\n \"branches_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/branches{/branch}\",\n \"tags_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/tags\",\n \"blobs_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/git/blobs{/sha}\",\n \"git_tags_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/git/tags{/sha}\",\n \"git_refs_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/git/refs{/sha}\",\n \"trees_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/git/trees{/sha}\",\n \"statuses_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/statuses/{sha}\",\n \"languages_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/languages\",\n \"stargazers_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/stargazers\",\n \"contributors_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/contributors\",\n \"subscribers_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/subscribers\",\n \"subscription_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/subscription\",\n \"commits_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/commits{/sha}\",\n \"git_commits_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/git/commits{/sha}\",\n \"comments_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/comments{/number}\",\n \"issue_comment_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/issues/comments{/number}\",\n \"contents_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/contents/{+path}\",\n \"compare_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/compare/{base}...{head}\",\n \"merges_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/merges\",\n \"archive_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/{archive_format}{/ref}\",\n \"downloads_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/downloads\",\n \"issues_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/issues{/number}\",\n \"pulls_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/pulls{/number}\",\n \"milestones_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/milestones{/number}\",\n \"notifications_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/notifications{?since,all,participating}\",\n \"labels_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/labels{/name}\",\n \"releases_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/releases{/id}\",\n \"deployments_url\": \"https://api.github.com/repos/Rennasccenth/Sharpening/deployments\",\n \"created_at\": \"2021-03-08T00:22:33Z\",\n \"updated_at\": \"2021-03-09T01:22:17Z\",\n \"pushed_at\": \"2021-03-09T01:22:15Z\",\n \"git_url\": \"git://github.com/Rennasccenth/Sharpening.git\",\n \"ssh_url\": \"git@github.com:Rennasccenth/Sharpening.git\",\n \"clone_url\": \"https://github.com/Rennasccenth/Sharpening.git\",\n \"svn_url\": \"https://github.com/Rennasccenth/Sharpening\",\n \"homepage\": null,\n \"size\": 733,\n \"stargazers_count\": 0,\n \"watchers_count\": 0,\n \"language\": \"C#\",\n \"has_issues\": true,\n \"has_projects\": true,\n \"has_downloads\": true,\n \"has_wiki\": true,\n \"has_pages\": false,\n \"forks_count\": 0,\n \"mirror_url\": null,\n \"archived\": false,\n \"disabled\": false,\n \"open_issues_count\": 0,\n \"license\": null,\n \"forks\": 0,\n \"open_issues\": 0,\n \"watchers\": 0,\n \"default_branch\": \"main\",\n \"permissions\": {\n \"admin\": true,\n \"push\": true,\n \"pull\": true\n }\n }\n]" 363 | } 364 | ] 365 | } 366 | ] 367 | } -------------------------------------------------------------------------------- /postman/Stone Desafio API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "27f26f39-5b50-487c-9873-4425b7559771", 4 | "name": "Stone Desafio API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Get User Most Popular Repository", 10 | "request": { 11 | "method": "GET", 12 | "header": [], 13 | "url": { 14 | "raw": "http://localhost:8080/users/rennasccenth/popular-repository", 15 | "protocol": "http", 16 | "host": [ 17 | "localhost" 18 | ], 19 | "port": "8080", 20 | "path": [ 21 | "users", 22 | "rennasccenth", 23 | "popular-repository" 24 | ] 25 | } 26 | }, 27 | "response": [] 28 | } 29 | ] 30 | } --------------------------------------------------------------------------------