assembla_api 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/Rakefile +22 -0
  4. data/lib/assembla_api.rb +130 -0
  5. data/lib/assembla_api/api.rb +373 -0
  6. data/lib/assembla_api/api/actions.rb +58 -0
  7. data/lib/assembla_api/api/arguments.rb +248 -0
  8. data/lib/assembla_api/api/config.rb +107 -0
  9. data/lib/assembla_api/api/config/property.rb +30 -0
  10. data/lib/assembla_api/api/config/property_set.rb +119 -0
  11. data/lib/assembla_api/api/factory.rb +33 -0
  12. data/lib/assembla_api/authorization.rb +76 -0
  13. data/lib/assembla_api/client.rb +21 -0
  14. data/lib/assembla_api/client/activity.rb +30 -0
  15. data/lib/assembla_api/client/authorizations.rb +143 -0
  16. data/lib/assembla_api/client/authorizations/app.rb +98 -0
  17. data/lib/assembla_api/client/portfolio.rb +19 -0
  18. data/lib/assembla_api/client/portfolio/invitations.rb +30 -0
  19. data/lib/assembla_api/client/portfolio/spaces.rb +13 -0
  20. data/lib/assembla_api/client/portfolio/standup_reports.rb +34 -0
  21. data/lib/assembla_api/client/portfolio/tasks.rb +13 -0
  22. data/lib/assembla_api/client/portfolio/ticket_reports.rb +13 -0
  23. data/lib/assembla_api/client/portfolio/tickets.rb +13 -0
  24. data/lib/assembla_api/client/portfolio/users.rb +13 -0
  25. data/lib/assembla_api/client/spaces.rb +140 -0
  26. data/lib/assembla_api/client/spaces/documents.rb +87 -0
  27. data/lib/assembla_api/client/spaces/milestones.rb +102 -0
  28. data/lib/assembla_api/client/spaces/space_tools.rb +65 -0
  29. data/lib/assembla_api/client/spaces/space_tools/merge_requests.rb +105 -0
  30. data/lib/assembla_api/client/spaces/space_tools/merge_requests/versions.rb +41 -0
  31. data/lib/assembla_api/client/spaces/space_tools/merge_requests/versions/comments.rb +34 -0
  32. data/lib/assembla_api/client/spaces/space_tools/merge_requests/versions/votes.rb +46 -0
  33. data/lib/assembla_api/client/spaces/ssh.rb +14 -0
  34. data/lib/assembla_api/client/spaces/ssh/actions.rb +76 -0
  35. data/lib/assembla_api/client/spaces/ssh/actions/launches.rb +14 -0
  36. data/lib/assembla_api/client/spaces/ssh/keys.rb +18 -0
  37. data/lib/assembla_api/client/spaces/ssh/launches.rb +29 -0
  38. data/lib/assembla_api/client/spaces/ssh/servers.rb +63 -0
  39. data/lib/assembla_api/client/spaces/standup_away_reports.rb +41 -0
  40. data/lib/assembla_api/client/spaces/standup_reports.rb +41 -0
  41. data/lib/assembla_api/client/spaces/tags.rb +96 -0
  42. data/lib/assembla_api/client/spaces/tickets.rb +154 -0
  43. data/lib/assembla_api/client/spaces/tickets/associations.rb +57 -0
  44. data/lib/assembla_api/client/spaces/tickets/comments.rb +45 -0
  45. data/lib/assembla_api/client/spaces/tickets/custom_fields.rb +57 -0
  46. data/lib/assembla_api/client/spaces/tickets/statuses.rb +55 -0
  47. data/lib/assembla_api/client/spaces/user_roles.rb +61 -0
  48. data/lib/assembla_api/client/spaces/users.rb +11 -0
  49. data/lib/assembla_api/client/spaces/webhooks.rb +63 -0
  50. data/lib/assembla_api/client/spaces/wiki_pages.rb +78 -0
  51. data/lib/assembla_api/client/spaces/wiki_pages/versions.rb +20 -0
  52. data/lib/assembla_api/client/tasks.rb +72 -0
  53. data/lib/assembla_api/client/users.rb +49 -0
  54. data/lib/assembla_api/client/users/keys.rb +97 -0
  55. data/lib/assembla_api/configuration.rb +71 -0
  56. data/lib/assembla_api/connection.rb +66 -0
  57. data/lib/assembla_api/constants.rb +74 -0
  58. data/lib/assembla_api/core_ext/array.rb +25 -0
  59. data/lib/assembla_api/core_ext/hash.rb +92 -0
  60. data/lib/assembla_api/core_ext/ordered_hash.rb +107 -0
  61. data/lib/assembla_api/deprecation.rb +39 -0
  62. data/lib/assembla_api/error.rb +37 -0
  63. data/lib/assembla_api/error/bad_request.rb +14 -0
  64. data/lib/assembla_api/error/client_error.rb +20 -0
  65. data/lib/assembla_api/error/forbidden.rb +14 -0
  66. data/lib/assembla_api/error/internal_server_error.rb +15 -0
  67. data/lib/assembla_api/error/invalid_options.rb +18 -0
  68. data/lib/assembla_api/error/not_acceptable.rb +15 -0
  69. data/lib/assembla_api/error/not_found.rb +14 -0
  70. data/lib/assembla_api/error/required_params.rb +18 -0
  71. data/lib/assembla_api/error/service_error.rb +68 -0
  72. data/lib/assembla_api/error/service_unavailable.rb +15 -0
  73. data/lib/assembla_api/error/unauthorized.rb +15 -0
  74. data/lib/assembla_api/error/unknown_media.rb +18 -0
  75. data/lib/assembla_api/error/unknown_value.rb +18 -0
  76. data/lib/assembla_api/error/unprocessable_entity.rb +14 -0
  77. data/lib/assembla_api/error/validations.rb +18 -0
  78. data/lib/assembla_api/ext/faraday.rb +38 -0
  79. data/lib/assembla_api/jsonable.rb +18 -0
  80. data/lib/assembla_api/middleware.rb +31 -0
  81. data/lib/assembla_api/mime_type.rb +33 -0
  82. data/lib/assembla_api/normalizer.rb +25 -0
  83. data/lib/assembla_api/null_encoder.rb +25 -0
  84. data/lib/assembla_api/page_iterator.rb +142 -0
  85. data/lib/assembla_api/page_links.rb +45 -0
  86. data/lib/assembla_api/paged_request.rb +40 -0
  87. data/lib/assembla_api/pagination.rb +102 -0
  88. data/lib/assembla_api/parameter_filter.rb +32 -0
  89. data/lib/assembla_api/params_hash.rb +101 -0
  90. data/lib/assembla_api/rate_limit.rb +25 -0
  91. data/lib/assembla_api/request.rb +85 -0
  92. data/lib/assembla_api/request/basic_auth.rb +33 -0
  93. data/lib/assembla_api/request/jsonize.rb +53 -0
  94. data/lib/assembla_api/request/key_auth.rb +31 -0
  95. data/lib/assembla_api/request/oauth2.rb +42 -0
  96. data/lib/assembla_api/request/verbs.rb +60 -0
  97. data/lib/assembla_api/response.rb +28 -0
  98. data/lib/assembla_api/response/header.rb +76 -0
  99. data/lib/assembla_api/response/jsonize.rb +29 -0
  100. data/lib/assembla_api/response/mashify.rb +24 -0
  101. data/lib/assembla_api/response/raise_error.rb +18 -0
  102. data/lib/assembla_api/response/xmlize.rb +26 -0
  103. data/lib/assembla_api/response_wrapper.rb +157 -0
  104. data/lib/assembla_api/ssl_certs/cacerts.pem +2183 -0
  105. data/lib/assembla_api/utils/url.rb +59 -0
  106. data/lib/assembla_api/validations.rb +25 -0
  107. data/lib/assembla_api/validations/format.rb +24 -0
  108. data/lib/assembla_api/validations/presence.rb +30 -0
  109. data/lib/assembla_api/validations/required.rb +24 -0
  110. data/lib/assembla_api/validations/token.rb +41 -0
  111. data/lib/assembla_api/version.rb +12 -0
  112. metadata +347 -0
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+ module MimeType
5
+
6
+ MEDIA_LOOKUP = {
7
+ 'json' => 'json',
8
+ 'blob' => 'raw',
9
+ 'raw' => 'raw+json',
10
+ 'text' => 'text+json',
11
+ 'html' => 'html+json',
12
+ 'full' => 'full+json'
13
+ }
14
+
15
+ # Parse media type param
16
+ #
17
+ def parse(media)
18
+ version = 'v1'
19
+ media.sub!(/^[.]*|[.]*$/,"")
20
+ media = media.include?('+') ? media.split('+')[0] : media
21
+ version, media = media.split('.') if media.include?('.')
22
+ media_type = lookup_media(media)
23
+ "application/vnd.assembla.#{version}.#{media_type}"
24
+ end
25
+
26
+ def lookup_media(name)
27
+ MEDIA_LOOKUP.fetch(name) do
28
+ raise ArgumentError, "Provided Media Type #{name} is not valid"
29
+ end
30
+ end
31
+
32
+ end # MimeType
33
+ end # Assembla
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+ # Deals with normalazing client supplied parameter keys.
5
+ module Normalizer
6
+ # Turns any keys from nested hashes including nested arrays into strings
7
+ #
8
+ def normalize!(params)
9
+ case params
10
+ when Hash
11
+ params.keys.each do |k|
12
+ params[k.to_s] = params.delete(k)
13
+ normalize!(params[k.to_s])
14
+ end
15
+ when Array
16
+ params.map! do |el|
17
+ normalize!(el)
18
+ end
19
+ else
20
+ params.to_s
21
+ end
22
+ return params
23
+ end
24
+ end # Normalizer
25
+ end # Assembla
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'faraday'
4
+
5
+ module Assembla
6
+
7
+ # Skip encoding of the key nested parameters
8
+ module NullParamsEncoder
9
+ if defined?(Faraday::NestedParamsEncoder)
10
+ class << self
11
+ Faraday::NestedParamsEncoder.singleton_methods do |m|
12
+ define_method m, ::Faraday::NestedParamsEncoder.method(m).to_proc
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.escape(s)
18
+ s.to_s
19
+ end
20
+
21
+ def self.unescape(s)
22
+ s.to_s
23
+ end
24
+ end # NullEncoder
25
+ end # Assembla
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+
3
+ require 'assembla_api/utils/url'
4
+ require 'uri'
5
+
6
+ module Assembla
7
+ class PageIterator
8
+ include Assembla::Constants
9
+ include Assembla::Utils::Url
10
+ include Assembla::PagedRequest
11
+
12
+ # Setup attribute accesor for all the link types
13
+ ATTRIBUTES = [ META_FIRST, META_NEXT, META_PREV, META_LAST ]
14
+
15
+ DEFAULT_SHA = 'master'
16
+
17
+ ATTRIBUTES.each do |attr|
18
+ attr_accessor :"#{attr}_page_uri", :"#{attr}_page"
19
+ end
20
+
21
+ attr_reader :current_api
22
+
23
+ def initialize(links, current_api)
24
+ @links = links
25
+ @current_api = current_api
26
+ update_page_links @links
27
+ end
28
+
29
+ def has_next?
30
+ next_page == 0 || !next_page_uri.nil?
31
+ end
32
+
33
+ def count
34
+ return nil unless last_page_uri
35
+ parse_query(URI(last_page_uri).query)['page']
36
+ end
37
+
38
+ # Perform http get request for the first resource
39
+ #
40
+ def first
41
+ return nil unless first_page_uri
42
+ perform_request(first_page_uri)
43
+ end
44
+
45
+ # Perform http get request for the next resource
46
+ #
47
+ def next
48
+ return nil unless has_next?
49
+ perform_request(next_page_uri)
50
+ end
51
+
52
+ # Perform http get request for the previous resource
53
+ #
54
+ def prev
55
+ return nil unless prev_page_uri
56
+ perform_request(prev_page_uri)
57
+ end
58
+
59
+ # Perform http get request for the last resource
60
+ #
61
+ def last
62
+ return nil unless last_page_uri
63
+ perform_request(last_page_uri)
64
+ end
65
+
66
+ # Returns the result for a specific page.
67
+ #
68
+ def get_page(page_number)
69
+ # Find URI that we can work with, if we cannot get the first or the
70
+ # last page URI then there is only one page.
71
+ page_uri = first_page_uri || last_page_uri
72
+ return nil unless page_uri
73
+ params = parse_query URI(page_uri).query
74
+ params['page'] = page_number
75
+ params['per_page'] = parse_per_page_number(page_uri)
76
+
77
+ response = page_request URI(page_uri).path, params
78
+ update_page_links response.links
79
+ response
80
+ end
81
+
82
+ private
83
+
84
+ def perform_request(attribute)
85
+ page_uri = URI(attribute)
86
+ params = parse_query(page_uri.query)
87
+
88
+ if next_page < 1
89
+ sha = sha(params)
90
+ params['sha'] = sha if sha
91
+ else
92
+ params['page'] = parse_page_number(attribute)
93
+ end
94
+ params['per_page'] = parse_per_page_number(attribute)
95
+
96
+ response = page_request(page_uri.path, params)
97
+ update_page_links response.links
98
+ response
99
+ end
100
+
101
+ def sha(params)
102
+ return params['last_sha'] if params.keys.include?('last_sha')
103
+ return DEFAULT_SHA if params.keys.include?('sha')
104
+ nil
105
+ end
106
+
107
+ def parse_per_page_number(uri) # :nodoc:
108
+ parse_page_params(uri, PARAM_PER_PAGE)
109
+ end
110
+
111
+ def parse_page_number(uri) # :nodoc:
112
+ parse_page_params(uri, PARAM_PAGE)
113
+ end
114
+
115
+ # Extracts query string parameter for given name
116
+ def parse_page_params(uri, attr) # :nodoc:
117
+ return -1 if uri.nil? || uri.empty?
118
+ parsed = nil
119
+ begin
120
+ parsed = URI.parse(uri)
121
+ rescue URI::Error => e
122
+ return -1
123
+ end
124
+ param = parse_query_for_param(parsed.query, attr)
125
+ return -1 if param.nil? || param.empty?
126
+ begin
127
+ return param.to_i
128
+ rescue ArgumentError => err
129
+ return -1
130
+ end
131
+ end
132
+
133
+ # Wholesale update of all link attributes
134
+ def update_page_links(links) # :nodoc:
135
+ ATTRIBUTES.each do |attr|
136
+ self.send(:"#{attr}_page_uri=", links.send(:"#{attr}"))
137
+ self.send(:"#{attr}_page=", parse_page_number(links.send(:"#{attr}")))
138
+ end
139
+ end
140
+
141
+ end # PageIterator
142
+ end # Assembla
@@ -0,0 +1,45 @@
1
+ module Assembla
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 Assembla::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 # Assembla
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+
5
+ # A module that adds http get request to response pagination
6
+ module PagedRequest
7
+ include Assembla::Constants
8
+
9
+ FIRST_PAGE = 1 # Default request page if none provided
10
+
11
+ PER_PAGE = 30 # Default number of items as specified by API
12
+
13
+ NOT_FOUND = -1 # Either page or per_page parameter not present
14
+
15
+ # Check if current api instance has default per_page param set,
16
+ # otherwise use global default.
17
+ #
18
+ def default_page_size
19
+ current_api.per_page ? current_api.per_page : PER_PAGE
20
+ end
21
+
22
+ def default_page
23
+ current_api.page ? current_api.page : FIRST_PAGE
24
+ end
25
+
26
+ # Perform http get request with pagination parameters
27
+ #
28
+ def page_request(path, params={})
29
+ if params[PARAM_PER_PAGE] == NOT_FOUND
30
+ params[PARAM_PER_PAGE] = default_page_size
31
+ end
32
+ if params[PARAM_PAGE] && params[PARAM_PAGE] == NOT_FOUND
33
+ params[PARAM_PAGE] = default_page
34
+ end
35
+
36
+ current_api.get_request(path, ParamsHash.new(params))
37
+ end
38
+
39
+ end # PagedRequest
40
+ end # Assembla
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+
5
+ # A module that decorates response with pagination helpers
6
+ module Pagination
7
+ include Assembla::Constants
8
+
9
+ # Return page links
10
+ def links
11
+ @links = Assembla::PageLinks.new(env[:response_headers])
12
+ end
13
+
14
+ # Retrive number of total pages base on current :per_page parameter
15
+ def count_pages
16
+ page_iterator.count.to_i
17
+ end
18
+
19
+ # Iterate over results set pages by automatically calling `next_page`
20
+ # until all pages are exhausted. Caution needs to be exercised when
21
+ # using this feature - 100 pages iteration will perform 100 API calls.
22
+ # By default this is off. You can set it on the client, individual API
23
+ # instances or just per given request.
24
+ #
25
+ def auto_paginate(auto=false)
26
+ if (current_api.auto_pagination? || auto) && self.body.is_a?(Array)
27
+ resources_bodies = []
28
+ each_page { |resource| resources_bodies += resource.body }
29
+ self.body = resources_bodies
30
+ end
31
+ self
32
+ end
33
+
34
+ # Iterator like each for response pages. If there are no pages to
35
+ # iterate over this method will return current page.
36
+ def each_page
37
+ yield self
38
+ while page_iterator.has_next?
39
+ yield next_page
40
+ end
41
+ end
42
+
43
+ # Retrives the result of the first page. Returns <tt>nil</tt> if there is
44
+ # no first page - either because you are already on the first page
45
+ # or there are no pages at all in the result.
46
+ def first_page
47
+ first_request = page_iterator.first
48
+ self.instance_eval { @env = first_request.env } if first_request
49
+ first_request
50
+ end
51
+
52
+ # Retrives the result of the next page. Returns <tt>nil</tt> if there is
53
+ # no next page or no pages at all.
54
+ def next_page
55
+ next_request = page_iterator.next
56
+ self.instance_eval { @env = next_request.env } if next_request
57
+ next_request
58
+ end
59
+
60
+ # Retrives the result of the previous page. Returns <tt>nil</tt> if there is
61
+ # no previous page or no pages at all.
62
+ def prev_page
63
+ prev_request = page_iterator.prev
64
+ self.instance_eval { @env = prev_request.env } if prev_request
65
+ prev_request
66
+ end
67
+ alias :previous_page :prev_page
68
+
69
+ # Retrives the result of the last page. Returns <tt>nil</tt> if there is
70
+ # no last page - either because you are already on the last page,
71
+ # there is only one page or there are no pages at all in the result.
72
+ def last_page
73
+ last_request = page_iterator.last
74
+ self.instance_eval { @env = last_request.env } if last_request
75
+ last_request
76
+ end
77
+
78
+ # Retrives a specific result for a page given page number.
79
+ # The <tt>page_number</tt> parameter is not validate, hitting a page
80
+ # that does not exist will return Assembla API error. Consequently, if
81
+ # there is only one page, this method returns nil
82
+ def page(page_number)
83
+ request = page_iterator.get_page(page_number)
84
+ self.instance_eval { @env = request.env } if request
85
+ request
86
+ end
87
+
88
+ # Returns <tt>true</tt> if there is another page in the result set,
89
+ # otherwise <tt>false</tt>
90
+ def has_next_page?
91
+ page_iterator.has_next?
92
+ end
93
+
94
+ private
95
+
96
+ # Internally used page iterator
97
+ def page_iterator # :nodoc:
98
+ @page_iterator = Assembla::PageIterator.new(links, current_api)
99
+ end
100
+
101
+ end # Pagination
102
+ end # Assembla
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Assembla
4
+ # Allows you to specify parameters keys which will be preserved
5
+ # in parameters hash and its subhashes. Any keys from the nested
6
+ # hash that do not match will be removed.
7
+ module ParameterFilter
8
+
9
+ # Removes any keys from nested hashes that don't match predefiend keys
10
+ #
11
+ def filter!(keys, params, options={:recursive => true}) # :nodoc:
12
+ case params
13
+ when Hash, ParamsHash
14
+ params.keys.each do |k, v|
15
+ unless (keys.include?(k) or Assembla::Validations::VALID_API_KEYS.include?(k))
16
+ params.delete(k)
17
+ else
18
+ filter!(keys, params[k]) if options[:recursive]
19
+ end
20
+ end
21
+ when Array
22
+ params.map! do |el|
23
+ filter!(keys, el) if options[:recursive]
24
+ end
25
+ else
26
+ params
27
+ end
28
+ return params
29
+ end
30
+
31
+ end # Filter
32
+ end # Assembla