elibri_api_client 1.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.
- 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
|
+
|