zumata 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d78ff8033bb65e030d681e5fa615bddac28c5eb3
4
+ data.tar.gz: afaf5d0ad9ca769b3f895c23d4c74a2bda98a5c6
5
+ SHA512:
6
+ metadata.gz: f0474a4eb7dda8af7f083e17b6f63b108d3f8e560e3347560b407c288ff534aca2c43c9cf8cb6a20125789373c7857551ad1103e5f687a5dc50a2d5e226d4dcc
7
+ data.tar.gz: 9d17d7d5822d84c5ea2c7f3b0de0cd8de0ad18cb6a82b6156787537463c0f2547bd80e5e82f6f17dfd2c1e15df3e1ecd8ce3c248b8b12875843113bfbff951f3
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ spec/cassettes/*.yml
16
+ t
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zumata.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Zumata
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.
@@ -0,0 +1,12 @@
1
+ # Zumata
2
+
3
+ For interaction with the Zumata API 2.0
4
+ http://developer.zumata.com/doc/zumata-api-2-0-documentation
5
+
6
+ ## Contributing
7
+
8
+ 1. Fork it ( https://github.com/[my-github-username]/zumata/fork )
9
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
10
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
11
+ 4. Push to the branch (`git push origin my-new-feature`)
12
+ 5. Create a new Pull Request
@@ -0,0 +1,10 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ # Default directory to look in is `/specs`
5
+ # Run with `rake spec`
6
+ RSpec::Core::RakeTask.new(:spec) do |task|
7
+ task.rspec_opts = ['--color', '--format', 'nested']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ require "zumata/version"
2
+ require "zumata/responses"
3
+ require "zumata/errors"
4
+ require "zumata/client"
5
+
6
+ module Zumata
7
+ end
@@ -0,0 +1,78 @@
1
+ require 'httparty'
2
+ require 'time'
3
+ require 'awesome_print'
4
+ require 'json'
5
+
6
+ VALID_STATUS_CODES = [200, 500]
7
+
8
+ module Zumata
9
+ class Client
10
+ include HTTParty
11
+
12
+ raise Zumata::ClientConfigError unless ENV["ZUMATA_API_URL"]
13
+ base_uri ENV["ZUMATA_API_URL"]
14
+
15
+ def initialize api_key
16
+ @api_key = api_key
17
+ @timeout = 600
18
+ end
19
+
20
+
21
+ # GET /search
22
+ def search_by_destination destination, opts={}
23
+
24
+ q = { api_key: opts[:api_key] || @api_key,
25
+ destination: destination,
26
+ rooms: opts[:rooms] || 1,
27
+ adults: opts[:adults] || 2,
28
+ gzip: true,
29
+ currency: opts[:currency] || "USD" }
30
+
31
+ # smart defaults
32
+ q[:checkin] = opts[:checkin] || (Time.now + 60*60*60*24).strftime("%m/%d/%Y")
33
+ q[:checkout] = opts[:checkout] || (Time.now + 61*60*60*24).strftime("%m/%d/%Y")
34
+
35
+ # optional
36
+ q[:lang] = opts[:lang] if opts[:lang]
37
+ q[:timeout] = opts[:timeout] if opts[:timeout]
38
+
39
+ res = self.class.get("/search", query: q).response
40
+
41
+ # todo - handle errors from search
42
+ Zumata::GenericResponse.new(context: q, code: res.code.to_i, body: res.body)
43
+ end
44
+
45
+
46
+ # POST /book_property
47
+ # internal: /book
48
+ def book booking_key, guest, payment, opts={}
49
+
50
+ # raise InvalidRequestError unless valid_guest_params?(guest)
51
+ # raise InvalidRequestError unless valid_payment_params?(payment)
52
+
53
+ body_params = { api_key: opts[:api_key] || @api_key,
54
+ booking_key: booking_key,
55
+ guest: guest,
56
+ payment: payment }
57
+
58
+ res = self.class.post("/book", body: body_params.to_json, headers: { 'Content-Type' => 'application/json' }, timeout: @timeout)
59
+
60
+ status_code = res.code.to_i
61
+ raise Zumata::GeneralError, res.body unless VALID_STATUS_CODES.include?(status_code)
62
+
63
+ case status_code
64
+ when 200
65
+ return Zumata::GenericResponse.new(context: body_params, code: status_code, body: res.body)
66
+ when 500
67
+ begin
68
+ parsed_json = JSON.parse(res.body)
69
+ error_msg = parsed_json["status"][0]["error"]
70
+ rescue JSON::ParserError, NoMethodError
71
+ raise Zumata::GeneralError, res.body
72
+ end
73
+ Zumata::ErrorHelper.handle_type(error_msg)
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,29 @@
1
+ module Zumata
2
+
3
+ class ZumataError < StandardError; end
4
+
5
+ class TestConfigError < ZumataError; end
6
+ class ClientConfigError < ZumataError; end
7
+
8
+ # Messages for non-200 responses
9
+ class GeneralError < ZumataError; end
10
+ class InvalidApiKeyError < ZumataError; end
11
+ class InvalidBookingKeyError < ZumataError; end
12
+ class TransactionError < ZumataError; end
13
+
14
+ module ErrorHelper
15
+ def self.handle_type message
16
+ case message
17
+ when "Invalid Api Key."
18
+ raise InvalidApiKeyError, message
19
+ when "Invalid/Expired Booking key"
20
+ raise InvalidBookingKeyError, message
21
+ when "Payment & Booking Transactions are not successful. Please contact us for more details."
22
+ raise TransactionError, message
23
+ else
24
+ raise GeneralError, message
25
+ end
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,13 @@
1
+ module Zumata
2
+
3
+ class GenericResponse
4
+ attr_reader :context, :code, :body
5
+
6
+ def initialize res
7
+ @context = res[:context]
8
+ @code = res[:code]
9
+ @body = res[:body]
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,3 @@
1
+ module Zumata
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'vcr'
2
+ require 'webmock/rspec'
3
+ require 'Zumata'
4
+
5
+ VCR.configure do |c|
6
+ c.cassette_library_dir = 'spec/cassettes'
7
+ c.hook_into :webmock
8
+ c.configure_rspec_metadata!
9
+ c.allow_http_connections_when_no_cassette = true
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.color = true
14
+ config.formatter = :documentation
15
+ config.failure_color = :magenta
16
+ end
@@ -0,0 +1,177 @@
1
+ require 'json'
2
+ require 'spec_helper'
3
+ require './lib/zumata'
4
+
5
+ describe "Zumata::Client" do
6
+
7
+ raise Zumata::TestConfigError unless ENV["ZUMATA_API_KEY"]
8
+ sample_api_key = ENV["ZUMATA_API_KEY"]
9
+
10
+ before(:each) do
11
+ @client = Zumata::Client.new(sample_api_key)
12
+ end
13
+
14
+ describe "search_by_destination" do
15
+
16
+ it 'returns a response with search completed status if the query is valid', :vcr => { :cassette_name => "search_by_destination_done", :record => :new_episodes } do
17
+
18
+ # note - when recording the cassette this requires a cached search w/ results to exist
19
+ destination_id = "f75a8cff-c26e-4603-7b45-1b0f8a5aa100" # Singapore
20
+ results = @client.search_by_destination destination_id
21
+ data = JSON.parse(results.body)
22
+ expect(data["searchCompleted"]).to_not be(nil)
23
+ expect(data["content"]["hotels"].length).to be > 0
24
+ end
25
+
26
+ end
27
+
28
+ describe "book (via stripe payment)" do
29
+
30
+ def sample_guest
31
+ return {
32
+ :salutation => "Mr",
33
+ :first_name => "Phantom",
34
+ :last_name => "Assassin",
35
+ :email => "jonathanbgomez@gmail.com",
36
+ :street => "1 Random St",
37
+ :city => "Melbourne",
38
+ :state => "VIC",
39
+ :postal_code => "3000",
40
+ :country => "Australia",
41
+ :room_remarks => "Room with a view, please.",
42
+ :nationality => "Australia"
43
+ }
44
+ end
45
+
46
+ def sample_payment stripe_token, usd_amount
47
+ amount = ('%.2f' % usd_amount.to_f).to_f
48
+ return {
49
+ :type => "stripe",
50
+ :contact => {
51
+ :first_name => "Phantom",
52
+ :last_name => "Assassin",
53
+ :email => "jonathanbgomez@gmail.com",
54
+ :street => "1 Random St",
55
+ :city => "Melbourne",
56
+ :state => "VIC",
57
+ :postal_code => "3000",
58
+ :country => "Australia"
59
+ },
60
+ :details => {
61
+ :stripe_token => stripe_token,
62
+ :amount => amount,
63
+ :currency => "USD"
64
+ },
65
+ :conversion => {
66
+ :converted_amount => amount,
67
+ :converted_currency => 'USD',
68
+ :exchange_rate => 1.00
69
+ }
70
+ }
71
+ end
72
+
73
+ def select_cheapest_package hotels
74
+ cheapest_package_rate = nil
75
+ cheapest_package_key = nil
76
+ hotels.each do |hotel|
77
+ hotel["rates"]["packages"].each do |package|
78
+ if cheapest_package_rate.nil? || package["roomRate"] < cheapest_package_rate
79
+ cheapest_package_rate = package["roomRate"]
80
+ cheapest_package_key = package["key"]
81
+ end
82
+ end
83
+ end
84
+ raise "Error finding cheap package" if cheapest_package_key.nil?
85
+ return cheapest_package_key, cheapest_package_rate
86
+ end
87
+
88
+ it 'books the hotel and returns booking information' do
89
+
90
+ destination_id = "53d32e78-c548-42af-5236-fb89e0977722" # Barcelona
91
+ results = VCR.use_cassette('search_by_destination_done_2', :record => :new_episodes) do
92
+ @client.search_by_destination destination_id
93
+ end
94
+
95
+ data = JSON.parse(results.body)
96
+ expect(data["searchCompleted"]).to_not be(nil)
97
+
98
+ cheapest_key, cheapest_rate = select_cheapest_package(data["content"]["hotels"])
99
+ guest_params = sample_guest
100
+ payment_params = sample_payment("tok_14dEyI4Zpcn7UAbK7HFGKKqG", cheapest_rate)
101
+
102
+ VCR.use_cassette('book_success', :record => :new_episodes) do
103
+ booking = @client.book cheapest_key, guest_params, payment_params, { api_key: ENV['ZUMATA_API_KEY_BOOK_SUCCESS'] }
104
+ res_body = JSON.parse(booking.body)
105
+ expect(res_body["status"]).to eq(nil)
106
+ expect(res_body["content"]["booking_id"]).not_to eq(nil)
107
+ end
108
+
109
+ end
110
+
111
+ it 'responds with an error when provided with an invalid api key' do
112
+
113
+ destination_id = "f75a8cff-c26e-4603-7b45-1b0f8a5aa100" # Singapore
114
+ results = VCR.use_cassette('search_by_destination_done', :record => :new_episodes) do
115
+ @client.search_by_destination(destination_id)
116
+ end
117
+
118
+ data = JSON.parse(results.body)
119
+ expect(data["searchCompleted"]).to_not be(nil)
120
+
121
+ cheapest_key, cheapest_rate = select_cheapest_package(data["content"]["hotels"])
122
+ guest_params = sample_guest
123
+ payment_params = sample_payment("tok_14bquA4Zpcn7UAbKnRf8XRNg", cheapest_rate)
124
+
125
+ VCR.use_cassette('book_invalid_api_key', :record => :new_episodes) do
126
+ expect{ @client.book cheapest_key, guest_params, payment_params, { api_key: ENV['ZUMATA_API_KEY_FAKE'] } }.to raise_error(Zumata::InvalidApiKeyError)
127
+ end
128
+
129
+ end
130
+
131
+ it 'responds with an error when the booking key is invalid' do
132
+
133
+ # note - create a cached search, then let it expire
134
+
135
+ destination_id = "f75a8cff-c26e-4603-7b45-1b0f8a5aa100" # Singapore
136
+ results = VCR.use_cassette('search_by_destination_done', :record => :new_episodes) do
137
+ @client.search_by_destination destination_id
138
+ end
139
+
140
+ data = JSON.parse(results.body)
141
+ expect(data["searchCompleted"]).to_not be(nil)
142
+
143
+ cheapest_key, cheapest_rate = select_cheapest_package(data["content"]["hotels"])
144
+ guest_params = sample_guest
145
+ payment_params = sample_payment("tok_14bquA4Zpcn7UAbKnRf8XRNg", cheapest_rate)
146
+
147
+ VCR.use_cassette('book_invalid_booking_key', :record => :new_episodes) do
148
+ expect{ @client.book cheapest_key, guest_params, payment_params, { api_key: ENV['ZUMATA_API_KEY_BOOK_SUCCESS'] } }.to raise_error(Zumata::InvalidBookingKeyError)
149
+ end
150
+
151
+ end
152
+
153
+ it 'responds with an error when the booking fails (e.g. stripe is not setup on payment provider)' do
154
+
155
+ destination_id = "53d32e78-c548-42af-5236-fb89e0977722" # Barcelona
156
+ results = VCR.use_cassette('search_by_destination_done_2', :record => :new_episodes) do
157
+ @client.search_by_destination destination_id
158
+ end
159
+
160
+ data = JSON.parse(results.body)
161
+ expect(data["searchCompleted"]).to_not be(nil)
162
+
163
+ cheapest_key, cheapest_rate = select_cheapest_package(data["content"]["hotels"])
164
+ guest_params = sample_guest
165
+ payment_params = sample_payment("tok_14bquA4Zpcn7UAbKnRf8XRNg", cheapest_rate)
166
+
167
+ VCR.use_cassette('book_invalid_stripe_setup', :record => :new_episodes) do
168
+ expect{ @client.book cheapest_key, guest_params, payment_params, { api_key: ENV['ZUMATA_API_KEY_NO_STRIPE'] } }.to raise_error(Zumata::TransactionError)
169
+ end
170
+
171
+ end
172
+
173
+ xit 'responds with an error when the payment is rejected' do
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zumata/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zumata"
8
+ spec.version = Zumata::VERSION
9
+ spec.authors = ["Jonathan Gomez"]
10
+ spec.email = ["jonathanbgomez@gmail.com"]
11
+ spec.summary = %q{Client for the Zumata API 2.0}
12
+ spec.description = %q{Power a hotel website - search hotels, create and manage bookings.}
13
+ spec.homepage = "https://github.com/Zumata/ruby-client"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "3.1.0"
24
+ spec.add_development_dependency "awesome_print", "1.2.0"
25
+ spec.add_development_dependency "vcr", "2.9.3"
26
+ spec.add_development_dependency "webmock", "1.18.0"
27
+
28
+ spec.add_runtime_dependency 'httparty', '~> 0.13', '>= 0.13.1'
29
+
30
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zumata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Gomez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-16 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.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.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: 3.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.9.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.9.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.18.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.18.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: httparty
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.13'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 0.13.1
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '0.13'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 0.13.1
117
+ description: Power a hotel website - search hotels, create and manage bookings.
118
+ email:
119
+ - jonathanbgomez@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - ".gitignore"
125
+ - Gemfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - lib/zumata.rb
130
+ - lib/zumata/client.rb
131
+ - lib/zumata/errors.rb
132
+ - lib/zumata/responses.rb
133
+ - lib/zumata/version.rb
134
+ - spec/spec_helper.rb
135
+ - spec/zumata_spec.rb
136
+ - zumata.gemspec
137
+ homepage: https://github.com/Zumata/ruby-client
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.2.2
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Client for the Zumata API 2.0
161
+ test_files:
162
+ - spec/spec_helper.rb
163
+ - spec/zumata_spec.rb