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,58 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+ # Responsible for providing inspection of api methods
5
+ class API
6
+ # Returns all API public methods for a given class.
7
+ #
8
+ # @return [nil]
9
+ #
10
+ # @api public
11
+ def self.extend_with_actions(child_class)
12
+ child_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
13
+ def self.actions
14
+ self.new.api_methods_in(#{child_class}) +
15
+ self.new.module_methods_in(#{child_class})
16
+ end
17
+
18
+ def actions
19
+ api_methods_in(#{child_class}) + module_methods_in(#{child_class})
20
+ end
21
+ RUBY_EVAL
22
+ end
23
+
24
+ # Finds api methods in a class
25
+ #
26
+ # @param [Class] klass
27
+ # The klass to inspect for methods.
28
+ #
29
+ # @api private
30
+ def api_methods_in(klass)
31
+ methods = klass.send(:instance_methods, false) - [:actions]
32
+ methods.sort.each_with_object([]) do |method_name, accumulator|
33
+ unless method_name.to_s.include?('with') ||
34
+ method_name.to_s.include?('without')
35
+ accumulator << method_name
36
+ end
37
+ accumulator
38
+ end
39
+ end
40
+
41
+ # Finds methods included through class modules
42
+ #
43
+ # @param [Class] klass
44
+ # The klass to inspect for methods.
45
+ #
46
+ # @api private
47
+ def module_methods_in(klass)
48
+ klass.included_modules.each_with_object([]) do |mod, accumulator|
49
+ if mod.to_s =~ /#{klass}/
50
+ mod.instance_methods(false).each do |method|
51
+ accumulator << method
52
+ end
53
+ end
54
+ accumulator
55
+ end
56
+ end
57
+ end # API
58
+ end # Assembla
@@ -0,0 +1,248 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+ class API
5
+ # A class responsible for handilng request arguments
6
+ class Arguments
7
+ include Normalizer
8
+ include ParameterFilter
9
+ include Validations
10
+
11
+ AUTO_PAGINATION = 'auto_pagination'.freeze
12
+
13
+ # Parameters passed to request
14
+ attr_reader :params
15
+
16
+ # The remaining unparsed arguments
17
+ attr_reader :remaining
18
+
19
+ # The request api
20
+ attr_reader :api
21
+
22
+ # Initialize an Arguments
23
+ #
24
+ # @param [Hash] options
25
+ #
26
+ # @option options [Array[String]] :required
27
+ # arguments that must be present before request is fired
28
+ #
29
+ # @option options [Assembla::API] :api
30
+ # the reference to the current api
31
+ #
32
+ # @api public
33
+ def initialize(options = {}, &block)
34
+ normalize! options
35
+
36
+ @api = options.fetch('api')
37
+ @required = options.fetch('required', []).map(&:to_s)
38
+ @optional = options.fetch('optional', []).map(&:to_s)
39
+ @assigns = {}
40
+
41
+ yield_or_eval(&block)
42
+ end
43
+
44
+ # Specify required attribute(s)
45
+ #
46
+ # @api public
47
+ def require(*attrs, &block)
48
+ attrs_clone = attrs.clone
49
+ @required = Array(attrs_clone)
50
+ self
51
+ end
52
+ alias :required :require
53
+
54
+ # Specify optional attribute(s)
55
+ #
56
+ # @api public
57
+ def optional(*attrs, &block)
58
+ end
59
+
60
+ # Hash like access to request arguments
61
+ #
62
+ # @param [String, Symbol] property
63
+ # the property name
64
+ #
65
+ # @api public
66
+ def [](property)
67
+ @assigns[property.to_s]
68
+ end
69
+
70
+ def []=(property, value)
71
+ @assigns[property.to_s] = value
72
+ end
73
+
74
+ def method_missing(method_name, *args, &block)
75
+ if @assigns.key?(method_name.to_s)
76
+ self[method_name]
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ def respond_to_missing?(method_name, include_private = false)
83
+ @assigns.key?(method_name) || super
84
+ end
85
+
86
+ # Parse arguments to allow for flexible api calls
87
+ #
88
+ # Arguments can be part of parameters hash or be simple string arguments.
89
+ #
90
+ # @api public
91
+ def parse(*args, &block)
92
+ options = ParamsHash.new(args.extract_options!)
93
+ normalize! options
94
+
95
+ if args.size.zero? # Arguments are inside the parameters hash
96
+ parse_hash(options)
97
+ else
98
+ parse_array(*args)
99
+ end
100
+ @params = options
101
+ @remaining = args[@required.size..-1]
102
+ extract_pagination(options)
103
+
104
+ yield_or_eval(&block)
105
+ self
106
+ end
107
+
108
+ # Remove unknown keys from parameters hash.
109
+ #
110
+ # = Parameters
111
+ # :recursive - boolean that toggles whether nested filtering should be applied
112
+ #
113
+ def permit(keys, key=nil, options={})
114
+ filter! keys, (key.nil? ? params : params[key]), options if keys.any?
115
+ self
116
+ end
117
+
118
+ # Check if required keys are present inside parameters hash.
119
+ #
120
+ # @api public
121
+ def assert_required(required)
122
+ assert_required_keys required, params
123
+ self
124
+ end
125
+
126
+ # Check if parameters match expected values.
127
+ #
128
+ # @api public
129
+ def assert_values(values, key=nil)
130
+ assert_valid_values values, (key.nil? ? params : params[key])
131
+ self
132
+ end
133
+
134
+ private
135
+
136
+ # Parse array arguments and assign in order to required properties
137
+ #
138
+ # @param [Array[Object]] args
139
+ #
140
+ # @raise ArgumentError
141
+ #
142
+ # @return [nil]
143
+ #
144
+ # @api public
145
+ def parse_array(*args)
146
+ assert_presence_of(*args)
147
+ @required.each_with_index do |req, indx|
148
+ @assigns[req] = args[indx]
149
+ end
150
+ check_requirement!(*args)
151
+ end
152
+
153
+ # Remove required arguments from parameters and
154
+ # validate their presence(if not nil or empty string).
155
+ #
156
+ # @param [Hash[String]] options
157
+ #
158
+ # @return [nil]
159
+ #
160
+ # @api private
161
+ def parse_hash(options)
162
+ options.each { |key, val| remove_required(options, key, val) }
163
+ hash = update_required_from_global
164
+ check_requirement!(*hash.keys)
165
+ end
166
+
167
+ # Remove required property from hash
168
+ #
169
+ # @param [Hash[String]] options
170
+ # the options to check
171
+ #
172
+ # @param [String] key
173
+ # the key to remove
174
+ #
175
+ # @param [String] val
176
+ # the value to assign
177
+ #
178
+ # @api private
179
+ def remove_required(options, key, val)
180
+ if @required.include?(key.to_s)
181
+ assert_presence_of(val)
182
+ options.delete(key)
183
+ @assigns[key.to_s] = val
184
+ end
185
+ end
186
+
187
+ # Update required property from globals if not present
188
+ #
189
+ # @return [Hash[String]]
190
+ #
191
+ # @api private
192
+ def update_required_from_global
193
+ @required.reduce({}) do |hash, property|
194
+ if @assigns.key?(property)
195
+ hash[property] = self[property]
196
+ elsif api_property?(property)
197
+ hash[property] = api.send(:"#{property}")
198
+ self[property] = hash[property]
199
+ end
200
+ hash
201
+ end
202
+ end
203
+
204
+ # Check if api has non-empty property
205
+ #
206
+ # @param [String] property
207
+ # the property to check
208
+ #
209
+ # @return [Boolean]
210
+ #
211
+ # @api private
212
+ def api_property?(property)
213
+ api.respond_to?(:"#{property}") && api.send(:"#{property}")
214
+ end
215
+
216
+ # Check if required arguments are present.
217
+ #
218
+ #
219
+ def check_requirement!(*args)
220
+ args_length = args.length
221
+ required_length = @required.length
222
+
223
+ if args_length < required_length
224
+ raise ArgumentError, "Wrong number of arguments " \
225
+ "(#{args_length} for #{required_length}). " \
226
+ "Expected `#{@required.join(', ')}` as required arguments, " \
227
+ "but got `#{args.join(", ")}`."
228
+ end
229
+ end
230
+
231
+ # Find auto_pagination parameter in options hash
232
+ #
233
+ def extract_pagination(options)
234
+ if (value = options.delete(AUTO_PAGINATION))
235
+ api.auto_pagination = value
236
+ end
237
+ end
238
+
239
+ # Evaluate a block
240
+ #
241
+ # @api privte
242
+ def yield_or_eval(&block)
243
+ return unless block
244
+ block.arity > 0 ? yield(self) : instance_eval(&block)
245
+ end
246
+ end # Arguments
247
+ end # Api
248
+ end # Assembla
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ require 'assembla_api/api/config/property'
4
+ require 'assembla_api/api/config/property_set'
5
+
6
+ module Assembla
7
+
8
+ class API
9
+ # A base class for constructing api configuration
10
+ class Config
11
+
12
+ # Defines a property on an object's class or instance
13
+ #
14
+ # @example
15
+ # class Configuration < Api::Config
16
+ # property :adapter, default: :net_http
17
+ # property :user, required: true
18
+ # end
19
+ #
20
+ # @param [Symbol] name
21
+ # the name of a property
22
+ #
23
+ # @param [#to_hash] options
24
+ # the extra options
25
+ #
26
+ # @return [self]
27
+ #
28
+ # @api public
29
+ def self.property(name, options = {})
30
+ self.property_set << Property.new(name, options)
31
+ update_subclasses(name, options)
32
+ self
33
+ end
34
+
35
+ def self.update_subclasses(name, options)
36
+ if defined?(@subclasses) && @subclasses
37
+ @subclasses.each { |klass| klass.property(name, options) }
38
+ end
39
+ end
40
+
41
+ # Check if property is defined
42
+ #
43
+ # @param [Symbol] name
44
+ # the name to check
45
+ #
46
+ # @return [Boolean]
47
+ #
48
+ # @api public
49
+ def self.property?(name)
50
+ property_set.include?(name)
51
+ end
52
+
53
+ class << self
54
+ attr_reader :property_set
55
+ end
56
+
57
+ instance_variable_set("@property_set", PropertySet.new(self))
58
+
59
+ def self.inherited(descendant)
60
+ super
61
+ (@subclasses ||= Set.new) << descendant
62
+ descendant.instance_variable_set('@property_set',
63
+ PropertySet.new(descendant, self.property_set.properties.dup))
64
+ end
65
+
66
+ def initialize(&block)
67
+ super(&block)
68
+ end
69
+
70
+ def property_names
71
+ self.class.property_set.properties.map(&:name)
72
+ end
73
+
74
+ def self.property_names
75
+ property_set.properties.map(&:name)
76
+ end
77
+
78
+ # Fetach all the properties and their values
79
+ #
80
+ # @return [Hash[Symbol]]
81
+ #
82
+ # @api public
83
+ def fetch(value = nil)
84
+ if value
85
+ self.class.property_set[value]
86
+ else
87
+ self.class.property_set.to_hash
88
+ end
89
+ end
90
+
91
+ # Provide access to properties
92
+ #
93
+ # @example
94
+ # config.call do |config|
95
+ # config.adapter = :net_http
96
+ # end
97
+ #
98
+ # @return [self]
99
+ #
100
+ # @api private
101
+ def call(&block)
102
+ block.call(self) if block_given?
103
+ self
104
+ end
105
+ end # Config
106
+ end # Api
107
+ end # Assembla
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ module Assembla
4
+ class API
5
+ class Config
6
+
7
+ # Property objects provide an interface for configuration options
8
+ class Property
9
+
10
+ attr_reader :name
11
+ attr_reader :default
12
+ attr_reader :required
13
+
14
+ def initialize(name, options)
15
+ @name = name
16
+ @default = options.fetch(:default, nil)
17
+ @required = options.fetch(:required, nil)
18
+ @options = options
19
+ end
20
+
21
+ # @api private
22
+ def define_accessor_methods(properties)
23
+ properties.define_reader_method(self, self.name, :public)
24
+ properties.define_writer_method(self, "#{self.name}=", :public)
25
+ end
26
+ end # Property
27
+
28
+ end # Config
29
+ end # Api
30
+ end # Assembla
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+ require 'set'
3
+
4
+ module Assembla
5
+ class API
6
+ class Config
7
+ # Class responsible for storing configuration properties
8
+ class PropertySet
9
+ include Enumerable
10
+
11
+ attr_reader :parent
12
+
13
+ attr_reader :properties
14
+
15
+ # Initialize an PropertySet
16
+ #
17
+ # @param [Object] parent
18
+ # @param [Set] properties
19
+ #
20
+ # @return [undefined]
21
+ #
22
+ # @api private
23
+ def initialize(parent = nil, properties = Set.new)
24
+ @parent = parent
25
+ @properties = properties
26
+ @map = {}
27
+ end
28
+
29
+ # Iterate over properties
30
+ #
31
+ # @yield [property]
32
+ #
33
+ # @yieldparam [Property] property
34
+ #
35
+ # @return [self]
36
+ #
37
+ # @api public
38
+ def each
39
+ return to_enum unless block_given?
40
+ @map.each { |name, property| yield property if name.is_a?(Symbol) }
41
+ self
42
+ end
43
+
44
+ # Adds property to the set
45
+ #
46
+ # @example
47
+ # properties_set << property
48
+ #
49
+ # @param [Property] property
50
+ #
51
+ # @return [self]
52
+ #
53
+ # @api public
54
+ def <<(property)
55
+ properties << property
56
+ update_map(property.name, property.default)
57
+ property.define_accessor_methods(self)
58
+ self
59
+ end
60
+
61
+ # Access property by name
62
+ #
63
+ # @api public
64
+ def [](name)
65
+ @map[name]
66
+ end
67
+ alias_method :fetch, :[]
68
+
69
+ # Set property value by name
70
+ #
71
+ # @api public
72
+ def []=(name, property)
73
+ update_map(name, property)
74
+ end
75
+
76
+ # Update map with index
77
+ #
78
+ # @api private
79
+ def update_map(name, property)
80
+ @map[name.to_sym] = @map[name.to_s.freeze] = property
81
+ end
82
+
83
+ # Convert properties to a hash of property names and
84
+ # corresponding values
85
+ #
86
+ # @api public
87
+ def to_hash
88
+ properties.each_with_object({}) do |property, props|
89
+ name = property.name
90
+ props[name] = self[name]
91
+ end
92
+ end
93
+
94
+ # Check if properties exist
95
+ #
96
+ # @api public
97
+ def empty?
98
+ @map.empty?
99
+ end
100
+
101
+ # @api private
102
+ def define_reader_method(property, method_name, visibility)
103
+ property_set = self
104
+ parent.send(:define_method, method_name) { property_set[property.name] }
105
+ parent.send(visibility, method_name)
106
+ end
107
+
108
+ # @api private
109
+ def define_writer_method(property, method_name, visibility)
110
+ property_set = self
111
+ parent.send(:define_method, method_name) do |value|
112
+ property_set[property.name]= value
113
+ end
114
+ parent.send(visibility, method_name)
115
+ end
116
+ end # PropertySet
117
+ end # Config
118
+ end # Api
119
+ end # Assembla