elibri_api_client 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +24 -0
- data/Rakefile +53 -0
- data/lib/elibri_api_client/api_adapters/v1.rb +171 -0
- data/lib/elibri_api_client/api_adapters/v1_helpers.rb +90 -0
- data/lib/elibri_api_client/api_adapters.rb +16 -0
- data/lib/elibri_api_client/core_extensions.rb +60 -0
- data/lib/elibri_api_client/version.rb +13 -0
- data/lib/elibri_api_client.rb +35 -0
- data/test/elibri_api_client_test.rb +50 -0
- data/test/elibri_api_v1_adapter_queue_test.rb +86 -0
- data/test/elibri_api_v1_adapter_test.rb +309 -0
- data/test/helper.rb +21 -0
- metadata +202 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "httparty", "~> 0.7.8"
|
4
|
+
gem "nokogiri", "~> 1.4.6"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "ruby-debug"
|
8
|
+
gem "mocha"
|
9
|
+
gem "minitest", ">= 0"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.6.2"
|
12
|
+
gem "rcov", ">= 0"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
columnize (0.3.3)
|
5
|
+
crack (0.1.8)
|
6
|
+
git (1.2.5)
|
7
|
+
httparty (0.7.8)
|
8
|
+
crack (= 0.1.8)
|
9
|
+
jeweler (1.6.2)
|
10
|
+
bundler (~> 1.0)
|
11
|
+
git (>= 1.2.5)
|
12
|
+
rake
|
13
|
+
linecache (0.46)
|
14
|
+
rbx-require-relative (> 0.0.4)
|
15
|
+
minitest (2.2.2)
|
16
|
+
mocha (0.9.12)
|
17
|
+
nokogiri (1.4.6)
|
18
|
+
rake (0.9.0)
|
19
|
+
rbx-require-relative (0.0.5)
|
20
|
+
rcov (0.9.9)
|
21
|
+
ruby-debug (0.10.4)
|
22
|
+
columnize (>= 0.1)
|
23
|
+
ruby-debug-base (~> 0.10.4.0)
|
24
|
+
ruby-debug-base (0.10.4)
|
25
|
+
linecache (>= 0.3)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.0.0)
|
32
|
+
httparty (~> 0.7.8)
|
33
|
+
jeweler (~> 1.6.2)
|
34
|
+
minitest
|
35
|
+
mocha
|
36
|
+
nokogiri (~> 1.4.6)
|
37
|
+
rcov
|
38
|
+
ruby-debug
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Marcin Urbanski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
= elibri_api_client
|
2
|
+
|
3
|
+
require 'elibri_api_client'
|
4
|
+
cli = Elibri::ApiClient.new(:host_uri => 'http://localhost:3010', :login => '1b20fa9d72234423979c', :password => '2847cbf4f15a4057e2ab')
|
5
|
+
|
6
|
+
cli.refill_all_queues!
|
7
|
+
cli.pending_data?
|
8
|
+
cli.last_pickups
|
9
|
+
|
10
|
+
cli.pending_queues.each do |pending_queue|
|
11
|
+
named_queue = pending_queue.pick_up!
|
12
|
+
|
13
|
+
named_queue.each_product do |product_xml, product_no|
|
14
|
+
# [...]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
== Copyright
|
21
|
+
|
22
|
+
Copyright (c) 2011 elibri.com.pl. See LICENSE.txt for
|
23
|
+
further details.
|
24
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
require './lib/elibri_api_client/version.rb'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
gem.name = "elibri_api_client"
|
18
|
+
gem.version = Elibri::ApiClient::Version::STRING
|
19
|
+
gem.homepage = "http://github.com/elibri/elibri_api_client"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{API client for elibri.com.pl publishing system}
|
22
|
+
gem.description = %Q{API client for elibri.com.pl publishing system}
|
23
|
+
gem.email = "marcin@urbanski.vdl.pl"
|
24
|
+
gem.authors = ["Marcin Urbański"]
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/*_test.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = Elibri::ApiClient::Version::STRING
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "elibri_api_client #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'elibri_api_client/api_adapters/v1_helpers'
|
2
|
+
|
3
|
+
module Elibri
|
4
|
+
class ApiClient
|
5
|
+
module ApiAdapters
|
6
|
+
|
7
|
+
# Adapter dla 1 wersji API
|
8
|
+
class V1
|
9
|
+
URI_PREFIX = '/api/v1'
|
10
|
+
|
11
|
+
include HTTParty
|
12
|
+
debug_output $stderr
|
13
|
+
|
14
|
+
|
15
|
+
def initialize(host_uri, login, password)
|
16
|
+
@host_uri = host_uri
|
17
|
+
@auth = {:username => login, :password => password}
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Wypełnij wszystkie kolejki oczekujące, wszystkimi dostępnymi danymi. Przydatne przy wykonywaniu
|
22
|
+
# pełnej synchronizacji pomiędzy naszą aplikacją a Elibri.
|
23
|
+
def refill_all_queues!
|
24
|
+
# Dla POST musi być jakieś 'body' requestu, bo serwery często rzucają wyjątkami (WEBrick w szczególności).
|
25
|
+
post '/queues/refill_all', :body => ' '
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Zwróć listę kolejek z oczekującymi danymi.
|
30
|
+
def pending_queues
|
31
|
+
resp = get '/queues/pending_data'
|
32
|
+
|
33
|
+
pending_queues = []
|
34
|
+
resp.parsed_response.css('queue').each do |queue_xml|
|
35
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, queue_xml)
|
36
|
+
pending_queues << queue if queue.items_total.nonzero?
|
37
|
+
end
|
38
|
+
pending_queues
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Czy są jakieś oczekujące dane w Elibri?
|
43
|
+
def pending_data?
|
44
|
+
!pending_queues.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Utwórz z danych oczekujących w kolejce np. 'pending_meta', kolejkę nazwaną.
|
49
|
+
# Tylko z kolejek nazwanych można pobierać dane. Jako argument przyjmuje nazwę kolejki (np. 'pending_meta')
|
50
|
+
# lub odpowiednią instancję Elibri::ApiClient::ApiAdapters::V1::Queue.
|
51
|
+
def pick_up_queue!(queue)
|
52
|
+
case queue
|
53
|
+
when Elibri::ApiClient::ApiAdapters::V1::Queue
|
54
|
+
queue_name = queue.name
|
55
|
+
when String
|
56
|
+
queue_name = queue
|
57
|
+
else
|
58
|
+
raise 'Specify queue as name or Elibri::ApiClient::ApiAdapters::V1::Queue instance'
|
59
|
+
end
|
60
|
+
|
61
|
+
response = post "/queues/#{queue_name}/pick_up", :body => ' '
|
62
|
+
picked_up_queue_xml = response.parsed_response.css('pick_up queue').first
|
63
|
+
Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, picked_up_queue_xml)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def each_page(queue, &block)
|
68
|
+
raise 'Need a Elibri::ApiClient::ApiAdapters::V1::Queue instance' unless queue.kind_of? Elibri::ApiClient::ApiAdapters::V1::Queue
|
69
|
+
|
70
|
+
page_no = 1
|
71
|
+
response = get "/queues/#{queue.name}/#{queue.queue_id}"
|
72
|
+
yield response.parsed_response.css('current_page content'), page_no
|
73
|
+
while next_page = response.parsed_response.css('next_page').first
|
74
|
+
response = get next_page['url']
|
75
|
+
page_no += 1
|
76
|
+
yield response.parsed_response.css('current_page content').first, page_no
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def each_product(queue, &block)
|
82
|
+
raise 'Need a Elibri::ApiClient::ApiAdapters::V1::Queue instance' unless queue.kind_of? Elibri::ApiClient::ApiAdapters::V1::Queue
|
83
|
+
|
84
|
+
product_no = 1
|
85
|
+
each_page(queue) do |products_page_xml, page_no|
|
86
|
+
products_page_xml.css('Product').each do |product_xml|
|
87
|
+
block.call(product_xml, product_no)
|
88
|
+
product_no += 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# Ostatnio utworzone nazwane kolejki. Gdy wysypie nam się aplikacja, można przeglądać ostatnie pickupy.
|
95
|
+
def last_pickups
|
96
|
+
last_pickups = []
|
97
|
+
%w{meta stocks}.each do |queue_name|
|
98
|
+
begin
|
99
|
+
response = get "/queues/#{queue_name}/last_pick_up"
|
100
|
+
queue_xml = response.parsed_response.css('queue').first
|
101
|
+
last_pickups << Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, queue_xml)
|
102
|
+
rescue NoRecentlyPickedUpQueues # Ignoruj błędy o braku ostatnich pickupów.
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
last_pickups
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# http://api.elibri.com.pl:80/api/v1
|
113
|
+
def full_api_uri
|
114
|
+
@host_uri + URI_PREFIX
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def get(request_uri, options = {})
|
119
|
+
options.merge!({:digest_auth => @auth})
|
120
|
+
request_uri = normalise_request_uri(request_uri)
|
121
|
+
|
122
|
+
response = self.class.get(request_uri, options)
|
123
|
+
raise_if_error_present_in response
|
124
|
+
response
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def post(request_uri, options = {})
|
129
|
+
options.merge!({:digest_auth => @auth})
|
130
|
+
request_uri = normalise_request_uri(request_uri)
|
131
|
+
|
132
|
+
response = self.class.post(request_uri, options)
|
133
|
+
raise_if_error_present_in response
|
134
|
+
response
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def normalise_request_uri(request_uri)
|
139
|
+
if request_uri.start_with? 'http://'
|
140
|
+
request_uri
|
141
|
+
elsif !request_uri.start_with? '/'
|
142
|
+
full_api_uri + '/' + request_uri
|
143
|
+
else
|
144
|
+
full_api_uri + request_uri
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def raise_if_error_present_in(response)
|
150
|
+
raise Unauthorized, 'Bad login or password' if response.code == 401
|
151
|
+
|
152
|
+
response_xml = response.parsed_response
|
153
|
+
if response_xml && !response_xml.css('error').empty?
|
154
|
+
error_id = response_xml.css('error').first['id']
|
155
|
+
error_message = response_xml.css('error message').first.text
|
156
|
+
|
157
|
+
# Rozpoznajemy ten kod błędu i możemy rzucić określoną klasą wyjątku:
|
158
|
+
if exception_class = EXCEPTION_CLASSES[error_id.to_s]
|
159
|
+
raise exception_class, error_message
|
160
|
+
else
|
161
|
+
# Jakiś nieznany błąd - rzucamy chociaż stringiem
|
162
|
+
raise UnknownError, "ELIBRI_API ERROR #{error_id}: #{error_message}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
# Klasy pomocnicze dla API w wersji 1
|
3
|
+
module Elibri
|
4
|
+
class ApiClient
|
5
|
+
module ApiAdapters
|
6
|
+
class V1
|
7
|
+
|
8
|
+
class UnknownError < RuntimeError; end
|
9
|
+
class Unauthorized < RuntimeError; end
|
10
|
+
class NotFound < RuntimeError; end
|
11
|
+
class Forbidden < RuntimeError; end
|
12
|
+
class ServerError < RuntimeError; end
|
13
|
+
class NoPendingData < RuntimeError; end
|
14
|
+
class NoRecentlyPickedUpQueues < RuntimeError; end
|
15
|
+
class QueueDoesNotExists < RuntimeError; end
|
16
|
+
class InvalidPageNumber < RuntimeError; end
|
17
|
+
|
18
|
+
# Klasy wyjątków rzucanych, gdy elibri zwróci określony błąd. Np. gdy dostaniemy:
|
19
|
+
# <error id="1001">
|
20
|
+
# <message>Queue does not exist</message>
|
21
|
+
# </error>
|
22
|
+
# Biblioteka rzuca wyjątkiem QueueDoesNotExists.
|
23
|
+
EXCEPTION_CLASSES = {
|
24
|
+
'404' => NotFound,
|
25
|
+
'403' => Forbidden,
|
26
|
+
'500' => ServerError,
|
27
|
+
'1001' => QueueDoesNotExists,
|
28
|
+
'1002' => NoPendingData,
|
29
|
+
'1003' => NoRecentlyPickedUpQueues,
|
30
|
+
'1004' => InvalidPageNumber
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
|
34
|
+
# Zamiast rzeźbić ciągle w XML`u, tworzymy instancje kolejek.
|
35
|
+
class Queue
|
36
|
+
|
37
|
+
attr_reader :name, :items_total, :picked_up_at, :last_insert_at, :queue_id, :url
|
38
|
+
|
39
|
+
def initialize(api_adapter, attributes = {})
|
40
|
+
@api_adapter = api_adapter
|
41
|
+
@name = attributes[:name]
|
42
|
+
@items_total = attributes[:items_total].to_i
|
43
|
+
@queue_id = attributes[:queue_id]
|
44
|
+
@url = attributes[:url]
|
45
|
+
|
46
|
+
@last_insert_at = Time.parse(attributes[:last_insert_at]) if attributes[:last_insert_at].present?
|
47
|
+
@picked_up_at = Time.parse(attributes[:picked_up_at]) if attributes[:picked_up_at].present?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Przekonwertuj kolejkę z danymi oczekującymi (np. 'pending_meta') na kolejkę nazwaną.
|
52
|
+
def pick_up!
|
53
|
+
@api_adapter.pick_up_queue!(self) unless picked_up?
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Hermetyzujemy stronicowanie danych. Programistę interesują tylko kolejne rekordy <Product>
|
58
|
+
def each_product(&block)
|
59
|
+
@api_adapter.each_product(self, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Czy to jest kolejka nazwana, czy oczekująca? Wszystkie kolejki z danymi oczekującymi mają w nazwie
|
64
|
+
# przedrostek 'pending_'. Np. 'pending_meta', 'pending_stocks'.
|
65
|
+
def picked_up?
|
66
|
+
!self.name.start_with?('pending_')
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Zbuduj instancję kolejki na podstawie XML`a.
|
71
|
+
def self.build_from_xml(api_adapter, queue_xml)
|
72
|
+
queue_xml = Nokogiri::XML(queue_xml).css('queue').first if queue_xml.is_a? String
|
73
|
+
Queue.new(api_adapter,
|
74
|
+
:name => queue_xml['name'],
|
75
|
+
:items_total => queue_xml['items_total'].to_i,
|
76
|
+
:last_insert_at => queue_xml['last_insert_at'],
|
77
|
+
:url => queue_xml['url'],
|
78
|
+
:queue_id => queue_xml['id'],
|
79
|
+
:picked_up_at => queue_xml['picked_up_at']
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'elibri_api_client/api_adapters/v1'
|
3
|
+
|
4
|
+
module Elibri
|
5
|
+
class ApiClient
|
6
|
+
|
7
|
+
# Moduł zawierający adaptery dla poszczególnych wersji API. Dzięki takiej architekturze
|
8
|
+
# dodanie kolejnej wersji API sprowadza się do utworzenia nowej klasy w tym module, np.
|
9
|
+
# Elibri::ApiClient::ApiAdapters::V3
|
10
|
+
module ApiAdapters
|
11
|
+
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# HTTParty ma parsować XML za pomocą Nokogiri. CRACK jest zbyt prosty do naszych zastosowań.
|
4
|
+
module HTTParty
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
def xml
|
8
|
+
@parsed_xml ||= Nokogiri::XML(body)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Ukradzione z ActiveSupport:
|
16
|
+
class Object
|
17
|
+
def blank?
|
18
|
+
respond_to?(:empty?) ? empty? : !self
|
19
|
+
end
|
20
|
+
|
21
|
+
def present?
|
22
|
+
!blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
def presence
|
26
|
+
self if present?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class NilClass
|
31
|
+
def blank?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class FalseClass
|
37
|
+
def blank?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class TrueClass
|
43
|
+
def blank?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
class String
|
50
|
+
def blank?
|
51
|
+
self !~ /\S/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class Numeric #:nodoc:
|
57
|
+
def blank?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require 'time'
|
3
|
+
require 'httparty'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'elibri_api_client/core_extensions'
|
7
|
+
require 'elibri_api_client/version'
|
8
|
+
require 'elibri_api_client/api_adapters'
|
9
|
+
|
10
|
+
module Elibri
|
11
|
+
class ApiClient
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
DEFAULT_API_HOST_URI = 'http://api.elibri.com.pl:80'
|
15
|
+
attr_reader :host_uri
|
16
|
+
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@login = options[:login]
|
20
|
+
@password = options[:password]
|
21
|
+
@host_uri = options[:host_uri] || DEFAULT_API_HOST_URI
|
22
|
+
|
23
|
+
# W przyszłości mogą być nowe wersje API, więc zostawiamy sobie furtkę w postaci adapterów:
|
24
|
+
api_version_str = options[:api_version] || 'v1'
|
25
|
+
adapter_class = Elibri::ApiClient::ApiAdapters.const_get(api_version_str.upcase) # Elibri::ApiClient::ApiAdapters::V1
|
26
|
+
@api_adapter = adapter_class.new(@host_uri, @login, @password)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Metody API delegujemy do odpowiedniego adaptera:
|
31
|
+
def_delegators :@api_adapter,
|
32
|
+
:refill_all_queues!, :pending_data?, :pending_queues, :pick_up_queue!, :last_pickups, :each_product, :each_page
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
|
4
|
+
# Testowy adapter
|
5
|
+
module Elibri
|
6
|
+
class ApiClient
|
7
|
+
module ApiAdapters
|
8
|
+
# Testowy adapter.
|
9
|
+
class V999
|
10
|
+
def initialize(host_uri, login, password)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe Elibri::ApiClient do
|
19
|
+
|
20
|
+
it "should be able to establish its version" do
|
21
|
+
assert_match /\d+\.\d+\.\d+/, Elibri::ApiClient::Version::STRING
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
it "should take some defaults in constructor" do
|
26
|
+
client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass')
|
27
|
+
assert_equal Elibri::ApiClient::DEFAULT_API_HOST_URI, client.host_uri
|
28
|
+
|
29
|
+
client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass', :host_uri => 'http://localhost:3000')
|
30
|
+
assert_equal 'http://localhost:3000', client.host_uri
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it "should enable user to pick a version of API" do
|
35
|
+
client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass', :api_version => 'v999')
|
36
|
+
assert_kind_of Elibri::ApiClient::ApiAdapters::V999, client.instance_variable_get('@api_adapter')
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
it "should delegate several methods to apropriate API adapter" do
|
41
|
+
client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass')
|
42
|
+
|
43
|
+
delegated_methods = %w{refill_all_queues! pending_data? pending_queues pick_up_queue! last_pickups each_product each_page}
|
44
|
+
delegated_methods.each {|method_name| client.instance_variable_get('@api_adapter').expects(method_name) }
|
45
|
+
delegated_methods.each {|method_name| client.send(method_name) }
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe Elibri::ApiClient::ApiAdapters::V1::Queue do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@api_adapter = mock('api_adapter')
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
it "should have several attributes" do
|
12
|
+
time = Time.now
|
13
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter,
|
14
|
+
:name => 'meta',
|
15
|
+
:items_total => 120,
|
16
|
+
:queue_id => '192f134e666df34464bcc14d0413',
|
17
|
+
:url => 'http://api.elibri.com.pl/api/v1/queues/meta/192f134e666df34464bcc14d0413',
|
18
|
+
:last_insert_at => time.to_s,
|
19
|
+
:picked_up_at => time.to_s
|
20
|
+
)
|
21
|
+
|
22
|
+
assert_equal 'meta', queue.name
|
23
|
+
assert_equal 120, queue.items_total
|
24
|
+
assert_equal '192f134e666df34464bcc14d0413', queue.queue_id
|
25
|
+
assert_equal 'http://api.elibri.com.pl/api/v1/queues/meta/192f134e666df34464bcc14d0413', queue.url
|
26
|
+
assert_equal time.to_i, queue.last_insert_at.to_i
|
27
|
+
assert_equal time.to_i, queue.picked_up_at.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
it "should be able to build itself from provided XML" do
|
33
|
+
xml = %Q{<queue name="stocks" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="http://api.elibri.com.pl/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 22:02:22 UTC"/>}
|
34
|
+
|
35
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(@api_adapter, xml)
|
36
|
+
assert_equal 'stocks', queue.name
|
37
|
+
assert_equal 1500, queue.items_total
|
38
|
+
assert_equal '192f134e666df34464bcc14d04135cda2bd13a0c', queue.queue_id
|
39
|
+
assert_equal 'http://api.elibri.com.pl/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0c', queue.url
|
40
|
+
assert_equal Time.parse("2011-02-05 21:02:22 UTC"), queue.last_insert_at
|
41
|
+
assert_equal Time.parse("2011-02-05 22:02:22 UTC"), queue.picked_up_at
|
42
|
+
|
43
|
+
xml = %Q{<queue name="pending_stocks" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC"/>}
|
44
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(@api_adapter, xml)
|
45
|
+
assert_nil queue.queue_id
|
46
|
+
assert_nil queue.picked_up_at
|
47
|
+
assert_nil queue.url
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
it "should be able to establish if its a picked up queue" do
|
52
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
|
53
|
+
assert !queue.picked_up?
|
54
|
+
|
55
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
|
56
|
+
assert queue.picked_up?
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
it "should be able to pick up itself when its a pending queue" do
|
63
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
|
64
|
+
@api_adapter.expects(:pick_up_queue!).with(queue)
|
65
|
+
queue.pick_up!
|
66
|
+
|
67
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
|
68
|
+
@api_adapter.expects(:pick_up_queue!).with(queue).never
|
69
|
+
queue.pick_up!
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
it "should provide iterator for traversing products list" do
|
75
|
+
block = lambda {|product_xml| product_xml.css('RecordReference') }
|
76
|
+
|
77
|
+
queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
|
78
|
+
@api_adapter.expects(:each_product).with(queue)
|
79
|
+
queue.each_product(&block)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe Elibri::ApiClient::ApiAdapters::V1 do
|
5
|
+
|
6
|
+
FAKE_API_HOST = 'http://localhost:5000'
|
7
|
+
|
8
|
+
def get_request_expected(request_uri)
|
9
|
+
Elibri::ApiClient::ApiAdapters::V1.expects(:get).with(
|
10
|
+
request_uri,
|
11
|
+
{:digest_auth => {:username => 'login', :password => 'password'}}
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def post_request_expected(request_uri)
|
17
|
+
Elibri::ApiClient::ApiAdapters::V1.expects(:post).with(
|
18
|
+
request_uri,
|
19
|
+
{:body => ' ', :digest_auth => {:username => 'login', :password => 'password'}}
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
before do
|
25
|
+
@adapter = Elibri::ApiClient::ApiAdapters::V1.new(FAKE_API_HOST, 'login', 'password')
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
it "should define several exception classes" do
|
30
|
+
exception_classes = %w{Unauthorized NotFound Forbidden ServerError NoPendingData NoRecentlyPickedUpQueues QueueDoesNotExists InvalidPageNumber}
|
31
|
+
exception_classes.each do |exception_class|
|
32
|
+
assert(Elibri::ApiClient::ApiAdapters::V1.const_get(exception_class) < RuntimeError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
it "should be able to refill all pending queues" do
|
38
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => nil)
|
39
|
+
post_request_expected("#{FAKE_API_HOST}/api/v1/queues/refill_all").returns(response_stub)
|
40
|
+
|
41
|
+
@adapter.refill_all_queues!
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
describe "when asked to establish pending queues list" do
|
46
|
+
|
47
|
+
describe "and there is pending data awaiting for pull" do
|
48
|
+
before do
|
49
|
+
xml = <<-XML
|
50
|
+
<pending_data>
|
51
|
+
<queue name="pending_meta" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC"/>
|
52
|
+
<queue name="pending_stocks" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC"/>
|
53
|
+
</pending_data>
|
54
|
+
XML
|
55
|
+
|
56
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
|
57
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").at_least_once.returns(response_stub)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "should return Queue instances with attributes filled from returned XML" do
|
62
|
+
assert @adapter.pending_data?
|
63
|
+
pending_queues = @adapter.pending_queues
|
64
|
+
assert_equal 2, pending_queues.size
|
65
|
+
|
66
|
+
pending_meta = pending_queues.find {|queue| queue.name == 'pending_meta' }
|
67
|
+
pending_stocks = pending_queues.find {|queue| queue.name == 'pending_stocks' }
|
68
|
+
|
69
|
+
assert_equal 24, pending_meta.items_total
|
70
|
+
assert_kind_of Time, pending_meta.last_insert_at
|
71
|
+
|
72
|
+
assert_equal 1500, pending_stocks.items_total
|
73
|
+
assert_kind_of Time, pending_stocks.last_insert_at
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
describe "and there is no pending data awaiting for pull" do
|
79
|
+
before do
|
80
|
+
xml = <<-XML
|
81
|
+
<pending_data>
|
82
|
+
<queue name="pending_meta" items_total="0" />
|
83
|
+
<queue name="pending_stocks" items_total="0" />
|
84
|
+
</pending_data>
|
85
|
+
XML
|
86
|
+
|
87
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
|
88
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").at_least_once.returns(response_stub)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
it "should return empty array and claim that there is no pending data" do
|
93
|
+
assert @adapter.pending_queues.empty?
|
94
|
+
assert !@adapter.pending_data?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
describe "when asked to pick up queue" do
|
101
|
+
before do
|
102
|
+
xml = <<-XML
|
103
|
+
<pick_up>
|
104
|
+
<queue name="meta" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
|
105
|
+
</pick_up>
|
106
|
+
XML
|
107
|
+
|
108
|
+
@response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
it "should be able to pick it up by name" do
|
113
|
+
post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").at_least_once.returns(@response_stub)
|
114
|
+
picked_up_queue = @adapter.pick_up_queue!('pending_meta')
|
115
|
+
assert_equal 24, picked_up_queue.items_total
|
116
|
+
assert_kind_of Time, picked_up_queue.picked_up_at
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
it "should be able to pick it up by Queue instance" do
|
121
|
+
post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").at_least_once.returns(@response_stub)
|
122
|
+
queue_to_pick_up = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
|
123
|
+
picked_up_queue = @adapter.pick_up_queue!(queue_to_pick_up)
|
124
|
+
assert_equal 24, picked_up_queue.items_total
|
125
|
+
assert_kind_of Time, picked_up_queue.picked_up_at
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
it "should raise error when cannot infer queue name from argument" do
|
130
|
+
post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").never
|
131
|
+
post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_stocks/pick_up").never
|
132
|
+
|
133
|
+
assert_raises(RuntimeError) { @adapter.pick_up_queue!(12345) }
|
134
|
+
assert_raises(RuntimeError) { @adapter.pick_up_queue!(Array.new) }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
it "should normalise request URI before performing real request" do
|
140
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => nil)
|
141
|
+
|
142
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d0413").once.returns(response_stub)
|
143
|
+
@adapter.send(:get, "#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d0413")
|
144
|
+
|
145
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/api_entrypoint").once.returns(response_stub)
|
146
|
+
@adapter.send(:get, '/api_entrypoint')
|
147
|
+
|
148
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/api_entrypoint_without_leading_slash").once.returns(response_stub)
|
149
|
+
@adapter.send(:get, 'api_entrypoint_without_leading_slash')
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
describe "when there was error reported" do
|
154
|
+
before do
|
155
|
+
@xml = %q{<error id="ERROR_CODE"> <message>Error message for error ERROR_CODE</message> </error>}
|
156
|
+
@exception_classes = {
|
157
|
+
'404' => Elibri::ApiClient::ApiAdapters::V1::NotFound,
|
158
|
+
'403' => Elibri::ApiClient::ApiAdapters::V1::Forbidden,
|
159
|
+
'500' => Elibri::ApiClient::ApiAdapters::V1::ServerError,
|
160
|
+
'1001' => Elibri::ApiClient::ApiAdapters::V1::QueueDoesNotExists,
|
161
|
+
'1002' => Elibri::ApiClient::ApiAdapters::V1::NoPendingData,
|
162
|
+
'1003' => Elibri::ApiClient::ApiAdapters::V1::NoRecentlyPickedUpQueues,
|
163
|
+
'1004' => Elibri::ApiClient::ApiAdapters::V1::InvalidPageNumber
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
it "should raise proper exception class" do
|
169
|
+
@exception_classes.each do |error_code, exception_class|
|
170
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML( @xml.gsub('ERROR_CODE', error_code) ))
|
171
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
|
172
|
+
assert_raises(exception_class) { @adapter.pending_queues }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
it "should raise Unauthorized, when there is 401 response code" do
|
178
|
+
response_stub = stub('response_stub', :code => 401, :parsed_response => nil)
|
179
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
|
180
|
+
assert_raises(Elibri::ApiClient::ApiAdapters::V1::Unauthorized) { @adapter.pending_queues }
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
it "should raise RuntimeError on unknown error code" do
|
185
|
+
response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML( @xml.gsub('ERROR_CODE', 'UNKNOWN_ERROR_CODE') ))
|
186
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
|
187
|
+
assert_raises(Elibri::ApiClient::ApiAdapters::V1::UnknownError) { @adapter.pending_queues }
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
describe "when asked to get latest pickups" do
|
194
|
+
describe "and there is no recently picked up queues" do
|
195
|
+
before do
|
196
|
+
xml = %q{<error id='1003'> <message>There is no recently picked up queue</message> </error>}
|
197
|
+
response_stub = stub('response_stub', :code => 412, :parsed_response => Nokogiri::XML(xml))
|
198
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/last_pick_up").once.returns(response_stub)
|
199
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/stocks/last_pick_up").once.returns(response_stub)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should return empty latest pickups list" do
|
203
|
+
assert @adapter.last_pickups.empty?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
describe "and there were recently picked up queues" do
|
209
|
+
before do
|
210
|
+
meta_xml = <<-XML
|
211
|
+
<pick_up>
|
212
|
+
<queue name="meta" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
|
213
|
+
</pick_up>
|
214
|
+
XML
|
215
|
+
|
216
|
+
stocks_xml = <<-XML
|
217
|
+
<pick_up>
|
218
|
+
<queue name="stocks" id="192f134e666df34464bcc14d04135cda2bd13a0d" url="#{FAKE_API_HOST}/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0d" items_total="50" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
|
219
|
+
</pick_up>
|
220
|
+
XML
|
221
|
+
|
222
|
+
meta_response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(meta_xml))
|
223
|
+
stocks_response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(stocks_xml))
|
224
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/last_pick_up").once.returns(meta_response_stub)
|
225
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/stocks/last_pick_up").once.returns(stocks_response_stub)
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
it "should return latest pickups as Queue instances" do
|
230
|
+
last_pickups = @adapter.last_pickups
|
231
|
+
assert_equal 2, last_pickups.size
|
232
|
+
assert(last_pickups.all? { |pickup| pickup.is_a? Elibri::ApiClient::ApiAdapters::V1::Queue })
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
describe "when asked to iterate by products" do
|
239
|
+
|
240
|
+
before do
|
241
|
+
@queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@adapter, :name => 'meta', :queue_id => 'QUEUE_ID')
|
242
|
+
|
243
|
+
first_page_xml = <<-XML
|
244
|
+
<queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
|
245
|
+
<items paginated="true">
|
246
|
+
<pages total="3" items_per_page="20" >
|
247
|
+
<next_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" />
|
248
|
+
<current_page page_no="1" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID" >
|
249
|
+
<content><Product>PRODUCT_FROM_PAGE_1</Product></content>
|
250
|
+
</current_page>
|
251
|
+
</pages>
|
252
|
+
</items>
|
253
|
+
</queue>
|
254
|
+
XML
|
255
|
+
|
256
|
+
second_page_xml = <<-XML
|
257
|
+
<queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
|
258
|
+
<items paginated="true">
|
259
|
+
<pages total="3" items_per_page="20" >
|
260
|
+
<previous_page page_no="1" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID" />
|
261
|
+
<next_page page_no="3" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3" />
|
262
|
+
<current_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" >
|
263
|
+
<content><Product>PRODUCT_FROM_PAGE_2</Product></content>
|
264
|
+
</current_page>
|
265
|
+
</pages>
|
266
|
+
</items>
|
267
|
+
</queue>
|
268
|
+
XML
|
269
|
+
|
270
|
+
third_page_xml = <<-XML
|
271
|
+
<queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
|
272
|
+
<items paginated="true">
|
273
|
+
<pages total="3" items_per_page="20" >
|
274
|
+
<previous_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" />
|
275
|
+
<current_page page_no="3" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3" >
|
276
|
+
<content><Product>PRODUCT_FROM_PAGE_3</Product></content>
|
277
|
+
</current_page>
|
278
|
+
</pages>
|
279
|
+
</items>
|
280
|
+
</queue>
|
281
|
+
XML
|
282
|
+
|
283
|
+
first_page_response_stub = stub('response1_stub', :code => 200, :parsed_response => Nokogiri::XML(first_page_xml))
|
284
|
+
second_page_response_stub = stub('response2_stub', :code => 200, :parsed_response => Nokogiri::XML(second_page_xml))
|
285
|
+
third_page_response_stub = stub('response3_stub', :code => 200, :parsed_response => Nokogiri::XML(third_page_xml))
|
286
|
+
|
287
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID").once.returns(first_page_response_stub)
|
288
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2").once.returns(second_page_response_stub)
|
289
|
+
get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3").once.returns(third_page_response_stub)
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
it "should be able to iterate through page products" do
|
294
|
+
expected_content = %w{<Product>PRODUCT_FROM_PAGE_1</Product> <Product>PRODUCT_FROM_PAGE_2</Product> <Product>PRODUCT_FROM_PAGE_3</Product>}
|
295
|
+
@adapter.each_page(@queue) do |page_content, page_no|
|
296
|
+
assert_equal expected_content[page_no-1], page_content.children.to_s
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
it "should be able to iterate through all product records" do
|
302
|
+
expected_records = %w{PRODUCT_FROM_PAGE_1 PRODUCT_FROM_PAGE_2 PRODUCT_FROM_PAGE_3}
|
303
|
+
@adapter.each_product(@queue) do |product_xml, product_no|
|
304
|
+
assert_equal expected_records[product_no-1], product_xml.text
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'minitest/autorun'
|
12
|
+
require 'mocha'
|
13
|
+
require 'ruby-debug'
|
14
|
+
Debugger.settings[:autoeval] = true rescue nil
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
17
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
18
|
+
require 'elibri_api_client'
|
19
|
+
|
20
|
+
class Test::Unit::TestCase
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elibri_api_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Marcin Urba\xC5\x84ski"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-06 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 19
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 7
|
32
|
+
- 8
|
33
|
+
version: 0.7.8
|
34
|
+
name: httparty
|
35
|
+
version_requirements: *id001
|
36
|
+
prerelease: false
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
type: :runtime
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 11
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 4
|
48
|
+
- 6
|
49
|
+
version: 1.4.6
|
50
|
+
name: nokogiri
|
51
|
+
version_requirements: *id002
|
52
|
+
prerelease: false
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
type: :development
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
name: ruby-debug
|
65
|
+
version_requirements: *id003
|
66
|
+
prerelease: false
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
type: :development
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
name: mocha
|
79
|
+
version_requirements: *id004
|
80
|
+
prerelease: false
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
type: :development
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
name: minitest
|
93
|
+
version_requirements: *id005
|
94
|
+
prerelease: false
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
type: :development
|
97
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 23
|
103
|
+
segments:
|
104
|
+
- 1
|
105
|
+
- 0
|
106
|
+
- 0
|
107
|
+
version: 1.0.0
|
108
|
+
name: bundler
|
109
|
+
version_requirements: *id006
|
110
|
+
prerelease: false
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
type: :development
|
113
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 11
|
119
|
+
segments:
|
120
|
+
- 1
|
121
|
+
- 6
|
122
|
+
- 2
|
123
|
+
version: 1.6.2
|
124
|
+
name: jeweler
|
125
|
+
version_requirements: *id007
|
126
|
+
prerelease: false
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
type: :development
|
129
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
hash: 3
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
version: "0"
|
138
|
+
name: rcov
|
139
|
+
version_requirements: *id008
|
140
|
+
prerelease: false
|
141
|
+
description: API client for elibri.com.pl publishing system
|
142
|
+
email: marcin@urbanski.vdl.pl
|
143
|
+
executables: []
|
144
|
+
|
145
|
+
extensions: []
|
146
|
+
|
147
|
+
extra_rdoc_files:
|
148
|
+
- LICENSE.txt
|
149
|
+
- README.rdoc
|
150
|
+
files:
|
151
|
+
- .document
|
152
|
+
- Gemfile
|
153
|
+
- Gemfile.lock
|
154
|
+
- LICENSE.txt
|
155
|
+
- README.rdoc
|
156
|
+
- Rakefile
|
157
|
+
- lib/elibri_api_client.rb
|
158
|
+
- lib/elibri_api_client/api_adapters.rb
|
159
|
+
- lib/elibri_api_client/api_adapters/v1.rb
|
160
|
+
- lib/elibri_api_client/api_adapters/v1_helpers.rb
|
161
|
+
- lib/elibri_api_client/core_extensions.rb
|
162
|
+
- lib/elibri_api_client/version.rb
|
163
|
+
- test/elibri_api_client_test.rb
|
164
|
+
- test/elibri_api_v1_adapter_queue_test.rb
|
165
|
+
- test/elibri_api_v1_adapter_test.rb
|
166
|
+
- test/helper.rb
|
167
|
+
has_rdoc: true
|
168
|
+
homepage: http://github.com/elibri/elibri_api_client
|
169
|
+
licenses:
|
170
|
+
- MIT
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
hash: 3
|
182
|
+
segments:
|
183
|
+
- 0
|
184
|
+
version: "0"
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
hash: 3
|
191
|
+
segments:
|
192
|
+
- 0
|
193
|
+
version: "0"
|
194
|
+
requirements: []
|
195
|
+
|
196
|
+
rubyforge_project:
|
197
|
+
rubygems_version: 1.6.2
|
198
|
+
signing_key:
|
199
|
+
specification_version: 3
|
200
|
+
summary: API client for elibri.com.pl publishing system
|
201
|
+
test_files: []
|
202
|
+
|