zumata 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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