Last active
April 17, 2019 09:40
-
-
Save jumarko/2bd8f89c39a7fc2f719ec185d8b0c3f5 to your computer and use it in GitHub Desktop.
fetch github repos branches via GraphQL API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; `get-repos-branches-page` ommited but it basically calls GitHub GraphQL API using the following query | |
(def repo-branches-query " | |
repo_name%s: repository(owner: \"%s\", name: \"%s\") { | |
owner { | |
login | |
} | |
name | |
defaultBranchRef { | |
name | |
} | |
refs(first: %d, after: \"%s\" refPrefix: \"refs/heads/\", orderBy: {field: ALPHABETICAL, direction: ASC}) { | |
pageInfo { | |
hasNextPage, | |
endCursor | |
}, | |
edges { | |
node { | |
name | |
} | |
} | |
} | |
}") | |
;; ... and then returns vector of repositories with the shape like this: | |
(def sample-repo-data | |
{:owner-login (get owner "login") | |
:name name | |
:default-branch (get defaultBranchRef "name") | |
:next-page? (get-in refs ["pageInfo" "hasNextPage"]) | |
:end-cursor (get-in refs ["pageInfo" "endCursor"]) | |
:refs (mapv (fn parse-branch [refs-edge] | |
(get-in refs-edge ["node" "name"])) | |
(get refs "edges"))}) | |
;;; specs | |
(s/def ::owner-login ::specs/non-empty-string) | |
(s/def ::name ::specs/non-empty-string) | |
;; nil end-cursor means "the first page" | |
(s/def ::end-cursor (s/nilable string?)) | |
(s/def ::repo (s/keys :req-un [::owner-login ::name] | |
:opt-un [::end-cursor])) | |
(s/def ::default-branch (s/nilable string?)) | |
(s/def ::branch-name ::specs/non-empty-string) | |
(s/def ::refs (s/coll-of ::branch-name)) | |
(s/def ::repo-with-branches (s/keys :req-un [::owner-login ::name ::default-branch ::refs])) | |
;;; get-repos-branches - paging implementation and calling `get-repos-branches-page` for fetching actual data | |
(s/fdef get-repos-branches | |
:args (s/cat | |
:repos (s/coll-of ::repo) | |
:access-token ::specs/non-empty-string | |
:max-branches pos-int?) | |
:ret (s/coll-of ::repo-with-branches)) | |
(defn get-repos-branches | |
"Lists branches for all given repos up to `max-branches`. | |
Default branch is always returned separately, even if not included in `refs`." | |
[repos access-token max-branches] | |
(log/infof "Listing branches (up to %d branches per repo) for %d repos: %s" | |
max-branches (count repos) (mapv :name repos)) | |
(loop [fetched-branches-so-far 0 | |
repos repos | |
repos-with-branches {}] | |
(if (or | |
(empty? repos) | |
(>= fetched-branches-so-far max-branches)) | |
(vals repos-with-branches) | |
(let [current-page-repos-data (get-repos-branches-page repos access-token) | |
;; this contains all repos with all their branches that we've fetched so far | |
repos-with-updated-branches (reduce (fn [updated-repos | |
{:keys [owner-login name refs] :as repo}] | |
(update updated-repos [owner-login name] | |
#(if % | |
(update % :refs into refs) | |
;; we're processing the first page | |
repo))) | |
repos-with-branches | |
current-page-repos-data)] | |
(recur (+ fetched-branches-so-far max-github-api-page-size) | |
;; continue only with repos which have more branches | |
(filterv :next-page? current-page-repos-data) | |
repos-with-updated-branches))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment