assembla_api 0.1.2

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.
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