duffel_api 0.0.1.pre.dev → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +82 -0
  3. data/.github/dependabot.yml +6 -0
  4. data/.gitignore +9 -0
  5. data/.rubocop.yml +9 -9
  6. data/CHANGELOG.md +1 -3
  7. data/CODE_OF_CONDUCT.md +1 -1
  8. data/Gemfile +10 -2
  9. data/LICENSE.txt +1 -1
  10. data/README.md +190 -33
  11. data/Rakefile +1 -3
  12. data/bin/console +7 -0
  13. data/bin/setup +2 -0
  14. data/duffel_api.gemspec +40 -0
  15. data/examples/book_and_change.rb +106 -0
  16. data/examples/book_with_seat.rb +91 -0
  17. data/examples/exploring_data.rb +43 -0
  18. data/examples/hold_and_pay_later.rb +82 -0
  19. data/examples/search_and_book.rb +91 -0
  20. data/lib/duffel_api/api_response.rb +17 -0
  21. data/lib/duffel_api/api_service.rb +37 -0
  22. data/lib/duffel_api/client.rb +87 -0
  23. data/lib/duffel_api/errors/airline_error.rb +8 -0
  24. data/lib/duffel_api/errors/api_error.rb +8 -0
  25. data/lib/duffel_api/errors/authentication_error.rb +8 -0
  26. data/lib/duffel_api/errors/error.rb +54 -0
  27. data/lib/duffel_api/errors/invalid_request_error.rb +8 -0
  28. data/lib/duffel_api/errors/invalid_state_error.rb +8 -0
  29. data/lib/duffel_api/errors/rate_limit_error.rb +8 -0
  30. data/lib/duffel_api/errors/validation_error.rb +8 -0
  31. data/lib/duffel_api/list_response.rb +27 -0
  32. data/lib/duffel_api/middlewares/raise_duffel_errors.rb +67 -0
  33. data/lib/duffel_api/paginator.rb +27 -0
  34. data/lib/duffel_api/request.rb +64 -0
  35. data/lib/duffel_api/resources/aircraft.rb +26 -0
  36. data/lib/duffel_api/resources/airline.rb +26 -0
  37. data/lib/duffel_api/resources/airport.rb +40 -0
  38. data/lib/duffel_api/resources/base_resource.rb +16 -0
  39. data/lib/duffel_api/resources/offer.rb +60 -0
  40. data/lib/duffel_api/resources/offer_passenger.rb +28 -0
  41. data/lib/duffel_api/resources/offer_request.rb +34 -0
  42. data/lib/duffel_api/resources/order.rb +58 -0
  43. data/lib/duffel_api/resources/order_cancellation.rb +34 -0
  44. data/lib/duffel_api/resources/order_change.rb +46 -0
  45. data/lib/duffel_api/resources/order_change_offer.rb +46 -0
  46. data/lib/duffel_api/resources/order_change_request.rb +30 -0
  47. data/lib/duffel_api/resources/payment.rb +26 -0
  48. data/lib/duffel_api/resources/payment_intent.rb +52 -0
  49. data/lib/duffel_api/resources/refund.rb +42 -0
  50. data/lib/duffel_api/resources/seat_map.rb +24 -0
  51. data/lib/duffel_api/resources/webhook.rb +32 -0
  52. data/lib/duffel_api/response.rb +45 -0
  53. data/lib/duffel_api/services/aircraft_service.rb +36 -0
  54. data/lib/duffel_api/services/airlines_service.rb +36 -0
  55. data/lib/duffel_api/services/airports_service.rb +36 -0
  56. data/lib/duffel_api/services/base_service.rb +29 -0
  57. data/lib/duffel_api/services/offer_passengers_service.rb +30 -0
  58. data/lib/duffel_api/services/offer_requests_service.rb +67 -0
  59. data/lib/duffel_api/services/offers_service.rb +36 -0
  60. data/lib/duffel_api/services/order_cancellations_service.rb +75 -0
  61. data/lib/duffel_api/services/order_change_offers_service.rb +36 -0
  62. data/lib/duffel_api/services/order_change_requests_service.rb +37 -0
  63. data/lib/duffel_api/services/order_changes_service.rb +56 -0
  64. data/lib/duffel_api/services/orders_service.rb +74 -0
  65. data/lib/duffel_api/services/payment_intents_service.rb +56 -0
  66. data/lib/duffel_api/services/payments_service.rb +26 -0
  67. data/lib/duffel_api/services/refunds_service.rb +36 -0
  68. data/lib/duffel_api/services/seat_maps_service.rb +19 -0
  69. data/lib/duffel_api/services/webhooks_service.rb +83 -0
  70. data/lib/duffel_api/version.rb +1 -1
  71. data/lib/duffel_api.rb +51 -4
  72. metadata +90 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e564fc3ef58a41bd22d8cd9f16e5f81adc6c84f795fd48b969e2b5abbc947df9
