diff --git a/README.md b/README.md index f574d8e..cdd2297 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ Here's a table that matches up the provided `GitHubType`s with their correspondi |---------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Owner` | login, e.g. `"octocat"` | [organizations](https://developer.github.com/v3/orgs/), [users](https://developer.github.com/v3/users/) | | `Repo` | full_name, e.g. `"JuliaWeb/GitHub.jl"` | [repositories](https://developer.github.com/v3/repos/) | -| `Commit` | sha, e.g. `"d069993b320c57b2ba27336406f6ec3a9ae39375"` | [repository commits](https://developer.github.com/v3/repos/commits/) | -| `GitCommit` | sha, e.g. `"d069993b320c57b2ba27336406f6ec3a9ae39375"` | [raw git commits](https://developer.github.com/v3/git/commits/) | +| `Commit` | sha, e.g. `"d069993b320c57b2ba27336406f6ec3a9ae39375"` | [repository commits](https://developer.github.com/v3/repos/commits/) | +| `GitCommit` | sha, e.g. `"d069993b320c57b2ba27336406f6ec3a9ae39375"` | [raw git commits](https://developer.github.com/v3/git/commits/) | | `Branch` | name, e.g. `master` | [repository branches](https://developer.github.com/v3/repos/#get-branch) | | `Content` | path, e.g. `"src/owners/owners.jl"` | [repository contents](https://developer.github.com/v3/repos/contents/) | | `Comment` | id, e.g. `162224613` | [commit comments](https://developer.github.com/v3/repos/comments/), [issue comments](https://developer.github.com/v3/issues/comments/), [PR review comments](https://developer.github.com/v3/pulls/comments/) | @@ -54,11 +54,11 @@ Here's a table that matches up the provided `GitHubType`s with their correspondi | `Issue` | number, e.g. `31` | [issues](https://developer.github.com/v3/issues/) | | `Team` | id, e.g. `1` | [teams](https://developer.github.com/v3/orgs/teams) | | `Gist` | id, e.g. `0bace7cc774df4b3a4b0ee9aaa271ef6` | [gists](https://developer.github.com/v3/gists) | -| `Review` | id, e.g. `1` | [reviews](https://developer.github.com/v3/pulls/reviews/) | -| `Blob` | sha, e.g. `"95c8d1aa2a7b1e6d672e15b67e0df4abbe57dcbe"` | [raw git blobs](https://developer.github.com/v3/git/blobs/) -| `Tree` | sha, e.g. `"78e524d5e979e326a7c144ce195bf94ca9b04fa0"` | [raw git trees](https://developer.github.com/v3/git/trees/) -| `Tag` | tag name, e.g. `v1.0` | [git tags](https://developer.github.com/v3/git/tags/) -| `References` | reference name, e.g. `heads/master` (note: omits leading `refs/`) | [git tags](https://developer.github.com/v3/git/refs/) +| `Review` | id, e.g. `1` | [reviews](https://developer.github.com/v3/pulls/reviews/) | +| `Blob` | sha, e.g. `"95c8d1aa2a7b1e6d672e15b67e0df4abbe57dcbe"` | [raw git blobs](https://developer.github.com/v3/git/blobs/) | +| `Tree` | sha, e.g. `"78e524d5e979e326a7c144ce195bf94ca9b04fa0"` | [raw git trees](https://developer.github.com/v3/git/trees/) | +| `Tag` | tag name, e.g. `v1.0` | [git tags](https://developer.github.com/v3/git/tags/) | +| `References` | reference name, e.g. `heads/master` (note: omits leading `refs/`) | [git tags](https://developer.github.com/v3/git/refs/) | You can inspect which fields are available for a type `G<:GitHubType` by calling `fieldnames(G)`. @@ -89,7 +89,7 @@ GitHub.jl implements a bunch of methods that make REST requests to GitHub's API. | method | return type | documentation | |------------------------------------------|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `repo(repo)` | `Repo` | [get `repo`](https://developer.github.com/v3/repos/#get) | +| `repo(repo)` | `Repo` | [get `repo`](https://developer.github.com/v3/repos/#get) | | `create_repo(owner, name, params=Dict{String,String}())` | `Repo` | [create a repository of the given `name` in the given `owner`'s account](https://developer.github.com/v3/repos/#create) | | `create_fork(repo)` | `Repo` | [create a fork of `repo`](https://developer.github.com/v3/repos/forks/#create-a-fork) | | `forks(repo)` | `Tuple{Vector{Repo}, Dict}` | [get `repo`'s forks](https://developer.github.com/v3/repos/forks/#list-forks) | diff --git a/REQUIRE b/REQUIRE index 35a3d93..5cde9ae 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,6 +1,8 @@ julia 0.6 +Compat 0.43 JSON MbedTLS -HTTP 0.5.4 -HttpCommon # for deprecations +HTTP 0.6 +Nullables + diff --git a/src/GitHub.jl b/src/GitHub.jl index d0cd0e2..2ba2191 100644 --- a/src/GitHub.jl +++ b/src/GitHub.jl @@ -4,6 +4,7 @@ module GitHub using Compat using Compat.Dates +using Nullables if VERSION >= v"0.7.0-DEV.2338" using Base64 @@ -15,8 +16,7 @@ end import HTTP, JSON, - MbedTLS, - HttpCommon # for deprecations + MbedTLS ######## # init # diff --git a/src/activity/events.jl b/src/activity/events.jl index a537baa..bd66aea 100644 --- a/src/activity/events.jl +++ b/src/activity/events.jl @@ -35,15 +35,15 @@ end # Validation Functions # ######################## -has_event_header(request::HTTP.Request) = haskey(HTTP.headers(request), "X-Github-Event") -event_header(request::HTTP.Request) = HTTP.headers(request)["X-Github-Event"] +has_event_header(request::HTTP.Request) = HTTP.hasheader(request, "X-Github-Event") +event_header(request::HTTP.Request) = HTTP.header(request, "X-Github-Event") -has_sig_header(request::HTTP.Request) = haskey(HTTP.headers(request), "X-Hub-Signature") -sig_header(request::HTTP.Request) = HTTP.headers(request)["X-Hub-Signature"] +has_sig_header(request::HTTP.Request) = HTTP.hasheader(request, "X-Hub-Signature") +sig_header(request::HTTP.Request) = HTTP.header(request, "X-Hub-Signature") function has_valid_secret(request::HTTP.Request, secret) if has_sig_header(request) - secret_sha = "sha1="*bytes2hex(MbedTLS.digest(MbedTLS.MD_SHA1, String(request), secret)) + secret_sha = "sha1="*bytes2hex(MbedTLS.digest(MbedTLS.MD_SHA1, HTTP.load(request), secret)) return sig_header(request) == secret_sha end return false @@ -62,34 +62,26 @@ end ################# struct EventListener - server::HTTP.Server + handle_request repos events function EventListener(handle; auth::Authorization = AnonymousAuth(), secret = nothing, events = nothing, repos = nothing, forwards = nothing) - if !(isa(forwards, Void)) + if !(isa(forwards, Nothing)) forwards = map(HTTP.URI, forwards) end - if !(isa(repos, Void)) + if !(isa(repos, Nothing)) repos = map(name, repos) end - server = HTTP.Server() do request, response - try - handle_event_request(request, handle; auth = auth, - secret = secret, events = events, - repos = repos, forwards = forwards) - catch err - bt = catch_backtrace() - print(STDERR, "SERVER ERROR: ") - Base.showerror(STDERR, err, bt) - return HTTP.Response(500) - end - end + handle_request = request::HTTP.Request -> + handle_event_request(request, handle; auth = auth, + secret = secret, events = events, + repos = repos, forwards = forwards) - return new(server, repos, events) + return new(handle_request, repos, events) end end @@ -97,34 +89,27 @@ function handle_event_request(request, handle; auth::Authorization = AnonymousAuth(), secret = nothing, events = nothing, repos = nothing, forwards = nothing) - if !(isa(secret, Void)) && !(has_valid_secret(request, secret)) + if !(isa(secret, Nothing)) && !(has_valid_secret(request, secret)) return HTTP.Response(400, "invalid signature") end - if !(isa(events, Void)) && !(is_valid_event(request, events)) + if !(isa(events, Nothing)) && !(is_valid_event(request, events)) return HTTP.Response(204, "event ignored") end - event = event_from_payload!(event_header(request), JSON.parse(String(request))) + event = event_from_payload!(event_header(request), JSON.parse(HTTP.load(request))) - if !(isa(repos, Void)) && !(from_valid_repo(event, repos)) + if !(isa(repos, Nothing)) && !(from_valid_repo(event, repos)) return HTTP.Response(400, "invalid repo") end - if !(isa(forwards, Void)) + if !(isa(forwards, Nothing)) for address in forwards HTTP.post(address, request) end end - retval = handle(event) - if retval isa HttpCommon.Response - Base.depwarn("event handlers should return an `HTTP.Response` instead of an `HttpCommon.Response`, - making a best effort to convert to an `HTTP.Response`", :handle_event_request) - retval = HTTP.Response(; status = retval.status, headers = convert(Dict{String, String}, retval.headers), - body = HTTP.FIFOBuffer(retval.data)) - end - return retval + return handle(event) end function Base.run(listener, args...; host = nothing, port = nothing, kwargs...) @@ -136,9 +121,9 @@ end function Base.run(listener::EventListener, host::HTTP.IPAddr, port::Int, args...; kwargs...) println("Listening for GitHub events sent to $port;") - println("Whitelisted events: $(isa(listener.events, Void) ? "All" : listener.events)") - println("Whitelisted repos: $(isa(listener.repos, Void) ? "All" : listener.repos)") - return HTTP.serve(listener.server, host, port, args...; kwargs...) + println("Whitelisted events: $(isa(listener.events, Nothing) ? "All" : listener.events)") + println("Whitelisted repos: $(isa(listener.repos, Nothing) ? "All" : listener.repos)") + HTTP.listen(listener.handle_request, host, port; kwargs...) end ################### diff --git a/src/apps/installations.jl b/src/apps/installations.jl index 0f768c7..98d709c 100644 --- a/src/apps/installations.jl +++ b/src/apps/installations.jl @@ -25,5 +25,5 @@ end headers["Accept"] = "application/vnd.github.machine-man-preview+json" results, page_data = github_paged_get(api, "/installation/repositories"; headers=headers, options...) - mapreduce(x->map(Repo, JSON.parse(String(x))["repositories"]), vcat, Repo[], results), page_data + mapreduce(x->map(Repo, JSON.parse(HTTP.load(x))["repositories"]), vcat, Repo[], results), page_data end \ No newline at end of file diff --git a/src/git/blob.jl b/src/git/blob.jl index ce7fa1a..cbd2421 100644 --- a/src/git/blob.jl +++ b/src/git/blob.jl @@ -1,7 +1,7 @@ mutable struct Blob <: GitHubType content::Nullable{String} encoding::Nullable{String} - url::Nullable{HttpCommon.URI} + url::Nullable{HTTP.URI} sha::Nullable{String} size::Nullable{Int} end diff --git a/src/git/gitcommit.jl b/src/git/gitcommit.jl index b666007..2c77404 100644 --- a/src/git/gitcommit.jl +++ b/src/git/gitcommit.jl @@ -1,6 +1,6 @@ mutable struct GitCommit <: GitHubType sha::Nullable{String} - url::Nullable{HttpCommon.URI} + url::Nullable{HTTP.URI} author::Nullable{Dict} commiter::Nullable{Dict} message::Nullable{String} diff --git a/src/git/reference.jl b/src/git/reference.jl index 39be9b6..df4a682 100644 --- a/src/git/reference.jl +++ b/src/git/reference.jl @@ -1,6 +1,6 @@ mutable struct Reference <: GitHubType ref::Nullable{String} - url::Nullable{HttpCommon.URI} + url::Nullable{HTTP.URI} object::Nullable{Dict} end diff --git a/src/git/tag.jl b/src/git/tag.jl index fe3d476..a071137 100644 --- a/src/git/tag.jl +++ b/src/git/tag.jl @@ -1,7 +1,7 @@ mutable struct Tag <: GitHubType tag::Nullable{String} sha::Nullable{String} - url::Nullable{HttpCommon.URI} + url::Nullable{HTTP.URI} message::Nullable{String} tagger::Nullable{Dict} object::Nullable{Dict} diff --git a/src/git/tree.jl b/src/git/tree.jl index 4c84d12..a6f51bf 100644 --- a/src/git/tree.jl +++ b/src/git/tree.jl @@ -1,6 +1,6 @@ mutable struct Tree <: GitHubType sha::Nullable{String} - url::Nullable{HttpCommon.URI} + url::Nullable{HTTP.URI} tree::Nullable{Vector} truncated::Nullable{Bool} end diff --git a/src/repositories/contents.jl b/src/repositories/contents.jl index 3332bac..3f7deb9 100644 --- a/src/repositories/contents.jl +++ b/src/repositories/contents.jl @@ -62,7 +62,7 @@ function permalink(content::Content, commit) prefix = get(content.typ) == "file" ? "blob" : "tree" rgx = Regex(string("/", prefix, "/.*?/")) replacement = string("/", prefix, "/", name(commit), "/") - return HTTP.URI(replace(url, rgx, replacement)) + return HTTP.URI(replace(url, rgx => replacement)) end ########################### diff --git a/src/utils/GitHubType.jl b/src/utils/GitHubType.jl index cb743d4..855462b 100644 --- a/src/utils/GitHubType.jl +++ b/src/utils/GitHubType.jl @@ -49,7 +49,7 @@ name(g::GitHubType) = get(namefield(g)) function extract_nullable(data::Dict, key, ::Type{T}) where {T} if haskey(data, key) val = data[key] - if !(isa(val, Void)) + if !(isa(val, Nothing)) if T <: Vector V = eltype(T) return Nullable{T}(V[prune_github_value(v, V) for v in val]) diff --git a/src/utils/auth.jl b/src/utils/auth.jl index 0e9cbf0..488692d 100644 --- a/src/utils/auth.jl +++ b/src/utils/auth.jl @@ -19,7 +19,7 @@ end #################### function base64_to_base64url(string) - replace(replace(replace(string, "=", ""), '+', '-'), '/', '_') + replace(replace(replace(string, "=" => ""), '+' => '-'), '/' => '_') end function JWTAuth(app_id::Int, key::MbedTLS.PKContext; iat = now(Dates.UTC), exp_mins = 1) diff --git a/src/utils/requests.jl b/src/utils/requests.jl index 4d47a1a..f3dec3c 100644 --- a/src/utils/requests.jl +++ b/src/utils/requests.jl @@ -12,7 +12,7 @@ struct GitHubWebAPI <: GitHubAPI endpoint::HTTP.URI end -const DEFAULT_API = GitHubWebAPI(HTTP.URL("https://api.github.com")) +const DEFAULT_API = GitHubWebAPI(HTTP.URI("https://api.github.com")) using Base.Meta """ @@ -40,7 +40,7 @@ end # Default API URIs # #################### -api_uri(api::GitHubWebAPI, path) = HTTP.URL(string(api.endpoint), path = path) +api_uri(api::GitHubWebAPI, path) = merge(api.endpoint, path = path) api_uri(api::GitHubAPI, path) = error("URI retrieval not implemented for this API type") ####################### @@ -54,10 +54,11 @@ function github_request(api::GitHubAPI, request_method, endpoint; params = github2json(params) api_endpoint = api_uri(api, endpoint) _headers = convert(Dict{String, String}, headers) + !haskey(_headers, "User-Agent") && (_headers["User-Agent"] = "GitHub-jl") if request_method == HTTP.get - r = request_method(string(api_endpoint), headers = _headers, query = params, allowredirects = allowredirects, statusraise = false) + r = request_method(merge(api_endpoint, query = params), _headers, redirect = allowredirects, status_exception = false) else - r = request_method(string(api_endpoint); headers = _headers, body = JSON.json(params), allowredirects = allowredirects, statusraise = false) + r = request_method(string(api_endpoint), _headers, JSON.json(params), redirect = allowredirects, status_exception = false) end handle_error && handle_response_error(r) return r @@ -69,11 +70,11 @@ gh_put(api::GitHubAPI, endpoint = ""; options...) = github_request(api, HTTP.put gh_delete(api::GitHubAPI, endpoint = ""; options...) = github_request(api, HTTP.delete, endpoint; options...) gh_patch(api::GitHubAPI, endpoint = ""; options...) = github_request(api, HTTP.patch, endpoint; options...) -gh_get_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh_get(api, endpoint; options...)))) -gh_post_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh_post(api, endpoint; options...)))) -gh_put_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh_put(api, endpoint; options...)))) -gh_delete_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh_delete(api, endpoint; options...)))) -gh_patch_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh_patch(api, endpoint; options...)))) +gh_get_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(HTTP.load((gh_get(api, endpoint; options...)))) +gh_post_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(HTTP.load((gh_post(api, endpoint; options...)))) +gh_put_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(HTTP.load((gh_put(api, endpoint; options...)))) +gh_delete_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(HTTP.load((gh_delete(api, endpoint; options...)))) +gh_patch_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(HTTP.load((gh_patch(api, endpoint; options...)))) ################# # Rate Limiting # @@ -85,8 +86,8 @@ gh_patch_json(api::GitHubAPI, endpoint = ""; options...) = JSON.parse(String((gh # Pagination # ############## -has_page_links(r) = haskey(HTTP.headers(r), "Link") -get_page_links(r) = split(HTTP.headers(r)["Link"], ',') +has_page_links(r) = HTTP.hasheader(r, "Link") +get_page_links(r) = split(HTTP.header(r, "Link",), ",") function find_page_link(links, rel) relstr = "rel=\"$(rel)\"" @@ -103,6 +104,7 @@ extract_page_url(link) = match(r"<.*?>", link).match[2:end-1] function github_paged_get(api, endpoint; page_limit = Inf, start_page = "", handle_error = true, headers = Dict(), params = Dict(), options...) _headers = convert(Dict{String, String}, headers) + !haskey(_headers, "User-Agent") && (_headers["User-Agent"] = "GitHub-jl") if isempty(start_page) r = gh_get(api, endpoint; handle_error = handle_error, headers = _headers, params = params, options...) else @@ -135,7 +137,7 @@ end function gh_get_paged_json(api, endpoint = ""; options...) results, page_data = github_paged_get(api, endpoint; options...) - return mapreduce(r -> JSON.parse(String(r)), vcat, results), page_data + return mapreduce(r -> JSON.parse(HTTP.load(r)), vcat, results), page_data end ################## @@ -146,7 +148,7 @@ function handle_response_error(r::HTTP.Response) if r.status >= 400 message, docs_url, errors = "", "", "" try - data = JSON.parse(String(r)) + data = JSON.parse(HTTP.load(r)) message = get(data, "message", "") docs_url = get(data, "documentation_url", "") errors = get(data, "errors", "") diff --git a/test/auth_tests.jl b/test/auth_tests.jl index d28891a..27876d0 100644 --- a/test/auth_tests.jl +++ b/test/auth_tests.jl @@ -7,13 +7,13 @@ _qCoY2kjLdriWiLFHznJvG6jfPHK-iX9VIolNjkiM9e4DG9Aq60UnZ_df40wZXd 696sRpgCakvIV3mQTmRv9IfOLVF9eRRD4yVvwTtYNGOqewpQqkPnm6K3ctYlQIX kwKMynp6R-CgbwRedA4n0WAvy1o14TyZZ-QAChQUcS-OKb0ZM4z-fbG5ZSpWP7f wsQxsZgWFIz6hodiw_q45bHYsLw -""",'\n',"") +""",'\n' => "") # Fix iat, to make sure the payload is reproducible auth = GitHub.JWTAuth(1234, joinpath(dirname(@__FILE__), "not_a_real_key.pem"); iat = DateTime("2016-9-15T14:00")) key = MbedTLS.PKContext() MbedTLS.parse_key!(key, - readstring(joinpath(dirname(@__FILE__), "not_a_real_key.pem"))) + read(joinpath(dirname(@__FILE__), "not_a_real_key.pem"), String)) auth = GitHub.JWTAuth(1234, joinpath(dirname(@__FILE__), "not_a_real_key.pem"); iat = DateTime("2016-9-15T14:00")) auth2 = GitHub.JWTAuth(1234, key; iat = DateTime("2016-9-15T14:00")) diff --git a/test/commit_comment.jl b/test/commit_comment.jl index d55315f..94268a2 100644 --- a/test/commit_comment.jl +++ b/test/commit_comment.jl @@ -16,5 +16,5 @@ function create_event() "X-Hub-Signature" => "sha1=494685b443b34bb0241a14f82604a0600b18f996", ) - return HTTP.Request(headers = headers, body = HTTP.FIFOBuffer(base64decode(data_base64))) + return HTTP.Request("GET", "http://www.github.com", headers, base64decode(data_base64)) end diff --git a/test/event_tests.jl b/test/event_tests.jl index 9114c05..1267ada 100644 --- a/test/event_tests.jl +++ b/test/event_tests.jl @@ -1,6 +1,6 @@ include("commit_comment.jl") event_request = create_event() -event_json = JSON.parse(String(event_request)) +event_json = JSON.parse(HTTP.load(event_request)) event = GitHub.event_from_payload!("commit_comment", event_json) @testset "WebhookEvent" begin @@ -26,7 +26,7 @@ end # testset repos = [Repo("JuliaCI/BenchmarkTrackers.jl"), "JuliaWeb/GitHub.jl"], events = ["commit_comment"], forwards = ["http://bob.com", HTTP.URI("http://jim.org")]) - r = listener.server.handler.func(HTTP.Request(), HTTP.Response()) + r = listener.handle_request(HTTP.Request()) r.status == 400 end end @@ -40,7 +40,7 @@ end repos = [Repo("JuliaCI/BenchmarkTrackers.jl"), "JuliaWeb/GitHub.jl"], forwards = ["http://bob.com", HTTP.URI("http://jim.org")], check_collab = false) - r = listener.listener.server.handler.func(HTTP.Request(), HTTP.Response()) + r = listener.listener.handle_request(HTTP.Request()) r.status == 400 end end diff --git a/test/read_only_api_tests.jl b/test/read_only_api_tests.jl index 08771c0..8cc4850 100644 --- a/test/read_only_api_tests.jl +++ b/test/read_only_api_tests.jl @@ -189,7 +189,7 @@ end @test entry["type"] == "blob" b = blob(github_jl, entry["sha"]; auth=auth) - @test contains(String(base64decode(replace(get(b.content),"\n",""))), "GitHub.jl") + @test contains(String(base64decode(replace(get(b.content),"\n" => ""))), "GitHub.jl") break end diff --git a/test/runtests.jl b/test/runtests.jl index a45ed33..c9eba60 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using GitHub, JSON, HTTP, MbedTLS +using GitHub, JSON, HTTP, MbedTLS, Nullables using Compat, Compat.Dates, Compat.Test using GitHub: Branch, name