github_api 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: https://api.github.com:443/repos/peter-murach/github/teams
6
+ body:
7
+ headers:
8
+ accept-encoding:
9
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
10
+ response: !ruby/struct:VCR::Response
11
+ status: !ruby/struct:VCR::ResponseStatus
12
+ code: 404
13
+ message: Not Found
14
+ headers:
15
+ server:
16
+ - nginx/1.0.4
17
+ date:
18
+ - Tue, 03 Jan 2012 16:15:19 GMT
19
+ content-type:
20
+ - application/json; charset=utf-8
21
+ transfer-encoding:
22
+ - chunked
23
+ connection:
24
+ - keep-alive
25
+ status:
26
+ - 404 Not Found
27
+ x-ratelimit-limit:
28
+ - "5000"
29
+ etag:
30
+ - "\"e66a7a6c91e2c26803f3f49feb7a883f\""
31
+ x-ratelimit-remaining:
32
+ - "4997"
33
+ content-encoding:
34
+ - gzip
35
+ body: !binary |
36
+ H4sIAAAAAAAAA6tWyk0tLk5MT1WyUvLLL1Fwyy/NS1GqBQB8+Ys1FwAAAA==
37
+
38
+ http_version: "1.1"
@@ -0,0 +1,28 @@
1
+ Feature: Accessing Repos Main API
2
+ In order to interact with github repositories
3
+ GithubAPI gem
4
+ Should return the expected results depending on passed parameters
5
+
6
+ Scenario: Returning all repository branches
7
+ Given I have "Github::Repos" instance
8
+ And I am looking for "branches" with the following params:
9
+ | user | repo |
10
+ | peter-murach | github |
11
+ And I make request within a cassette named "repos/branches"
12
+ Then the response should be "200"
13
+
14
+ Scenario: Returning all repository tags
15
+ Given I have "Github::Repos" instance
16
+ And I am looking for "tags" with the following params:
17
+ | user | repo |
18
+ | peter-murach | github |
19
+ And I make request within a cassette named "repos/tags"
20
+ Then the response should be "200"
21
+
22
+ Scenario: Returning all repositories for the user
23
+ Given I have "Github::Repos" instance
24
+ And I am looking for "list_repos" with the following params:
25
+ | user |
26
+ | peter-murach |
27
+ And I make request with hash params within a cassette named "repos/tags"
28
+ Then the response should be "200"
@@ -0,0 +1,11 @@
1
+ Before do
2
+ step "I have github instance"
3
+ end
4
+
5
+ When /^(.*) within a cassette named "([^"]*)"$/ do |step_to_call, cassette_name|
6
+ VCR.use_cassette(cassette_name) { step step_to_call }
7
+ end
8
+
9
+ Then /^the response should be "([^"]*)"$/ do |expected_response|
10
+ @response.status.should eql expected_response.to_i
11
+ end
@@ -2,6 +2,14 @@ Given /^I have github instance$/ do
2
2
  @github = Github.new
3
3
  end
4
4
 
5
+ Given /^I have "([^"]*)" instance$/ do |api_classes|
6
+ constant = Object
7
+ api_classes.split('::').each do |api_class|
8
+ constant = constant.const_get api_class
9
+ end
10
+ @instance = constant.new
11
+ end
12
+
5
13
  When /^I fetch "([^"]*)"$/ do |method|
6
14
  @response = @github.send(method.to_sym)
7
15
  end
@@ -9,3 +17,18 @@ end
9
17
  When /^I will have access to "([^"]*)" API$/ do |api|
10
18
  @response.class.to_s.should match api
11
19
  end
20
+
21
+ When /^I am looking for "([^"]*)" with the following params:$/ do |method, table|
22
+ table.hashes.each do |attributes|
23
+ @method = method.to_sym
24
+ @attributes = attributes
25
+ end
26
+ end
27
+
28
+ When /^I make request$/ do
29
+ @response = @instance.send @method, *@attributes.values
30
+ end
31
+
32
+ When /^I make request with hash params$/ do
33
+ @response = @instance.send @method, @attributes
34
+ end
@@ -1,6 +1,12 @@
1
+ require 'vcr'
2
+
1
3
  VCR.config do |conf|