4
- data.tar.gz: 12bee5c0d8e169e26f0e253e47194e299afd743ab9601cf3c89744f46407f53d
3
+ metadata.gz: bb4caaadd883236526bb2e8bdbc78b4f96f2264ae3a09f0c6b2d645eb0e63b4f
4
+ data.tar.gz: 81a190fbc71c4b5033647393634e0759fdc18513809c1f81fa80dc2ca2453a44
5
5
  SHA512:
6
- metadata.gz: 58813b477f7a9877471d4a2f77ad19d9c1bbd3a1e665394bf87903d5402341f644cb55dd177424abb51400d6ba1218698527d72f1ac9cbe8d5e7da4993b71b18
7
- data.tar.gz: 995b3113fea6d0a25de8fa545d34eae59f8818c12462a6719a23a278064db72deae3c39948f57aa1bf832989d0e322e86deacfd1d3bd76a0b5f80a916bffa0d9
6
+ metadata.gz: 70950a1db91a40ba7fbc1fe2b0b232f8553ab16c497225641ca13d4244374efc9c8b5d80174477e2ab8c3dedd085ea99abc71bdc2159c3af4083ebeab9ac35f6
7
+ data.tar.gz: 4686727e0c4e1d005713b44e684f91fa931f73d214a778fad5b2cb83724d5592b4d4aace1810f95d9c544d92d37a62eb903af9a4d2765d18ad9ad14a1a5d2fac
@@ -0,0 +1,82 @@
1
+ version: 2.1
2
+
3
+ commands:
4
+ setup_ruby:
5
+ steps:
6
+ - run:
7
+ name: Install bundler gem
8
+ command: gem install bundler --version '~> 2.2'
9
+ - run:
10
+ name: Install Ruby dependencies
11
+ command: bundle check || bundle install
12
+
13
+ executors:
14
+ ruby:
15
+ parameters:
16
+ image:
17
+ default: "cimg/ruby:3.0"
18
+ type: string
19
+ docker:
20
+ - image: << parameters.image >>
21
+ resource_class: small
22
+
23
+ orbs:
24
+ ruby: circleci/ruby@1.2
25
+
26
+ jobs:
27
+ test:
28
+ parameters:
29
+ ruby-image:
30
+ type: string
31
+ executor:
32
+ name: ruby
33
+ image: << parameters.ruby-image >>
34
+ steps:
35
+ - checkout
36
+ - setup_ruby
37
+ - ruby/rubocop-check
38
+ - ruby/rspec-test
39
+ coverage:
40
+ executor: ruby
41
+ environment:
42
+ COVERAGE: true
43
+ steps:
44
+ - checkout
45
+ - setup_ruby
46
+ - run:
47
+ name: Run tests with Simplecov enabled
48
+ command: bundle exec rspec spec
49
+ - store_artifacts:
50
+ path: coverage
51
+ code_examples:
52
+ executor: ruby
53
+ steps:
54
+ - checkout
55
+ - setup_ruby
56
+ - run:
57
+ name: Run "search and book" example
58
+ command: bundle exec ruby examples/search_and_book.rb
59
+ - run:
60
+ name: Run "hold and pay later" example
61
+ command: bundle exec ruby examples/hold_and_pay_later.rb
62
+ - run:
63
+ name: Run "exploring data" example
64
+ command: bundle exec ruby examples/exploring_data.rb
65
+ - run:
66
+ name: Run "book with seat" example
67
+ command: bundle exec ruby examples/book_with_seat.rb
68
+ - run:
69
+ name: Run "book and change" example
70
+ command: bundle exec ruby examples/book_and_change.rb
71
+ workflows:
72
+ version: 2
73
+ build_and_test:
74
+ jobs:
75
+ - test:
76
+ matrix:
77
+ parameters:
78
+ ruby-image: ["cimg/ruby:2.6", "cimg/ruby:2.7", "cimg/ruby:3.0", "cimg/ruby:3.1"]
79
+ - coverage
80
+ - code_examples:
81
+ requires:
82
+ - test
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rubocop.yml CHANGED
@@ -1,13 +1,13 @@
1
+ require: rubocop-rake
2
+
3
+ inherit_gem:
4
+ gc_ruboconfig: rubocop.yml
1
5
  AllCops:
