bubbles-rest-client 0.0.6

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,201 @@
1
+ require 'bubbles/rest_environment'
2
+
3
+ module Bubbles
4
+ class << self
5
+ attr_writer :configuration
6
+ end
7
+
8
+ ##
9
+ # Configure the Bubbles instance.
10
+ #
11
+ # Use this method if you want to configure the Bubbles instance, typically during intialization of your Gem or
12
+ # application.
13
+ #
14
+ # @example In app/config/initializers/bubbles.rb
15
+ # Bubbles.configure do |config|
16
+ # config.endpoints = [
17
+ # {
18
+ # :type => :get,
19
+ # :location => :version,
20
+ # :authenticated => false,
21
+ # :api_key_required => false
22
+ # }
23
+ # ]
24
+ # end
25
+ def self.configure
26
+ yield(configuration)
27
+ end
28
+
29
+ def self.configuration
30
+ @configuration ||= Configuration.new
31
+ end
32
+
33
+ ##
34
+ # The configuration of the Bubbles rest client.
35
+ #
36
+ # Use this class if you want to retrieve configuration values set during initialization.
37
+ #
38
+ class Configuration
39
+ def initialize
40
+ @local_environment_scheme = 'http'
41
+ @local_environment_host = '127.0.0.1'
42
+ @local_environment_port = '1234'
43
+
44
+ @staging_environment_scheme = 'http'
45
+ @staging_environment_host = '127.0.0.1'
46
+ @staging_environment_port = '1234'
47
+
48
+ @production_environment_scheme = 'http'
49
+ @production_environment_host = '127.0.0.1'
50
+ @production_environment_port = '1234'
51
+
52
+ @endpoints = Hash.new
53
+ end
54
+
55
+ ##
56
+ # Retrieve the local {RestEnvironment} object defined as part of this Configuration.
57
+ #
58
+ def local_environment
59
+ RestEnvironment.new(@local_environment_scheme, @local_environment_host, @local_environment_port)
60
+ end
61
+
62
+ ##
63
+ # Set the local environment.
64
+ #
65
+ # @param [Object] env The environment, as a generic Ruby Object.
66
+ #
67
+ # @example In app/config/initializers/bubbles.rb
68
+ # Bubbles.configure do |config|
69
+ # config.local_environment = {
70
+ # :scheme => 'https',
71
+ # :host => 'api.somehost.com',
72
+ # :port => '443'
73
+ # }
74
+ # end
75
+ #
76
+ def local_environment=(env)
77
+ @local_environment_scheme = env[:scheme]
78
+ @local_environment_host = env[:host]
79
+ @local_environment_port = env[:port]
80
+ end
81
+
82
+ ##
83
+ # Retrieve the staging {RestEnvironment} object defined as part of this Configuration.
84
+ #
85
+ def staging_environment
86
+ RestEnvironment.new(@staging_environment_scheme, @staging_environment_host, @staging_environment_port)
87
+ end
88
+
89
+ ##
90
+ # Set the staging environment.
91
+ #
92
+ # @param [Object] env The environment, as a generic Ruby Object.
93
+ #
94
+ # @example In app/config/initializers/bubbles.rb
95
+ # Bubbles.configure do |config|
96
+ # config.staging_environment = {
97
+ # :scheme => 'https',
98
+ # :host => 'api.somehost.com',
99
+ # :port => '443'
100
+ # }
101
+ # end
102
+ #
103
+ def staging_environment=(env)
104
+ @staging_environment_scheme = env[:scheme]
105
+ @staging_environment_host = env[:host]
106
+ @staging_environment_port = env[:port]
107
+ end
108
+
109
+ ##
110
+ # Retrieve the production {RestEnvironment} object defined as part of this Configuration.
111
+ #
112
+ def production_environment
113
+ RestEnvironment.new(@production_environment_scheme, @production_environment_host, @production_environment_port)
114
+ end
115
+
116
+ ##
117
+ # Set the production environment.
118
+ #
119
+ # @param [Object] env The environment, as a generic Ruby Object.
120
+ #
121
+ # @example In app/config/initializers/bubbles.rb
122
+ # Bubbles.configure do |config|
123
+ # config.production_environment = {
124
+ # :scheme => 'https',
125
+ # :host => 'api.somehost.com',
126
+ # :port => '443'
127
+ # }
128
+ # end
129
+ #
130
+ def production_environment=(env)
131
+ @production_environment_scheme = env[:scheme]
132
+ @production_environment_host = env[:host]
133
+ @production_environment_port = env[:port]
134
+ end
135
+
136
+ ##
137
+ # Retrieve the list of +Endpoint+s configured in this +Configuration+ object.
138
+ #
139
+ # @return {Array} An Array of {Endpoint}s.
140
+ #
141
+ def endpoints
142
+ @endpoints
143
+ end
144
+
145
+ ##
146
+ # Add all {Endpoint} objects within this {Configuration} instance.
147
+ #
148
+ # {Endpoint} objects are defined using two required parameters: type and location, and three optional parameters:
149
+ # authenticated, api_key_required and name.
150
+ # - method: Indicates the HTTP method used to access the endpoint. Must be one of {Endpoint::METHODS}.
151
+ # - location: Indicates the path at which the {Endpoint} can be accessed on the host environment.
152
+ # - authenticated: (Optional) A true or false value indicating whether the {Endpoint} requires an authorization
153
+ # token to access it. Defaults to false.
154
+ # - api_key_required: (Optional) A true or false value indicating whether the {Endpoint} requires a API key to
155
+ # access it. Defaults to false.
156
+ # - name: (Optional): A +String+ indicating the name of the method to add. If not provided, the method name will
157
+ # be the same as the +location+.
158
+ #
159
+ def endpoints=(endpoints)
160
+ new_endpoints = Hash.new
161
+ endpoints.each do |ep|
162
+ endpoint_object = Endpoint.new ep[:method], ep[:location].to_s, ep[:authenticated], ep[:api_key_required], ep[:name]
163
+
164
+ new_endpoints[endpoint_object.get_key_string] = endpoint_object
165
+ end
166
+
167
+ @endpoints = new_endpoints
168
+
169
+ # Define all of the endpoints as methods on RestEnvironment
170
+ @endpoints.values.each do |endpoint|
171
+ if endpoint.name != nil
172
+ endpoint_name_as_sym = endpoint.name.to_sym
173
+ else
174
+ endpoint_name_as_sym = endpoint.get_location_string.to_sym
175
+ end
176
+
177
+ if Bubbles::RestEnvironment.instance_methods(false).include? (endpoint_name_as_sym)
178
+ Bubbles::RestEnvironment.class_exec do
179
+ remove_method endpoint_name_as_sym
180
+ end
181
+ end
182
+
183
+ if endpoint.method == :get
184
+ if endpoint.authenticated?
185
+ Bubbles::RestEnvironment.class_exec do
186
+ define_method(endpoint_name_as_sym) do |auth_token|
187
+ RestClientResources.execute_get_authenticated self, endpoint, auth_token
188
+ end
189
+ end
190
+ else
191
+ Bubbles::RestEnvironment.class_exec do
192
+ define_method(endpoint_name_as_sym) do
193
+ RestClientResources.execute_get_unauthenticated self, endpoint
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,181 @@
1
+ require 'addressable/template'
2
+
3
+ module Bubbles
4
+ ##
5
+ # Representation of a single API endpoint within the Bubbles infrastructure.
6
+ #
7
+ # In order to access an API Endpoint, an {RestEnvironment} must also be provided. This class is an abstract
8
+ # representation of an +Endpoint+, without any information provided as part of the Environment. In other words, an
9
+ # Endpoint can be used with any +RestEnvironment+.
10
+ #
11
+ class Endpoint
12
+ ## Controls the method used to access the endpoint. Must be one of {Endpoint::Methods}.
13
+ # @return [Symbol] the method used to access the endpoint. Will always be one of the symbols defined in {Endpoint::METHODS}.
14
+ attr_accessor :method
15
+
16
+ ## Controls the location, relative to the web root of the host, used to access the endpoint.
17
+ # @return [String] the location relative to the web root of the host used to access the endpoint
18
+ attr_accessor :location
19
+
20
+ ## Controls whether authentication is required to access this endpoint. Defaults to false.
21
+ # @return [Boolean] true, if authentication is required to access this endpoint; false, otherwise.
22
+ attr_accessor :authentication_required
23
+
24
+ ## Controls whether an API key is required to access this endpoint. Defaults to false.
25
+ # @return [Boolean] true, if an API key is required to access this endpoint; false, otherwise.
26
+ attr_accessor :api_key_required
27
+
28
+ ## A template for specifying the complete URL for endpoints.
29
+ API_URL = ::Addressable::Template.new("{scheme}://{host}/{endpoint}")
30
+
31
+ ## A template for specifying the complete URL for endpoints, with a port attached to the host.
32
+ API_URL_WITH_PORT = ::Addressable::Template.new("{scheme}://{host}:{port}/{endpoint}")
33
+
34
+
35
+ ## The HTTP methods supported by a rest client utilizing Bubbles.
36
+ METHODS = %w[get].freeze
37
+
38
+ ##
39
+ # Construct a new instance of an Endpoint.
40
+ #
41
+ # @param [Symbol] method The type of the new Endpoint to create. Must be one of the methods in {Endpoint::METHODS}.
42
+ # @param [String] location The location, relative to the root of the host, at which the endpoint resides.
43
+ # @param [Boolean] auth_required If true, then authorization/authentication is required to access this endpoint.
44
+ # Defaults to +false+.
45
+ # @param [Boolean] api_key_required If true, then an API key is required to access this endpoint. Defaults to
46
+ # +false+.
47
+ # @param [String] name An optional name which will be given to the method that will execute this {Endpoint} within
48
+ # the context of a {RestClientResources} object.
49
+ #
50
+ def initialize(method, location, auth_required = false, api_key_required = false, name = nil)
51
+ @method = method
52
+ @location = location
53
+ @auth_required = auth_required
54
+ @api_key_required = api_key_required
55
+ @name = name
56
+
57
+ # Strip the leading slash from the endpoint location, if it's there
58
+ if @location.to_s[0] == '/'
59
+ @location = @location.to_s.slice(1, @location.to_s.length)
60
+ end
61
+ end
62
+
63
+ ##
64
+ # Retrieve a +String+ that will identify this +Endpoint+ uniquely within a hash table.
65
+ #
66
+ # @return [String] A unique identifier for this Endpoint, including its method (get/post/put/etc..), location, whether or not it is authenticated, and whether it needs an API key to successfully execute.
67
+ #
68
+ def get_key_string
69
+ auth_string = '-unauthenticated'
70
+ if @auth_required
71
+ auth_string = '-authenticated'
72
+ end
73
+
74
+ api_key_string = ''
75
+ if @api_key_required
76
+ api_key_string = '-with-api-key'
77
+ end
78
+
79
+ method.to_s + "-" + @location.to_s + auth_string + api_key_string
80
+ end
81
+
82
+ ##
83
+ # Retrieve the base URL template for this +Endpoint+, given a +RestEnvironment+.
84
+ #
85
+ # @param [RestEnvironment] env The +RestEnvironment+ to use to access this endpoint.
86
+ #
87
+ # @return [Addressable::Template] A +Template+ containing the URL to use to access this +Endpoint+.
88
+ #
89
+ def get_base_url(env)
90
+ unless env.port == 80 || env.port == 443
91
+ return API_URL_WITH_PORT
92
+ end
93
+
94
+ API_URL
95
+ end
96
+
97
+ ##
98
+ # Retrieve the URL to access this +Endpoint+, as a +String+ with all parameters expanded.
99
+ #
100
+ # @param [RestEnvironment] env The +RestEnvironment+ to use to access this +Endpoint+.
101
+ #
102
+ # @return [String] A +String+ containing the full URL to access this +Endpoint+ on the given {RestEnvironment}.
103
+ #
104
+ def get_expanded_url(env)
105
+ url = get_base_url env
106
+
107
+ if is_complex?
108
+ special_url_string = '{scheme}://{environment_host}/'
109
+ unless @port == 80 || @port == 443
110
+ special_url_string = '{scheme}://{environment_host}:{port}/'
111
+ end
112
+
113
+ special_url_string = special_url_string + @location
114
+ url = ::Addressable::Template.new(special_url_string)
115
+
116
+ return url.expand(scheme: env.scheme, host: env.host, port: env.port)
117
+ end
118
+
119
+ url.expand(scheme: env.scheme, host: env.host, port: env.port, endpoint: @location)
120
+ end
121
+
122
+ ##
123
+ # Determine if the location for this Endpoint is complex.
124
+ #
125
+ # @return [Boolean] true, if the location for this Endpoint is complex (contains a '/'); false, otherwise.
126
+ def is_complex?
127
+ @location.include? '/'
128
+ end
129
+
130
+ ##
131
+ # Retrieve a String representing the location of this Endpoint.
132
+ #
133
+ # Complex Endpoints will have instances of '/' replaced with '_'.
134
+ #
135
+ # @return [String] The string representation of the location of this endpoint.
136
+ def get_location_string
137
+ unless is_complex?
138
+ return @location
139
+ end
140
+
141
+ @location.to_s.gsub('/', '_')
142
+ end
143
+
144
+ ##
145
+ # Determine if this +Endpoint+ requires authentication/authorization to utilize
146
+ #
147
+ # @return [Boolean] true, if this +Endpoint+ requires authentication/authorization to use; false, otherwise.
148
+ def authenticated?
149
+ @auth_required
150
+ end
151
+
152
+ ##
153
+ # Set the name of the method on {RestClientResources} used to access this {Endpoint}.
154
+ #
155
+ # @param [String] name The name of the method used to access this {Endpoint}.
156
+ #
157
+ def name=(name)
158
+ @name = name
159
+ end
160
+
161
+ ##
162
+ # Retrieve the name of the method on {RestClientResources} used to access this {Endpoint}.
163
+ #
164
+ # @return [String] A String containing the name of the method on {RestClientResources} used to access this
165
+ # {Endpoint}, or +nil+ if one wasn't provided.
166
+ #
167
+ def name
168
+ @name
169
+ end
170
+
171
+ ##
172
+ # Determine if this {Endpoint} has a method name, different from the +location+ name, specified for it.
173
+ #
174
+ # @return [Boolean] true, if this {Endpoint} has a method name that is different than the +location+ name specified
175
+ # for the +Endpoint+, to be defined on {RestClientResources}; false, otherwise.
176
+ #
177
+ def name?
178
+ @name == nil
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,124 @@
1
+ require 'bubbles/config'
2
+ require 'bubbles/rest_environment'
3
+
4
+ module Bubbles
5
+ class RestClientResources
6
+ def local_environment
7
+ Bubbles.configuration.local_environment
8
+ end
9
+
10
+ def staging_environment
11
+ Bubbles.configuration.staging_environment
12
+ end
13
+
14
+ def production_environment
15
+ Bubbles.configuration.production_environment
16
+ end
17
+
18
+ ##
19
+ # Create a new instance of +RestClientResources+.
20
+ #
21
+ # @param env The +RestEnvironment+ that should be used for this set of resources.
22
+ # @param api_key The API key to use to send to the host for unauthenticated requests.
23
+ #
24
+ def initialize(env, api_key)
25
+ unless env
26
+ env = :local
27
+ end
28
+
29
+ unless api_key
30
+ api_key = ''
31
+ end
32
+
33
+ @environment = get_environment env
34
+ @api_key = api_key
35
+ @auth_token = nil
36
+ end
37
+
38
+ ##
39
+ # Execute a GET request without authentication.
40
+ #
41
+ # @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
42
+ # @param [Endpoint] endpoint The +Endpoint+ which should be requested
43
+ #
44
+ # @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
45
+ #
46
+ def self.execute_get_unauthenticated(env, endpoint)
47
+ url = endpoint.get_expanded_url env
48
+
49
+ begin
50
+ if env.scheme == 'https'
51
+ response = RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
52
+ .get({
53
+ :content_type => :json,
54
+ :accept => :json
55
+ })
56
+ else
57
+ response = RestClient.get(url.to_s,
58
+ {
59
+ :content_type => :json
60
+ })
61
+ end
62
+ rescue Errno::ECONNREFUSED
63
+ return {:error => 'Unable to connect to host ' + env.host.to_s + ':' + env.port.to_s}.to_json
64
+ end
65
+
66
+ response
67
+ end
68
+
69
+ ##
70
+ # Execute a GET request with authentication.
71
+ #
72
+ # Currently, only Authorization: Bearer is supported.
73
+ #
74
+ # @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
75
+ # @param [Endpoint] endpoint The +Endpoint+ which should be requested
76
+ # @param [String] auth_token The authorization token to use for authentication.
77
+ #
78
+ # @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
79
+ #
80
+ def self.execute_get_authenticated(env, endpoint, auth_token)
81
+ url = endpoint.get_expanded_url env
82
+
83
+ begin
84
+ if env.scheme == 'https'
85
+ response = RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
86
+ .get({
87
+ :authorization => 'Bearer ' + auth_token,
88
+ :content_type => :json,
89
+ :accept => :json
90
+ })
91
+ else
92
+ response = RestClient.get(url.to_s,
93
+ {
94
+ :authorization => 'Bearer ' + auth_token,
95
+ :content_type => :json
96
+ })
97
+ end
98
+ rescue Errno::ECONNREFUSED
99
+ return {:error => 'Unable to connect to host ' + env.host.to_s + ':' + env.port.to_s}.to_json
100
+ end
101
+
102
+ response
103
+ end
104
+
105
+ ##
106
+ # Retrieve the {RestEnvironment} to utilize from a {Symbol} describing it.
107
+ #
108
+ # @param [Symbol] environment A {Symbol} describing the environment to use. Must be one of:
109
+ # [:production, :staging, :local, nil]. If +nil+, note that +:production+ will be used.
110
+ #
111
+ # @return [RestEnvironment] The {RestEnvironment} corresponding to the given {Symbol}.
112
+ #
113
+ def get_environment(environment)
114
+ if !environment || environment == :production
115
+ return self.production_environment
116
+ elsif environment == :staging
117
+ return self.staging_environment
118
+ end
119
+
120
+
121
+ self.local_environment
122
+ end
123
+ end
124
+ end