octokit 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -2
- data/lib/octokit/client/authorizations.rb +15 -1
- data/lib/octokit/client/users.rb +5 -1
- data/lib/octokit/configurable.rb +5 -0
- data/lib/octokit/error.rb +23 -3
- data/lib/octokit/version.rb +1 -1
- data/spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/creates_a_new_authorization_with_options.json +1 -0
- data/spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/returns_an_existing_API_authorization_if_one_already_exists.json +1 -0
- data/spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_a_new_API_authorization_each_time.json +1 -0
- data/spec/cassettes/Octokit_Client_Authorizations/_create_authorization/{creates_a_new_authorization_with_options.json → without_idempotent_true/creates_a_new_authorization_with_options.json} +0 -0
- data/spec/cassettes/Octokit_Client_Authorizations/_create_authorization/{creates_an_API_authorization.json → without_idempotent_true/creates_an_API_authorization.json} +0 -0
- data/spec/helper.rb +14 -0
- data/spec/octokit/client/authorizations_spec.rb +47 -11
- data/spec/octokit/client/users_spec.rb +7 -4
- data/spec/octokit/client_spec.rb +30 -0
- metadata +12 -6
data/README.md
CHANGED
@@ -43,8 +43,8 @@ configuration) or as client instance methods.
|
|
43
43
|
```ruby
|
44
44
|
# Provide authentication credentials
|
45
45
|
Octokit.configure do |c|
|
46
|
-
c.login 'defunkt'
|
47
|
-
c.password 'c0d3b4ssssss!'
|
46
|
+
c.login = 'defunkt'
|
47
|
+
c.password = 'c0d3b4ssssss!'
|
48
48
|
end
|
49
49
|
|
50
50
|
# Fetch the current user
|
@@ -297,6 +297,7 @@ client instances based on unique configuration options. Breaking changes also
|
|
297
297
|
include:
|
298
298
|
|
299
299
|
* `:oauth_token` is now `:access_token`
|
300
|
+
* `:auto_traversal` is now `:auto_paginate`
|
300
301
|
* `Hashie::Mash` has been removed. Responses now return a `Sawyer::Resource`
|
301
302
|
object. This new type behaves mostly like a Ruby `Hash`, but does not fully
|
302
303
|
support the `Hashie::Mash` API.
|
@@ -45,6 +45,9 @@ module Octokit
|
|
45
45
|
# @option options [Array] :scopes A list of scopes that this authorization is in.
|
46
46
|
# @option options [String] :note A note to remind you what the OAuth token is for.
|
47
47
|
# @option options [String] :note_url A URL to remind you what app the OAuth token is for.
|
48
|
+
# @option options [Boolean] :idempotent If true, will return an existing authorization if one has already been created.
|
49
|
+
# @option options [String] :client_id Client Id we received when our application was registered with GitHub.
|
50
|
+
# @option options [String] :client_id Client Secret we received when our application was registered with GitHub.
|
48
51
|
#
|
49
52
|
# @return [Sawyer::Resource] A single authorization for the authenticated user
|
50
53
|
# @see http://developer.github.com/v3/oauth/#scopes Available scopes
|
@@ -52,10 +55,21 @@ module Octokit
|
|
52
55
|
# @example Create a new authorization for user ctshryock's project Zoidberg
|
53
56
|
# client = Octokit::Client.new(:login => 'ctshryock', :password => 'secret')
|
54
57
|
# client.create_authorization({:scopes => ["public_repo","gist"], :note => "Why not Zoidberg?", :note_url=> "https://en.wikipedia.org/wiki/Zoidberg"})
|
58
|
+
# @example Create a new OR return an existing authorization to be used by a specific client for user ctshryock's project Zoidberg
|
59
|
+
# client = Octokit::Client.new(:login => 'ctshryock', :password => 'secret')
|
60
|
+
# client.create_authorization({:idempotent => true, :client_id => 'xxxx', :client_secret => 'yyyy', :scopes => ["user"]})
|
55
61
|
def create_authorization(options = {})
|
56
62
|
# Techincally we can omit scopes as GitHub has a default, however the
|
57
63
|
# API will reject us if we send a POST request with an empty body.
|
58
|
-
|
64
|
+
|
65
|
+
if options.delete :idempotent
|
66
|
+
client_id, client_secret = fetch_client_id_and_secret(options)
|
67
|
+
raise ArgumentError.new("Client ID and Secret required for idempotent authorizations") unless client_id && client_secret
|
68
|
+
|
69
|
+
put "authorizations/clients/#{client_id}", options.merge(:client_secret => client_secret)
|
70
|
+
else
|
71
|
+
post 'authorizations', options
|
72
|
+
end
|
59
73
|
end
|
60
74
|
|
61
75
|
# Update an authorization for the authenticated user.
|
data/lib/octokit/client/users.rb
CHANGED
@@ -50,7 +50,11 @@ module Octokit
|
|
50
50
|
options.merge!({
|
51
51
|
:code => code,
|
52
52
|
:client_id => app_id,
|
53
|
-
:client_secret => app_secret
|
53
|
+
:client_secret => app_secret,
|
54
|
+
:headers => {
|
55
|
+
:content_type => 'application/json',
|
56
|
+
:accept => 'application/json'
|
57
|
+
}
|
54
58
|
})
|
55
59
|
post "#{web_endpoint}login/oauth/access_token", options
|
56
60
|
end
|
data/lib/octokit/configurable.rb
CHANGED
@@ -115,5 +115,10 @@ module Octokit
|
|
115
115
|
def options
|
116
116
|
Hash[Octokit::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
117
117
|
end
|
118
|
+
|
119
|
+
def fetch_client_id_and_secret(overrides = {})
|
120
|
+
opts = options.merge(overrides)
|
121
|
+
opts.values_at :client_id, :client_secret
|
122
|
+
end
|
118
123
|
end
|
119
124
|
end
|
data/lib/octokit/error.rb
CHANGED
@@ -8,12 +8,18 @@ module Octokit
|
|
8
8
|
# @param [Hash] response HTTP response
|
9
9
|
# @return [Octokit::Error]
|
10
10
|
def self.from_response(response)
|
11
|
-
status
|
12
|
-
body
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
headers = response[:response_headers]
|
13
14
|
|
14
15
|
if klass = case status
|
15
16
|
when 400 then Octokit::BadRequest
|
16
|
-
when 401
|
17
|
+
when 401
|
18
|
+
if Octokit::OneTimePasswordRequired.required_header(headers)
|
19
|
+
Octokit::OneTimePasswordRequired
|
20
|
+
else
|
21
|
+
Octokit::Unauthorized
|
22
|
+
end
|
17
23
|
when 403
|
18
24
|
if body =~ /rate limit exceeded/i
|
19
25
|
Octokit::TooManyRequests
|
@@ -100,6 +106,20 @@ module Octokit
|
|
100
106
|
# Raised when GitHub returns a 401 HTTP status code
|
101
107
|
class Unauthorized < Error; end
|
102
108
|
|
109
|
+
# Raised when GitHub returns a 401 HTTP status code
|
110
|
+
# and headers include "X-GitHub-OTP"
|
111
|
+
class OneTimePasswordRequired < Error
|
112
|
+
HEADER = /required; (?<delivery>\w+)/i
|
113
|
+
|
114
|
+
def self.required_header(headers)
|
115
|
+
HEADER.match headers['X-GitHub-OTP'].to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def password_delivery
|
119
|
+
@password_delivery ||= self.class.required_header(@response[:response_headers])[:delivery]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
103
123
|
# Raised when GitHub returns a 403 HTTP status code
|
104
124
|
class Forbidden < Error; end
|
105
125
|
|
data/lib/octokit/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{"http_interactions":[{"request":{"method":"put","uri":"https://<GITHUB_LOGIN>:<GITHUB_PASSWORD>@api.github.com/authorizations/clients/<GITHUB_CLIENT_ID>","body":{"encoding":"UTF-8","base64_string":"eyJjbGllbnRfaWQiOiI8R0lUSFVCX0NMSUVOVF9JRD4iLCJjbGllbnRfc2Vj\ncmV0IjoiPEdJVEhVQl9DTElFTlRfU0VDUkVUPiIsInNjb3BlcyI6WyJnaXN0\nIl19\n"},"headers":{"Accept":["application/vnd.github.beta+json"],"User-Agent":["Octokit Ruby Gem 2.0.0"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Aug 2013 19:36:24 GMT"],"Content-Type":["application/json; charset=utf-8"],"Status":["201 Created"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4991"],"X-Ratelimit-Reset":["1377808149"],"Location":["https://api.github.com/authorizations/3459491"],"X-Github-Media-Type":["github.beta; format=json"],"X-Content-Type-Options":["nosniff"],"Content-Length":["336"],"Access-Control-Allow-Credentials":["true"],"Access-Control-Expose-Headers":["ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes"],"Access-Control-Allow-Origin":["*"],"Etag":["\"123a8d275487ebdcf01ff056e6a7e178\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Github-Request-Id":["0eac2c58-1f44-4ee3-b754-12409a31bad8"]},"body":{"encoding":"US-ASCII","base64_string":"eyJpZCI6MzQ1OTQ5MSwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9h\ndXRob3JpemF0aW9ucy8zNDU5NDkxIiwiYXBwIjp7Im5hbWUiOiJPY3Rva2l0\nIERldmVsb3BtZW50IiwidXJsIjoiaHR0cDovL29jdG9raXQuZGV2IiwiY2xp\nZW50X2lkIjoiPEdJVEhVQl9DTElFTlRfSUQ+In0sInRva2VuIjoiNjFhZmNi\nZDM0YmY2NTIyOWU5MDJiMTBiNGYwNGE0NWRkMGJhODI1NSIsIm5vdGUiOm51\nbGwsIm5vdGVfdXJsIjpudWxsLCJjcmVhdGVkX2F0IjoiMjAxMy0wOC0yOVQx\nOTozNjoyNFoiLCJ1cGRhdGVkX2F0IjoiMjAxMy0wOC0yOVQxOTozNjoyNFoi\nLCJzY29wZXMiOlsiZ2lzdCJdfQ==\n"},"http_version":null},"recorded_at":"Thu, 29 Aug 2013 19:36:24 GMT"}],"recorded_with":"VCR 2.4.0"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"http_interactions":[{"request":{"method":"put","uri":"https://<GITHUB_LOGIN>:<GITHUB_PASSWORD>@api.github.com/authorizations/clients/<GITHUB_CLIENT_ID>","body":{"encoding":"UTF-8","base64_string":"eyJjbGllbnRfaWQiOiI8R0lUSFVCX0NMSUVOVF9JRD4iLCJjbGllbnRfc2Vj\ncmV0IjoiPEdJVEhVQl9DTElFTlRfU0VDUkVUPiJ9\n"},"headers":{"Accept":["application/vnd.github.beta+json"],"User-Agent":["Octokit Ruby Gem 2.0.0"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Aug 2013 19:36:24 GMT"],"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4990"],"X-Ratelimit-Reset":["1377808149"],"X-Github-Media-Type":["github.beta; format=json"],"X-Content-Type-Options":["nosniff"],"Content-Length":["336"],"Access-Control-Allow-Credentials":["true"],"Access-Control-Expose-Headers":["ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes"],"Access-Control-Allow-Origin":["*"],"Etag":["\"123a8d275487ebdcf01ff056e6a7e178\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Github-Request-Id":["9e4afd9a-8f33-47a8-a0cd-8601d1779724"],"Vary":["Accept-Encoding"]},"body":{"encoding":"US-ASCII","base64_string":"eyJpZCI6MzQ1OTQ5MSwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9h\ndXRob3JpemF0aW9ucy8zNDU5NDkxIiwiYXBwIjp7Im5hbWUiOiJPY3Rva2l0\nIERldmVsb3BtZW50IiwidXJsIjoiaHR0cDovL29jdG9raXQuZGV2IiwiY2xp\nZW50X2lkIjoiPEdJVEhVQl9DTElFTlRfSUQ+In0sInRva2VuIjoiNjFhZmNi\nZDM0YmY2NTIyOWU5MDJiMTBiNGYwNGE0NWRkMGJhODI1NSIsIm5vdGUiOm51\nbGwsIm5vdGVfdXJsIjpudWxsLCJjcmVhdGVkX2F0IjoiMjAxMy0wOC0yOVQx\nOTozNjoyNFoiLCJ1cGRhdGVkX2F0IjoiMjAxMy0wOC0yOVQxOTozNjoyNFoi\nLCJzY29wZXMiOlsiZ2lzdCJdfQ==\n"},"http_version":null},"recorded_at":"Thu, 29 Aug 2013 19:36:24 GMT"},{"request":{"method":"put","uri":"https://<GITHUB_LOGIN>:<GITHUB_PASSWORD>@api.github.com/authorizations/clients/<GITHUB_CLIENT_ID>","body":{"encoding":"UTF-8","base64_string":"eyJjbGllbnRfaWQiOiI8R0lUSFVCX0NMSUVOVF9JRD4iLCJjbGllbnRfc2Vj\ncmV0IjoiPEdJVEhVQl9DTElFTlRfU0VDUkVUPiJ9\n"},"headers":{"Accept":["application/vnd.github.beta+json"],"User-Agent":["Octokit Ruby Gem 2.0.0"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Aug 2013 19:36:24 GMT"],"Content-Type":["application/json; charset=utf-8"],"Status":["200 OK"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4989"],"X-Ratelimit-Reset":["1377808149"],"X-Github-Media-Type":["github.beta; format=json"],"X-Content-Type-Options":["nosniff"],"Content-Length":["336"],"Access-Control-Allow-Credentials":["true"],"Access-Control-Expose-Headers":["ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes"],"Access-Control-Allow-Origin":["*"],"Etag":["\"123a8d275487ebdcf01ff056e6a7e178\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Github-Request-Id":["0132512e-b1c7-41f0-b662-e3cdb67abd92"],"Vary":["Accept-Encoding"]},"body":{"encoding":"US-ASCII","base64_string":"eyJpZCI6MzQ1OTQ5MSwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9h\ndXRob3JpemF0aW9ucy8zNDU5NDkxIiwiYXBwIjp7Im5hbWUiOiJPY3Rva2l0\nIERldmVsb3BtZW50IiwidXJsIjoiaHR0cDovL29jdG9raXQuZGV2IiwiY2xp\nZW50X2lkIjoiPEdJVEhVQl9DTElFTlRfSUQ+In0sInRva2VuIjoiNjFhZmNi\nZDM0YmY2NTIyOWU5MDJiMTBiNGYwNGE0NWRkMGJhODI1NSIsIm5vdGUiOm51\nbGwsIm5vdGVfdXJsIjpudWxsLCJjcmVhdGVkX2F0IjoiMjAxMy0wOC0yOVQx\nOTozNjoyNFoiLCJ1cGRhdGVkX2F0IjoiMjAxMy0wOC0yOVQxOTozNjoyNFoi\nLCJzY29wZXMiOlsiZ2lzdCJdfQ==\n"},"http_version":null},"recorded_at":"Thu, 29 Aug 2013 19:36:24 GMT"}],"recorded_with":"VCR 2.4.0"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"http_interactions":[{"request":{"method":"post","uri":"https://<GITHUB_LOGIN>:<GITHUB_PASSWORD>@api.github.com/authorizations","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"Accept":["application/vnd.github.beta+json"],"User-Agent":["Octokit Ruby Gem 2.0.0"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Aug 2013 19:36:23 GMT"],"Content-Type":["application/json; charset=utf-8"],"Status":["201 Created"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4993"],"X-Ratelimit-Reset":["1377808149"],"Location":["https://api.github.com/authorizations/3459488"],"X-Github-Media-Type":["github.beta; format=json"],"X-Content-Type-Options":["nosniff"],"Content-Length":["365"],"Access-Control-Allow-Credentials":["true"],"Access-Control-Expose-Headers":["ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes"],"Access-Control-Allow-Origin":["*"],"Etag":["\"1444ccd8d64cae453cd92448ff936d37\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Github-Request-Id":["a27f469a-1e1c-49ab-9080-829e48d3430f"]},"body":{"encoding":"US-ASCII","base64_string":"eyJpZCI6MzQ1OTQ4OCwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9h\ndXRob3JpemF0aW9ucy8zNDU5NDg4IiwiYXBwIjp7Im5hbWUiOiJHaXRIdWIg\nQVBJIiwidXJsIjoiaHR0cDovL2RldmVsb3Blci5naXRodWIuY29tL3YzL29h\ndXRoLyNvYXV0aC1hdXRob3JpemF0aW9ucy1hcGkiLCJjbGllbnRfaWQiOiIw\nMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LCJ0b2tlbiI6IjdlMTY5NmNlM2JlOGRl\nMGQ1ZTA4ODE1MzgzNTNmMTYzYTZhNjZhMmMiLCJub3RlIjpudWxsLCJub3Rl\nX3VybCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMTMtMDgtMjlUMTk6MzY6MjNa\nIiwidXBkYXRlZF9hdCI6IjIwMTMtMDgtMjlUMTk6MzY6MjNaIiwic2NvcGVz\nIjpbXX0=\n"},"http_version":null},"recorded_at":"Thu, 29 Aug 2013 19:36:23 GMT"},{"request":{"method":"post","uri":"https://<GITHUB_LOGIN>:<GITHUB_PASSWORD>@api.github.com/authorizations","body":{"encoding":"UTF-8","base64_string":"e30=\n"},"headers":{"Accept":["application/vnd.github.beta+json"],"User-Agent":["Octokit Ruby Gem 2.0.0"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Server":["GitHub.com"],"Date":["Thu, 29 Aug 2013 19:36:23 GMT"],"Content-Type":["application/json; charset=utf-8"],"Status":["201 Created"],"X-Ratelimit-Limit":["5000"],"X-Ratelimit-Remaining":["4992"],"X-Ratelimit-Reset":["1377808149"],"Location":["https://api.github.com/authorizations/3459490"],"X-Github-Media-Type":["github.beta; format=json"],"X-Content-Type-Options":["nosniff"],"Content-Length":["365"],"Access-Control-Allow-Credentials":["true"],"Access-Control-Expose-Headers":["ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes"],"Access-Control-Allow-Origin":["*"],"Etag":["\"39f0b27375ce598f0cf13230ea91b0d9\""],"Cache-Control":["max-age=0, private, must-revalidate"],"X-Github-Request-Id":["2574b4a5-0b8a-46eb-8571-8858eb8a1b0c"]},"body":{"encoding":"US-ASCII","base64_string":"eyJpZCI6MzQ1OTQ5MCwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9h\ndXRob3JpemF0aW9ucy8zNDU5NDkwIiwiYXBwIjp7Im5hbWUiOiJHaXRIdWIg\nQVBJIiwidXJsIjoiaHR0cDovL2RldmVsb3Blci5naXRodWIuY29tL3YzL29h\ndXRoLyNvYXV0aC1hdXRob3JpemF0aW9ucy1hcGkiLCJjbGllbnRfaWQiOiIw\nMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LCJ0b2tlbiI6IjM0ZjRlM2M3N2VjZTZm\nMzFmOTY5MTNhMzRlNDMyYTE2NWI4Yjg1OWUiLCJub3RlIjpudWxsLCJub3Rl\nX3VybCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMTMtMDgtMjlUMTk6MzY6MjNa\nIiwidXBkYXRlZF9hdCI6IjIwMTMtMDgtMjlUMTk6MzY6MjNaIiwic2NvcGVz\nIjpbXX0=\n"},"http_version":null},"recorded_at":"Thu, 29 Aug 2013 19:36:23 GMT"}],"recorded_with":"VCR 2.4.0"}
|
File without changes
|
File without changes
|
data/spec/helper.rb
CHANGED
@@ -30,6 +30,12 @@ VCR.configure do |c|
|
|
30
30
|
c.filter_sensitive_data("<<ACCESS_TOKEN>>") do
|
31
31
|
ENV['OCTOKIT_TEST_GITHUB_TOKEN']
|
32
32
|
end
|
33
|
+
c.filter_sensitive_data("<GITHUB_CLIENT_ID>") do
|
34
|
+
ENV['OCTOKIT_TEST_GITHUB_CLIENT_ID']
|
35
|
+
end
|
36
|
+
c.filter_sensitive_data("<GITHUB_CLIENT_SECRET>") do
|
37
|
+
ENV['OCTOKIT_TEST_GITHUB_CLIENT_SECRET']
|
38
|
+
end
|
33
39
|
c.default_cassette_options = {
|
34
40
|
:serialize_with => :json,
|
35
41
|
# TODO: Track down UTF-8 issue and remove
|
@@ -53,6 +59,14 @@ def test_github_token
|
|
53
59
|
ENV.fetch 'OCTOKIT_TEST_GITHUB_TOKEN'
|
54
60
|
end
|
55
61
|
|
62
|
+
def test_github_client_id
|
63
|
+
ENV.fetch 'OCTOKIT_TEST_GITHUB_CLIENT_ID'
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_github_client_secret
|
67
|
+
ENV.fetch 'OCTOKIT_TEST_GITHUB_CLIENT_SECRET'
|
68
|
+
end
|
69
|
+
|
56
70
|
def stub_delete(url)
|
57
71
|
stub_request(:delete, github_url(url))
|
58
72
|
end
|
@@ -8,18 +8,54 @@ describe Octokit::Client::Authorizations do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
describe ".create_authorization", :vcr do
|
11
|
-
|
12
|
-
authorization
|
13
|
-
|
14
|
-
|
11
|
+
context 'without :idempotent => true' do
|
12
|
+
it "creates an API authorization" do
|
13
|
+
authorization = @client.create_authorization
|
14
|
+
expect(authorization.app.name).to_not be_nil
|
15
|
+
assert_requested :post, basic_github_url("/authorizations")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "creates a new API authorization each time" do
|
19
|
+
first_authorization = @client.create_authorization
|
20
|
+
second_authorization = @client.create_authorization
|
21
|
+
expect(first_authorization.id).to_not eq second_authorization.id
|
22
|
+
end
|
23
|
+
|
24
|
+
it "creates a new authorization with options" do
|
25
|
+
info = {
|
26
|
+
:scopes => ["gist"],
|
27
|
+
}
|
28
|
+
authorization = @client.create_authorization info
|
29
|
+
expect(authorization.scopes).to be_kind_of Array
|
30
|
+
assert_requested :post, basic_github_url("/authorizations")
|
31
|
+
end
|
15
32
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
33
|
+
|
34
|
+
context 'with :idempotent => true' do
|
35
|
+
subject do
|
36
|
+
lambda do |info = {}|
|
37
|
+
@client.create_authorization({
|
38
|
+
:idempotent => true,
|
39
|
+
:client_id => test_github_client_id,
|
40
|
+
:client_secret => test_github_client_secret
|
41
|
+
}.merge(info))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "creates a new authorization with options" do
|
46
|
+
info = {
|
47
|
+
:scopes => ["gist"],
|
48
|
+
}
|
49
|
+
authorization = subject.call info
|
50
|
+
expect(authorization.scopes).to be_kind_of Array
|
51
|
+
assert_requested :put, basic_github_url("/authorizations/clients/#{test_github_client_id}")
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns an existing API authorization if one already exists' do
|
55
|
+
first_authorization = subject.call
|
56
|
+
second_authorization = subject.call
|
57
|
+
expect(first_authorization.id).to eql second_authorization.id
|
58
|
+
end
|
23
59
|
end
|
24
60
|
end # .create_authorization
|
25
61
|
|
@@ -198,14 +198,17 @@ describe Octokit::Client::Users do
|
|
198
198
|
end
|
199
199
|
end # .subscriptions
|
200
200
|
|
201
|
-
describe '.
|
201
|
+
describe '.exchange_code_for_token' do
|
202
202
|
it 'returns the access_token' do
|
203
203
|
VCR.turn_off!
|
204
|
-
stub_post("https://github.com/login/oauth/access_token").
|
205
|
-
|
204
|
+
post = stub_post("https://github.com/login/oauth/access_token").
|
205
|
+
with(:headers => {
|
206
|
+
:accept => "application/json",
|
207
|
+
:content_type => "application/json"
|
208
|
+
}).to_return(json_response("web_flow_token.json"))
|
206
209
|
response = Octokit.exchange_code_for_token('code', 'id_here', 'secret_here')
|
207
210
|
expect(response.access_token).to eq 'this_be_ye_token/use_it_wisely'
|
208
|
-
assert_requested
|
211
|
+
assert_requested post
|
209
212
|
VCR.turn_on!
|
210
213
|
end
|
211
214
|
end # .access_token
|
data/spec/octokit/client_spec.rb
CHANGED
@@ -480,4 +480,34 @@ describe Octokit::Client do
|
|
480
480
|
end
|
481
481
|
end
|
482
482
|
|
483
|
+
it "knows the difference between unauthorized and needs OTP" do
|
484
|
+
stub_get('/authorizations').to_return(:status => 401)
|
485
|
+
expect { Octokit.get('/authorizations') }.to raise_error Octokit::Unauthorized
|
486
|
+
|
487
|
+
stub_get('/authorizations/1').to_return \
|
488
|
+
:status => 401,
|
489
|
+
:headers => {
|
490
|
+
:content_type => "application/json",
|
491
|
+
"X-GitHub-OTP" => "required; sms"
|
492
|
+
},
|
493
|
+
:body => {:message => "Must specify two-factor authentication OTP code."}.to_json
|
494
|
+
expect { Octokit.get('/authorizations/1') }.to raise_error Octokit::OneTimePasswordRequired
|
495
|
+
end
|
496
|
+
|
497
|
+
it "knows the password delivery mechanism when needs OTP" do
|
498
|
+
stub_get('/authorizations/1').to_return \
|
499
|
+
:status => 401,
|
500
|
+
:headers => {
|
501
|
+
:content_type => "application/json",
|
502
|
+
"X-GitHub-OTP" => "required; app"
|
503
|
+
},
|
504
|
+
:body => {:message => "Must specify two-factor authentication OTP code."}.to_json
|
505
|
+
|
506
|
+
begin
|
507
|
+
Octokit.get('/authorizations/1')
|
508
|
+
rescue Octokit::OneTimePasswordRequired => otp_error
|
509
|
+
expect(otp_error.password_delivery).to eql 'app'
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
483
513
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octokit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
14
|
+
date: 2013-09-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -111,8 +111,11 @@ files:
|
|
111
111
|
- spec/cassettes/Octokit_Client/error_handling/raises_on_404.json
|
112
112
|
- spec/cassettes/Octokit_Client_Authorizations/_authorization/returns_a_single_authorization.json
|
113
113
|
- spec/cassettes/Octokit_Client_Authorizations/_authorizations/lists_existing_authorizations.json
|
114
|
-
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/creates_a_new_authorization_with_options.json
|
115
|
-
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/
|
114
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/creates_a_new_authorization_with_options.json
|
115
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/returns_an_existing_API_authorization_if_one_already_exists.json
|
116
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_a_new_API_authorization_each_time.json
|
117
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_a_new_authorization_with_options.json
|
118
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_an_API_authorization.json
|
116
119
|
- spec/cassettes/Octokit_Client_Authorizations/_scopes/checks_the_scopes_on_a_one-off_token.json
|
117
120
|
- spec/cassettes/Octokit_Client_Authorizations/_scopes/checks_the_scopes_on_the_current_token.json
|
118
121
|
- spec/cassettes/Octokit_Client_Authorizations/_update_authorization/updates_and_existing_authorization.json
|
@@ -455,8 +458,11 @@ test_files:
|
|
455
458
|
- spec/cassettes/Octokit_Client/error_handling/raises_on_404.json
|
456
459
|
- spec/cassettes/Octokit_Client_Authorizations/_authorization/returns_a_single_authorization.json
|
457
460
|
- spec/cassettes/Octokit_Client_Authorizations/_authorizations/lists_existing_authorizations.json
|
458
|
-
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/creates_a_new_authorization_with_options.json
|
459
|
-
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/
|
461
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/creates_a_new_authorization_with_options.json
|
462
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/with_idempotent_true/returns_an_existing_API_authorization_if_one_already_exists.json
|
463
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_a_new_API_authorization_each_time.json
|
464
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_a_new_authorization_with_options.json
|
465
|
+
- spec/cassettes/Octokit_Client_Authorizations/_create_authorization/without_idempotent_true/creates_an_API_authorization.json
|
460
466
|
- spec/cassettes/Octokit_Client_Authorizations/_scopes/checks_the_scopes_on_a_one-off_token.json
|
461
467
|
- spec/cassettes/Octokit_Client_Authorizations/_scopes/checks_the_scopes_on_the_current_token.json
|
462
468
|
- spec/cassettes/Octokit_Client_Authorizations/_update_authorization/updates_and_existing_authorization.json
|