aliexpress 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +38 -0
- data/.travis.yml +10 -0
- data/Gemfile +15 -0
- data/LICENSE.md +21 -0
- data/README.md +117 -0
- data/Rakefile +3 -0
- data/aliexpress.gemspec +29 -0
- data/bin/console +21 -0
- data/lib/aliexpress.rb +45 -0
- data/lib/aliexpress/affiliate.rb +141 -0
- data/lib/aliexpress/auth.rb +86 -0
- data/lib/aliexpress/base.rb +87 -0
- data/lib/aliexpress/product.rb +22 -0
- data/lib/aliexpress/response_error.rb +11 -0
- data/spec/affiliate_spec.rb +70 -0
- data/spec/aliexpress_spec.rb +94 -0
- data/spec/auth_spec.rb +41 -0
- data/spec/base_spec.rb +49 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/auth.rb +12 -0
- data/spec/support/webmock.rb +3 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72fe66faf65e1f93edd912f2ba24148ca70c5ae1
|
4
|
+
data.tar.gz: 5ec3aa35f98e2deea15312fd5d243169d281dd43
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db423a70fbb44d9063a2e7e5f809aaa3948a0bf7bbf7bcd5ea69bb7b53e53d75e3e77de01cf51d509eafcd193182d3f635b754b060fdc7b902f5f49b96344cec
|
7
|
+
data.tar.gz: 2caf268645d24586782369475cb5437e7ced287508746db1e4afa52616df55130638dd558bfb51b5f4ae1101f6046ca26e2b9ef5ec4a9fd524e14a0e6255c8a2
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.2
|
3
|
+
|
4
|
+
Metrics/AbcSize:
|
5
|
+
Enabled: false
|
6
|
+
Metrics/CyclomaticComplexity:
|
7
|
+
Enabled: false
|
8
|
+
Metrics/PerceivedComplexity:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Metrics/LineLength:
|
12
|
+
Enabled: false
|
13
|
+
Metrics/ClassLength:
|
14
|
+
Enabled: false
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Enabled: false
|
17
|
+
Metrics/BlockLength:
|
18
|
+
Enabled: false
|
19
|
+
Metrics/ParameterLists:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Lint/EndAlignment:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/Documentation:
|
26
|
+
Enabled: false
|
27
|
+
Style/IndentationWidth:
|
28
|
+
Enabled: false
|
29
|
+
Style/ElseAlignment:
|
30
|
+
Enabled: false
|
31
|
+
Style/GuardClause:
|
32
|
+
Enabled: false
|
33
|
+
Style/NumericLiterals:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
# This is temporary - it can be remove after updating rubocop to the latest.
|
37
|
+
Style/PercentLiteralDelimiters:
|
38
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Tophatter
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/tophatter/aliexpress-api-ruby.svg?branch=master)](https://travis-ci.org/tophatter/aliexpress-api-ruby)
|
2
|
+
[![Coverage Status](https://coveralls.io/repos/github/tophatter/aliexpress-api-ruby/badge.svg?branch=master)](https://coveralls.io/github/tophatter/aliexpress-api-ruby?branch=master)
|
3
|
+
|
4
|
+
### AliExpress SDK for Ruby:
|
5
|
+
|
6
|
+
#### Authentication:
|
7
|
+
|
8
|
+
To use this SDK, you must have an app key and app secret (a.k.a. client id and client secret). These can be obtained [here](https://portals.aliexpress.com/adcenter/apiSetting.htm) if you're using the affiliate program, or [here](http://isvus.aliexpress.com/isv/index.htm) if you're using the ISV API.
|
9
|
+
|
10
|
+
In the affiliate program API settings, you'll see the following:
|
11
|
+
|
12
|
+
![](https://i.imgur.com/ycMNgMY.png)
|
13
|
+
|
14
|
+
The "api key" is your client id, and the "digital signature" is your client secret.
|
15
|
+
|
16
|
+
The API uses OAuth for authentication. You need to first go through a manual authorization sequence to get a short-live authorization code. That code can be exchanged for an access token that last 10 hours, and a refresh token that lasts significantly longer.
|
17
|
+
|
18
|
+
Using this gem's console, you get obtain an access token as follows:
|
19
|
+
|
20
|
+
```
|
21
|
+
bash $ bin/console
|
22
|
+
[1] pry(main)> AliExpress.client_id = [YOUR CLIENT ID]
|
23
|
+
[2] pry(main)> AliExpress.client_secret = [YOUR CLIENT SECRET]
|
24
|
+
[3] pry(main)> AliExpress::Auth.authorize
|
25
|
+
Open https://gw.api.alibaba.com/auth/authorize.htm?_aop_signature=REDACTED&client_id=REDACTED&redirect_uri=REDACTED&site=aliexpress&state=REDACTED in your browser, and then enter the code below.
|
26
|
+
authorization_code> [ENTER YOUR CODE HERE]
|
27
|
+
{
|
28
|
+
"refresh_token_timeout" => "20180402143144000-0700",
|
29
|
+
"aliId" => "[REDACTED]",
|
30
|
+
"resource_owner" => "[REDACTED]",
|
31
|
+
:access_token => "[REDACTED]",
|
32
|
+
:refresh_token => "[REDACTED]",
|
33
|
+
:expires_at => 1507194954
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
Once your tokens are obtained, you can instruct this SDK to use them:
|
38
|
+
|
39
|
+
```
|
40
|
+
AliExpress.access_token = [YOUR ACCESS TOKEN]
|
41
|
+
AliExpress.refresh_token = [YOUR REFRESH TOKEN]
|
42
|
+
```
|
43
|
+
|
44
|
+
If your access token expires, you can get a new one with the following:
|
45
|
+
|
46
|
+
```
|
47
|
+
AliExpress::Auth.refresh
|
48
|
+
```
|
49
|
+
|
50
|
+
#### Affiliate API:
|
51
|
+
|
52
|
+
##### api.listPromotionProduct
|
53
|
+
|
54
|
+
```
|
55
|
+
[1] pry(main)> AliExpress::Affiliate.list_promotion_product(keywords: 'drone')
|
56
|
+
D, [2017-10-05T11:23:03.971387 #69079] DEBUG -- : GET https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.listPromotionProduct/[REDACTED]?fields=productId%2CproductTitle%2CproductUrl%2CimageUrl%2CallImageUrls%2CoriginalPrice%2CsalePrice%2ClocalPrice%2Cdiscount%2Cvolume%2CpackageType%2ClotNum%2CvalidTime&keywords=drone
|
57
|
+
[
|
58
|
+
[ 0] {
|
59
|
+
"lotNum" => 1,
|
60
|
+
"packageType" => "piece",
|
61
|
+
"imageUrl" => "https://ae01.alicdn.com/kf/HTB1nxEjSFXXXXcDXpXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg",
|
62
|
+
"allImageUrls" => "https://ae01.alicdn.com/kf/HTB1nxEjSFXXXXcDXpXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg,https://ae01.alicdn.com/kf/HTB1O6LUSFXXXXX5aXXXq6xXFXXXB/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg,https://ae01.alicdn.com/kf/HTB1aA.DSFXXXXXgXXXXq6xXFXXX3/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg,https://ae01.alicdn.com/kf/HTB1N.kySFXXXXaUXXXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg,https://ae01.alicdn.com/kf/HTB1lc_1SFXXXXcgXVXXq6xXFXXX9/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg,https://ae01.alicdn.com/kf/HTB16lEkSFXXXXcpXpXXq6xXFXXXK/100-Original-SYMA-X5C-Upgrade-Version-RC-font-b-Drone-b-font-With-2MP-HD-Camera.jpg",
|
63
|
+
"productId" => 32817202058,
|
64
|
+
"discount" => "5%",
|
65
|
+
"originalPrice" => "US $27.99",
|
66
|
+
"productTitle" => "100% Original SYMA X5C (Upgrade Version) RC <font><b>Drone</b></font> With 2MP HD Camera 6-Axis RC Quadcopter Helicopter X5 Dron Without Camera",
|
67
|
+
"validTime" => "2017-10-08",
|
68
|
+
"productUrl" => "https://www.aliexpress.com/item/SYMA-X5C-1-Upgrade-Version-SYMA-X5C-Professional-RC-Drone-6-Axis-Helicopter-Quadcopter-With-2MP/32817202058.html",
|
69
|
+
"volume" => "14",
|
70
|
+
"salePrice" => "US $26.59"
|
71
|
+
},
|
72
|
+
<snip>
|
73
|
+
```
|
74
|
+
|
75
|
+
##### api.getPromotionProductDetail
|
76
|
+
|
77
|
+
```
|
78
|
+
[1] pry(main)> AliExpress::Affiliate.get_promotion_product_detail(32817202058)
|
79
|
+
D, [2017-10-05T11:24:19.235970 #69079] DEBUG -- : GET https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.getPromotionProductDetail/[REDACTED]?fields=productId%2CproductTitle%2CproductUrl%2CimageUrl%2CallImageUrls%2CoriginalPrice%2CsalePrice%2ClocalPrice%2Cdiscount%2Cvolume%2CpackageType%2ClotNum%2CvalidTime&productId=32817202058
|
80
|
+
{
|
81
|
+
"lotNum" => 1,
|
82
|
+
"packageType" => "piece",
|
83
|
+
"imageUrl" => "https://ae01.alicdn.com/kf/HTB1nxEjSFXXXXcDXpXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg",
|
84
|
+
"allImageUrls" => "https://ae01.alicdn.com/kf/HTB1nxEjSFXXXXcDXpXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg,https://ae01.alicdn.com/kf/HTB1O6LUSFXXXXX5aXXXq6xXFXXXB/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg,https://ae01.alicdn.com/kf/HTB1aA.DSFXXXXXgXXXXq6xXFXXX3/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg,https://ae01.alicdn.com/kf/HTB1N.kySFXXXXaUXXXXq6xXFXXXF/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg,https://ae01.alicdn.com/kf/HTB1lc_1SFXXXXcgXVXXq6xXFXXX9/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg,https://ae01.alicdn.com/kf/HTB16lEkSFXXXXcpXpXXq6xXFXXXK/100-Original-SYMA-X5C-Upgrade-Version-RC-Drone-With-2MP-HD-Camera-6-Axis-RC-Quadcopter.jpg",
|
85
|
+
"productId" => 32817202058,
|
86
|
+
"discount" => "5%",
|
87
|
+
"originalPrice" => "US $27.99",
|
88
|
+
"productTitle" => "100% Original SYMA X5C (Upgrade Version) RC Drone With 2MP HD Camera 6-Axis RC Quadcopter Helicopter X5 Dron Without Camera",
|
89
|
+
"validTime" => "2017-10-08",
|
90
|
+
"productUrl" => "https://www.aliexpress.com/item/SYMA-X5C-1-Upgrade-Version-SYMA-X5C-Professional-RC-Drone-6-Axis-Helicopter-Quadcopter-With-2MP/32817202058.html",
|
91
|
+
"volume" => "14",
|
92
|
+
"salePrice" => "US $26.59"
|
93
|
+
}
|
94
|
+
```
|
95
|
+
|
96
|
+
##### api.listHotProducts
|
97
|
+
|
98
|
+
```
|
99
|
+
[1] pry(main)> AliExpress::Affiliate.list_hot_products(3)
|
100
|
+
D, [2017-10-05T11:21:54.835675 #68982] DEBUG -- : GET https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.listHotProducts/[REDACTED]?categoryId=3&language=en&localCurrency=USD
|
101
|
+
[
|
102
|
+
[ 0] {
|
103
|
+
"localPrice" => "21.00 $",
|
104
|
+
"imageUrl" => "https://ae01.alicdn.com/kf/HTB1YsQwQFXXXXcvXpXXq6xXFXXX0/Preself-Hoodies-Sweatshirt-Women-Casual-Outwear-Hoody-Loose-Long-Sleeve-Mantle-Hooded-Cover-Pullover-Clothes-2017.jpg",
|
105
|
+
"productUrl" => "https://www.aliexpress.com/item/New-Women-Loose-Fashion-Autumn-Winter-Hooded-Oversize-Sweatshirt-Coat-Outwear/32763830156.html",
|
106
|
+
"validTime" => "2017-10-18",
|
107
|
+
"productTitle" => "Preself Hoodies Sweatshirt Women Casual Outwear Hoody Loose Long Sleeve Mantle Hooded Cover Pullover Clothes 2017 New",
|
108
|
+
"volume" => "",
|
109
|
+
"salePrice" => "US $21.00",
|
110
|
+
"productId" => 32763830156
|
111
|
+
},
|
112
|
+
<snip>
|
113
|
+
```
|
114
|
+
|
115
|
+
#### ISV API:
|
116
|
+
|
117
|
+
In progress.
|
data/Rakefile
ADDED
data/aliexpress.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# To publish the next version:
|
2
|
+
# gem build aliexpress.gemspec
|
3
|
+
# gem push aliexpress-{VERSION}.gem
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'aliexpress'
|
6
|
+
s.version = '0.4'
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.licenses = ['MIT']
|
9
|
+
s.authors = ['Chris Estreich']
|
10
|
+
s.email = ['chris@tophatter.com']
|
11
|
+
s.homepage = 'https://github.com/tophatter/aliexpress-api-ruby'
|
12
|
+
s.summary = 'AliExpress SDK for ruby.'
|
13
|
+
s.description = 'AliExpress SDK for ruby.'
|
14
|
+
|
15
|
+
s.extra_rdoc_files = ['README.md']
|
16
|
+
|
17
|
+
s.required_ruby_version = '~> 2.0'
|
18
|
+
|
19
|
+
s.add_dependency 'rest-client', '~> 1.6'
|
20
|
+
s.add_dependency 'addressable'
|
21
|
+
s.add_dependency 'oauth2'
|
22
|
+
|
23
|
+
s.post_install_message = 'Documentation is available at: http://isvus.aliexpress.com/isv/index.htm'
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
28
|
+
s.require_paths = ['lib']
|
29
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup' # Otherwise "require 'aliexpress'" won't work.
|
4
|
+
require 'aliexpress'
|
5
|
+
require 'awesome_print'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
%w(client_id client_secret access_token refresh_token).each do |method|
|
9
|
+
begin
|
10
|
+
AliExpress.send("#{method}=", File.read(".#{method}").chomp)
|
11
|
+
puts "AliExpress.#{method}: #{AliExpress.send(method)}"
|
12
|
+
rescue Errno::ENOENT
|
13
|
+
puts "No .#{method} file, using default: #{AliExpress.send(method)}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
AliExpress.logger = Logger.new(STDOUT)
|
18
|
+
AliExpress.logger.level = Logger::DEBUG
|
19
|
+
|
20
|
+
AwesomePrint.pry!
|
21
|
+
Pry.start
|
data/lib/aliexpress.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
require 'addressable'
|
4
|
+
require 'oauth2'
|
5
|
+
|
6
|
+
module AliExpress
|
7
|
+
class << self
|
8
|
+
attr_accessor :client_id, :client_secret, :access_token, :refresh_token
|
9
|
+
|
10
|
+
attr_writer :protocol, :host, :base_uri
|
11
|
+
attr_writer :currency, :language
|
12
|
+
attr_writer :logger
|
13
|
+
|
14
|
+
def protocol
|
15
|
+
@protocol || 'https'
|
16
|
+
end
|
17
|
+
|
18
|
+
def host
|
19
|
+
@host || 'gw.api.alibaba.com'
|
20
|
+
end
|
21
|
+
|
22
|
+
def base_uri
|
23
|
+
@base_uri || "#{protocol}://#{host}/openapi"
|
24
|
+
end
|
25
|
+
|
26
|
+
def currency
|
27
|
+
@currency || 'USD'
|
28
|
+
end
|
29
|
+
|
30
|
+
def language
|
31
|
+
@language || 'en'
|
32
|
+
end
|
33
|
+
|
34
|
+
def logger
|
35
|
+
@logger ||= Logger.new('/dev/null')
|
36
|
+
@logger
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require File.dirname(__FILE__) + '/aliexpress/base'
|
42
|
+
require File.dirname(__FILE__) + '/aliexpress/response_error'
|
43
|
+
require File.dirname(__FILE__) + '/aliexpress/auth'
|
44
|
+
require File.dirname(__FILE__) + '/aliexpress/product'
|
45
|
+
require File.dirname(__FILE__) + '/aliexpress/affiliate'
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module AliExpress
|
2
|
+
class Affiliate < Base
|
3
|
+
CATEGORIES = {
|
4
|
+
'Apparel & Accessories' => 3,
|
5
|
+
'Automobiles & Motorcycles' => 34,
|
6
|
+
'Beauty & Health' => 66,
|
7
|
+
'Books for Local Russian' => 200004360,
|
8
|
+
'Computer & Office' => 7,
|
9
|
+
'Home Improvement' => 13,
|
10
|
+
'Consumer Electronics' => 44,
|
11
|
+
'Electrical Equipment & Supplies' => 5,
|
12
|
+
'Electronic Components & Supplies' => 502,
|
13
|
+
'Food' => 2,
|
14
|
+
'Furniture' => 1503,
|
15
|
+
'Hair & Accessories' => 200003655,
|
16
|
+
'Hardware' => 42,
|
17
|
+
'Home & Garden' => 15,
|
18
|
+
'Home Appliances' => 6,
|
19
|
+
'Industry & Business' => 200001996,
|
20
|
+
'Jewelry & Accessories' => 36,
|
21
|
+
'Lights & Lighting' => 39,
|
22
|
+
'Luggage & Bags' => 1524,
|
23
|
+
'Mother & Kids' => 1501,
|
24
|
+
'Office & School Supplies' => 21,
|
25
|
+
'Phones & Telecommunications' => 509,
|
26
|
+
'Security & Protection' => 30,
|
27
|
+
'Shoes' => 322,
|
28
|
+
'Special Category' => 200001075,
|
29
|
+
'Sports & Entertainment' => 18,
|
30
|
+
'Tools' => 1420,
|
31
|
+
'Toys & Hobbies' => 26,
|
32
|
+
'Travel and Coupon Services' => 200003498,
|
33
|
+
'Watches' => 1511,
|
34
|
+
'Weddings & Events' => 320
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
FILTERS = %w(category keywords min_price max_price high_quality).freeze
|
38
|
+
SORTS = %w(orignalPriceUp orignalPriceDown sellerRateUp sellerRateDown).freeze
|
39
|
+
FIELDS = %w(productId productTitle productUrl imageUrl allImageUrls originalPrice salePrice localPrice discount volume).freeze
|
40
|
+
|
41
|
+
SUCCESS = 20010000
|
42
|
+
ERRORS = {
|
43
|
+
20020000 => 'System error.',
|
44
|
+
20030000 => 'Unauthorized transfer request.',
|
45
|
+
20030010 => 'Required parameters.',
|
46
|
+
20030020 => 'Invalid protocol format.',
|
47
|
+
20030030 => 'API version input parameter error.',
|
48
|
+
20030040 => 'API namespace input parameter error.',
|
49
|
+
20030050 => 'API name input parameter error.',
|
50
|
+
20030060 => 'Fields input parameter error.',
|
51
|
+
20030070 => 'Keyword input parameter error.',
|
52
|
+
20030080 => 'Category ID input parameter error.',
|
53
|
+
20030140 => 'Page number input parameter error.',
|
54
|
+
20030150 => 'Page size input parameter error (default = 20, max = 40).',
|
55
|
+
20030160 => 'Sort input parameter error.'
|
56
|
+
}.freeze
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def search(query:, filters: {}, sort: 'orignalPriceUp', page: nil, per_page: 40, fields: FIELDS)
|
60
|
+
response = get(
|
61
|
+
api_call: 'api.listPromotionProduct',
|
62
|
+
params: {
|
63
|
+
keywords: query,
|
64
|
+
# filters
|
65
|
+
categoryId: CATEGORIES[filters[:category]] || filters[:category],
|
66
|
+
originalPriceFrom: filters[:min_price],
|
67
|
+
originalPriceTo: filters[:max_price],
|
68
|
+
highQualityItems: filters[:high_quality],
|
69
|
+
# sorting
|
70
|
+
sort: sort,
|
71
|
+
# paging
|
72
|
+
pageNo: page,
|
73
|
+
pageSize: per_page,
|
74
|
+
# response format
|
75
|
+
fields: fields.join(',')
|
76
|
+
}.delete_if { |_k, v| v.nil? }
|
77
|
+
)
|
78
|
+
|
79
|
+
raise_response_error(response) unless response['errorCode'] == SUCCESS
|
80
|
+
response
|
81
|
+
end
|
82
|
+
|
83
|
+
def find(id, fields: FIELDS)
|
84
|
+
response = get(
|
85
|
+
api_call: 'api.getPromotionProductDetail',
|
86
|
+
params: { productId: id, fields: fields.join(',') }
|
87
|
+
)
|
88
|
+
|
89
|
+
raise_response_error(response) unless response['errorCode'] == SUCCESS
|
90
|
+
response
|
91
|
+
end
|
92
|
+
|
93
|
+
def similar(id)
|
94
|
+
response = get(
|
95
|
+
api_call: 'api.listSimilarProducts',
|
96
|
+
params: { productId: id }
|
97
|
+
)
|
98
|
+
|
99
|
+
raise_response_error(response) unless response['errorCode'] == SUCCESS
|
100
|
+
response
|
101
|
+
end
|
102
|
+
|
103
|
+
def popular(category)
|
104
|
+
response = get(
|
105
|
+
api_call: 'api.listHotProducts',
|
106
|
+
params: { categoryId: CATEGORIES[category] || category }
|
107
|
+
)
|
108
|
+
|
109
|
+
raise_response_error(response) unless response['errorCode'] == SUCCESS
|
110
|
+
response
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def get(hash)
|
116
|
+
defaults = {
|
117
|
+
api_namespace: 'portals.open',
|
118
|
+
api_version: 2,
|
119
|
+
auth: false,
|
120
|
+
sign: false,
|
121
|
+
params: {
|
122
|
+
localCurrency: currency,
|
123
|
+
language: language
|
124
|
+
}
|
125
|
+
}
|
126
|
+
params = defaults[:params].merge(hash.delete(:params))
|
127
|
+
super(defaults.merge(hash).merge(params: params))
|
128
|
+
end
|
129
|
+
|
130
|
+
def raise_response_error(response)
|
131
|
+
exception = ResponseError.new(
|
132
|
+
code: response['error_code'] || response['errorCode'],
|
133
|
+
message: response['error_message'] || ERRORS[response['errorCode']] || response['errorCode'],
|
134
|
+
response: response
|
135
|
+
)
|
136
|
+
|
137
|
+
raise exception
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module AliExpress
|
2
|
+
class Auth < Base
|
3
|
+
class << self
|
4
|
+
def authorize(state = nil)
|
5
|
+
oauth_client = client
|
6
|
+
oauth_params = params(state || SecureRandom.hex(24))
|
7
|
+
oauth_params[:_aop_signature] = sign(oauth_params)
|
8
|
+
logger.debug oauth_params
|
9
|
+
url = oauth_client.authorize_url(oauth_params)
|
10
|
+
|
11
|
+
if RUBY_PLATFORM == 'x86_64-darwin16'
|
12
|
+
begin
|
13
|
+
system('open', url)
|
14
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
15
|
+
logger.debug "Call to open failed: #{e.message}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$stdout.puts "Open #{url} in your browser, and then enter the code below."
|
20
|
+
$stdout.print 'authorization_code> '
|
21
|
+
authorization_code = $stdin.gets
|
22
|
+
|
23
|
+
token = oauth_client.get_token(
|
24
|
+
code: authorization_code,
|
25
|
+
grant_type: 'authorization_code',
|
26
|
+
need_refresh_token: 'true',
|
27
|
+
parse: :json,
|
28
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob'
|
29
|
+
).to_hash
|
30
|
+
|
31
|
+
token
|
32
|
+
end
|
33
|
+
|
34
|
+
def refresh
|
35
|
+
if refresh_token
|
36
|
+
response = post(
|
37
|
+
api_call: 'getToken',
|
38
|
+
api_namespace: 'system.oauth2',
|
39
|
+
api_version: '1',
|
40
|
+
# protocol: 'http',
|
41
|
+
auth: false,
|
42
|
+
sign: false,
|
43
|
+
params: {
|
44
|
+
grant_type: 'refresh_token',
|
45
|
+
client_id: client_id,
|
46
|
+
client_secret: client_secret,
|
47
|
+
refresh_token: refresh_token
|
48
|
+
}
|
49
|
+
)
|
50
|
+
AliExpress.access_token = response['access_token']
|
51
|
+
else
|
52
|
+
raise Exception, 'You must have a refresh_token to refresh authorization. You need to call `AliExpress::Auth.authorize` and follow the instructions to generate one.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def client
|
59
|
+
OAuth2::Client.new(
|
60
|
+
client_id,
|
61
|
+
client_secret,
|
62
|
+
site: 'https://gw.api.alibaba.com',
|
63
|
+
authorize_url: '/auth/authorize.htm',
|
64
|
+
token_url: "/openapi/http/1/system.oauth2/getToken/#{client_id}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def sign(hash)
|
69
|
+
OpenSSL::HMAC.hexdigest(
|
70
|
+
OpenSSL::Digest.new('sha1'),
|
71
|
+
client_secret,
|
72
|
+
hash.sort.flatten.join.to_s
|
73
|
+
).upcase
|
74
|
+
end
|
75
|
+
|
76
|
+
def params(state)
|
77
|
+
{
|
78
|
+
client_id: client_id,
|
79
|
+
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
80
|
+
site: 'aliexpress',
|
81
|
+
state: state
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module AliExpress
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def client_id
|
5
|
+
AliExpress.client_id
|
6
|
+
end
|
7
|
+
|
8
|
+
def client_secret
|
9
|
+
AliExpress.client_secret
|
10
|
+
end
|
11
|
+
|
12
|
+
def access_token
|
13
|
+
AliExpress.access_token
|
14
|
+
end
|
15
|
+
|
16
|
+
def refresh_token
|
17
|
+
AliExpress.refresh_token
|
18
|
+
end
|
19
|
+
|
20
|
+
def currency
|
21
|
+
AliExpress.currency
|
22
|
+
end
|
23
|
+
|
24
|
+
def language
|
25
|
+
AliExpress.language
|
26
|
+
end
|
27
|
+
|
28
|
+
def logger
|
29
|
+
AliExpress.logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(params)
|
33
|
+
request(params.merge(method: :get))
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(params)
|
37
|
+
request(params.merge(method: :post))
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def sign_payload(path, payload)
|
43
|
+
signature_factor = path + payload.map { |key, value| "#{key}#{value}" }.sort.join
|
44
|
+
payload[:_aop_signature] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), client_secret, signature_factor).upcase
|
45
|
+
logger.debug "signature_factor=#{signature_factor}, signature=#{payload[:_aop_signature]}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def request(method:, protocol: 'param2', api_version: 1, api_namespace: 'aliexpress.open', api_call:, auth: true, sign: true, params: {})
|
49
|
+
path = [protocol, api_version, api_namespace, api_call, client_id].join('/')
|
50
|
+
|
51
|
+
payload = params
|
52
|
+
payload[:access_token] = access_token if auth
|
53
|
+
sign_payload(path, payload) if sign
|
54
|
+
url = [AliExpress.base_uri, path].join('/')
|
55
|
+
|
56
|
+
resp = if method == :get
|
57
|
+
unless params.length.zero?
|
58
|
+
uri = Addressable::URI.new
|
59
|
+
uri.query_values = params
|
60
|
+
url += "?#{uri.query}"
|
61
|
+
end
|
62
|
+
|
63
|
+
logger.debug "GET #{url}"
|
64
|
+
RestClient.get(url)
|
65
|
+
else
|
66
|
+
logger.debug "POST #{url} | #{payload}"
|
67
|
+
RestClient.post(url, payload)
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
JSON.parse(resp.body)
|
72
|
+
rescue JSON::ParserError
|
73
|
+
resp.body
|
74
|
+
end
|
75
|
+
rescue RestClient::Exception => exception
|
76
|
+
resp = begin
|
77
|
+
JSON.parse(exception.response)
|
78
|
+
rescue JSON::ParserError
|
79
|
+
exception.response
|
80
|
+
end
|
81
|
+
|
82
|
+
logger.debug "#{exception.class.name} (#{exception.response.code}): #{resp.inspect}"
|
83
|
+
resp
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AliExpress
|
2
|
+
class Product < Base
|
3
|
+
class << self
|
4
|
+
def find(id)
|
5
|
+
post(api_call: 'api.findAeProductById', params: { productId: id })
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def get(params)
|
11
|
+
super(default_params.merge(params))
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_params
|
15
|
+
{
|
16
|
+
api_namespace: 'aliexpress.open',
|
17
|
+
api_version: 1
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rspec spec/affiliate_spec.rb
|
4
|
+
describe AliExpress::Affiliate do
|
5
|
+
let(:response) do
|
6
|
+
mock = double
|
7
|
+
allow(mock).to receive(:code).and_return(200)
|
8
|
+
allow(mock).to receive(:body).and_return(json)
|
9
|
+
mock
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:success) { 20010000 }
|
13
|
+
let(:hash) { { 'errorCode' => success } }
|
14
|
+
let(:json) { hash.to_json }
|
15
|
+
|
16
|
+
# before do
|
17
|
+
# AliExpress.logger = Logger.new(STDOUT)
|
18
|
+
# AliExpress.logger.level = Logger::DEBUG
|
19
|
+
# end
|
20
|
+
|
21
|
+
describe '.search' do
|
22
|
+
let(:url) { 'https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.listPromotionProduct/fake-client-id?categoryId=3&fields=productId&keywords=drone&language=en&localCurrency=USD&pageNo=2&pageSize=5&sort=orignalPriceDown' }
|
23
|
+
|
24
|
+
it 'returns a successful response' do
|
25
|
+
expect(RestClient).to receive(:get).exactly(1).times.with(url).and_return(response)
|
26
|
+
response = AliExpress::Affiliate.search(query: 'drone', filters: { category: 'Apparel & Accessories' }, sort: 'orignalPriceDown', page: 2, per_page: 5, fields: %w(productId))
|
27
|
+
expect(response['errorCode']).to eq(success)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.find' do
|
32
|
+
let(:url) { 'https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.getPromotionProductDetail/fake-client-id?fields=productId&language=en&localCurrency=USD&productId=12345' }
|
33
|
+
|
34
|
+
it 'returns a product' do
|
35
|
+
expect(RestClient).to receive(:get).exactly(1).times.with(url).and_return(response)
|
36
|
+
response = AliExpress::Affiliate.find(12345, fields: %w(productId))
|
37
|
+
expect(response['errorCode']).to eq(success)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '.similar' do
|
42
|
+
let(:url) { 'https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.listSimilarProducts/fake-client-id?language=en&localCurrency=USD&productId=12345' }
|
43
|
+
|
44
|
+
it 'returns a product' do
|
45
|
+
expect(RestClient).to receive(:get).exactly(1).times.with(url).and_return(response)
|
46
|
+
response = AliExpress::Affiliate.similar(12345)
|
47
|
+
expect(response['errorCode']).to eq(success)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.popular' do
|
52
|
+
let(:url) { 'https://gw.api.alibaba.com/openapi/param2/2/portals.open/api.listHotProducts/fake-client-id?categoryId=3&language=en&localCurrency=USD' }
|
53
|
+
|
54
|
+
context 'when a category id is used' do
|
55
|
+
it 'returns a successful response' do
|
56
|
+
expect(RestClient).to receive(:get).exactly(1).times.with(url).and_return(response)
|
57
|
+
response = AliExpress::Affiliate.popular(3)
|
58
|
+
expect(response['errorCode']).to eq(success)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when a category name is used' do
|
63
|
+
it 'returns a successful response' do
|
64
|
+
expect(RestClient).to receive(:get).exactly(1).times.with(url).and_return(response)
|
65
|
+
response = AliExpress::Affiliate.popular('Apparel & Accessories')
|
66
|
+
expect(response['errorCode']).to eq(success)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rspec spec/aliexpress_spec.rb
|
4
|
+
describe AliExpress do
|
5
|
+
describe '.client_id' do
|
6
|
+
subject { AliExpress.client_id }
|
7
|
+
it { is_expected.to eq('fake-client-id') }
|
8
|
+
|
9
|
+
context('when assigned') do
|
10
|
+
before { AliExpress.client_id = 'foo' }
|
11
|
+
it { is_expected.to eq('foo') }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.client_secret' do
|
16
|
+
subject { AliExpress.client_secret }
|
17
|
+
it { is_expected.to eq('fake-client-secret') }
|
18
|
+
|
19
|
+
context('when assigned') do
|
20
|
+
before { AliExpress.client_secret = 'foo' }
|
21
|
+
it { is_expected.to eq('foo') }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.access_token' do
|
26
|
+
subject { AliExpress.access_token }
|
27
|
+
it { is_expected.to eq('fake-access-token') }
|
28
|
+
|
29
|
+
context('when assigned') do
|
30
|
+
before { AliExpress.access_token = 'foo' }
|
31
|
+
it { is_expected.to eq('foo') }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.refresh_token' do
|
36
|
+
subject { AliExpress.refresh_token }
|
37
|
+
it { is_expected.to eq('fake-refresh-token') }
|
38
|
+
|
39
|
+
context('when assigned') do
|
40
|
+
before { AliExpress.refresh_token = 'foo' }
|
41
|
+
it { is_expected.to eq('foo') }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.protocol' do
|
46
|
+
subject { AliExpress.protocol }
|
47
|
+
it { is_expected.to eq('https') }
|
48
|
+
|
49
|
+
context('when assigned') do
|
50
|
+
before { AliExpress.protocol = 'foo' }
|
51
|
+
it { is_expected.to eq('foo') }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.host' do
|
56
|
+
subject { AliExpress.host }
|
57
|
+
it { is_expected.to eq('gw.api.alibaba.com') }
|
58
|
+
|
59
|
+
context('when assigned') do
|
60
|
+
before { AliExpress.host = 'foo' }
|
61
|
+
it { is_expected.to eq('foo') }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '.base_uri' do
|
66
|
+
subject { AliExpress.base_uri }
|
67
|
+
it { is_expected.to eq('https://gw.api.alibaba.com/openapi') }
|
68
|
+
|
69
|
+
context('when assigned') do
|
70
|
+
before { AliExpress.base_uri = 'foo' }
|
71
|
+
it { is_expected.to eq('foo') }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.currency' do
|
76
|
+
subject { AliExpress.currency }
|
77
|
+
it { is_expected.to eq('USD') }
|
78
|
+
|
79
|
+
context('when assigned') do
|
80
|
+
before { AliExpress.currency = 'foo' }
|
81
|
+
it { is_expected.to eq('foo') }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '.language' do
|
86
|
+
subject { AliExpress.language }
|
87
|
+
it { is_expected.to eq('en') }
|
88
|
+
|
89
|
+
context('when assigned') do
|
90
|
+
before { AliExpress.language = 'foo' }
|
91
|
+
it { is_expected.to eq('foo') }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/spec/auth_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rspec spec/auth_spec.rb
|
4
|
+
describe AliExpress::Auth do
|
5
|
+
describe '.authorize' do
|
6
|
+
let(:authorization_code) { 'b5ce38e1-833c-4f15-bd29-65e85db9bd2e' }
|
7
|
+
let(:response) do
|
8
|
+
{
|
9
|
+
'refresh_token_timeout' => '20180402143144000-0700',
|
10
|
+
'aliId' => 'fake-ali-id',
|
11
|
+
'resource_owner' => 'fake-resource-owner',
|
12
|
+
access_token: 'new-fake-access-token',
|
13
|
+
refresh_token: 'new-fake-refresh-token',
|
14
|
+
expires_at: 1507266354
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'creates a new access token' do
|
19
|
+
stub_const('RUBY_PLATFORM', 'test')
|
20
|
+
allow($stdin).to receive(:gets).and_return(authorization_code)
|
21
|
+
allow($stdout).to receive(:puts).and_return(nil)
|
22
|
+
allow($stdout).to receive(:print).and_return(nil)
|
23
|
+
expect_any_instance_of(OAuth2::Client).to receive(:get_token).exactly(1).times.with(hash_including(code: authorization_code)).and_return(response)
|
24
|
+
expect(AliExpress::Auth.authorize).to eq(response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.refresh' do
|
29
|
+
let(:response) do
|
30
|
+
mock = double
|
31
|
+
allow(mock).to receive(:code).and_return(200)
|
32
|
+
allow(mock).to receive(:body).and_return('{"aliId":"fake-ali-id","resource_owner":"fake-resource-owner","expires_in":"36000","access_token":"jabber_wocky"}')
|
33
|
+
mock
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'exchanges the refresh token for a new access token' do
|
37
|
+
expect(RestClient).to receive(:post).exactly(1).times.and_return(response)
|
38
|
+
expect(AliExpress::Auth.refresh).to eq('jabber_wocky')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rspec spec/base_spec.rb
|
4
|
+
describe AliExpress::Base do
|
5
|
+
let(:response) do
|
6
|
+
mock = double
|
7
|
+
allow(mock).to receive(:code).and_return(200)
|
8
|
+
allow(mock).to receive(:body).and_return('{}')
|
9
|
+
mock
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.get' do
|
13
|
+
context 'when auth is true' do
|
14
|
+
it 'uses the access token' do
|
15
|
+
expect(RestClient).to receive(:get).exactly(1).times.with('https://gw.api.alibaba.com/openapi/param2/1/aliexpress.open/foo/fake-client-id?access_token=fake-access-token').and_return(response)
|
16
|
+
AliExpress::Base.get(api_call: 'foo', sign: false, auth: true)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when sign is true' do
|
21
|
+
it 'signs the request' do
|
22
|
+
expect(RestClient).to receive(:get).exactly(1).times.with('https://gw.api.alibaba.com/openapi/param2/1/aliexpress.open/foo/fake-client-id?_aop_signature=8F19FBA00C4BF35E43FA9ED143639337047624C0').and_return(response)
|
23
|
+
AliExpress::Base.get(api_call: 'foo', sign: true, auth: false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when bogus params are used' do
|
28
|
+
it 'raises an error' do
|
29
|
+
expect { AliExpress::Base.get(api_call: 'foo', foo: 'bar') }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.post' do
|
35
|
+
context 'when auth is true' do
|
36
|
+
it 'uses the access token' do
|
37
|
+
expect(RestClient).to receive(:post).exactly(1).times.with('https://gw.api.alibaba.com/openapi/param2/1/aliexpress.open/foo/fake-client-id', access_token: 'fake-access-token').and_return(response)
|
38
|
+
AliExpress::Base.post(api_call: 'foo', sign: false, auth: true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when sign is true' do
|
43
|
+
it 'signs the request' do
|
44
|
+
expect(RestClient).to receive(:post).exactly(1).times.with('https://gw.api.alibaba.com/openapi/param2/1/aliexpress.open/foo/fake-client-id', _aop_signature: '8F19FBA00C4BF35E43FA9ED143639337047624C0').and_return(response)
|
45
|
+
AliExpress::Base.post(api_call: 'foo', sign: true, auth: false)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before(:each) do
|
3
|
+
AliExpress.client_id = 'fake-client-id'
|
4
|
+
AliExpress.client_secret = 'fake-client-secret'
|
5
|
+
AliExpress.access_token = 'fake-access-token'
|
6
|
+
AliExpress.refresh_token = 'fake-refresh-token'
|
7
|
+
|
8
|
+
%i(protocol host base_uri currency language logger).each do |method|
|
9
|
+
AliExpress.send("#{method}=", nil)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aliexpress
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Estreich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-10-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rest-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: addressable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oauth2
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: AliExpress SDK for ruby.
|
56
|
+
email:
|
57
|
+
- chris@tophatter.com
|
58
|
+
executables:
|
59
|
+
- console
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files:
|
62
|
+
- README.md
|
63
|
+
files:
|
64
|
+
- ".coveralls.yml"
|
65
|
+
- ".gitignore"
|
66
|
+
- ".rspec"
|
67
|
+
- ".rubocop.yml"
|
68
|
+
- ".travis.yml"
|
69
|
+
- Gemfile
|
70
|
+
- LICENSE.md
|
71
|
+
- README.md
|
72
|
+
- Rakefile
|
73
|
+
- aliexpress.gemspec
|
74
|
+
- bin/console
|
75
|
+
- lib/aliexpress.rb
|
76
|
+
- lib/aliexpress/affiliate.rb
|
77
|
+
- lib/aliexpress/auth.rb
|
78
|
+
- lib/aliexpress/base.rb
|
79
|
+
- lib/aliexpress/product.rb
|
80
|
+
- lib/aliexpress/response_error.rb
|
81
|
+
- spec/affiliate_spec.rb
|
82
|
+
- spec/aliexpress_spec.rb
|
83
|
+
- spec/auth_spec.rb
|
84
|
+
- spec/base_spec.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
- spec/support/auth.rb
|
87
|
+
- spec/support/webmock.rb
|
88
|
+
homepage: https://github.com/tophatter/aliexpress-api-ruby
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message: 'Documentation is available at: http://isvus.aliexpress.com/isv/index.htm'
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '2.0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.6.12
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: AliExpress SDK for ruby.
|
112
|
+
test_files:
|
113
|
+
- spec/affiliate_spec.rb
|
114
|
+
- spec/aliexpress_spec.rb
|
115
|
+
- spec/auth_spec.rb
|
116
|
+
- spec/base_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/support/auth.rb
|
119
|
+
- spec/support/webmock.rb
|