2
6
  TargetRubyVersion: 2.6
7
+ NewCops: enable
3
8
 
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
9
+ RSpec/ExampleLength:
10
+ Enabled: false
11
11
 
12
- Layout/LineLength:
13
- Max: 120
12
+ RSpec/MultipleExpectations:
13
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,3 @@
1
- ## [Unreleased]
2
-
3
- ## [0.0.1-dev] - 2021-12-07
1
+ ## [0.1.0] - 2021-12-31
4
2
 
5
3
  - Initial release
data/CODE_OF_CONDUCT.md CHANGED
@@ -39,7 +39,7 @@ This Code of Conduct applies within all community spaces, and also applies when
39
39
 
40
40
  ## Enforcement
41
41
 
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at sasha@duffel.com. All complaints will be reviewed and investigated promptly and fairly.
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at tim@duffel.com. All complaints will be reviewed and investigated promptly and fairly.
43
43
 
44
44
  All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
45
 
data/Gemfile CHANGED
@@ -2,10 +2,18 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ # Specify your gem's dependencies in duffel_api.gemspec
5
6
  gemspec
6
7
 
7
8
  group :development, :test do
9
+ gem "gc_ruboconfig", "~> 2.29.0"
10
+ gem "pry", "~> 0.14.1"
8
11
  gem "rake", "~> 13.0"
9
- gem "rspec", "~> 3.0"
10
- gem "rubocop", "~> 1.21"
12
+ gem "rspec", "~> 3.10.0"
13
+ gem "rspec-its", "~> 1.3.0"
14
+ gem "rspec_junit_formatter", "~> 0.4.1"
15
+ gem "rubocop", "~> 1.24.0"
16
+ gem "rubocop-rake", "~> 0.6.0"
17
+ gem "simplecov", "~> 0.21.2"
18
+ gem "webmock", "~> 3.14.0"
11
19
  end
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 Duffel
3
+ Copyright (c) 2021 Duffel Technology Ltd
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,55 +1,212 @@
1
- # DuffelAPI
1
+ # Duffel API Ruby client library
2
2
 