2
4
  conf.stub_with :webmock
3
5
  conf.cassette_library_dir = 'features/cassettes'
4
6
  conf.default_cassette_options = { :record => :new_episodes }
5
7
  conf.filter_sensitive_data('<***>') { ''}
6
8
  end
9
+
10
+ VCR.cucumber_tags do |t|
11
+ t.tag '@live', :record => :all
12
+ end
data/lib/github_api.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'github_api/version'
4
4
  require 'github_api/configuration'
5
+ require 'github_api/constants'
6
+ require 'github_api/utils/url'
5
7
  require 'github_api/connection'
6
8
 
7
9
  module Github
@@ -12,7 +14,12 @@ module Github
12
14
  #
13
15
  # @return [Github::Client]
14
16
  def new(options = {}, &block)
15
- Github::Client.new(options, &block)
17
+ @@api_client = Github::Client.new(options, &block)
18
+ end
19
+
20
+ # Returns handle for the client instance
21
+ def api_client
22
+ @@api_client
16
23
  end
17
24
 
18
25
  # Delegate to Github::Client
@@ -68,6 +75,9 @@ module Github
68
75
  :CoreExt => 'core_ext',
69
76
  :MimeType => 'mime_type',
70
77
  :Authorization => 'authorization',
71
- :Authorizations => 'authorizations'
78
+ :Authorizations => 'authorizations',
79
+ :PageLinks => 'page_links',
80
+ :PageIterator => 'page_iterator',
81
+ :PagedRequest => 'paged_request'
72
82
 
73
83
  end # Github
@@ -18,8 +18,9 @@ module Github
18
18
  include Request
19
19
 
20
20
  VALID_API_KEYS = [
21
- :per_page,
22
- :pagination
21
+ 'page',
22
+ 'per_page',
23
+ 'jsonp_callback'
23
24
  ]
24
25
 
25
26
  attr_reader *Configuration::VALID_OPTIONS_KEYS
@@ -61,10 +62,13 @@ module Github
61
62
  end
62
63
  end
63
64
 
64
- # Responds to attribute query
65
+ # Responds to attribute query or attribute clear
65
66
  def method_missing(method, *args, &block) # :nodoc:
66
- if method.to_s =~ /^(.*)\?$/
67
+ case method.to_s
68
+ when /^(.*)\?$/
67
69
  return !self.send($1.to_s).nil?
70
+ when /^clear_(.*)$/
71
+ self.send("#{$1.to_s}=", nil)
68
72
  else
69
73
  super
70
74
  end
@@ -123,7 +127,7 @@ module Github
123
127
  case params
124
128
  when Hash
125
129
  params.keys.each do |k, v|
126
- unless keys.include? k
130
+ unless (keys.include?(k) or VALID_API_KEYS.include?(k))
127
131
  params.delete(k)
128
132
  else
129
133
  _filter_params_keys(keys, params[k])
@@ -25,9 +25,9 @@ module Github
25
25
  def default_options(options={}) # :nodoc:
