sevennet-api 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: feec2c6c92b4918ac8868c0fc2dcb091e9e4059f
4
+ data.tar.gz: 45af5f721d89164fa394017b1373afec3125af15
5
+ SHA512:
6
+ metadata.gz: b2e9b4ac974db660e07fcd66a40897279f829515b488617a66654c4bee025b874e7b6a3158f6197aec175d84f2891599da3f0a4bbe593f0a04ae5cf7b11de653
7
+ data.tar.gz: cb1c5348382a40e89b295e80db76a30eefce1727c14e561eb57241c9b1b29e9fc79fa27631e490cd7c621fd70150edba9022f87913d988bca3359e58b5f38a1b
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/fixtures/*.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sevennet-api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 shoprev
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Sevennet::Api
2
+
3
+ Generic 7netshopping Ruby API using Nokogiri. Uses Response and Element wrapper classes for easy access to the REST API XML output.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'sevennet-api'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install sevennet-api
18
+
19
+ ## Usage
20
+
21
+ require 'sevennet/api'
22
+
23
+ Sevennet::Api.configure do |options|
24
+ options[:ApiUserId] = 'your api user id'
25
+ options[:APISecretKey] = 'your api secret key'
26
+ end
27
+
28
+ # GetShoppingCategory
29
+ res = Sevennet::Api.get_shopping_category('') # root category
30
+ res = Sevennet::Api.get_shopping_category('books') # books category
31
+ res.categories.each do |category|
32
+ children = category.get('ChildCategory')
33
+ children.each do |child|
34
+ code = child.get('CategoryCode')
35
+ ...
36
+ end
37
+ end
38
+
39
+ # SearchProduct
40
+ res = Sevennet::Api.search_product('ruby', {:ShoppingSortOrder => 'reviews'}) # keyword match
41
+ res = Sevennet::Api.search_product('', {:CategoryCode = 'books'}) # category match
42
+ res = Sevennet::Api.search_product('ruby', {:CategoryCode = 'books'}) # keyword and category match
43
+ res.products.each do |product|
44
+ code = product.get('ProductCode')
45
+ ...
46
+ end
47
+
48
+ # SearchRanking
49
+ res = Sevennet::Api.search_ranking('books', {:GenerationCond => 10, :SexCond => 'male'})
50
+ res.products.each do |product|
51
+ code = product.get('ProductCode')
52
+ ...
53
+ end
54
+
55
+ # SearchProductReview
56
+ res = Sevennet::Api.search_product_review('2110150300') # ProductCode
57
+ res = Sevennet::Api.search_product_review('21-101-503-00',:type => 'ProductStandardCode') # ISBN JAN etc
58
+ res.reviews.each do |review|
59
+ title = review.get('CommentTitle')
60
+ ...
61
+ end
62
+
63
+ # GetSpcCategory
64
+ res = Sevennet::Api.get_spc_category('') # root category
65
+ res = Sevennet::Api.get_spc_category('home') # home category
66
+ res.categories.each do |category|
67
+ children = category.get('ChildCategory')
68
+ children.each do |child|
69
+ code = child.get('CategoryCode')
70
+ ...
71
+ end
72
+ end
73
+
74
+ # SearchSpcShop
75
+ res = Sevennet::Api.search_spc_shop('', {:SpcSortOrder => 'name') # all
76
+ res = Sevennet::Api.search_spc_shop('ruby', {:SpcSortOrder => 'name') # keyword match
77
+ res.shops.each do |shop|
78
+ id = shop.get('SpcShopId')
79
+ ...
80
+ end
81
+
82
+ # some common response object methods
83
+ res.has_error? # return true if there is an error
84
+ res.error # return error message if there is any
85
+ res.total_amount # return total results
86
+
87
+ # Extend Sevennet::Api, replace 'other_operation' with the appropriate name
88
+ module Sevennet
89
+ class Api
90
+ def self.other_operation(item_id, opts={})
91
+ opts[:operation] = '[other valid operation supported by 7netshopping API]'
92
+
93
+ # setting default option value
94
+ opts[:item_id] = item_id
95
+
96
+ self.send_request(opts)
97
+ end
98
+ end
99
+ end
100
+
101
+ Sevennet::Api.other_operation('[item_id]', :param1 => 'abc', :param2 => 'xyz')
102
+
103
+ Refer to 7netshopping API documentation for more information on other valid operations, request parameters and the XML response output: http://www.7netshopping.jp/
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,329 @@
1
+ # coding: utf-8
2
+ require 'net/http'
3
+ require 'nokogiri'
4
+ require 'cgi'
5
+ require 'base64'
6
+ require 'openssl'
7
+
8
+ module Sevennet
9
+ class RequestError < StandardError; end
10
+
11
+ class Api
12
+ SERVICE_URL = 'http://api.7netshopping.jp/ws/affiliate/rest'
13
+
14
+ @@options = {
15
+ :Version => "2010-08-01",
16
+ :ResponseFormat => "XML"
17
+ }
18
+
19
+ # Default search options
20
+ def self.options
21
+ @@options
22
+ end
23
+
24
+ # Set default search options
25
+ def self.options=(opts)
26
+ @@options = opts
27
+ end
28
+
29
+ def self.configure(&proc)
30
+ raise ArgumentError, "Block is required." unless block_given?
31
+ yield @@options
32
+ end
33
+
34
+ # Search spc categories by category code.
35
+ def self.get_spc_category(category_code, opts = {})
36
+ opts[:operation] = 'GetSpcCategory'
37
+ opts[:CategoryCode] = category_code
38
+
39
+ self.send_request(opts)
40
+ end
41
+
42
+ # Search spc shops with search terms.
43
+ def self.search_spc_shop(terms, opts = {})
44
+ opts[:operation] = 'SearchSpcShop'
45
+ opts[:KeywordIn] = CGI.escape(terms.to_s)
46
+
47
+ self.send_request(opts)
48
+ end
49
+
50
+ # Search categories by category code.
51
+ def self.get_shopping_category(category_code, opts = {})
52
+ opts[:operation] = 'GetShoppingCategory'
53
+ opts[:CategoryCode] = category_code
54
+
55
+ self.send_request(opts)
56
+ end
57
+
58
+ # Search products with search terms.
59
+ def self.search_product(terms, opts = {})
60
+ if terms.to_s.empty? && (options[:CategoryCode].to_s.empty? && opts[:CategoryCode].to_s.empty?)
61
+ raise ArgumentError, "CategoryCode or KeywordIn is required."
62
+ end
63
+
64
+ opts[:operation] = 'SearchProduct'
65
+ opts[:KeywordIn] = CGI.escape(terms.to_s)
66
+
67
+ self.send_request(opts)
68
+ end
69
+
70
+ # Search products by category code.
71
+ def self.search_ranking(category_code, opts = {})
72
+ opts[:operation] = 'SearchRanking'
73
+ opts[:CategoryCode] = category_code
74
+
75
+ raise ArgumentError, "CategoryCode is required." if category_code.to_s.empty?
76
+
77
+ self.send_request(opts)
78
+ end
79
+
80
+ # Search a product reviews with product code.
81
+ # For other search type other than keywords, please specify :type => 'ProductStandardCode'.
82
+ def self.search_product_review(product_code, opts = {})
83
+ opts[:operation] = 'SearchProductReview'
84
+
85
+ type = (opts.delete(:type) || options.delete(:type))
86
+ if type
87
+ opts[type.to_sym] = product_code
88
+ else
89
+ opts[:ProductCode] = product_code
90
+ end
91
+
92
+ raise ArgumentError, "ProductCode is required." if product_code.to_s.empty?
93
+
94
+ self.send_request(opts)
95
+ end
96
+
97
+ # Search products by contents.
98
+ def self.search_content_match_product(content, opts = {})
99
+
100
+ raise ArgumentError, "Content is required." if content.to_s.empty?
101
+
102
+ opts[:operation] = 'SearchContentMatchProduct'
103
+ opts[:Content] = CGI.escape(content)
104
+
105
+ self.send_request(opts)
106
+ end
107
+
108
+ # Search products by category code and contents.
109
+ def self.search_content_match_ranking(category_code, content, opts = {})
110
+
111
+ raise ArgumentError, "TopCategoryCode and Content is required." if content.to_s.empty? || category_code.to_s.empty?
112
+
113
+ opts[:operation] = 'SearchContentMatchRanking'
114
+ opts[:Content] = CGI.escape(content)
115
+ opts[:TopCategoryCode] = category_code
116
+
117
+
118
+ self.send_request(opts)
119
+ end
120
+
121
+ # Generic send request to API REST service. You have to specify the :operation parameter.
122
+ def self.send_request(opts)
123
+ opts = self.options.merge(opts) if self.options
124
+
125
+ # Include other required options
126
+ opts[:Timestamp] = Time.now.strftime('%Y-%m-%dT%XZ')
127
+
128
+ request_url = prepare_url(opts)
129
+
130
+ res = Net::HTTP.get_response(URI::parse(request_url))
131
+ unless res.kind_of? Net::HTTPSuccess
132
+ raise Sevennet::RequestError, "HTTP Response: #{res.code} #{res.message}"
133
+ end
134
+ Response.new(res.body)
135
+ end
136
+
137
+ # Response object returned after a REST call to sevennet api.
138
+ class Response
139
+
140
+ # XML input is in string format
141
+ def initialize(xml)
142
+ @doc = Nokogiri::XML(xml, nil, 'UTF-8')
143
+ @doc.remove_namespaces!
144
+ end
145
+
146
+ # Return Nokogiri::XML::Document object.
147
+ def doc
148
+ @doc
149
+ end
150
+
151
+ # Return true if response has an error.
152
+ def has_error?
153
+ !(error.nil? || error.empty?)
154
+ end
155
+
156
+ # Return error message.
157
+ def error
158
+ Element.get(@doc, "//ApiErrorMessage")
159
+ end
160
+
161
+ # Return error code
162
+ def error_code
163
+ Element.get(@doc, "//ApiErrorStatus")
164
+ end
165
+
166
+ # Return an array of Sevennet::Element category objects.
167
+ def categories
168
+ @categories ||= (@doc/"Category").collect { |it| Element.new(it) }
169
+ end
170
+
171
+ # Return an array of Sevennet::Element shop objects.
172
+ def shops
173
+ @shops ||= (@doc/"SpcShop").collect { |it| Element.new(it) }
174
+ end
175
+
176
+ # Return an array of Sevennet::Element product objects.
177
+ def products
178
+ @products ||= (@doc/"Product").collect { |it| Element.new(it) }
179
+ end
180
+
181
+ # Return an array of Sevennet::Element review objects.
182
+ def reviews
183
+ @reviews ||= (@doc/"ProductReview").collect { |it| Element.new(it) }
184
+ end
185
+
186
+ # Return total results.
187
+ def total_amount
188
+ @total_amount ||= Element.get(@doc, "//TotalAmount").to_i
189
+ end
190
+ end
191
+
192
+ private
193
+ def self.prepare_url(opts)
194
+ secret_key = opts.delete(:APISecretKey)
195
+ operation = opts.delete(:operation)
196
+ request_url = "#{SERVICE_URL}/#{operation}"
197
+
198
+ qs = []
199
+
200
+ opts = opts.collect do |a,b|
201
+ [a.to_s, b.to_s]
202
+ end
203
+
204
+ opts = opts.sort do |c,d|
205
+ c[0].to_s <=> d[0].to_s
206
+ end
207
+
208
+ opts.each do |e|
209
+ next if e[1].empty? || e[1].nil?
210
+ qs << "#{e[0]}=#{e[1]}"
211
+ end
212
+
213
+ request_to_sign ="GET|#{request_url}|#{qs.join('|')}"
214
+ signature = "&Signature=#{sign_request(request_to_sign, secret_key)}"
215
+ "#{request_url}?#{qs.join('&')}#{signature}"
216
+ end
217
+
218
+ def self.sign_request(url, key)
219
+ signature = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, key, CGI.escape(url))
220
+ signature = Base64.encode64(signature).chomp
221
+ signature
222
+ end
223
+ end
224
+
225
+ # Internal wrapper class to provide convenient method to access Nokogiri element value.
226
+ class Element
227
+ class << self
228
+ # Return the text value of an element.
229
+ def get(element, path='.')
230
+ return unless element
231
+ result = element.at_xpath(path)
232
+ result = result.inner_html if result
233
+ result
234
+ end
235
+
236
+ # Return an unescaped text value of an element.
237
+ def get_unescaped(element, path='.')
238
+ result = self.get(element, path)
239
+ CGI.unescape(result) if result
240
+ end
241
+
242
+ # Return an array of values based on the given path.
243
+ def get_array(element, path='.')
244
+ return unless element
245
+
246
+ result = element/path
247
+ if (result.is_a? Nokogiri::XML::NodeSet) || (result.is_a? Array)
248
+ result.collect { |item| self.get(item) }
249
+ else
250
+ [self.get(result)]
251
+ end
252
+ end
253
+
254
+ # Return child element text values of the given path.
255
+ def get_hash(element, path='.')
256
+ return unless element
257
+
258
+ result = element.at_xpath(path)
259
+ if result
260
+ hash = {}
261
+ result = result.children
262
+ result.each do |item|
263
+ hash[item.name] = item.inner_html
264
+ end
265
+ hash
266
+ end
267
+ end
268
+ end
269
+
270
+ # Pass Nokogiri::XML::Element object
271
+ def initialize(element)
272
+ @element = element
273
+ end
274
+
275
+ # Returns Nokogiri::XML::Element object
276
+ def elem
277
+ @element
278
+ end
279
+
280
+ # Returns a Nokogiri::XML::NodeSet of elements matching the given path. Example: element/"author".
281
+ def /(path)
282
+ elements = @element/path
283
+ return nil if elements.size == 0
284
+ elements
285
+ end
286
+
287
+ # Return an array of Sevennet::Element matching the given path
288
+ def get_elements(path)
289
+ elements = self./(path)
290
+ return unless elements
291
+ elements = elements.map{|element| Element.new(element)}
292
+ end
293
+
294
+ # Similar with search_and_convert but always return first element if more than one elements found
295
+ def get_element(path)
296
+ elements = get_elements(path)
297
+ elements[0] if elements
298
+ end
299
+
300
+ # Get the text value of the given path, leave empty to retrieve current element value.
301
+ def get(path='.')
302
+ Element.get(@element, path)
303
+ end
304
+
305
+ # Get the unescaped HTML text of the given path.
306
+ def get_unescaped(path='.')
307
+ Element.get_unescaped(@element, path)
308
+ end
309
+
310
+ # Get the array values of the given path.
311
+ def get_array(path='.')
312
+ Element.get_array(@element, path)
313
+ end
314
+
315
+ # Get the children element text values in hash format with the element names as the hash keys.
316
+ def get_hash(path='.')
317
+ Element.get_hash(@element, path)
318
+ end
319
+
320
+ def attributes
321
+ return unless self.elem
322
+ self.elem.attributes
323
+ end
324
+
325
+ def to_s
326
+ elem.to_s if elem
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,5 @@
1
+ module Sevennet
2
+ class Api
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sevennet/api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sevennet-api"
8
+ spec.version = Sevennet::Api::VERSION
9
+ spec.authors = ["shoprev"]
10
+ spec.email = ["admin@shoprev.net"]
11
+ spec.description = %q{Generic Ruby 7netshopping API}
12
+ spec.summary = %q{Generic Ruby 7netshopping API}
13
+ spec.homepage = "https://github.com/shoprev/sevennet-api"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "vcr"
25
+ spec.add_runtime_dependency "nokogiri"
26
+ end
@@ -0,0 +1,484 @@
1
+ # coding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ share_examples_for 'found products search keyword or options' do
5
+ subject { response }
6
+ its(:total_amount) { should > 0 }
7
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
8
+ it { should_not be_has_error }
9
+ context 'products.first' do
10
+ subject { response.products.first/"ProductCode" }
11
+ it { should_not be_empty }
12
+ end
13
+ end
14
+
15
+ share_examples_for 'not found products search keyword or options' do
16
+ subject { response }
17
+ its(:total_amount) { should == 0 }
18
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
19
+ its('products') { should == [] }
20
+ it { should_not be_has_error }
21
+ end
22
+
23
+ share_examples_for 'category common test' do
24
+ subject { response }
25
+ its(:total_amount) { should == 0 }
26
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
27
+ its('categories.first') { should be_kind_of Sevennet::Element }
28
+ it { should_not be_has_error }
29
+ context 'child category' do
30
+ subject { response.categories.first/"ChildCategory" }
31
+ it { should_not be_empty }
32
+ end
33
+ end
34
+
35
+ describe "Sevennet::Api.search_content_match_ranking" do
36
+ let(:response) {
37
+ VCR.use_cassette('search_content_match_ranking_' + top_category_code.to_s + '_' + content.to_s + '_' + opts.to_s) do
38
+ Sevennet::Api.search_content_match_ranking(top_category_code,content)
39
+ end
40
+ }
41
+ let(:options) {
42
+ Sevennet::Api.configure do |options|
43
+ options[:StartIndex] = 1
44
+ options[:ResultAmount] = 5
45
+ end
46
+ }
47
+ let(:no_option) {
48
+ Sevennet::Api.configure do |options|
49
+ options[:StartIndex] = nil
50
+ options[:ResultAmount] = nil
51
+ end
52
+ }
53
+
54
+ # context 'search keyword and options' do
55
+ # let(:top_category_code) { 'magazine' }
56
+ # let(:content) { 'focus' }
57
+ # let(:opts) { options }
58
+ # it_should_behave_like 'found products search keyword or options'
59
+ # end
60
+
61
+ # context 'search keyword without options' do
62
+ # let(:top_category_code) { 'magazine' }
63
+ # let(:content) { 'focus' }
64
+ # let(:opts) { no_option }
65
+ # it_should_behave_like 'found products search keyword or options'
66
+ # end
67
+
68
+ context 'search options without keyword' do
69
+ let(:top_category_code) { 'magazine' }
70
+ let(:content) { nil }
71
+ let(:opts) { options }
72
+ it { lambda{ response }.should raise_error( ArgumentError, "TopCategoryCode and Content is required.") }
73
+ end
74
+
75
+ context 'search without keyword and options' do
76
+ let(:top_category_code) { 'magazine' }
77
+ let(:content) { nil }
78
+ let(:opts) { no_option }
79
+ it { lambda{ response }.should raise_error( ArgumentError, "TopCategoryCode and Content is required.") }
80
+ end
81
+
82
+ context 'not found search keyword and options' do
83
+ let(:top_category_code) { 'magazine' }
84
+ let(:content) { 'focus friday' }
85
+ let(:opts) { options }
86
+ it_should_behave_like 'not found products search keyword or options'
87
+ end
88
+
89
+ context 'not found search keyword without options' do
90
+ let(:top_category_code) { 'magazine' }
91
+ let(:content) { 'focus friday' }
92
+ let(:opts) { no_option }
93
+ it_should_behave_like 'not found products search keyword or options'
94
+ end
95
+ end
96
+
97
+ describe "Sevennet::Api.search_content_match_product" do
98
+ let(:response) {
99
+ VCR.use_cassette('search_content_match_product_' + content.to_s + '_' + opts.to_s) do
100
+ Sevennet::Api.search_content_match_product(content)
101
+ end
102
+ }
103
+ let(:options) {
104
+ Sevennet::Api.configure do |options|
105
+ options[:StartIndex] = 1
106
+ options[:ResultAmount] = 5
107
+ end
108
+ }
109
+ let(:no_option) {
110
+ Sevennet::Api.configure do |options|
111
+ options[:StartIndex] = nil
112
+ options[:ResultAmount] = nil
113
+ end
114
+ }
115
+
116
+ context 'search keyword and options' do
117
+ let(:content) { 'ruby perl javascript' }
118
+ let(:opts) { options }
119
+ it_should_behave_like 'found products search keyword or options'
120
+ end
121
+
122
+ context 'search keyword without options' do
123
+ let(:content) { 'ruby perl javascript' }
124
+ let(:opts) { no_option }
125
+ it_should_behave_like 'found products search keyword or options'
126
+ end
127
+
128
+ context 'search options without keyword' do
129
+ let(:content) { nil }
130
+ let(:opts) { options }
131
+ it { lambda{ response }.should raise_error( ArgumentError, "Content is required.") }
132
+ end
133
+
134
+ context 'search without keyword and options' do
135
+ let(:content) { nil }
136
+ let(:opts) { no_option }
137
+ it { lambda{ response }.should raise_error( ArgumentError, "Content is required.") }
138
+ end
139
+
140
+ context 'not found search keyword and options' do
141
+ let(:content) { 'titanium coffeescript' }
142
+ let(:opts) { options }
143
+ it_should_behave_like 'not found products search keyword or options'
144
+ end
145
+
146
+ context 'not found search keyword without options' do
147
+ let(:content) { 'titanium coffeescript' }
148
+ let(:opts) { no_option }
149
+ it_should_behave_like 'not found products search keyword or options'
150
+ end
151
+ end
152
+
153
+ describe "Sevennet::Api.search_product_review" do
154
+ let(:response) {
155
+ VCR.use_cassette('search_product_review_' + product_code.to_s + '_' + opts.to_s) do
156
+ Sevennet::Api.search_product_review(product_code)
157
+ end
158
+ }
159
+ let(:options) {
160
+ Sevennet::Api.configure do |options|
161
+ options[:StartIndex] = 1
162
+ options[:ResultAmount] = 5
163
+ options[:type] = 'ProductStandardCode'
164
+ end
165
+ }
166
+ let(:no_option) {
167
+ Sevennet::Api.configure do |options|
168
+ options[:StartIndex] = nil
169
+ options[:ResultAmount] = nil
170
+ options[:type] = nil
171
+ end
172
+ }
173
+
174
+ share_examples_for 'found review search keyword or options' do
175
+ subject { response }
176
+ its(:total_amount) { should > 0 }
177
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
178
+ it { should_not be_has_error }
179
+ context 'reviews.first' do
180
+ subject { response.reviews.first/"CommentTitle" }
181
+ it { should_not be_empty }
182
+ end
183
+ end
184
+
185
+ share_examples_for 'not found review search keyword or options' do
186
+ subject { response }
187
+ its(:total_amount) { should == 0 }
188
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
189
+ its('reviews') { should == [] }
190
+ it { should_not be_has_error }
191
+ end
192
+
193
+ context 'search keyword and options' do
194
+ let(:product_code) { '4901901397298' }
195
+ let(:opts) { options }
196
+ it_should_behave_like 'found review search keyword or options'
197
+ end
198
+
199
+ context 'search keyword without options' do
200
+ let(:product_code) { '2110150300' }
201
+ let(:opts) { no_option }
202
+ it_should_behave_like 'found review search keyword or options'
203
+ end
204
+
205
+ context 'search options without keyword' do
206
+ let(:product_code) { nil }
207
+ let(:opts) { options }
208
+ it { lambda{ response }.should raise_error( ArgumentError, "ProductCode is required.") }
209
+ end
210
+
211
+ context 'search without keyword and options' do
212
+ let(:product_code) { nil }
213
+ let(:opts) { no_option }
214
+ it { lambda{ response }.should raise_error( ArgumentError, "ProductCode is required.") }
215
+ end
216
+
217
+ context 'not found search keyword and options' do
218
+ let(:product_code) { '978-4-04-110518-4' }
219
+ let(:opts) { options }
220
+ it_should_behave_like 'not found review search keyword or options'
221
+ end
222
+
223
+ context 'not found search keyword without options' do
224
+ let(:product_code) { '1106317843' }
225
+ let(:opts) { no_option }
226
+ it_should_behave_like 'not found review search keyword or options'
227
+ end
228
+ end
229
+
230
+ describe "Sevennet::Api.search_ranking" do
231
+ let(:response) {
232
+ VCR.use_cassette('search_ranking_' + category_code.to_s + '_' + opts.to_s) do
233
+ Sevennet::Api.search_ranking(category_code)
234
+ end
235
+ }
236
+ let(:options) {
237
+ Sevennet::Api.configure do |options|
238
+ options[:StartIndex] = 1
239
+ options[:ResultAmount] = 5
240
+ options[:TermCond] = 'last1w'
241
+ end
242
+ }
243
+ let(:no_option) {
244
+ Sevennet::Api.configure do |options|
245
+ options[:StartIndex] = nil
246
+ options[:ResultAmount] = nil
247
+ options[:TermCond] = nil
248
+ end
249
+ }
250
+
251
+ context 'search keyword and options' do
252
+ let(:category_code) { 'cd' }
253
+ let(:opts) { options }
254
+ it_should_behave_like 'found products search keyword or options'
255
+ end
256
+
257
+ context 'search keyword without options' do
258
+ let(:category_code) { 'cd' }
259
+ let(:opts) { no_option }
260
+ it_should_behave_like 'found products search keyword or options'
261
+ end
262
+
263
+ context 'search options without keyword' do
264
+ let(:category_code) { nil }
265
+ let(:opts) { options }
266
+ it { lambda{ response }.should raise_error( ArgumentError, "CategoryCode is required.") }
267
+ end
268
+
269
+ context 'search without keyword and options' do
270
+ let(:category_code) { nil }
271
+ let(:opts) { no_option }
272
+ it { lambda{ response }.should raise_error( ArgumentError, "CategoryCode is required.") }
273
+ end
274
+
275
+ context 'not found search keyword and options' do
276
+ let(:category_code) { 'rubyssssss' }
277
+ let(:opts) { options }
278
+ it_should_behave_like 'not found products search keyword or options'
279
+ end
280
+
281
+ context 'not found search keyword without options' do
282
+ let(:category_code) { 'rubyssssss' }
283
+ let(:opts) { no_option }
284
+ it_should_behave_like 'not found products search keyword or options'
285
+ end
286
+ end
287
+
288
+ describe "Sevennet::Api.search_product" do
289
+ let(:response) {
290
+ VCR.use_cassette('search_product_' + terms.to_s + '_' + opts.to_s) do
291
+ Sevennet::Api.search_product(terms)
292
+ end
293
+ }
294
+ let(:options) {
295
+ Sevennet::Api.configure do |options|
296
+ options[:StartIndex] = 1
297
+ options[:ResultAmount] = 5
298
+ options[:CategoryCode] = 'cd'
299
+ end
300
+ }
301
+ let(:no_option) {
302
+ Sevennet::Api.configure do |options|
303
+ options[:StartIndex] = nil
304
+ options[:ResultAmount] = nil
305
+ options[:CategoryCode] = nil
306
+ end
307
+ }
308
+
309
+ context 'search keyword and options' do
310
+ let(:terms) { 'au' }
311
+ let(:opts) { options }
312
+ it_should_behave_like 'found products search keyword or options'
313
+ end
314
+
315
+ context 'search keyword without options' do
316
+ let(:terms) { 'au' }
317
+ let(:opts) { no_option }
318
+ it_should_behave_like 'found products search keyword or options'
319
+ end
320
+
321
+ context 'search options without keyword' do
322
+ let(:terms) { nil }
323
+ let(:opts) { options }
324
+ it_should_behave_like 'found products search keyword or options'
325
+ end
326
+
327
+ context 'search without keyword and options' do
328
+ let(:terms) { nil }
329
+ let(:opts) { no_option }
330
+ it { lambda{response}.should raise_error( ArgumentError, "CategoryCode or KeywordIn is required.") }
331
+ end
332
+
333
+ context 'not found search keyword and options' do
334
+ let(:terms) { 'rubyssssss' }
335
+ let(:opts) { options }
336
+ it_should_behave_like 'not found products search keyword or options'
337
+ end
338
+
339
+ context 'not found search keyword without options' do
340
+ let(:terms) { 'rubyssssss' }
341
+ let(:opts) { no_option }
342
+ it_should_behave_like 'not found products search keyword or options'
343
+ end
344
+ end
345
+
346
+ describe "Sevennet::Api.search_spc_shop" do
347
+ let(:response) {
348
+ VCR.use_cassette('search_spc_shop_' + terms.to_s + '_' + opts.to_s) do
349
+ Sevennet::Api.search_spc_shop(terms)
350
+ end
351
+ }
352
+ let(:options) {
353
+ Sevennet::Api.configure do |options|
354
+ options[:StartIndex] = 1
355
+ options[:ResultAmount] = 5
356
+ options[:SpcSortOrder] = 'name'
357
+ end
358
+ }
359
+ let(:no_option) {
360
+ Sevennet::Api.configure do |options|
361
+ options[:StartIndex] = nil
362
+ options[:ResultAmount] = nil
363
+ options[:SpcSortOrder] = nil
364
+ end
365
+ }
366
+ share_examples_for 'found spc shop search keyword or options' do
367
+ subject { response }
368
+ its(:total_amount) { should > 0 }
369
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
370
+ it { should_not be_has_error }
371
+ context 'shops.first' do
372
+ subject { response.shops.first/"SpcShopId" }
373
+ it { should_not be_empty }
374
+ end
375
+ end
376
+
377
+ share_examples_for 'not found spc shop search keyword or options' do
378
+ subject { response }
379
+ its(:total_amount) { should == 0 }
380
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
381
+ its('shops') { should == [] }
382
+ it { should_not be_has_error }
383
+ end
384
+
385
+ context 'search keyword and options' do
386
+ let(:terms) { 'au' }
387
+ let(:opts) { options }
388
+ it_should_behave_like 'found spc shop search keyword or options'
389
+ end
390
+
391
+ context 'search keyword without options' do
392
+ let(:terms) { 'au' }
393
+ let(:opts) { no_option }
394
+ it_should_behave_like 'found spc shop search keyword or options'
395
+ end
396
+
397
+ context 'search options without keyword' do
398
+ let(:terms) { nil }
399
+ let(:opts) { options }
400
+ it_should_behave_like 'found spc shop search keyword or options'
401
+ end
402
+
403
+ context 'search without keyword and options' do
404
+ let(:terms) { nil }
405
+ let(:opts) { no_option }
406
+ it_should_behave_like 'found spc shop search keyword or options'
407
+ end
408
+
409
+ context 'not found search keyword and options' do
410
+ let(:terms) { 'ruby' }
411
+ let(:opts) { options }
412
+ it_should_behave_like 'not found spc shop search keyword or options'
413
+ end
414
+
415
+ context 'not found search keyword without options' do
416
+ let(:terms) { 'ruby' }
417
+ let(:opts) { no_option }
418
+ it_should_behave_like 'not found spc shop search keyword or options'
419
+ end
420
+ end
421
+
422
+ describe "Sevennet::Api.get_shopping_category" do
423
+ let(:response) {
424
+ VCR.use_cassette('get_shopping_category_' + category_code.to_s) do
425
+ Sevennet::Api.get_shopping_category(category_code)
426
+ end
427
+ }
428
+
429
+ context 'root category' do
430
+ let(:category_code) { '' }
431
+ it_should_behave_like 'category common test'
432
+ end
433
+
434
+ context 'level 1 category' do
435
+ let(:category_code) { 'books' }
436
+ it_should_behave_like 'category common test'
437
+ end
438
+
439
+ context 'level 2 category' do
440
+ let(:category_code) { 'literature' }
441
+ it_should_behave_like 'category common test'
442
+ end
443
+
444
+ context 'wrong category code' do
445
+ let(:category_code) { '9989989898989898' }
446
+ subject { response }
447
+ its(:total_amount) { should == 0 }
448
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
449
+ its('categories.first') { should be_nil }
450
+ it { should_not be_has_error }
451
+ end
452
+ end
453
+
454
+ describe "Sevennet::Api.get_spc_category" do
455
+ let(:response) {
456
+ VCR.use_cassette('get_spc_category_' + category_code.to_s) do
457
+ Sevennet::Api.get_spc_category(category_code)
458
+ end
459
+ }
460
+
461
+ context 'root category' do
462
+ let(:category_code) { '' }
463
+ it_should_behave_like 'category common test'
464
+ end
465
+
466
+ context 'level 1 category' do
467
+ let(:category_code) { 'books' }
468
+ it_should_behave_like 'category common test'
469
+ end
470
+
471
+ context 'level 2 category' do
472
+ let(:category_code) { 'literature' }
473
+ it_should_behave_like 'category common test'
474
+ end
475
+
476
+ context 'wrong category code' do
477
+ let(:category_code) { '9989989898989898' }
478
+ subject { response }
479
+ its(:total_amount) { should == 0 }
480
+ its(:doc) { should be_kind_of Nokogiri::XML::Document }
481
+ its('categories.first') { should be_nil }
482
+ it { should_not be_has_error }
483
+ end
484
+ end
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+ #require 'webmock/rspec'
3
+ #WebMock.disable_net_connect!
4
+ require 'vcr'
5
+
6
+ VCR.configure do |c|
7
+ c.cassette_library_dir = File.expand_path(File.dirname(__FILE__) + '/fixtures')
8
+ c.hook_into :webmock
9
+ # c.allow_http_connections_when_no_cassette = true
10
+ c.default_cassette_options = {
11
+ :match_requests_on => [:method,
12
+ VCR.request_matchers.uri_without_param(:Timestamp,:Signature)]
13
+ }
14
+ end
15
+
16
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/sevennet/api')
17
+
18
+ Sevennet::Api.configure do |options|
19
+ options[:ApiUserId] = 'your api user id'
20
+ options[:APISecretKey] = 'your api secret key'
21
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sevennet-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - shoprev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: vcr
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Generic Ruby 7netshopping API
84
+ email:
85
+ - admin@shoprev.net
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - lib/sevennet/api.rb
96
+ - lib/sevennet/api/version.rb
97
+ - sevennet-api.gemspec
98
+ - spec/sevennet/api_spec.rb
99
+ - spec/spec_helper.rb
100
+ homepage: https://github.com/shoprev/sevennet-api
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.0.3
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Generic Ruby 7netshopping API
124
+ test_files:
125
+ - spec/sevennet/api_spec.rb
126
+ - spec/spec_helper.rb