recombee_api_client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +157 -0
- data/Rakefile +6 -0
- data/lib/recombee_api_client/api/add_bookmark.rb +70 -0
- data/lib/recombee_api_client/api/add_cart_addition.rb +70 -0
- data/lib/recombee_api_client/api/add_detail_view.rb +73 -0
- data/lib/recombee_api_client/api/add_group.rb +53 -0
- data/lib/recombee_api_client/api/add_item.rb +56 -0
- data/lib/recombee_api_client/api/add_item_property.rb +59 -0
- data/lib/recombee_api_client/api/add_purchase.rb +70 -0
- data/lib/recombee_api_client/api/add_rating.rb +73 -0
- data/lib/recombee_api_client/api/add_series.rb +53 -0
- data/lib/recombee_api_client/api/add_user.rb +54 -0
- data/lib/recombee_api_client/api/batch.rb +64 -0
- data/lib/recombee_api_client/api/delete_bookmark.rb +61 -0
- data/lib/recombee_api_client/api/delete_cart_addition.rb +61 -0
- data/lib/recombee_api_client/api/delete_detail_view.rb +61 -0
- data/lib/recombee_api_client/api/delete_group.rb +56 -0
- data/lib/recombee_api_client/api/delete_item.rb +58 -0
- data/lib/recombee_api_client/api/delete_item_property.rb +54 -0
- data/lib/recombee_api_client/api/delete_purchase.rb +61 -0
- data/lib/recombee_api_client/api/delete_rating.rb +61 -0
- data/lib/recombee_api_client/api/delete_series.rb +56 -0
- data/lib/recombee_api_client/api/delete_user.rb +56 -0
- data/lib/recombee_api_client/api/get_item_property_info.rb +54 -0
- data/lib/recombee_api_client/api/get_item_values.rb +55 -0
- data/lib/recombee_api_client/api/insert_to_group.rb +69 -0
- data/lib/recombee_api_client/api/insert_to_series.rb +72 -0
- data/lib/recombee_api_client/api/item_based_recommendation.rb +131 -0
- data/lib/recombee_api_client/api/list_group_items.rb +53 -0
- data/lib/recombee_api_client/api/list_groups.rb +50 -0
- data/lib/recombee_api_client/api/list_item_bookmarks.rb +54 -0
- data/lib/recombee_api_client/api/list_item_cart_additions.rb +54 -0
- data/lib/recombee_api_client/api/list_item_detail_views.rb +54 -0
- data/lib/recombee_api_client/api/list_item_properties.rb +51 -0
- data/lib/recombee_api_client/api/list_item_purchases.rb +54 -0
- data/lib/recombee_api_client/api/list_item_ratings.rb +54 -0
- data/lib/recombee_api_client/api/list_items.rb +63 -0
- data/lib/recombee_api_client/api/list_series.rb +50 -0
- data/lib/recombee_api_client/api/list_series_items.rb +53 -0
- data/lib/recombee_api_client/api/list_user_bookmarks.rb +53 -0
- data/lib/recombee_api_client/api/list_user_cart_additions.rb +53 -0
- data/lib/recombee_api_client/api/list_user_detail_views.rb +53 -0
- data/lib/recombee_api_client/api/list_user_purchases.rb +53 -0
- data/lib/recombee_api_client/api/list_user_ratings.rb +53 -0
- data/lib/recombee_api_client/api/list_users.rb +50 -0
- data/lib/recombee_api_client/api/merge_users.rb +72 -0
- data/lib/recombee_api_client/api/remove_from_group.rb +59 -0
- data/lib/recombee_api_client/api/remove_from_series.rb +62 -0
- data/lib/recombee_api_client/api/request.rb +7 -0
- data/lib/recombee_api_client/api/reset_database.rb +51 -0
- data/lib/recombee_api_client/api/set_item_values.rb +70 -0
- data/lib/recombee_api_client/api/user_based_recommendation.rb +116 -0
- data/lib/recombee_api_client/errors.rb +44 -0
- data/lib/recombee_api_client/version.rb +3 -0
- data/lib/recombee_api_client.rb +105 -0
- data/recombee_api_client.gemspec +26 -0
- metadata +173 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# This file is auto-generated, do not edit
|
3
|
+
#
|
4
|
+
|
5
|
+
module RecombeeApiClient
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative '../errors'
|
8
|
+
|
9
|
+
##
|
10
|
+
#Removes an existing group item from the group.
|
11
|
+
class RemoveFromGroup < ApiRequest
|
12
|
+
attr_reader :group_id, :item_type, :item_id
|
13
|
+
attr_accessor :timeout
|
14
|
+
|
15
|
+
##
|
16
|
+
# * *Required arguments*
|
17
|
+
# - +group_id+ -> ID of the group from which a group item is to be removed.
|
18
|
+
# - +item_type+ -> Type of the item to be removed.
|
19
|
+
# - +item_id+ -> ID of the item iff `itemType` is `item`. ID of the group iff `itemType` is `group`.
|
20
|
+
#
|
21
|
+
def initialize(group_id, item_type, item_id)
|
22
|
+
@group_id = group_id
|
23
|
+
@item_type = item_type
|
24
|
+
@item_id = item_id
|
25
|
+
@timeout = 1000
|
26
|
+
end
|
27
|
+
|
28
|
+
# HTTP method
|
29
|
+
def method
|
30
|
+
:delete
|
31
|
+
end
|
32
|
+
|
33
|
+
# Values of body parameters as a Hash
|
34
|
+
def body_parameters
|
35
|
+
p = Hash.new
|
36
|
+
p
|
37
|
+
end
|
38
|
+
|
39
|
+
# Values of query path parameters as a Hash.
|
40
|
+
# name of parameter => value of the parameter
|
41
|
+
def query_parameters
|
42
|
+
params = {}
|
43
|
+
params['itemType'] = @item_type
|
44
|
+
params['itemId'] = @item_id
|
45
|
+
params
|
46
|
+
end
|
47
|
+
|
48
|
+
# Relative path to the endpoint
|
49
|
+
def basic_path
|
50
|
+
"/{databaseId}/groups/#{@group_id}/items/"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Relative path to the endpoint including query parameters
|
54
|
+
def path
|
55
|
+
p = "/{databaseId}/groups/#{@group_id}/items/?itemType=#{@item_type}&itemId=#{@item_id}"
|
56
|
+
p
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#
|
2
|
+
# This file is auto-generated, do not edit
|
3
|
+
#
|
4
|
+
|
5
|
+
module RecombeeApiClient
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative '../errors'
|
8
|
+
|
9
|
+
##
|
10
|
+
#Removes an existing series item from the series.
|
11
|
+
class RemoveFromSeries < ApiRequest
|
12
|
+
attr_reader :series_id, :item_type, :item_id, :time
|
13
|
+
attr_accessor :timeout
|
14
|
+
|
15
|
+
##
|
16
|
+
# * *Required arguments*
|
17
|
+
# - +series_id+ -> ID of the series from which a series item is to be removed.
|
18
|
+
# - +item_type+ -> Type of the item to be removed.
|
19
|
+
# - +item_id+ -> ID of the item iff `itemType` is `item`. ID of the series iff `itemType` is `series`.
|
20
|
+
# - +time+ -> Time index of the item to be removed.
|
21
|
+
#
|
22
|
+
def initialize(series_id, item_type, item_id, time)
|
23
|
+
@series_id = series_id
|
24
|
+
@item_type = item_type
|
25
|
+
@item_id = item_id
|
26
|
+
@time = time
|
27
|
+
@timeout = 1000
|
28
|
+
end
|
29
|
+
|
30
|
+
# HTTP method
|
31
|
+
def method
|
32
|
+
:delete
|
33
|
+
end
|
34
|
+
|
35
|
+
# Values of body parameters as a Hash
|
36
|
+
def body_parameters
|
37
|
+
p = Hash.new
|
38
|
+
p
|
39
|
+
end
|
40
|
+
|
41
|
+
# Values of query path parameters as a Hash.
|
42
|
+
# name of parameter => value of the parameter
|
43
|
+
def query_parameters
|
44
|
+
params = {}
|
45
|
+
params['itemType'] = @item_type
|
46
|
+
params['itemId'] = @item_id
|
47
|
+
params['time'] = @time
|
48
|
+
params
|
49
|
+
end
|
50
|
+
|
51
|
+
# Relative path to the endpoint
|
52
|
+
def basic_path
|
53
|
+
"/{databaseId}/series/#{@series_id}/items/"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Relative path to the endpoint including query parameters
|
57
|
+
def path
|
58
|
+
p = "/{databaseId}/series/#{@series_id}/items/?itemType=#{@item_type}&itemId=#{@item_id}&time=#{@time}"
|
59
|
+
p
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#
|
2
|
+
# This file is auto-generated, do not edit
|
3
|
+
#
|
4
|
+
|
5
|
+
module RecombeeApiClient
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative '../errors'
|
8
|
+
|
9
|
+
##
|
10
|
+
#Completely erases all your data, including items, item properties, series, user database, purchases, ratings, detail views, and bookmarks. Make sure the request to be never executed in production environment! Resetting your database is irreversible.
|
11
|
+
#
|
12
|
+
class ResetDatabase < ApiRequest
|
13
|
+
|
14
|
+
attr_accessor :timeout
|
15
|
+
|
16
|
+
##
|
17
|
+
#
|
18
|
+
def initialize()
|
19
|
+
@timeout = 3000
|
20
|
+
end
|
21
|
+
|
22
|
+
# HTTP method
|
23
|
+
def method
|
24
|
+
:delete
|
25
|
+
end
|
26
|
+
|
27
|
+
# Values of body parameters as a Hash
|
28
|
+
def body_parameters
|
29
|
+
p = Hash.new
|
30
|
+
p
|
31
|
+
end
|
32
|
+
|
33
|
+
# Values of query path parameters as a Hash.
|
34
|
+
# name of parameter => value of the parameter
|
35
|
+
def query_parameters
|
36
|
+
params = {}
|
37
|
+
params
|
38
|
+
end
|
39
|
+
|
40
|
+
# Relative path to the endpoint
|
41
|
+
def basic_path
|
42
|
+
"/{databaseId}/"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Relative path to the endpoint including query parameters
|
46
|
+
def path
|
47
|
+
p = "/{databaseId}/"
|
48
|
+
p
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# This file is auto-generated, do not edit
|
3
|
+
#
|
4
|
+
|
5
|
+
module RecombeeApiClient
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative '../errors'
|
8
|
+
|
9
|
+
##
|
10
|
+
#Set/update (some) property values of a given item.
|
11
|
+
#
|
12
|
+
class SetItemValues < ApiRequest
|
13
|
+
attr_reader :item_id, :values
|
14
|
+
attr_accessor :timeout
|
15
|
+
|
16
|
+
##
|
17
|
+
# * *Required arguments*
|
18
|
+
# - +item_id+ -> ID of the item which will be modified.
|
19
|
+
#
|
20
|
+
# - +values+ -> The values for the individual properties.
|
21
|
+
#
|
22
|
+
#Example of body:
|
23
|
+
#```
|
24
|
+
# {
|
25
|
+
# "string_property": "strvalue",
|
26
|
+
# "integer_property": 42,
|
27
|
+
# "!cascadeCreate": true
|
28
|
+
# }
|
29
|
+
#```
|
30
|
+
#
|
31
|
+
#Special parameter `!cascadeCreate` may be used. It indicates that the item of the given itemId should be created if it does not exist in the database, as if the corresponding PUT method was used. Note the exclamation mark (!) at the beginning of the parameter's name to distinguish it from item property names.
|
32
|
+
#
|
33
|
+
#
|
34
|
+
def initialize(item_id, values)
|
35
|
+
@item_id = item_id
|
36
|
+
@values = values
|
37
|
+
@timeout = 1000
|
38
|
+
end
|
39
|
+
|
40
|
+
# HTTP method
|
41
|
+
def method
|
42
|
+
:post
|
43
|
+
end
|
44
|
+
|
45
|
+
# Values of body parameters as a Hash
|
46
|
+
def body_parameters
|
47
|
+
p = Hash.new
|
48
|
+
p = p.merge(@values)
|
49
|
+
p
|
50
|
+
end
|
51
|
+
|
52
|
+
# Values of query path parameters as a Hash.
|
53
|
+
# name of parameter => value of the parameter
|
54
|
+
def query_parameters
|
55
|
+
params = {}
|
56
|
+
params
|
57
|
+
end
|
58
|
+
|
59
|
+
# Relative path to the endpoint
|
60
|
+
def basic_path
|
61
|
+
"/{databaseId}/items/#{@item_id}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Relative path to the endpoint including query parameters
|
65
|
+
def path
|
66
|
+
p = "/{databaseId}/items/#{@item_id}"
|
67
|
+
p
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#
|
2
|
+
# This file is auto-generated, do not edit
|
3
|
+
#
|
4
|
+
|
5
|
+
module RecombeeApiClient
|
6
|
+
require_relative 'request'
|
7
|
+
require_relative '../errors'
|
8
|
+
|
9
|
+
##
|
10
|
+
#Based on user's past interactions (purchases, ratings, etc.) with the items, recommends top-N items that are most likely to be of high value for a given user.
|
11
|
+
#
|
12
|
+
class UserBasedRecommendation < ApiRequest
|
13
|
+
attr_reader :user_id, :count, :filter, :booster, :allow_nonexistent, :diversity, :min_relevance, :rotation_rate, :rotation_time
|
14
|
+
attr_accessor :timeout
|
15
|
+
|
16
|
+
##
|
17
|
+
# * *Required arguments*
|
18
|
+
# - +user_id+ -> ID of the user whose personalized recommendations are to be generated.
|
19
|
+
# - +count+ -> Number of items to be recommended (N for the top-N recommendation).
|
20
|
+
#
|
21
|
+
# * *Optional arguments (given as hash optional)*
|
22
|
+
# - +filter+ -> Boolean-returning [ReQL](https://docs.recombee.com/reql.html) expression which allows you to filter recommended items based on the values of their attributes.
|
23
|
+
# - +booster+ -> Number-returning [ReQL](https://docs.recombee.com/reql.html) expression which allows you to boost recommendation rate of some items based on the values of their attributes.
|
24
|
+
# - +allowNonexistent+ -> If the user does not exist in the database, returns a list of non-personalized recommendations instead of causing HTTP 404 error.
|
25
|
+
# - +diversity+ -> **Expert option** Real number from [0.0, 1.0] which determines how much mutually dissimilar should the recommended items be. The default value is 0.0, i.e., no diversification. Value 1.0 means maximal diversification.
|
26
|
+
#
|
27
|
+
# - +minRelevance+ -> **Expert option** Specifies the threshold of how much relevant must the recommended items be to the user. Possible values one of: "low", "medium", "high". The default value is "low", meaning that the system attempts to recommend number of items equal to *count* at any cost. If there are not enough data (such as interactions or item properties), this may even lead to bestseller-based recommendations to be appended to reach the full *count*. This behavior may be suppressed by using "medium" or "high" values. In such case, the system only recommends items of at least the requested qualit, and may return less than *count* items when there is not enough data to fulfill it.
|
28
|
+
#
|
29
|
+
# - +rotationRate+ -> **Expert option** If your users browse the system in real-time, it may easily happen that you wish to offer them recommendations multiple times. Here comes the question: how much should the recommendations change? Should they remain the same, or should they rotate? Recombee API allows you to control this per-request in backward fashion. You may penalize an item for being recommended in the near past. For the specific user, `rotationRate=1` means maximal rotation, `rotationRate=0` means absolutely no rotation. You may also use, for example `rotationRate=0.2` for only slight rotation of recommended items.
|
30
|
+
#
|
31
|
+
# - +rotationTime+ -> **Expert option** Taking *rotationRate* into account, specifies how long time it takes to an item to fully recover from the penalization. By example, `rotationTime=7200.0` means that items recommended more than 2 hours ago are definitely not penalized anymore. Currently, the penalization is linear, so for `rotationTime=7200.0`, an item is still penalized by `0.5` to the user after 1 hour. |
|
32
|
+
#
|
33
|
+
#
|
34
|
+
def initialize(user_id, count, optional = {})
|
35
|
+
@user_id = user_id
|
36
|
+
@count = count
|
37
|
+
@filter = optional['filter']
|
38
|
+
@booster = optional['booster']
|
39
|
+
@allow_nonexistent = optional['allowNonexistent']
|
40
|
+
@diversity = optional['diversity']
|
41
|
+
@min_relevance = optional['minRelevance']
|
42
|
+
@rotation_rate = optional['rotationRate']
|
43
|
+
@rotation_time = optional['rotationTime']
|
44
|
+
@optional = optional
|
45
|
+
@timeout = 3000
|
46
|
+
@optional.each do |par, _|
|
47
|
+
fail UnknownOptionalParameter.new(par) unless ["filter","booster","allowNonexistent","diversity","minRelevance","rotationRate","rotationTime"].include? par
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# HTTP method
|
52
|
+
def method
|
53
|
+
:get
|
54
|
+
end
|
55
|
+
|
56
|
+
# Values of body parameters as a Hash
|
57
|
+
def body_parameters
|
58
|
+
p = Hash.new
|
59
|
+
p
|
60
|
+
end
|
61
|
+
|
62
|
+
# Values of query path parameters as a Hash.
|
63
|
+
# name of parameter => value of the parameter
|
64
|
+
def query_parameters
|
65
|
+
params = {}
|
66
|
+
params['count'] = @count
|
67
|
+
params['filter'] = @optional['filter'] if @optional['filter']
|
68
|
+
params['booster'] = @optional['booster'] if @optional['booster']
|
69
|
+
params['allowNonexistent'] = @optional['allowNonexistent'] if @optional['allowNonexistent']
|
70
|
+
params['diversity'] = @optional['diversity'] if @optional['diversity']
|
71
|
+
params['minRelevance'] = @optional['minRelevance'] if @optional['minRelevance']
|
72
|
+
params['rotationRate'] = @optional['rotationRate'] if @optional['rotationRate']
|
73
|
+
params['rotationTime'] = @optional['rotationTime'] if @optional['rotationTime']
|
74
|
+
params
|
75
|
+
end
|
76
|
+
|
77
|
+
# Relative path to the endpoint
|
78
|
+
def basic_path
|
79
|
+
"/{databaseId}/users/#{@user_id}/recomms/"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Relative path to the endpoint including query parameters
|
83
|
+
def path
|
84
|
+
p = "/{databaseId}/users/#{@user_id}/recomms/?count=#{@count}"
|
85
|
+
if @optional.include? 'filter'
|
86
|
+
p += (p.include? '?') ? '&' : '?'
|
87
|
+
p += "filter=#{@optional['filter']}"
|
88
|
+
end
|
89
|
+
if @optional.include? 'booster'
|
90
|
+
p += (p.include? '?') ? '&' : '?'
|
91
|
+
p += "booster=#{@optional['booster']}"
|
92
|
+
end
|
93
|
+
if @optional.include? 'allowNonexistent'
|
94
|
+
p += (p.include? '?') ? '&' : '?'
|
95
|
+
p += "allowNonexistent=#{@optional['allowNonexistent']}"
|
96
|
+
end
|
97
|
+
if @optional.include? 'diversity'
|
98
|
+
p += (p.include? '?') ? '&' : '?'
|
99
|
+
p += "diversity=#{@optional['diversity']}"
|
100
|
+
end
|
101
|
+
if @optional.include? 'minRelevance'
|
102
|
+
p += (p.include? '?') ? '&' : '?'
|
103
|
+
p += "minRelevance=#{@optional['minRelevance']}"
|
104
|
+
end
|
105
|
+
if @optional.include? 'rotationRate'
|
106
|
+
p += (p.include? '?') ? '&' : '?'
|
107
|
+
p += "rotationRate=#{@optional['rotationRate']}"
|
108
|
+
end
|
109
|
+
if @optional.include? 'rotationTime'
|
110
|
+
p += (p.include? '?') ? '&' : '?'
|
111
|
+
p += "rotationTime=#{@optional['rotationTime']}"
|
112
|
+
end
|
113
|
+
p
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RecombeeApiClient
|
2
|
+
# Errors in API
|
3
|
+
class APIError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Response is not 200 or 201
|
7
|
+
class ResponseError < APIError
|
8
|
+
attr_reader :request
|
9
|
+
attr_reader :status_code
|
10
|
+
attr_reader :description
|
11
|
+
|
12
|
+
def initialize(request, status_code, description)
|
13
|
+
@status_code = status_code
|
14
|
+
@description = description
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"ResponseError: status: #{@status_code}, description: #{@description}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class UnknownOptionalParameter < StandardError
|
23
|
+
def initialize(parameter)
|
24
|
+
@parameter = parameter
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"UnknownOptionalParameter: unknown parameter #{@parameter} was given to the request"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ApiTimeout < APIError
|
33
|
+
attr_reader :request
|
34
|
+
|
35
|
+
def initialize(request)
|
36
|
+
@request = request
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"ApiTimeout: client did not get response within #{@request.timeout} ms"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'recombee_api_client/version'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'digest/hmac'
|
4
|
+
require 'httparty'
|
5
|
+
require 'json'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'net/https'
|
8
|
+
require 'timeout'
|
9
|
+
|
10
|
+
require 'recombee_api_client/errors'
|
11
|
+
Gem.find_files('recombee_api_client/api/*.rb').each { |path| require path }
|
12
|
+
|
13
|
+
module RecombeeApiClient
|
14
|
+
class RecombeeClient
|
15
|
+
include HTTParty
|
16
|
+
|
17
|
+
def initialize(account, token, options = {})
|
18
|
+
@account = account
|
19
|
+
@token = token
|
20
|
+
@base_uri = options[:base_uri] ||= 'https://rapi.recombee.com'
|
21
|
+
end
|
22
|
+
|
23
|
+
def send(request)
|
24
|
+
@request = request
|
25
|
+
uri = request.path
|
26
|
+
uri.slice! ('/{databaseId}/')
|
27
|
+
uri = URI.escape uri
|
28
|
+
timeout = request.timeout / 1000
|
29
|
+
# puts uri
|
30
|
+
begin
|
31
|
+
case request.method
|
32
|
+
when :put
|
33
|
+
hmac_put(uri, timeout)
|
34
|
+
when :get
|
35
|
+
hmac_get(uri, timeout)
|
36
|
+
when :post
|
37
|
+
hmac_post(uri, timeout, request.body_parameters.to_json)
|
38
|
+
when :delete
|
39
|
+
hmac_delete(uri, timeout)
|
40
|
+
end
|
41
|
+
rescue Timeout::Error
|
42
|
+
fail ApiTimeout.new(@request)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def hmac_put(uri, timeout, options = {})
|
49
|
+
r = self.class.put(sign_url(uri), query: options, timeout: timeout)
|
50
|
+
check_errors r
|
51
|
+
r.body
|
52
|
+
end
|
53
|
+
|
54
|
+
def hmac_get(uri, timeout, options = {})
|
55
|
+
r = self.class.get(sign_url(uri), query: options, timeout: timeout)
|
56
|
+
check_errors r
|
57
|
+
JSON.parse(r.body)
|
58
|
+
end
|
59
|
+
|
60
|
+
def hmac_post(uri, timeout, options = {})
|
61
|
+
url = sign_url(uri)
|
62
|
+
# pass arguments in body
|
63
|
+
r = self.class.post(url, body: options,
|
64
|
+
headers: { 'Content-Type' => 'application/json' },
|
65
|
+
timeout: timeout)
|
66
|
+
check_errors r
|
67
|
+
begin
|
68
|
+
return JSON.parse(r.body)
|
69
|
+
rescue JSON::ParserError
|
70
|
+
return r.body
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def hmac_delete(uri, timeout, options = {})
|
75
|
+
r = self.class.delete(sign_url(uri), query: options, timeout: timeout)
|
76
|
+
check_errors r
|
77
|
+
r.body
|
78
|
+
end
|
79
|
+
|
80
|
+
def check_errors(response)
|
81
|
+
status_code = response.code
|
82
|
+
return if status_code == 200 || status_code == 201
|
83
|
+
fail ResponseError.new(@request, status_code, response.body)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sign request with HMAC, request URI must be exacly the same
|
87
|
+
# We have 30s to complete request with this token
|
88
|
+
def sign_url(req)
|
89
|
+
uri = "/#{@account}/#{req}"
|
90
|
+
time = hmac_time(uri)
|
91
|
+
sign = hmac_sign(uri, time)
|
92
|
+
@base_uri + uri + time + "&hmac_sign=#{sign}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def hmac_time(uri)
|
96
|
+
res = (uri.include? '?') ? '&' : '?'
|
97
|
+
res << "hmac_timestamp=#{Time.now.utc.to_i}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def hmac_sign(uri, time)
|
101
|
+
url = uri + time
|
102
|
+
Digest::HMAC.hexdigest(url, @token, Digest::SHA1)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|