issue 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/issue.rb +4 -3
- data/lib/issue/error.rb +15 -0
- data/lib/issue/payload.rb +112 -0
- data/lib/issue/version.rb +1 -1
- data/lib/issue/webhook.rb +92 -0
- metadata +34 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10960451c8f6e5f752cc0a2b892ae8b8d122a14e1aaedc3671daa2da41cb80b8
|
4
|
+
data.tar.gz: b84a85ae117417a348dacfde4b387128190dcb72232e69abb96805eed8585926
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60caa264fa776efee538761cfa9d06794859bca2490247e9cfdc76e1ea2c86a6e78b6e19fead11f98f522607c26224114b7db564404dd39657c35043f605ef87
|
7
|
+
data.tar.gz: 501ac0d34f7c80334a66ce90574b2677750b1b23287d2bb65682783fd21413504dec767e6decba5307ac8ddcf0c20beaa5df188f1e23d097ae2849baa66f2da0
|
data/lib/issue.rb
CHANGED
data/lib/issue/error.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Issue
|
2
|
+
class Error
|
3
|
+
attr :status
|
4
|
+
attr :message
|
5
|
+
|
6
|
+
# Initialize Issue::Error object with:
|
7
|
+
# status: html status code
|
8
|
+
# msg: message to send back in response
|
9
|
+
def initialize(status, msg)
|
10
|
+
@status = status
|
11
|
+
@message = msg
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Issue
|
5
|
+
class Payload
|
6
|
+
attr_accessor :context
|
7
|
+
|
8
|
+
# Initialize Issue::Payload object with:
|
9
|
+
# json_data: the json sent from a GitHub webhook
|
10
|
+
# event: the value of the HTTP_X_GITHUB_EVENT header
|
11
|
+
#
|
12
|
+
# Initializing a new Issue::Payload instance makes all this info
|
13
|
+
# from the json webhook available via accessor methods:
|
14
|
+
#
|
15
|
+
# action
|
16
|
+
# event
|
17
|
+
# issue_id
|
18
|
+
# issue_title
|
19
|
+
# issue_body
|
20
|
+
# issue_author
|
21
|
+
# repo
|
22
|
+
# sender
|
23
|
+
# event_action
|
24
|
+
# raw_payload
|
25
|
+
#
|
26
|
+
# And if the case the event is 'issue_comment' also:
|
27
|
+
#
|
28
|
+
# comment_body
|
29
|
+
# comment_created_at
|
30
|
+
# comment_url
|
31
|
+
#
|
32
|
+
def initialize(json_data, event)
|
33
|
+
action = json_data.dig("action")
|
34
|
+
sender = json_data.dig("sender", "login")
|
35
|
+
issue_id = json_data.dig("issue", "number")
|
36
|
+
issue_title = json_data.dig("issue", "title")
|
37
|
+
issue_body = json_data.dig("issue", "body")
|
38
|
+
issue_labels = json_data.dig("issue", "labels")
|
39
|
+
issue_author = json_data.dig("issue", "user", "login")
|
40
|
+
repo = json_data.dig("repository", "full_name")
|
41
|
+
|
42
|
+
@context = OpenStruct.new(
|
43
|
+
action: action,
|
44
|
+
event: event,
|
45
|
+
issue_id: issue_id,
|
46
|
+
issue_title: issue_title,
|
47
|
+
issue_body: issue_body,
|
48
|
+
issue_author: issue_author,
|
49
|
+
issue_labels: issue_labels,
|
50
|
+
repo: repo,
|
51
|
+
sender: sender,
|
52
|
+
event_action: "#{event}.#{action}",
|
53
|
+
raw_payload: json_data
|
54
|
+
)
|
55
|
+
|
56
|
+
if event == "issue_comment"
|
57
|
+
@context[:comment_body] = json_data.dig("comment", "body")
|
58
|
+
@context[:comment_created_at] = json_data.dig("comment", "created_at")
|
59
|
+
@context[:comment_url] = json_data.dig("comment", "html_url")
|
60
|
+
end
|
61
|
+
|
62
|
+
@context.each_pair do |method_name, value|
|
63
|
+
define_singleton_method(method_name) {value}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# True if the payload is coming from an issue that has just been opened
|
68
|
+
def opened?
|
69
|
+
action == "opened" || action == "reopened"
|
70
|
+
end
|
71
|
+
|
72
|
+
# True if the payload is coming from an issue that has just been closed
|
73
|
+
def closed?
|
74
|
+
action == "closed"
|
75
|
+
end
|
76
|
+
|
77
|
+
# True if the payload is coming from a new comment
|
78
|
+
def commented?
|
79
|
+
action == "created"
|
80
|
+
end
|
81
|
+
|
82
|
+
# True if the payload is coming from an edition of a comment or issue
|
83
|
+
def edited?
|
84
|
+
action == "edited"
|
85
|
+
end
|
86
|
+
|
87
|
+
# True if the payload is coming from locking an issue
|
88
|
+
def locked?
|
89
|
+
action == "locked"
|
90
|
+
end
|
91
|
+
|
92
|
+
# True if the payload is coming from unlocking an issue
|
93
|
+
def unlocked?
|
94
|
+
action == "unlocked"
|
95
|
+
end
|
96
|
+
|
97
|
+
# True if the payload is coming from pinning or unpinning an issue
|
98
|
+
def pinned?
|
99
|
+
action == "pinned" || action == "unpinned"
|
100
|
+
end
|
101
|
+
|
102
|
+
# True if the payload is coming from un/assigning an issue
|
103
|
+
def assigned?
|
104
|
+
action == "assigned" || action == "unassigned"
|
105
|
+
end
|
106
|
+
|
107
|
+
# True if the payload is coming from un/labeling an issue
|
108
|
+
def labeled?
|
109
|
+
action == "labeled" || action == "unlabeled"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/issue/version.rb
CHANGED
@@ -0,0 +1,92 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "json"
|
3
|
+
require "openssl"
|
4
|
+
require "rack"
|
5
|
+
|
6
|
+
module Issue
|
7
|
+
class Webhook
|
8
|
+
attr_accessor :secret_token
|
9
|
+
attr_accessor :request
|
10
|
+
attr_accessor :accept_origin
|
11
|
+
attr_accessor :discard_sender
|
12
|
+
attr_accessor :accept_events
|
13
|
+
attr_accessor :error
|
14
|
+
attr_accessor :payload
|
15
|
+
|
16
|
+
# Initialize the Issue::Webhook object
|
17
|
+
# This method should receive a Hash with the following settings:
|
18
|
+
# * secret_token: the GitHub secret token needed to verify the request signature
|
19
|
+
# * accept_events: an Array of valid values for the HTTP_X_GITHUB_EVENT header. If empty any event will be precessed.
|
20
|
+
# * origin: the respository where the webhook should be sent to be accepted. If empty any request will be precessed.
|
21
|
+
# * discard_sender: an optional GitHub user handle to discard all events triggered by it.
|
22
|
+
def initialize(settings={})
|
23
|
+
@secret_token = settings[:secret_token]
|
24
|
+
@accept_origin = settings[:origin]
|
25
|
+
@discard_sender = settings[:discard_sender]
|
26
|
+
@accept_events = [settings[:accept_events]].flatten.compact.uniq.map(&:to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method will parse the passed request.
|
30
|
+
# If the request signature is incorrect or any of the conditions set
|
31
|
+
# via the initialization settings are not met an error will be created
|
32
|
+
# with the appropiate html status and message. Otherwise a Issue::Payload
|
33
|
+
# object will be created with the information contained in the request payload.
|
34
|
+
#
|
35
|
+
# This method returns a pair [payload, error] where only one of them will be nil
|
36
|
+
def parse_request(request)
|
37
|
+
@payload = nil
|
38
|
+
@error = nil
|
39
|
+
@request = request
|
40
|
+
|
41
|
+
if verify_signature
|
42
|
+
parse_payload
|
43
|
+
end
|
44
|
+
|
45
|
+
return [payload, error]
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method returns True if parsing a request has generated an Issue::Error object
|
49
|
+
# That object will be available at the #error accessor method.
|
50
|
+
def errored?
|
51
|
+
!error.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def verify_signature
|
57
|
+
gh_signature = request.get_header "HTTP_X_HUB_SIGNATURE"
|
58
|
+
return error!(500, "Can't compute signature") if secret_token.nil? || secret_token.empty?
|
59
|
+
return error!(403, "Request missing signature") if gh_signature.nil? || gh_signature.empty?
|
60
|
+
request.body.rewind
|
61
|
+
payload_body = request.body.read
|
62
|
+
signature = "sha1=" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), secret_token, payload_body)
|
63
|
+
return error!(403, "Signatures didn't match!") unless Rack::Utils.secure_compare(signature, gh_signature)
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_payload
|
68
|
+
begin
|
69
|
+
request.body.rewind
|
70
|
+
json_payload = JSON.parse(request.body.read)
|
71
|
+
event = request.get_header "HTTP_X_GITHUB_EVENT"
|
72
|
+
sender = json_payload.dig("sender", "login")
|
73
|
+
origin = json_payload.dig("repository", "full_name")
|
74
|
+
rescue JSON::ParserError
|
75
|
+
return error!(400, "Malformed request")
|
76
|
+
end
|
77
|
+
|
78
|
+
return error!(422, "No payload") if json_payload.nil? || json_payload.empty?
|
79
|
+
return error!(422, "No event") if event.nil?
|
80
|
+
return error!(200, "Event discarded") unless (accept_events.empty? || accept_events.include?(event))
|
81
|
+
return error!(200, "Event origin discarded") if (discard_sender && sender == discard_sender)
|
82
|
+
return error!(403, "Event origin not allowed") if (accept_origin && origin != accept_origin)
|
83
|
+
|
84
|
+
@payload = Issue::Payload.new(json_payload, event)
|
85
|
+
end
|
86
|
+
|
87
|
+
def error!(status, msg)
|
88
|
+
@error = Issue::Error.new(status, msg)
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: issue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juanjo Bazán
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: openssl
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -47,14 +75,17 @@ extra_rdoc_files: []
|
|
47
75
|
files:
|
48
76
|
- README.md
|
49
77
|
- lib/issue.rb
|
78
|
+
- lib/issue/error.rb
|
79
|
+
- lib/issue/payload.rb
|
50
80
|
- lib/issue/version.rb
|
81
|
+
- lib/issue/webhook.rb
|
51
82
|
homepage: http://github.com/xuanxu/issue
|
52
83
|
licenses:
|
53
84
|
- MIT
|
54
85
|
metadata:
|
55
86
|
bug_tracker_uri: https://github.com/xuanxu/issue/issues
|
56
87
|
changelog_uri: https://github.com/xuanxu/issue/blob/master/CHANGELOG.md
|
57
|
-
documentation_uri: https://www.rubydoc.info/gems/
|
88
|
+
documentation_uri: https://www.rubydoc.info/gems/issue
|
58
89
|
homepage_uri: http://github.com/xuanxu/issue
|
59
90
|
source_code_uri: http://github.com/xuanxu/issue
|
60
91
|
post_install_message:
|