Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ addons:
- hdf5-tools

sudo: false
#script:
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
# - julia -e 'Pkg.clone(pwd()); Pkg.build("GitHub"); Pkg.test("GitHub"; coverage=true)';
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.build("GitHub"); Pkg.checkout("HTTP"); Pkg.test("GitHub"; coverage=true)';
after_success:
- julia -e 'cd(Pkg.dir("GitHub")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Here's a table of contents for this rather lengthy README:

GitHub's JSON responses are parsed and returned to the caller as types of the form `G<:GitHub.GitHubType`. Here's some useful information about these types:

- All fields are `Nullable`.
- All fields can be [`nothing`] which matches `null` from the GitHub API.
- Field names generally match the corresponding field in GitHub's JSON representation (the exception is `"type"`, which has the corresponding field name `typ` to avoid the obvious language conflict).
- `GitHubType`s can be passed as arguments to API methods in place of (and in combination with) regular identifying properties. For example, `create_status(repo, commit)` could be called as:

Expand All @@ -55,10 +55,10 @@ Here's a table that matches up the provided `GitHubType`s with their correspondi
| `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/)
| `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)`.
Expand Down Expand Up @@ -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) |
Expand Down Expand Up @@ -482,14 +482,14 @@ listener = GitHub.CommentListener(trigger; auth = myauth, secret = mysecret) do
reply_to = event.payload["issue"]["number"]
elseif event.kind == "commit_comment"
comment_kind = :commit
reply_to = get(comment.commit_id)
reply_to = comment.commit_id
elseif event.kind == "pull_request_review_comment"
comment_kind = :review
reply_to = event.payload["pull_request"]["number"]
# load required query params for review comment creation
comment_params["commit_id"] = get(comment.commit_id)
comment_params["path"] = get(comment.path)
comment_params["position"] = get(comment.position)
comment_params["commit_id"] = comment.commit_id
comment_params["path"] = comment.path
comment_params["position"] = comment.position
end

# send the comment creation request to GitHub
Expand Down
1 change: 0 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ julia 0.6
JSON
MbedTLS
HTTP 0.5.4
HttpCommon # for deprecations
6 changes: 3 additions & 3 deletions src/GitHub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ module GitHub

using Compat
using Compat.Dates

if VERSION >= v"0.7.0-DEV.2338"
using Base64
end

const ?{T} = Union{T, Nothing}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is allowed on 0.7. IIRC Carlo Baldassi submitted a PR that removes the parsing of ? as an identifier.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want a shorthand for Union{T, Nothing}, I recommend something like Maybe{T}.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll change this.


##########
# import #
##########

import HTTP,
JSON,
MbedTLS,
HttpCommon # for deprecations
MbedTLS

#############
# Utilities #
Expand Down
75 changes: 30 additions & 45 deletions src/activity/events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
#####################

mutable struct WebhookEvent
kind::String
payload::Dict
repository::Repo
sender::Owner
kind :: String
payload :: Dict
repository :: Repo
sender :: Owner
end

function event_from_payload!(kind, data::Dict)
Expand Down Expand Up @@ -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
Expand All @@ -62,69 +62,54 @@ 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

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))
return HTTP.Response(400, "invalid signature")
if !(isa(secret, Nothing)) && !(has_valid_secret(request, secret))
return HTTP.Response(400)
end

if !(isa(events, Void)) && !(is_valid_event(request, events))
return HTTP.Response(204, "event ignored")
if !(isa(events, Nothing)) && !(is_valid_event(request, events))
return HTTP.Response(204)
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))
return HTTP.Response(400, "invalid repo")
if !(isa(repos, Nothing)) && !(from_valid_repo(event, repos))
return HTTP.Response(400)
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...)
Expand All @@ -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

###################
Expand Down Expand Up @@ -194,7 +179,7 @@ function handle_comment(handle, event::WebhookEvent, auth::Authorization,

trigger_match = match(trigger, body_container["body"])

if trigger_match == nothing
if trigger_match === nothing
return HTTP.Response(204, "trigger match not found")
end

Expand Down
14 changes: 7 additions & 7 deletions src/apps/apps.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
mutable struct App <: GitHubType
id::Nullable{Int}
owner::Nullable{Owner}
name::Nullable{String}
description::Nullable{String}
external_url::Nullable{String}
html_url::Nullable{String}
id :: ?{Int}
owner :: ?{Owner}
name :: ?{String}
description :: ?{String}
external_url :: ?{String}
html_url :: ?{String}
end

namefield(a::App) = a.id
name(a::App) = a.id
App(data::Dict) = json2github(App, data)

@api_default function app(api::GitHubAPI; headers = Dict(), kwargs...)
Expand Down
11 changes: 5 additions & 6 deletions src/apps/installations.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
mutable struct Installation <: GitHubType
id::Nullable{Int}
id :: ?{Int}
end

namefield(i::Installation) = i.id
name(i::Installation) = i.id

Installation(data::Dict) = json2github(Installation, data)
Installation(id::Int) = Installation(Dict("id" => id))

@api_default function create_access_token(api::GitHubAPI, i::Installation, auth::JWTAuth; headers = Dict(), options...)
headers["Accept"] = "application/vnd.github.machine-man-preview+json"
payload = gh_post_json(api, "/installations/$(get(i.id))/access_tokens", auth = auth,
payload = gh_post_json(api, "/installations/$(i.id)/access_tokens", auth = auth,
headers=headers, options...)
OAuth2(payload["token"])
end
Expand All @@ -25,5 +24,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
end
mapreduce(x->map(Repo, JSON.parse(HTTP.load(x))["repositories"]), vcat, Repo[], results), page_data
end
40 changes: 20 additions & 20 deletions src/gists/gist.jl
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
mutable struct Gist <: GitHubType
url::Nullable{HTTP.URI}
forks_url::Nullable{HTTP.URI}
commits_url::Nullable{HTTP.URI}
id::Nullable{String}
description::Nullable{String}
public::Nullable{Bool}
owner::Nullable{Owner}
user::Nullable{Owner}
truncated::Nullable{Bool}
comments::Nullable{Int}
comments_url::Nullable{HTTP.URI}
html_url::Nullable{HTTP.URI}
git_pull_url::Nullable{HTTP.URI}
git_push_url::Nullable{HTTP.URI}
created_at::Nullable{Dates.DateTime}
updated_at::Nullable{Dates.DateTime}
forks::Nullable{Vector{Gist}}
files::Nullable{Dict}
history::Nullable{Vector{Dict}}
url :: ?{HTTP.URI}
forks_url :: ?{HTTP.URI}
commits_url :: ?{HTTP.URI}
id :: ?{String}
description :: ?{String}
public :: ?{Bool}
owner :: ?{Owner}
user :: ?{Owner}
truncated :: ?{Bool}
comments :: ?{Int}
comments_url :: ?{HTTP.URI}
html_url :: ?{HTTP.URI}
git_pull_url :: ?{HTTP.URI}
git_push_url :: ?{HTTP.URI}
created_at :: ?{Dates.DateTime}
updated_at :: ?{Dates.DateTime}
forks :: ?{Vector{Gist}}
files :: ?{Dict}
history :: ?{Vector{Dict}}
end

Gist(data::Dict) = json2github(Gist, data)
Gist(id::AbstractString) = Gist(Dict("id" => id))

namefield(gist::Gist) = gist.id
name(gist::Gist) = gist.id

###############
# API Methods #
Expand Down
12 changes: 6 additions & 6 deletions src/git/blob.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
mutable struct Blob <: GitHubType
content::Nullable{String}
encoding::Nullable{String}
url::Nullable{HttpCommon.URI}
sha::Nullable{String}
size::Nullable{Int}
content :: ?{String}
encoding :: ?{String}
url :: ?{HTTP.URI}
sha :: ?{String}
size :: ?{Int}
end

Blob(data::Dict) = json2github(Blob, data)
Blob(sha::AbstractString) = Blob(Dict("sha" => sha))

namefield(blob::Blob) = blob.sha
name(blob::Blob) = blob.sha

@api_default function blob(api::GitHubAPI, repo, blob_obj; options...)
result = gh_get_json(api, "/repos/$(name(repo))/git/blobs/$(name(blob_obj))"; options...)
Expand Down
18 changes: 9 additions & 9 deletions src/git/gitcommit.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
mutable struct GitCommit <: GitHubType
sha::Nullable{String}
url::Nullable{HttpCommon.URI}
author::Nullable{Dict}
commiter::Nullable{Dict}
message::Nullable{String}
tree::Nullable{Dict}
parents::Nullable{Vector}
verification::Nullable{Dict}
sha :: ?{String}
url :: ?{HTTP.URI}
author :: ?{Dict}
commiter :: ?{Dict}
message :: ?{String}
tree :: ?{Dict}
parents :: ?{Vector}
verification :: ?{Dict}
end

GitCommit(data::Dict) = json2github(GitCommit, data)
namefield(gitcommit::GitCommit) = gitcommit.sha
name(gitcommit::GitCommit) = gitcommit.sha

@api_default function gitcommit(api::GitHubAPI, repo, commit_obj; options...)
result = gh_get_json(api, "/repos/$(name(repo))/git/commits/$(name(commit_obj))"; options...)
Expand Down
Loading