26
26
  {
27
27
  :headers => {
28
- 'Accept' => '*/*', #accepts,
29
- 'User-Agent' => user_agent,
30
- 'Content-Type' => 'application/x-www-form-urlencoded'
28
+ :accept => '*/*', #accepts,
29
+ :user_agent => user_agent,
30
+ :content_type => 'application/x-www-form-urlencoded'
31
31
  },
32
32
  :ssl => { :verify => false },
33
33
  :url => endpoint
@@ -49,7 +49,7 @@ module Github
49
49
 
50
50
  @connection ||= begin
51
51
  Faraday.new(merged_options.merge(connection_options)) do |builder|
52
- puts options.inspect
52
+ puts options.inspect if ENV['DEBUG']
53
53
 
54
54
  builder.use Faraday::Request::JSON
55
55
  builder.use Faraday::Request::Multipart
@@ -0,0 +1,38 @@
1
+ module Github
2
+ module Constants
3
+ extend self
4
+
5
+ # Response headers
6
+ RATELIMIT_REMAINING = 'X-RateLimit-Remaining'.freeze
7
+
8
+ RATELIMIT_LIMIT = 'X-RateLimit-Limit'.freeze
9
+
10
+ CONTENT_TYPE = 'Content-Type'.freeze
11
+
12
+ CONTENT_LENGTH = 'content-length'.freeze
13
+
14
+ # Link headers
15
+ HEADER_LINK = "Link".freeze
16
+
17
+ HEADER_NEXT = "X-Next".freeze
18
+
19
+ HEADER_LAST = "X-Last".freeze
20
+
21
+ META_REL = "rel".freeze
22
+
23
+ META_LAST = "last".freeze
24
+
25
+ META_NEXT = "next".freeze
26
+
27
+ META_FIRST = "first".freeze
28
+
29
+ META_PREV = "prev".freeze
30
+
31
+ PARAM_PAGE = "page".freeze
32
+
33
+ PARAM_PER_PAGE = "per_page".freeze
34
+
35
+ PARAM_START_PAGE = "start_page".freeze
36
+
37
+ end # Constants
38
+ end # Github
@@ -5,7 +5,7 @@ module Github
5
5
  attr_reader :response_message, :response_headers
6
6
 
7
7
  def initialize(message, headers)
8
- @response_message = message
8
+ @response_message = message
9
9
  super message
10
10
  end
11
11
 
@@ -21,11 +21,14 @@ module Github
21
21
  class Unauthorised < Error; end
22
22
 
23
23
  # Raised when Github returns the HTTP status code 403
24
- class Forbidden < Error; end
24
+ class Forbidden < Error; end
25
25
 
26
26
  # Raised when Github returns the HTTP status code 404
27
27
  class ResourceNotFound < Error; end
28
28
 
29
+ # Raised when Github returns the HTTP status code 422
30
+ class UnprocessableEntity < Error; end
31
+
29
32
  # Raised when Github returns the HTTP status code 500
30
33
  class InternalServerError < Error; end
31
34
 
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+
3
+ require 'github_api/utils/url'
4
+
5
+ module Github
6
+ class PageIterator
7
+ include Github::Constants
8
+ include Github::Utils::Url
9
+ include Github::PagedRequest
10
+
11
+ # Query string separator
12
+ QUERY_STR_SEP = '?'.freeze
13
+
14
+ # Setup attribute accesor for all the link types
15
+ ATTRIBUTES = [ META_FIRST, META_NEXT, META_PREV, META_LAST ]
16
+
17
+ ATTRIBUTES.each do |attr|
18
+ attr_accessor :"#{attr}_page_uri", :"#{attr}_page"
19
+ end
20
+
21
+ def initialize(env)
22
+ @links = Github::PageLinks.new(env[:response_headers])
23
+ update_page_links @links
24
+ end
25
+
26
+ def has_next?
27
+ next_page == 0 || !next_page_uri.nil?
28
+ end
29
+
30
+ def first
31
+ return nil unless first_page_uri
32
+ response = page_request first_page_uri.split(QUERY_STR_SEP)[0],
33
+ 'per_page' => parse_per_page_number(first_page_uri)
34
+ update_page_links response.links
35
+ response
36
+ end
37
+
38
+ def next
39
+ return nil unless has_next?
40
+
41
+ response = page_request next_page_uri.split(QUERY_STR_SEP)[0],
42
+ 'page' => next_page,
43
+ 'per_page'=> parse_per_page_number(next_page_uri)
44
+ update_page_links response.links
45
+ response
46
+ end
47
+
48
+ def prev
49
+ return nil unless prev_page_uri
50
+ response = page_request prev_page_uri.split(QUERY_STR_SEP)[0],
51
+ 'page' => prev_page,
52
+ 'per_page'=> parse_per_page_number(prev_page_uri)
53
+ update_page_links response.links
54
+ response
55
+ end
56
+
57
+ def last
58
+ return nil unless last_page_uri
59
+ response = page_request last_page_uri.split(QUERY_STR_SEP)[0],
60
+ 'page' => last_page,
61
+ 'per_page' => parse_per_page_number(last_page_uri)
62
+ update_page_links response.links
63
+ response
64
+ end
65
+
66
+ # Returns the result for a specific page.
67
+ def get_page(page_number)
68
+ # Find URI that we can work with, if we cannot get the first or the
69
+ # last page URI then there is only one page.
70
+ page_uri = first_page_uri || last_page_uri
71
+ return nil unless page_uri
72
+
73
+ response = page_request page_uri.split(QUERY_STR_SEP)[0],
74
+ 'page' => page_number,
75
+ 'per_page' => parse_per_page_number(page_uri)
76
+ update_page_links response.links
77
+ response
78
+ end
79
+
80
+
81
+ private
82
+
83
+ def parse_per_page_number(uri) # :nodoc:
84
+ parse_page_params(uri, PARAM_PER_PAGE)
85
+ end
86
+
87
+ def parse_page_number(uri) # :nodoc:
88
+ parse_page_params(uri, PARAM_PAGE)
89
+ end
90
+
91
+ # Extracts query string parameter for given name
92
+ def parse_page_params(uri, attr) # :nodoc:
93
+ return -1 if uri.nil? || uri.empty?
94
+ parsed = nil
95
+ begin
96
+ parsed = URI.parse(uri)
97
+ rescue URI::Error => e
98
+ return -1
99
+ end
100
+ param = parse_query_for_param(parsed.query, attr)
101
+ return -1 if param.nil? || param.empty?
102
+ begin
103
+ return param.to_i
104
+ rescue ArgumentError => err
105
+ return -1
106
+ end
107
+ end
108
+
109
+ # Wholesale update of all link attributes
110
+ def update_page_links(links) # :nodoc:
111
+ ATTRIBUTES.each do |attr|
112
+ self.send(:"#{attr}_page_uri=", links.send(:"#{attr}"))
113
+ self.send(:"#{attr}_page=", parse_page_number(links.send(:"#{attr}")))
114
+ end
115
+ end
116
+
117
+ end # PageIterator
118
+ end # Github
@@ -0,0 +1,45 @@
1
+ module Github
2
+ # Determines the links in the current response link header to be used
3
+ # to find the links to other pages of request responses. These will
4
+ # only be present if the result set size exceeds the per page limit.
5
+ class PageLinks
6
+ include Github::Constants
7
+
8
+ DELIM_LINKS = ",".freeze # :nodoc:
9
+
10
+ # Hold the extracted values for URI from the Link header
11
+ # for the first, last, next and previous page.
12
+ attr_accessor :first, :last, :next, :prev
13
+
14
+ # Parses links from executed request
15
+ #
16
+ def initialize(response_headers)
17
+ link_header = response_headers[HEADER_LINK]
18
+ if link_header
19
+ return unless link_header =~ /(next|first|last|prev)/
20
+
21
+ link_header.split(DELIM_LINKS).each do |link|
22
+ if link.strip =~ /<([^>]+)>; rel=\"([^\"]+)\"/
23
+ url_part, meta_part = $1, $2
24
+ next if !url_part || !meta_part
25
+ case meta_part
26
+ when META_FIRST
27
+ self.first = url_part
28
+ when META_LAST
29
+ self.last = url_part
30
+ when META_NEXT
31
+ self.next = url_part
32
+ when META_PREV
33
+ self.prev = url_part
34
+ end
35
+ end
36
+ end
37
+ else
38
+ # When on the first page
39
+ self.next = response_headers[HEADER_NEXT]
40
+ self.last = response_headers[HEADER_LAST]
41
+ end
42
+ end
43
+
44
+ end # PageLinks
45
+ end # Github