blomming_api 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,140 @@
1
+ require 'method_source'
2
+
3
+ module BlommingApi
4
+
5
+ AUTHORS = ["Giorgio Robino"]
6
+ EMAILS = ["giorgio.robino@gmail.com"]
7
+ SUMMARY = %q{www.blomming.com social commerce API's wrapper}
8
+ DESCRIPTION = %q{www.blomming.com social commerce API's wrapper: supply a client access layer that embed authentication and communication details, supply API endpoints wrappers.}
9
+ HOMEPAGE = "https://github.com/solyaris/blomming_api"
10
+
11
+ def BlommingApi::about
12
+ "\tgem : blomming_api (v.#{BlommingApi::VERSION})\n"\
13
+ "\tsummary : #{BlommingApi::SUMMARY}\n"\
14
+ "\tauthors : #{BlommingApi::AUTHORS.join(',')} (#{BlommingApi::EMAILS.join(',')})\n"\
15
+ "\thomepage : #{BlommingApi::HOMEPAGE}\n"
16
+ end
17
+
18
+ def BlommingApi::endpoints_help
19
+ puts "BUY endpoints available methods:\n\n"
20
+
21
+ buy_endpoints_methods = BlommingApi::BuyEndpoints.instance_methods(false)
22
+
23
+ buy_endpoints_methods.each do |method_name|
24
+ display_method_info method_name
25
+ end
26
+
27
+ puts "SELL endpoints available methods:\n\n"
28
+
29
+ sell_endpoints_methods = BlommingApi::SellEndpoints.instance_methods(false)
30
+
31
+ sell_endpoints_methods.each do |method_name|
32
+ display_method_info method_name
33
+ end
34
+ end
35
+
36
+ def BlommingApi::display_method_info (method_name)
37
+ method_comment = BlommingApi::Client.instance_method(method_name.to_sym).comment
38
+
39
+ #method_comment.gsub! "#", "\t#"
40
+
41
+ method_source = BlommingApi::Client.instance_method(method_name.to_sym).source
42
+
43
+ method_def = method_source.split("\n").first.strip[/def (?<match>.*)/,"match"]
44
+ location = BlommingApi::Client.instance_method(method_name.to_sym).source_location
45
+
46
+ puts "#{method_comment}"
47
+ puts "\t#{method_def}"
48
+ puts "\tfile: #{location.first}"
49
+ puts "\tline: #{location.last}"
50
+ end
51
+ private_class_method :display_method_info
52
+
53
+
54
+ def BlommingApi::authentication_help
55
+ <<-CONFIGURATION_HELP
56
+ Authentication set-up
57
+
58
+ In order to be granted to access to Blomming API, each client must be identified by some credential values (oauth server authentication).
59
+
60
+ Get your Blomming API credentials
61
+ =================================
62
+
63
+ API credentials are generated by Blomming tech team for a per 3rd part application use. Please contact [api@blomming.com](mailto:api@blomming.com) and explain briefly why do you need them and how do you plan to use Blomming service. Blomming tech team will be happy to give you the full access to API!
64
+
65
+ Buy Services Authentication
66
+ ---------------------------
67
+
68
+ To access Blomming APIs, each client must be identified by two credential values required as parameters of initial Blomming OAuth server bearer token request:
69
+
70
+ - *Application ID*
71
+ - *Secret*
72
+
73
+ Sell Services Authentication
74
+ ----------------------------
75
+
76
+ Application ID and Secret values, are all you need to use buy services, but in case of sell services, you must authenticate supplying also your Blomming account cusername and password:
77
+
78
+ - *Username*
79
+ - *Password*
80
+
81
+
82
+ Set-up your *blommimg_api* configuration file
83
+ =============================================
84
+
85
+ Using the blomming_api gem, a client must be "initialized" with a YAML configuration file (.yml), in order to store all Blomming API credentials data and some default API values, among others:
86
+
87
+ - *domain* (production/staging API urls)
88
+ - *api_version* (API release number)
89
+
90
+
91
+ You have to set-up all data on a blommimg_api YAML configuration file `<your_config_file.yml>`, following these two possible skeletons:
92
+
93
+
94
+ Config file for *BUY services* authentication
95
+ ---------------------------------------------
96
+
97
+ Config file example: `your/path/to/buy_services_stage_config.yml` :
98
+
99
+ description: my account for buy services, access to staging server
100
+
101
+ services: buy
102
+
103
+ client_id: __copy_here_your_blomming_api_client_id__
104
+ client_secret: __copy_here_your_blomming_api_client_secret__
105
+
106
+ domain: https://blomming-api-staging.herokuapp.com
107
+ api_version: /v1
108
+
109
+ default_currency: USD
110
+ default_locale: US
111
+
112
+ verbose: false
113
+
114
+
115
+ Config file for *SELL services* authentication
116
+ ----------------------------------------------
117
+
118
+ Config file example `your/path/to/buy_services_prod_config.yml`:
119
+
120
+ description: my account for sell services, access to production server
121
+
122
+ services: sell
123
+
124
+ client_id: __copy_here_your_blomming_api_client_id__
125
+ client_secret: __copy_here_your_blomming_api_client_secret__
126
+
127
+ username: __copy_here_your_blomming_account_username__
128
+ password: __copy_here_your_blomming_account_password__
129
+
130
+ domain: https://api.blomming.com
131
+ api_version: /v1
132
+
133
+ default_currency: EUR
134
+ default_locale: it
135
+
136
+ verbose: true
137
+
138
+ CONFIGURATION_HELP
139
+ end
140
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+ require 'rest_client'
4
+
5
+ module BlommingApi
6
+ module OauthEndpoint
7
+ #
8
+ # authentication request
9
+ #
10
+ # => to get initial OAuth access token:
11
+ #
12
+ # authenticate :initialize
13
+ #
14
+ # => to get refresh token in case of OAuth access token expired
15
+ #
16
+ # authenticate :refresh
17
+ #
18
+ def authenticate(type=:initialize)
19
+ url = api_url('/oauth/token')
20
+ services = @services.downcase.to_sym
21
+
22
+ # client_id and client_secret are required for any kind of request
23
+ auth_params = { client_id: @client_id, client_secret: @client_secret }
24
+
25
+ # authentication type must be :initalize or :refresh
26
+ if type == :initialize
27
+
28
+ # authentication for services :buy or :sell
29
+ if services == :buy
30
+ @grant_type = "client_credentials"
31
+ auth_params.merge! grant_type: @grant_type
32
+
33
+ elsif services == :sell
34
+ @grant_type = "password"
35
+ auth_params.merge! \
36
+ grant_type: @grant_type, username: @username, password: @password
37
+
38
+ else
39
+ raise"FATAL: config value for: services (#@services): invalid"
40
+ end
41
+
42
+ elsif type == :refresh
43
+ @grant_type = "refresh_token"
44
+ auth_params.merge! \
45
+ grant_type: @grant_type, refresh_token: @refresh_token
46
+
47
+ else
48
+ raise "FATAL: incorrect authentication type (#{type})"
49
+ end
50
+
51
+ data = load_or_retry do
52
+ RestClient.post url, auth_params
53
+ end
54
+
55
+ #puts_response_header(__method__, data) if @verbose_access_token
56
+ #puts_access_token(access_token) if @verbose
57
+
58
+ # assign all token info to instance variables
59
+ @token_type = data["token_type"]
60
+ @expires_in = data["expires_in"]
61
+ @refresh_token = data["refresh_token"]
62
+ @access_token = data["access_token"]
63
+ end
64
+ private :authenticate
65
+
66
+
67
+ def show_authentication (config_file)
68
+ "\tservices: #{@services}\n" +
69
+ "\tgrant_type: #{@grant_type}\n" +
70
+ "\tusername: #{@username}\n" +
71
+ "\tpassword: #{@password}\n" +
72
+ "\tclient_id: #{@client_id}\n" +
73
+ "\tclient_secret: #{@client_secret}\n\n" +
74
+
75
+ "\ttoken_type: #{@token_type}\n" +
76
+ "\texpires_in: #{@expires_in}\n" +
77
+ "\trefresh_token: #{@refresh_token}\n" +
78
+ "\taccess_token: #{@access_token}\n\n"
79
+ end
80
+ public :show_authentication
81
+
82
+ end
83
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+
4
+ module BlommingApi
5
+ module PrivateHelpers
6
+ #
7
+ # build complete URL of any API request endpoint URI
8
+ #
9
+ def api_url (api_endpoint_uri)
10
+ "#{@domain}#{@api_version}#{api_endpoint_uri}"
11
+ end
12
+
13
+ #
14
+ # return the hash to be used as HTTP header in all API requests,
15
+ # embedding authentication token and all optional parameters
16
+ #
17
+ def request_params(params={})
18
+ { authorization: "Bearer #{@access_token}", params: params }
19
+ end
20
+
21
+ #
22
+ # private helper: manage RestClient exceptions. iterator.
23
+ #
24
+ def load_or_retry (retry_seconds=2, &restclient_call_block)
25
+ begin
26
+ json_data = restclient_call_block.call
27
+
28
+ # IP connection error, wrong url, etc.
29
+ rescue SocketError => e
30
+ STDERR.puts "#{Time.now}: socket error: #{e}. Possible net connection problems. Retry in #{retry_seconds} seconds."
31
+ sleep retry_seconds
32
+ retry
33
+
34
+ # restclient exceptions
35
+ rescue => e
36
+
37
+ #
38
+ # HTTP status code manager
39
+ #
40
+ if 401 == e.response.code
41
+ # Client authentication failed due to unknown client, no client authentication included,
42
+ # or unsupported authentication method
43
+ # After your bearer token has expired, each request done with that stale token will return an HTTP code 401
44
+ STDERR.puts "#{Time.now}: restclient error. http status code: #{e.response.code}: #{e.response.body}. Invalid or expired token. Retry in #{retry_seconds} seconds."
45
+
46
+ # opinabile mettere la sleep qui, ma meglio per evitare loop stretto
47
+ sleep retry_seconds
48
+
49
+ # richiede il token daccapo. da rivedere.
50
+ authenticate :refresh
51
+ retry
52
+
53
+ elsif 404 == e.response.code
54
+ STDERR.puts "#{Time.now}: restclient error. http status code: #{e.response.code}: #{e.response.body}. Exit."
55
+ exit
56
+
57
+ # Invalid or blank request body given (selll services endpoints)
58
+ elsif 422 == e.response.code
59
+ STDERR.puts "#{Time.now}: restclient error. http status code: #{e.response.code}: #{e.response.body}. Exit."
60
+ exit
61
+
62
+ elsif [500, 520].include? e.response.code
63
+ STDERR.puts "#{Time.now}: restclient error. http status code: #{e.response.code}: #{e.response.body}. Retry in #{retry_seconds} seconds."
64
+ sleep retry_seconds
65
+ retry
66
+
67
+ else
68
+ STDERR.puts "#{Time.now}: restclient error. http status code: #{e.response.code}: #{e.response.body}. Exit."
69
+ exit
70
+ end
71
+ end
72
+
73
+ # HTTP status 200: return data (json to hash)
74
+ MultiJson.load json_data
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+ require 'rest_client'
4
+
5
+ module BlommingApi
6
+ module PublicHelpers
7
+
8
+ #
9
+ # all_pages
10
+ # It's a Ruby block iterator that retrieve all items of all pages of any API endpoint.
11
+ # Usage example:
12
+ # all_pages { |page, per_page|
13
+ # client.shop_items(shop_id, {:page => page, :per_page => per_page})
14
+ # }
15
+ #
16
+ def all_pages (verbose=:stdout, per_page=16, &endpoint_call_block)
17
+ page = 1
18
+ data = []
19
+
20
+ unless block_given?
21
+ raise 'method require a block! usage: #{__method__} '\
22
+ '{ |page, per_page| endpoint_method(..., {page: page, per_page: per_page}) }'
23
+ end
24
+
25
+ print 'collecting all items from de-pagination ' if verbose==:stdout
26
+
27
+ loop do
28
+ print "." if verbose==:stdout
29
+
30
+ # run block passing local variables: page, per_page.
31
+ data_single_page = endpoint_call_block.call page, per_page
32
+
33
+ # debug
34
+ # data_single_page.each_with_index { |item, index|
35
+ # puts "#{index+1}: title: #{item["title"]}, id: #{item["id"]}, shop: #{item["shop"]["id"]}" }
36
+
37
+ data.concat data_single_page
38
+
39
+ break if (data_single_page.size < per_page) || data_single_page.empty?
40
+ page += 1
41
+ end
42
+
43
+ print "\n" if verbose==:stdout
44
+ data
45
+ end
46
+
47
+ def puts_response_header(method, data)
48
+ puts "#{method.to_s} response header_params:"
49
+ puts data.header_params.to_s
50
+ puts
51
+ puts "#{method.to_s} response data:"
52
+ puts data
53
+ puts
54
+ end
55
+
56
+ def dump_pretty (json_data)
57
+ # JSON.pretty_generate(JSON.parse(json_data))
58
+ puts MultiJson.dump json_data, :pretty => true
59
+ end
60
+
61
+
62
+ #
63
+ # class methods
64
+ #
65
+
66
+ #
67
+ # search in +data+ hash field +name+ and get value of corresponding 'id'
68
+ # (per endpoint categories, collections)
69
+ #
70
+ def self.id_from_name (name, data)
71
+ id = nil
72
+ data.each { |item|
73
+ if name == item["name"]
74
+ # estrae l'id dal campo: items_url.
75
+ id = item["items_url"].split('/')[-2] # scan( /\d+/ ).last
76
+ break
77
+ end
78
+ }
79
+ id
80
+ end
81
+
82
+ #
83
+ # collect key values associated to a key in +array+ of hashes
84
+ # return nil if +array+ doesn't exist
85
+ # return array containing a values list associated to +key_name+
86
+ #
87
+ def self.collect_key_values (array, key_name)
88
+ return nil if array.nil?
89
+ return array.collect { |item| item[key_name] }
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,300 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+ require 'rest_client'
4
+
5
+ module BlommingApi
6
+ module SellEndpoints
7
+
8
+ #
9
+ # PAYMENT_TYPES
10
+ #
11
+ def sell_payment_types_find (params={})
12
+ url = api_url "/sell/payment_types​/user_list"
13
+ req = request_params(params)
14
+
15
+ load_or_retry do
16
+ RestClient.get url, req
17
+ end
18
+ end
19
+
20
+ def sell_payment_types_create (payload, params={})
21
+ url = api_url "/sell/payment_types​/new"
22
+ load = MultiJson.dump payload
23
+ req = request_params(params)
24
+
25
+ load_or_retry do
26
+ RestClient.post url, load, req
27
+ end
28
+ end
29
+
30
+ def sell_payment_types_update (id, payload, params={})
31
+ url = api_url "/sell/payment_types​/#{id}"
32
+ load = MultiJson.dump payload
33
+ req = request_params(params)
34
+
35
+ load_or_retry do
36
+ RestClient.put url, load, req
37
+ end
38
+ end
39
+
40
+ def sell_payment_types_delete (id, params={})
41
+ url = api_url "/sell/payment_types​/#{id}"
42
+ req = request_params(params)
43
+
44
+ load_or_retry do
45
+ RestClient.delete url, req
46
+ end
47
+ end
48
+
49
+ #
50
+ # REGISTER
51
+ #
52
+ def sell_register (payload, params={})
53
+ url = api_url "/sell/register"
54
+ load = MultiJson.dump payload
55
+ req = request_params(params)
56
+
57
+ load_or_retry do
58
+ RestClient.post url, load, req
59
+ end
60
+ end
61
+
62
+ #
63
+ # SHIPPING_COUNTRIES
64
+ #
65
+ def sell_shipping_countries_all (params={})
66
+ url = api_url "/sell/shipping_countries​​/all"
67
+ req = request_params({locale: @locale}.merge(params))
68
+
69
+ load_or_retry do
70
+ RestClient.get url, req
71
+ end
72
+ end
73
+
74
+ #
75
+ # SHIPPING_PROFILES
76
+ #
77
+
78
+ def sell_shipping_profiles (params={})
79
+ url = api_url "/sell/shipping_profiles"
80
+ req = request_params(params)
81
+
82
+ load_or_retry do
83
+ RestClient.get url, req
84
+ end
85
+ end
86
+
87
+ def sell_shipping_profile_create (payload, params={})
88
+ url = api_url "/sell/shipping_profiles"
89
+ load = MultiJson.dump payload
90
+ req = request_params(params)
91
+
92
+ load_or_retry do
93
+ RestClient.post url, load, req
94
+ end
95
+ end
96
+
97
+ =begin
98
+ def sell_shipping_profile_update (payload, params={})
99
+ url = api_url "/sell/shipping_profiles"
100
+ load = MultiJson.dump payload
101
+ req = request_params(params)
102
+
103
+ load_or_retry do
104
+ RestClient.put url, load, req
105
+ end
106
+ end
107
+ =end
108
+
109
+ def sell_shipping_profile_update (payload, params={})
110
+ url = api_url "/sell/shipping_profiles/#{id}"
111
+ load = MultiJson.dump payload
112
+ req = request_params(params)
113
+
114
+ load_or_retry do
115
+ RestClient.put url, load, req
116
+ end
117
+ end
118
+
119
+ def sell_shipping_profile_delete (id, params={})
120
+ url = api_url "/sell/payment_types​/#{id}"
121
+ req = request_params(params)
122
+
123
+ load_or_retry do
124
+ RestClient.delete url, req
125
+ end
126
+ end
127
+
128
+ def sell_shipping_profile_find (id, params={})
129
+ url = api_url "/sell/payment_types​/#{id}"
130
+ req = request_params(params)
131
+
132
+ load_or_retry do
133
+ RestClient.get url, req
134
+ end
135
+ end
136
+
137
+ def sell_shipping_profile_item_create (payload, item_id, params={})
138
+ url = api_url "/sell/shipping_profiles/items​/#{item_id}"
139
+ load = MultiJson.dump payload
140
+ req = request_params(params)
141
+
142
+ load_or_retry do
143
+ RestClient.post url, load, req
144
+ end
145
+ end
146
+
147
+
148
+ #
149
+ # SHIPPING_REGIONS
150
+ #
151
+ def sell_shipping_regions (params={})
152
+ url = api_url "/sell/shipping_regions​/all"
153
+ req = request_params({locale: @locale}.merge(params))
154
+
155
+ load_or_retry do
156
+ RestClient.get url, req
157
+ end
158
+ end
159
+
160
+ #
161
+ # SHOP_DASHBOARD
162
+ #
163
+ def sell_shop_dashboard (params={})
164
+ url = api_url "/sell/shop/dashboard"
165
+ req = request_params({locale: @locale}.merge(params))
166
+
167
+ load_or_retry do
168
+ RestClient.get url, req
169
+ end
170
+ end
171
+
172
+ #
173
+ # SHOP_ITEMS
174
+ #
175
+ def sell_shop_items (params={})
176
+ url = api_url '/sell/shop/items'
177
+ req = request_params(params)
178
+
179
+ load_or_retry do
180
+ RestClient.get url, req
181
+ end
182
+ end
183
+
184
+ def sell_shop_item_create (payload, params={})
185
+ url = api_url '/sell/shop/items/new'
186
+ load = MultiJson.dump payload
187
+ req = request_params(params)
188
+
189
+ load_or_retry do
190
+ RestClient.post url, load, req
191
+ end
192
+ end
193
+
194
+ def sell_shop_item_find (item_id, params={})
195
+ url = api_url "/sell/shop/items/#{item_id}"
196
+ req = request_params(params)
197
+
198
+ load_or_retry do
199
+ RestClient.get url, req
200
+ end
201
+ end
202
+
203
+ def sell_shop_item_update (item_id, payload, params={})
204
+ url = api_url "/sell/shop/items/#{item_id}"
205
+ load = MultiJson.dump payload
206
+ req = request_params(params)
207
+
208
+ load_or_retry do
209
+ RestClient.put url, load, req
210
+ end
211
+ end
212
+
213
+ def sell_shop_item_delete (item_id, params={})
214
+ url = api_url "/sell/shop/items/#{item_id}"
215
+ req = request_params(params)
216
+
217
+ load_or_retry do
218
+ RestClient.delete url, req
219
+ end
220
+ end
221
+
222
+
223
+ #
224
+ # SHOP_ORDERS
225
+ #
226
+ def sell_shop_orders_states (params={})
227
+ url = api_url "/sell/shop/orders/states"
228
+ req = request_params(params)
229
+
230
+ load_or_retry do
231
+ RestClient.get url, req
232
+ end
233
+ end
234
+
235
+ def sell_shop_orders (order_status, params={})
236
+ url = api_url "/sell/shop/orders"
237
+ req = request_params({ order_status: order_status, currency: @currency, locale: @locale}.merge(params))
238
+
239
+ load_or_retry do
240
+ RestClient.get url, req
241
+ end
242
+ end
243
+
244
+ def sell_shop_orders_order_number (order_number, params={})
245
+ url = api_url "/sell/shop/orders/#{order_number}"
246
+ req = request_params(params)
247
+
248
+ load_or_retry do
249
+ RestClient.get url, req
250
+ end
251
+ end
252
+
253
+ def sell_shop_orders_order_number_change_state (state, params={})
254
+ url = api_url "/sell/shop/orders/#{order_number}/change_state"
255
+ req = request_params({state: state}.merge(params))
256
+
257
+ load_or_retry do
258
+ # POST with a hash sends parameters as a urlencoded form body
259
+ RestClient.post url, req
260
+ end
261
+ end
262
+
263
+
264
+ def sell_shop_orders_order_number_request_cancellation (reason_string, params={})
265
+ url = api_url "/sell/shop/orders/#{order_number}/request_cancellation"
266
+ req = request_params(params)
267
+ load = MultiJson.dump reason: reason_string
268
+
269
+ load_or_retry do
270
+ # POST with raw payloads
271
+ RestClient.post url, load, req
272
+ end
273
+ end
274
+
275
+ #
276
+ # SHOP_SHIPPING_PROFILES
277
+ #
278
+ def sell_shop_shipping_profiles (params={})
279
+ url = api_url "/sell/shop/shipping_profiles"
280
+ req = request_params({locale: @locale}.merge(params))
281
+
282
+ load_or_retry do
283
+ RestClient.get url, req
284
+ end
285
+ end
286
+
287
+ #
288
+ # SHOP_USER_DETAILS
289
+ #
290
+ def sell_shop_user_details (params={})
291
+ url = api_url "/sell/shop/user_details"
292
+ req = request_params({locale: @locale}.merge(params))
293
+
294
+ load_or_retry do
295
+ RestClient.get url, req
296
+ end
297
+ end
298
+
299
+ end
300
+ end
@@ -0,0 +1,3 @@
1
+ module BlommingApi
2
+ VERSION = "0.4.1"
3
+ end