3
- A Ruby client library for the [Duffel API](https://duffel.com/docs/api)
4
- (currently in `beta`).
3
+ A Ruby client library for the [Duffel API](https://duffel.com/docs/api).
4
+
5
+ ## Contents
6
+
7
+ - [Requirements](#requirements)
8
+ - [Installation](#installation)
9
+ - [Usage](#usage)
10
+
11
+ ## Requirements
12
+
13
+ * Ruby 2.6 or later
14
+ * A Duffel API access token (get started [here](https://duffel.com/docs/guides/quick-start) ✨)
5
15
 
6
16
  ## Installation
7
17
 
8
- Add this line to your application's Gemfile:
18
+ In most cases, you'll want to add `duffel_api` to your project as a dependency by listing it in your `Gemfile`, and then running `bundle`:
9
19
 
10
20
  ```ruby
11
- gem 'duffel_api'
21
+ gem "duffel_api", "~> 0.1.0"
12
22
  ```
13
23
 
14
- And then execute:
24
+ You can install `duffel_api` outside of the context of a project by running `gem install duffel_api` - for example if you want to play with the client library in `irb`.
25
+
26
+ ## Usage
15
27
 
16
- $ bundle install
28
+ You can see a complete end-to-end example of searching and booking using the client library in [`example/search_and_book.rb`](https://github.com/duffelhq/duffel-api-ruby/blob/main/examples/search_and_book.rb).
17
29
 
18
- Or install it yourself as:
30
+ ### Initialising the client
19
31
 
20
- $ gem install duffel_api
32
+ All of the library's functionality is accessed from a `DuffelAPI::Client` instance.
21
33
 
22
- ## Usage
34
+ To initialise a `DuffelAPI::Client`, all you'll need is your API access token:
35
+
36
+ ```ruby
37
+ require "duffel_api"
38
+
39
+ client = DuffelAPI::Client.new(access_token: "duffel_test_000000000")
40
+ ```
41
+
42
+ ### Resources in the Duffel API
43
+
44
+ In this readme, we'll use the term "resources" to refer to the different Duffel concepts that you can *act on* in the API - for example airports, offers, orders and payment intents.
45
+
46
+ We'll refer to instances of each of these resources - an airport, an offer, a payment intent - as "records".
47
+
48
+ In the [Duffel API reference](https://duffel.com/docs/api/), the resources are listed in the sidebar. For each resource, you'll find:
49
+
50
+ * a schema, which describes the data attributes we expose for each record from this resource
51
+ * a list of actions you can perform related to that resource (e.g. for [Orders](https://duffel.com/docs/api/orders), you can "Get a single order", "Update a single order", "List orders" and "Create an order")
52
+
53
+ The Ruby client library is structured around these resources. Each resource has its own "service" which you use to perform actions. These services are accessible from your `Client` instance:
54
+
55
+ ```ruby
56
+ client.orders
57
+ client.offers
58
+ client.payment_intents
59
+ ```
60
+
61
+ __To see what actions are available for each resource, check out the definitions for the service classes [here](https://github.com/duffelhq/duffel-api-ruby/tree/main/lib/duffel_api/services).__
62
+
63
+ ### Creating a record
64
+
65
+ Most resources allow you to create a record. In fact, the most important flows in the Duffel API start with creating a record. You'll do this with the `#create` method exposed on a service.
66
+
67
+ For example, you'll search for flights by creating an offer request:
68
+
69
+ ```ruby
70
+ offer_request = client.offer_requests.create(params: {
71
+ cabin_class: "economy",
72
+ passengers: [{
73
+ age: 28
74
+ }],
75
+ slices: [{
76
+ origin: "LHR",
77
+ destination: "NYC",
78
+ departure_date: "2022-12-31"
79
+ }],
80
+ # This attribute is sent as a query parameter rather than in the body like the others.
81
+ # Worry not! The library handles this complexity for you.
82
+ return_offers: false
83
+ })
84
+
85
+ puts "You've created an offer request, #{offer_request.id}."
86
+ ```
87
+
88
+ The `#create` method returns the created record.
89
+
90
+ ### Listing records
91
+
92
+ Many resources in the Duffel API (e.g. airports, orders and offers) allow you to list their records.
93
+
94
+ For example, you can get a list of all the airports that Duffel knows about - that is, a list of airport records.
95
+
96
+ For performance reasons, we [paginate](https://duffel.com/docs/api/overview/pagination) records in the API when listing. You can only see up to 200 at a time. You'll need to page through, like moving through pages of a book.
97
+
98
+ This is quite fiddly, so the client library does it for you in the `#all` method exposed by relevant services. All you have to do is something like this:
99
+
100
+ ```ruby
101
+ client.offer_requests.all.each do |offer_request|
102
+ puts "Loaded offer request #{order_request.id}"
103
+ end
104
+ ```
105
+
106
+ Sometimes, you'll want to specify filters or sort orders when listing,like this:
107
+
108
+ ```ruby
109
+ # The filters you can use for a given resource are documented in the API Reference
110
+ client.offers.
111
+ all(params: { offer_request_id: "ofr_123", sort: "total_amount" }).
112
+ each do |order|
113
+ puts "Loaded order #{order.id}"
114
+ end
115
+ ```
116
+
117
+ A call to `#all` returns a Ruby [`Enumerator`](https://ruby-doc.org/core-2.6/Enumerator.html), which behaves a lot like an array - you can get the number of records with `#length`, loop through it with `#each`, etc.
118
+
119
+ If you prefer, you can also page through records manually using a service's `#list` method (e.g. `client.orders.list`) which returns a `DuffelAPI::ListResponse`.
120
+
121
+ The records in the page are returned by `#records` (`client.orders.list.records`) and the cursor for the next page (if there is one) can be found with `#after` (`client.orders.list.after`)
122
+
123
+ #### An exception: seat maps
124
+
125
+ Watch out! There is one kind of list in the Duffel API which isn't paginated: seat maps.
126
+
127
+ When you call `client.seat_maps.list(params: { offer_id: "off_123" })`, all of the seat maps will be returned at once in a single `ListResponse`.
128
+
129
+ ### Fetching single records
130
+
131
+ Many resources in the Duffel API allow you fetch a single record (e.g. *an* airport, *a* payment intent) if you know its ID.
132
+
133
+ You do that using the `#get` method on a resource like this:
134
+
135
+ ```ruby
136
+ order = client.orders.get("ord_123")
137
+ puts "Your booking reference is #{order.booking_reference}."
138
+ ```
139
+
140
+ The `#get` method returns the record.
141
+
142
+ ### Updating a record
143
+
144
+ Some records in the Duffel API allow you to update them after they've been created, if you know their ID.
145
+
146
+ That works like this using the `#update` method on a resource:
147
+
148
+ ```ruby
149
+ client.webhooks.update("sev_0000AEdmUJKCvFK45qMFBg", params: {
150
+ active: false
151
+ })
152
+ ```
153
+
154
+ The `#update` method returns the updated record.
155
+
156
+ ### Performing an action on a record
157
+
158
+ Some resources allow you to perform special actions on their records - for example confirming an order cancellation or pinging a webhook.
159
+
160
+ The methods you'll use to do this aren't named consistently, because each resource has different actions. For example, you'll call `#confirm` to confirm an order cancellation but `#ping` to ping a webhook.
161
+
162
+ It'll look a bit like this:
163
+
164
+ ```ruby
165
+ client.order_cancellations.confirm("ore_0000AEUvjGoJlav2j6FDlZ")
166
+ ```
167
+
168
+ Sometimes, you'll need to pass extra data when performing the action. That works like this:
169
+
170
+ ```ruby
171
+ client.order_changes.confirm("oce_0000AEdlOBVlABkDhgsUqW", params: {
172
+ payment: {
173
+ type: "balance",
174
+ currency: "GBP",
175
+ amount: "125.00",
176
+ }
177
+ })
178
+ ```
179
+
180
+ In general, these action methods return the record you've acted on.
181
+
182
+ #### An exception: pinging a webhook
183
+
184
+ Watch out! There is one action in the API which doesn't return the record you've acted on.
185
+
186
+ When you ping a webhook with `client.webhooks.ping("sev_0000AEdmUJKCvFK45qMFBg")`, it'll return a `DuffelAPI::Services::WebhooksService::PingResult` if successful, or otherwise it'll raise an error.
187
+
188
+ ### Handling errors
189
+
190
+ When the Duffel API returns an error, the library will raise an exception.
191
+
192
+ We have an exception class for each of the possible `type`s of error which the API can return, documented [here](https://duffel.com/docs/api/overview/errors) in the API reference. For example, if the API returns an error with `type` `invalid_state_error`, the library will raise a `DuffelAPI::Errors::InvalidStateError` exception.
23
193
 
24
- TODO: Write usage instructions here
194
+ You can find all of those error classes [here](https://github.com/duffelhq/duffel-api-ruby/tree/main/lib/duffel_api/errors).
25
195
 
26
- ## Development
196
+ You can rescue all of these errors and get important information with them using instances methods: `#message`, `#title`, `#code`, `#request_id`, etc.
27
197
 
28
- After checking out the repo, run `bin/setup` to install dependencies. Then, run
29
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
30
- prompt that will allow you to experiment.
198
+ If the client library is unable to connect to Duffel, an appropriate exception will be raised, for example:
31
199
 
32
- To install this gem onto your local machine, run `bundle exec rake install`. To
33
- release a new version, update the version number in `version.rb`, and then run
34
- `bundle exec rake release`, which will create a git tag for the version, push
35
- git commits and the created tag, and push the `.gem` file to
36
- [rubygems.org](https://rubygems.org).
200
+ * `Faraday::TimeoutError` in case of a timeout
201
+ * `Faraday::ConnectionFailed` in case of a connection issue (e.g. problems with DNS resolution)
202
+ * `DuffelAPI::Errors::Error` for `5XX` errors returned from by Duffel's infrastructure, but not by the API itself (e.g. a load balancer)
37
203
 
38
- ## Contributing
204
+ ### Accessing the raw API response
39
205
 
40
- Bug reports and pull requests are welcome on GitHub at
41
- https://github.com/duffelhq/duffel-api-ruby. This project is intended to be a safe,
42
- welcoming space for collaboration, and contributors are expected to adhere to
43
- the [code of
44
- conduct](https://github.com/sgerrand/duffel_api/blob/main/CODE_OF_CONDUCT.md).
206
+ Sometimes, you might want to get lower-level details about the response you received from the Duffel API - for example the raw body or headers.
45
207
 
46
- ## License
208
+ If an error has been raised, you can call `#api_response` on the exception, which returns a `DuffelAPI::APIResponse`. If you're looking at a `ListResponse` or any resource, you can call `#api_response` on that.
47
209
 
48
- The gem is available as open source under the terms of the [MIT
49
- License](https://opensource.org/licenses/MIT).
210
+ From the `APIResponse`, you can call `#headers`, `#status_code`, `#raw_body`, `#parsed_body`, `#meta` or `#request_id` to get key information from the response.
50
211
 
51
- ## Code of Conduct
52
212
 
53
- Everyone interacting in the Ruby Duffel API project's codebases, issue trackers,
54
- chat rooms and mailing lists is expected to follow the [code of
55
- conduct](https://github.com/duffelhq/duffel-api-ruby/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
4
5
  require "rspec/core/rake_task"
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
8
  RuboCop::RakeTask.new
11
9
 
12
10
  task default: %i[spec rubocop]
data/bin/console CHANGED
@@ -4,5 +4,12 @@
4
4
  require "bundler/setup"
5
5
  require "duffel_api"
6
6
 
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
7
14
  require "irb"
8
15
  IRB.start(__FILE__)
data/bin/setup CHANGED
@@ -4,3 +4,5 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/duffel_api/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "duffel_api"
7
+ spec.version = DuffelAPI::VERSION
8
+ spec.authors = ["The Duffel team"]
9
+ spec.email = ["help@duffel.com"]
10
+
11
+ spec.summary = "A Ruby client for interacting with the Duffel API"
12
+ spec.homepage = "https://github.com/duffelhq/duffel-api-ruby"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = "https://github.com/duffelhq/duffel-api-ruby/blob/main/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "faraday", [">= 0.9.2", "< 2"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+
35
+ # For more information and examples about making a new gem, checkout our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ spec.metadata = {
38
+ "rubygems_mfa_required" => "true",
39
+ }
40
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "duffel_api"
4
+
5
+ client = DuffelAPI::Client.new(
6
+ access_token: ENV["DUFFEL_ACCESS_TOKEN"],
7
+ )
8
+
9
+ # 365 days from now
10
+ departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d")
11
+
12
+ offer_request = client.offer_requests.create(params: {
13
+ cabin_class: "economy",
14
+ passengers: [{
15
+ age: 28,
16
+ }],
17
+ slices: [{
18
+ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways
19
+ # results.
20
+ origin: "LHR",
21
+ destination: "STN",
22
+ departure_date: departure_date,
23
+ }],
24
+ # This attribute is sent as a query parameter rather than in the body like the others.
25
+ # Worry not! The library handles this complexity for you.
26
+ return_offers: false,
27
+ })
28
+
29
+ puts "Created offer request: #{offer_request.id}"
30
+
31
+ offers = client.offers.all(params: { offer_request_id: offer_request.id })
32
+
33
+ puts "Got #{offers.count} offers"
34
+
35
+ selected_offer = offers.first
36
+
37
+ puts "Selected offer #{selected_offer.id} to book"
38
+
39
+ priced_offer = client.offers.get(selected_offer.id)
40
+
41
+ puts "The final price for offer #{priced_offer.id} is #{priced_offer.total_amount} " \
42
+ "#{priced_offer.total_currency}"
43
+
44
+ order = client.orders.create(params: {
45
+ selected_offers: [priced_offer.id],
46
+ payments: [
47
+ {
48
+ type: "balance",
49
+ amount: priced_offer.total_amount,
50
+ currency: priced_offer.total_currency,
51
+ },
52
+ ],
53
+ passengers: [
54
+ {
55
+ id: priced_offer.passengers.first["id"],
56
+ title: "mr",
57
+ gender: "m",
58
+ given_name: "Tim",
59
+ family_name: "Rogers",
60
+ born_on: "1993-04-01",
61
+ phone_number: "+441290211999",
62
+ email: "tim@duffel.com",
63
+ },
64
+ ],
65
+ })
66
+
67
+ puts "Created order #{order.id} with booking reference #{order.booking_reference}"
68
+
69
+ order_change_request = client.order_change_requests.create(params: {
70
+ order_id: order.id,
71
+ slices: {
72
+ add: [{
73
+ cabin_class: "economy",
74
+ departure_date: "2022-12-25",
75
+ origin: "LHR",
76
+ destination: "STN",
77
+ }],
78
+ remove: [{
79
+ slice_id: order.slices.first["id"],
80
+ }],
81
+ },
82
+ })
83
+
84
+ order_change_offers = client.order_change_offers.
85
+ all(params: { order_change_request_id: order_change_request.id })
86
+
87
+ puts "Got #{order_change_offers.count} options for changing the order; picking first " \
88
+ "option"
89
+
90
+ order_change = client.order_changes.create(params: {
91
+ selected_order_change_offer: order_change_offers.first.id,
92
+ order_id: order.id,
93
+ })
94
+
95
+ puts "Created order change #{order_change.id}, confirming..."
96
+
97
+ client.order_changes.confirm(order_change.id, params: {
98
+ payment: {
99
+ type: "balance",
100
+ amount: order_change.change_total_amount,
101
+ currency: order_change.change_total_currency,
102
+ },
103
+ })
104
+
105
+ puts "Processed change to order #{order.id} costing " \
106
+ "#{order_change.change_total_amount} #{order_change.change_total_currency}"