From 608260d2cd3c2aa9098e4fea096deca5f0857fac Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 18:32:46 +0100 Subject: [PATCH 01/13] Make examples time aware Time moves on and the ages in these examples are no longer valid. These changes replace static ages with calculated equivalents so that they can be rerun at any time with confidence. --- examples/book_and_change.rb | 11 ++++++++--- examples/book_with_extra_baggage.rb | 9 +++++++-- examples/book_with_seat.rb | 9 +++++++-- examples/hold_and_pay_later.rb | 9 +++++++-- examples/search_and_book.rb | 9 +++++++-- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/examples/book_and_change.rb b/examples/book_and_change.rb index 45c5bf4..1ef401b 100644 --- a/examples/book_and_change.rb +++ b/examples/book_and_change.rb @@ -9,10 +9,15 @@ # 365 days from now departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d") +birth_date = Date.parse("1993-04-01") +current_date = Date.today +age = current_date.year - birth_date.year +age += 1 if current_date.yday > birth_date.yday + offer_request = client.offer_requests.create(params: { cabin_class: "economy", passengers: [{ - age: 28, + age: age, }], slices: [{ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways @@ -57,7 +62,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "1993-04-01", + born_on: "#{birth_date}", phone_number: "+441290211999", email: "tim@duffel.com", }, @@ -71,7 +76,7 @@ slices: { add: [{ cabin_class: "economy", - departure_date: "2022-12-25", + departure_date: "#{current_date + 90}", origin: "LHR", destination: "STN", }], diff --git a/examples/book_with_extra_baggage.rb b/examples/book_with_extra_baggage.rb index 0325dbd..83c7ec3 100644 --- a/examples/book_with_extra_baggage.rb +++ b/examples/book_with_extra_baggage.rb @@ -10,10 +10,15 @@ # 365 days from now departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d") +birth_date = Date.parse("1993-04-01") +current_date = Date.today +age = current_date.year - birth_date.year +age += 1 if current_date.yday > birth_date.yday + offer_request = client.offer_requests.create(params: { cabin_class: "economy", passengers: [{ - age: 28, + age: age, }], slices: [{ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways @@ -75,7 +80,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "1993-04-01", + born_on: "#{birth_date}", phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/book_with_seat.rb b/examples/book_with_seat.rb index f7569b4..eb2d4e9 100644 --- a/examples/book_with_seat.rb +++ b/examples/book_with_seat.rb @@ -10,10 +10,15 @@ # 365 days from now departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d") +birth_date = Date.parse("1993-04-01") +current_date = Date.today +age = current_date.year - birth_date.year +age += 1 if current_date.yday > birth_date.yday + offer_request = client.offer_requests.create(params: { cabin_class: "economy", passengers: [{ - age: 28, + age: age, }], slices: [{ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways @@ -83,7 +88,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "1993-04-01", + born_on: "#{birth_date}", phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/hold_and_pay_later.rb b/examples/hold_and_pay_later.rb index d45b624..ff1aab7 100644 --- a/examples/hold_and_pay_later.rb +++ b/examples/hold_and_pay_later.rb @@ -9,10 +9,15 @@ # 365 days from now departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d") +birth_date = Date.parse("1993-04-01") +current_date = Date.today +age = current_date.year - birth_date.year +age += 1 if current_date.yday > birth_date.yday + offer_request = client.offer_requests.create(params: { cabin_class: "economy", passengers: [{ - age: 28, + age: age, }], slices: [{ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways @@ -52,7 +57,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "1993-04-01", + born_on: "#{birth_date}", phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/search_and_book.rb b/examples/search_and_book.rb index add28bd..1e8aaa3 100644 --- a/examples/search_and_book.rb +++ b/examples/search_and_book.rb @@ -10,10 +10,15 @@ # 365 days from now departure_date = (Time.now + (60 * 60 * 24 * 365)).strftime("%Y-%m-%d") +birth_date = Date.parse("1993-04-01") +current_date = Date.today +age = current_date.year - birth_date.year +age += 1 if current_date.yday > birth_date.yday + offer_request = client.offer_requests.create(params: { cabin_class: "economy", passengers: [{ - age: 28, + age: age, }], slices: [{ # We use a non-sensical route to make sure we get speedy, reliable Duffel Airways @@ -72,7 +77,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "1993-04-01", + born_on: "#{birth_date}", phone_number: "+441290211999", email: "tim@duffel.com", }, From 1c9faeca6a2fe29daba584e65b7df0f02bf77017 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 13 Sep 2024 15:17:16 +0100 Subject: [PATCH 02/13] Manage GitHub Actions updates with Dependabot See: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#configuration-options-for-the-dependabotyml-file --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4695914..fa62e47 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 1152098cf848312ce3c829a15d56e27e1d426726 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 17:39:39 +0100 Subject: [PATCH 03/13] `bundle update gc_ruboconfig rubocop` --- .rubocop.yml | 1 + Gemfile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index cf6b6a8..cc9d3de 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,7 @@ require: rubocop-rake inherit_gem: gc_ruboconfig: rubocop.yml + AllCops: TargetRubyVersion: 2.6 NewCops: enable diff --git a/Gemfile b/Gemfile index de28229..51c15c0 100644 --- a/Gemfile +++ b/Gemfile @@ -6,13 +6,13 @@ source "https://rubygems.org" gemspec group :development, :test do - gem "gc_ruboconfig", "~> 3.6.0" + gem "gc_ruboconfig", "~> 5.0.1" gem "pry", "~> 0.14.1" gem "rake", "~> 13.0" gem "rspec", "~> 3.12.0" gem "rspec-its", "~> 1.3.0" gem "rspec_junit_formatter", "~> 0.6.0" - gem "rubocop", "~> 1.50.0" + gem "rubocop", "~> 1.65.0" gem "rubocop-rake", "~> 0.6.0" gem "simplecov", "~> 0.22.0" gem "webmock", "~> 3.18.1" From 74fa099cc3326215d6702d4e48760782d8ca08ab Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 17:42:56 +0100 Subject: [PATCH 04/13] Fix RuboCop offense: Gemspec/DevelopmentDependencies Gemspec/DevelopmentDependencies: Specify development dependencies in Gemfile. --- Gemfile | 1 + duffel_api.gemspec | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 51c15c0..4761055 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ source "https://rubygems.org" gemspec group :development, :test do + gem "appraisal", "~> 2.4" gem "gc_ruboconfig", "~> 5.0.1" gem "pry", "~> 0.14.1" gem "rake", "~> 13.0" diff --git a/duffel_api.gemspec b/duffel_api.gemspec index 5e34d6b..28a8f70 100644 --- a/duffel_api.gemspec +++ b/duffel_api.gemspec @@ -32,8 +32,6 @@ Gem::Specification.new do |spec| spec.add_dependency "base16", "~> 0.0.2" spec.add_dependency "faraday", ">= 0.9.2", "< 3" - spec.add_development_dependency "appraisal", "~> 2.4" - # For more information and examples about making a new gem, checkout our # guide at: https://bundler.io/guides/creating_gem.html spec.metadata = { From 321c9cada9c39f1969ae940b444786b701815733 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 17:48:30 +0100 Subject: [PATCH 05/13] Fix silently broken test case Pull test case out of setup. --- .../services/offers_service_spec.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/spec/duffel_api/services/offers_service_spec.rb b/spec/duffel_api/services/offers_service_spec.rb index 8152e19..7aad0c3 100644 --- a/spec/duffel_api/services/offers_service_spec.rb +++ b/spec/duffel_api/services/offers_service_spec.rb @@ -297,15 +297,16 @@ context "with parameters" do let!(:stub) do stub_request(:get, "https://api.duffel.com/air/offers/off_0000AEdGRhtp5AUUdJqMxo"). - with(query: { "return_available_services" => true }) - to_return( - body: response_body, - headers: response_headers, - ) - it "makes the expected request to the Duffel API" do - client.offers.get(id, params: { return_available_services: true }) - expect(stub).to have_been_requested - end + with(query: { "return_available_services" => true }). + to_return( + body: response_body, + headers: response_headers, + ) + end + + it "makes the expected request to the Duffel API" do + client.offers.get(id, params: { return_available_services: true }) + expect(stub).to have_been_requested end end end From a1737f6609a94034cf47056e671fcbf40a90be00 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 17:51:57 +0100 Subject: [PATCH 06/13] CI: update Ruby test matrix - Remove Ruby 2.6: rubocop >= 1.51.0 depends on Ruby >= 2.7.0 - Add modern, generally available Ruby versions: 3.2, 3.3 --- .github/workflows/ci.yaml | 3 ++- .rubocop.yml | 2 +- duffel_api.gemspec | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index afd057d..be26fa5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -103,10 +103,11 @@ jobs: fail-fast: false matrix: ruby: - - ruby-2.6 - ruby-2.7 - ruby-3.0 - ruby-3.1 + - ruby-3.2 + - ruby-3.3 steps: - uses: actions/checkout@v3 diff --git a/.rubocop.yml b/.rubocop.yml index cc9d3de..9224163 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,7 +4,7 @@ inherit_gem: gc_ruboconfig: rubocop.yml AllCops: - TargetRubyVersion: 2.6 + TargetRubyVersion: 2.7 NewCops: enable RSpec/ExampleLength: diff --git a/duffel_api.gemspec b/duffel_api.gemspec index 28a8f70..41ee031 100644 --- a/duffel_api.gemspec +++ b/duffel_api.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |spec| spec.summary = "A Ruby client for interacting with the Duffel API" spec.homepage = "https://github.com/duffelhq/duffel-api-ruby" spec.license = "MIT" - spec.required_ruby_version = ">= 2.6.0" + spec.required_ruby_version = ">= 2.7.0" spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage From 131db12d4283ef7d090b9af9cfc5a0e1e8d087f0 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 17:57:50 +0100 Subject: [PATCH 07/13] `bundle exec appraisal update` --- gemfiles/faraday_1.gemfile | 13 +-- gemfiles/faraday_1.gemfile.lock | 163 ++++++++++++++++++++------------ gemfiles/faraday_2.gemfile | 13 +-- gemfiles/faraday_2.gemfile.lock | 163 ++++++++++++++++++++------------ 4 files changed, 220 insertions(+), 132 deletions(-) diff --git a/gemfiles/faraday_1.gemfile b/gemfiles/faraday_1.gemfile index 0234a18..fb103af 100644 --- a/gemfiles/faraday_1.gemfile +++ b/gemfiles/faraday_1.gemfile @@ -7,16 +7,17 @@ source "https://rubygems.org" gem "faraday", "1.8.0" group :development, :test do - gem "gc_ruboconfig", "~> 2.32.0" + gem "appraisal", "~> 2.4" + gem "gc_ruboconfig", "~> 5.0.1" gem "pry", "~> 0.14.1" gem "rake", "~> 13.0" - gem "rspec", "~> 3.11.0" + gem "rspec", "~> 3.12.0" gem "rspec-its", "~> 1.3.0" - gem "rspec_junit_formatter", "~> 0.5.0" - gem "rubocop", "~> 1.25.0" + gem "rspec_junit_formatter", "~> 0.6.0" + gem "rubocop", "~> 1.65.0" gem "rubocop-rake", "~> 0.6.0" - gem "simplecov", "~> 0.21.2" - gem "webmock", "~> 3.14.0" + gem "simplecov", "~> 0.22.0" + gem "webmock", "~> 3.18.1" gem "yard", "~> 0.9.27" end diff --git a/gemfiles/faraday_1.gemfile.lock b/gemfiles/faraday_1.gemfile.lock index f11db11..7d8dcd6 100644 --- a/gemfiles/faraday_1.gemfile.lock +++ b/gemfiles/faraday_1.gemfile.lock @@ -1,26 +1,42 @@ PATH remote: .. specs: - duffel_api (0.2.0) + duffel_api (0.4.0) base16 (~> 0.0.2) faraday (>= 0.9.2, < 3) GEM remote: https://rubygems.org/ specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - appraisal (2.4.1) + activesupport (7.1.3.4) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + appraisal (2.5.0) bundler rake thor (>= 0.14.0) ast (2.4.2) base16 (0.0.2) + base64 (0.2.0) + bigdecimal (3.1.8) coderay (1.1.3) - crack (0.4.5) + concurrent-ruby (1.3.3) + connection_pool (2.4.1) + crack (1.0.0) + bigdecimal rexml - diff-lcs (1.5.0) + diff-lcs (1.5.1) docile (1.4.0) + drb (2.2.1) faraday (1.8.0) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -40,95 +56,122 @@ GEM faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) - gc_ruboconfig (2.32.0) - rubocop (>= 1.25) - rubocop-performance (>= 1.13) - rubocop-rspec (>= 2.8.0) - hashdiff (1.0.1) - method_source (1.0.0) - multipart-post (2.1.1) - parallel (1.21.0) - parser (3.1.0.0) + gc_ruboconfig (5.0.1) + rubocop (>= 1.63) + rubocop-factory_bot (>= 2.26.1) + rubocop-performance (>= 1.21) + rubocop-rails (>= 2.25.0) + rubocop-rspec (>= 3.0.1) + rubocop-rspec_rails (>= 2.30.0) + hashdiff (1.1.0) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + json (2.7.2) + language_server-protocol (3.17.0.3) + method_source (1.1.0) + minitest (5.24.1) + multipart-post (2.4.1) + mutex_m (0.2.0) + parallel (1.25.1) + parser (3.3.4.0) ast (~> 2.4.1) - pry (0.14.1) + racc + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.6) + public_suffix (6.0.0) + racc (1.8.0) + rack (3.1.7) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.2.0) - rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rake (13.2.1) + regexp_parser (2.9.2) + rexml (3.3.1) + strscan + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.3) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.4) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) + rspec-support (~> 3.12.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.11.0) + rspec-mocks (3.12.7) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - rspec_junit_formatter (0.5.1) + rspec-support (~> 3.12.0) + rspec-support (3.12.2) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.25.1) + rubocop (1.65.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.15.1, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.15.1) - parser (>= 3.0.1.1) - rubocop-performance (1.13.2) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.25.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.8.0) - rubocop (~> 1.19) - ruby-progressbar (1.11.0) + rubocop-rspec (3.0.3) + rubocop (~> 1.61) + rubocop-rspec_rails (2.30.0) + rubocop (~> 1.61) + rubocop-rspec (~> 3, >= 3.0.1) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - thor (1.2.1) - unicode-display_width (2.1.0) - webmock (3.14.0) + simplecov_json_formatter (0.1.4) + strscan (3.1.0) + thor (1.3.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + webmock (3.18.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) - yard (0.9.27) - webrick (~> 1.7.0) + yard (0.9.36) PLATFORMS + arm64-darwin-23 x86_64-darwin-20 DEPENDENCIES appraisal (~> 2.4) duffel_api! faraday (= 1.8.0) - gc_ruboconfig (~> 2.32.0) + gc_ruboconfig (~> 5.0.1) pry (~> 0.14.1) rake (~> 13.0) - rspec (~> 3.11.0) + rspec (~> 3.12.0) rspec-its (~> 1.3.0) - rspec_junit_formatter (~> 0.5.0) - rubocop (~> 1.25.0) + rspec_junit_formatter (~> 0.6.0) + rubocop (~> 1.65.0) rubocop-rake (~> 0.6.0) - simplecov (~> 0.21.2) - webmock (~> 3.14.0) + simplecov (~> 0.22.0) + webmock (~> 3.18.1) yard (~> 0.9.27) BUNDLED WITH diff --git a/gemfiles/faraday_2.gemfile b/gemfiles/faraday_2.gemfile index 81fd5cd..3452c11 100644 --- a/gemfiles/faraday_2.gemfile +++ b/gemfiles/faraday_2.gemfile @@ -7,16 +7,17 @@ source "https://rubygems.org" gem "faraday", "2.0.1" group :development, :test do - gem "gc_ruboconfig", "~> 2.32.0" + gem "appraisal", "~> 2.4" + gem "gc_ruboconfig", "~> 5.0.1" gem "pry", "~> 0.14.1" gem "rake", "~> 13.0" - gem "rspec", "~> 3.11.0" + gem "rspec", "~> 3.12.0" gem "rspec-its", "~> 1.3.0" - gem "rspec_junit_formatter", "~> 0.5.0" - gem "rubocop", "~> 1.25.0" + gem "rspec_junit_formatter", "~> 0.6.0" + gem "rubocop", "~> 1.65.0" gem "rubocop-rake", "~> 0.6.0" - gem "simplecov", "~> 0.21.2" - gem "webmock", "~> 3.14.0" + gem "simplecov", "~> 0.22.0" + gem "webmock", "~> 3.18.1" gem "yard", "~> 0.9.27" end diff --git a/gemfiles/faraday_2.gemfile.lock b/gemfiles/faraday_2.gemfile.lock index 039592c..92f49b3 100644 --- a/gemfiles/faraday_2.gemfile.lock +++ b/gemfiles/faraday_2.gemfile.lock @@ -1,118 +1,161 @@ PATH remote: .. specs: - duffel_api (0.2.0) + duffel_api (0.4.0) base16 (~> 0.0.2) faraday (>= 0.9.2, < 3) GEM remote: https://rubygems.org/ specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - appraisal (2.4.1) + activesupport (7.1.3.4) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + appraisal (2.5.0) bundler rake thor (>= 0.14.0) ast (2.4.2) base16 (0.0.2) + base64 (0.2.0) + bigdecimal (3.1.8) coderay (1.1.3) - crack (0.4.5) + concurrent-ruby (1.3.3) + connection_pool (2.4.1) + crack (1.0.0) + bigdecimal rexml - diff-lcs (1.5.0) + diff-lcs (1.5.1) docile (1.4.0) + drb (2.2.1) faraday (2.0.1) faraday-net_http (~> 2.0) ruby2_keywords (>= 0.0.4) - faraday-net_http (2.0.1) - gc_ruboconfig (2.32.0) - rubocop (>= 1.25) - rubocop-performance (>= 1.13) - rubocop-rspec (>= 2.8.0) - hashdiff (1.0.1) - method_source (1.0.0) - parallel (1.21.0) - parser (3.1.0.0) + faraday-net_http (2.1.0) + gc_ruboconfig (5.0.1) + rubocop (>= 1.63) + rubocop-factory_bot (>= 2.26.1) + rubocop-performance (>= 1.21) + rubocop-rails (>= 2.25.0) + rubocop-rspec (>= 3.0.1) + rubocop-rspec_rails (>= 2.30.0) + hashdiff (1.1.0) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + json (2.7.2) + language_server-protocol (3.17.0.3) + method_source (1.1.0) + minitest (5.24.1) + mutex_m (0.2.0) + parallel (1.25.1) + parser (3.3.4.0) ast (~> 2.4.1) - pry (0.14.1) + racc + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.6) + public_suffix (6.0.0) + racc (1.8.0) + rack (3.1.7) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.2.0) - rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rake (13.2.1) + regexp_parser (2.9.2) + rexml (3.3.1) + strscan + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.3) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.4) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) + rspec-support (~> 3.12.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.11.0) + rspec-mocks (3.12.7) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - rspec_junit_formatter (0.5.1) + rspec-support (~> 3.12.0) + rspec-support (3.12.2) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.25.1) + rubocop (1.65.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.15.1, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.15.1) - parser (>= 3.0.1.1) - rubocop-performance (1.13.2) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.25.1) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.8.0) - rubocop (~> 1.19) - ruby-progressbar (1.11.0) + rubocop-rspec (3.0.3) + rubocop (~> 1.61) + rubocop-rspec_rails (2.30.0) + rubocop (~> 1.61) + rubocop-rspec (~> 3, >= 3.0.1) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - thor (1.2.1) - unicode-display_width (2.1.0) - webmock (3.14.0) + simplecov_json_formatter (0.1.4) + strscan (3.1.0) + thor (1.3.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + webmock (3.18.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) - yard (0.9.27) - webrick (~> 1.7.0) + yard (0.9.36) PLATFORMS + arm64-darwin-23 x86_64-darwin-20 DEPENDENCIES appraisal (~> 2.4) duffel_api! faraday (= 2.0.1) - gc_ruboconfig (~> 2.32.0) + gc_ruboconfig (~> 5.0.1) pry (~> 0.14.1) rake (~> 13.0) - rspec (~> 3.11.0) + rspec (~> 3.12.0) rspec-its (~> 1.3.0) - rspec_junit_formatter (~> 0.5.0) - rubocop (~> 1.25.0) + rspec_junit_formatter (~> 0.6.0) + rubocop (~> 1.65.0) rubocop-rake (~> 0.6.0) - simplecov (~> 0.21.2) - webmock (~> 3.14.0) + simplecov (~> 0.22.0) + webmock (~> 3.18.1) yard (~> 0.9.27) BUNDLED WITH From 5ad0eb5497f45c8c8cc98b688f1a6835dc638773 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 13 Sep 2024 15:31:24 +0100 Subject: [PATCH 08/13] Correct RuboCop offense: Style/SuperArguments Style/SuperArguments: Call super without arguments and parentheses when the signature is identical. --- lib/duffel_api/middlewares/rate_limiter.rb | 2 +- lib/duffel_api/resources/aircraft.rb | 2 +- lib/duffel_api/resources/airline.rb | 2 +- lib/duffel_api/resources/airport.rb | 2 +- lib/duffel_api/resources/offer.rb | 2 +- lib/duffel_api/resources/offer_passenger.rb | 2 +- lib/duffel_api/resources/offer_request.rb | 2 +- lib/duffel_api/resources/order.rb | 2 +- lib/duffel_api/resources/order_cancellation.rb | 2 +- lib/duffel_api/resources/order_change.rb | 2 +- lib/duffel_api/resources/order_change_offer.rb | 2 +- lib/duffel_api/resources/order_change_request.rb | 2 +- lib/duffel_api/resources/payment.rb | 2 +- lib/duffel_api/resources/payment_intent.rb | 2 +- lib/duffel_api/resources/refund.rb | 2 +- lib/duffel_api/resources/seat_map.rb | 2 +- lib/duffel_api/resources/webhook.rb | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/duffel_api/middlewares/rate_limiter.rb b/lib/duffel_api/middlewares/rate_limiter.rb index e623008..73b8281 100644 --- a/lib/duffel_api/middlewares/rate_limiter.rb +++ b/lib/duffel_api/middlewares/rate_limiter.rb @@ -15,7 +15,7 @@ def mutex end def initialize(app, options = {}) - super(app, options) + super end def call(env) diff --git a/lib/duffel_api/resources/aircraft.rb b/lib/duffel_api/resources/aircraft.rb index 8948b69..ca6824f 100644 --- a/lib/duffel_api/resources/aircraft.rb +++ b/lib/duffel_api/resources/aircraft.rb @@ -20,7 +20,7 @@ def initialize(object, response = nil) @id = object["id"] @name = object["name"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/airline.rb b/lib/duffel_api/resources/airline.rb index 36a8617..5384f61 100644 --- a/lib/duffel_api/resources/airline.rb +++ b/lib/duffel_api/resources/airline.rb @@ -28,7 +28,7 @@ def initialize(object, response = nil) @logo_lockup_url = object["logo_lockup_url"] @logo_symbol_url = object["logo_symbol_url"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/airport.rb b/lib/duffel_api/resources/airport.rb index ec6f4ca..28c6873 100644 --- a/lib/duffel_api/resources/airport.rb +++ b/lib/duffel_api/resources/airport.rb @@ -48,7 +48,7 @@ def initialize(object, response = nil) @name = object["name"] @time_zone = object["time_zone"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/offer.rb b/lib/duffel_api/resources/offer.rb index 182bb2e..801f0f7 100644 --- a/lib/duffel_api/resources/offer.rb +++ b/lib/duffel_api/resources/offer.rb @@ -51,7 +51,7 @@ def initialize(object, response = nil) @total_emissions_kg = object["total_emissions_kg"] @updated_at = object["updated_at"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/offer_passenger.rb b/lib/duffel_api/resources/offer_passenger.rb index 414dea8..9e23e83 100644 --- a/lib/duffel_api/resources/offer_passenger.rb +++ b/lib/duffel_api/resources/offer_passenger.rb @@ -32,7 +32,7 @@ def initialize(object, response = nil) @family_name = object["family_name"] @age = object["age"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/offer_request.rb b/lib/duffel_api/resources/offer_request.rb index b407117..e66a8f7 100644 --- a/lib/duffel_api/resources/offer_request.rb +++ b/lib/duffel_api/resources/offer_request.rb @@ -36,7 +36,7 @@ def initialize(object, response = nil) @passengers = object["passengers"] @slices = object["slices"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/order.rb b/lib/duffel_api/resources/order.rb index 5452d27..785a126 100644 --- a/lib/duffel_api/resources/order.rb +++ b/lib/duffel_api/resources/order.rb @@ -49,7 +49,7 @@ def initialize(object, response = nil) @total_amount = object["total_amount"] @total_currency = object["total_currency"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/order_cancellation.rb b/lib/duffel_api/resources/order_cancellation.rb index 98e21a6..87d499e 100644 --- a/lib/duffel_api/resources/order_cancellation.rb +++ b/lib/duffel_api/resources/order_cancellation.rb @@ -27,7 +27,7 @@ def initialize(object, response = nil) @refund_currency = object["refund_currency"] @refund_to = object["refund_to"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/order_change.rb b/lib/duffel_api/resources/order_change.rb index f48167b..75fe02b 100644 --- a/lib/duffel_api/resources/order_change.rb +++ b/lib/duffel_api/resources/order_change.rb @@ -37,7 +37,7 @@ def initialize(object, response = nil) @refund_to = object["refund_to"] @slices = object["slices"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/order_change_offer.rb b/lib/duffel_api/resources/order_change_offer.rb index 3a898a3..f5693b2 100644 --- a/lib/duffel_api/resources/order_change_offer.rb +++ b/lib/duffel_api/resources/order_change_offer.rb @@ -37,7 +37,7 @@ def initialize(object, response = nil) @slices = object["slices"] @updated_at = object["updated_at"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/order_change_request.rb b/lib/duffel_api/resources/order_change_request.rb index 27a1ab8..b0f2e1b 100644 --- a/lib/duffel_api/resources/order_change_request.rb +++ b/lib/duffel_api/resources/order_change_request.rb @@ -23,7 +23,7 @@ def initialize(object, response = nil) @slices = object["slices"] @updated_at = object["updated_at"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/payment.rb b/lib/duffel_api/resources/payment.rb index 5601620..61bfbdb 100644 --- a/lib/duffel_api/resources/payment.rb +++ b/lib/duffel_api/resources/payment.rb @@ -19,7 +19,7 @@ def initialize(object, response = nil) @id = object["id"] @type = object["type"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/payment_intent.rb b/lib/duffel_api/resources/payment_intent.rb index d0d9d9a..3399b4d 100644 --- a/lib/duffel_api/resources/payment_intent.rb +++ b/lib/duffel_api/resources/payment_intent.rb @@ -43,7 +43,7 @@ def initialize(object, response = nil) @status = object["status"] @updated_at = object["updated_at"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/refund.rb b/lib/duffel_api/resources/refund.rb index 4804616..f3d0ff8 100644 --- a/lib/duffel_api/resources/refund.rb +++ b/lib/duffel_api/resources/refund.rb @@ -33,7 +33,7 @@ def initialize(object, response = nil) @status = object["status"] @updated_at = object["updated_at"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/seat_map.rb b/lib/duffel_api/resources/seat_map.rb index ed948e4..6d4e030 100644 --- a/lib/duffel_api/resources/seat_map.rb +++ b/lib/duffel_api/resources/seat_map.rb @@ -17,7 +17,7 @@ def initialize(object, response = nil) @segment_id = object["segment_id"] @slice_id = object["slice_id"] - super(object, response) + super end end end diff --git a/lib/duffel_api/resources/webhook.rb b/lib/duffel_api/resources/webhook.rb index 820ac8d..6913b0e 100644 --- a/lib/duffel_api/resources/webhook.rb +++ b/lib/duffel_api/resources/webhook.rb @@ -25,7 +25,7 @@ def initialize(object, response = nil) @updated_at = object["updated_at"] @url = object["url"] - super(object, response) + super end end end From 2a13f9f717e063adcd9133291c7b6680ea95332d Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 13 Sep 2024 15:33:56 +0100 Subject: [PATCH 09/13] Correct RuboCop offense: Style/RedundantInterpolation Style/RedundantInterpolation: Prefer to_s over string interpolation. --- examples/book_and_change.rb | 4 ++-- examples/book_with_extra_baggage.rb | 2 +- examples/book_with_seat.rb | 2 +- examples/hold_and_pay_later.rb | 2 +- examples/search_and_book.rb | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/book_and_change.rb b/examples/book_and_change.rb index 1ef401b..2386664 100644 --- a/examples/book_and_change.rb +++ b/examples/book_and_change.rb @@ -62,7 +62,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "#{birth_date}", + born_on: birth_date.to_s, phone_number: "+441290211999", email: "tim@duffel.com", }, @@ -76,7 +76,7 @@ slices: { add: [{ cabin_class: "economy", - departure_date: "#{current_date + 90}", + departure_date: (current_date + 90).to_s, origin: "LHR", destination: "STN", }], diff --git a/examples/book_with_extra_baggage.rb b/examples/book_with_extra_baggage.rb index 83c7ec3..002d78a 100644 --- a/examples/book_with_extra_baggage.rb +++ b/examples/book_with_extra_baggage.rb @@ -80,7 +80,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "#{birth_date}", + born_on: birth_date.to_s, phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/book_with_seat.rb b/examples/book_with_seat.rb index eb2d4e9..96abc05 100644 --- a/examples/book_with_seat.rb +++ b/examples/book_with_seat.rb @@ -88,7 +88,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "#{birth_date}", + born_on: birth_date.to_s, phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/hold_and_pay_later.rb b/examples/hold_and_pay_later.rb index ff1aab7..55ce2ad 100644 --- a/examples/hold_and_pay_later.rb +++ b/examples/hold_and_pay_later.rb @@ -57,7 +57,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "#{birth_date}", + born_on: birth_date.to_s, phone_number: "+441290211999", email: "tim@duffel.com", }, diff --git a/examples/search_and_book.rb b/examples/search_and_book.rb index 1e8aaa3..b6dc1f2 100644 --- a/examples/search_and_book.rb +++ b/examples/search_and_book.rb @@ -77,7 +77,7 @@ gender: "m", given_name: "Tim", family_name: "Rogers", - born_on: "#{birth_date}", + born_on: birth_date.to_s, phone_number: "+441290211999", email: "tim@duffel.com", }, From f45af271bb0148b0e48630fefece57f48dbfbb5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:21:00 +0000 Subject: [PATCH 10/13] Bump actions/upload-artifact from 2 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index be26fa5..b9f5586 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -51,7 +51,7 @@ jobs: COVERAGE: true run: bundle exec rspec - name: Upload coverage results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage From 4e647999e09b69253b79be8fd6d3847c0ec517d9 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 16:28:17 +0100 Subject: [PATCH 11/13] Implement Cities API See: https://duffel.com/docs/api/v1/cities --- lib/duffel_api.rb | 2 + lib/duffel_api/client.rb | 5 + lib/duffel_api/resources/city.rb | 31 + lib/duffel_api/services/cities_service.rb | 59 + .../services/cities_service_spec.rb | 205 ++ spec/fixtures/cities/list.json | 1801 +++++++++++++++++ spec/fixtures/cities/show.json | 94 + 7 files changed, 2197 insertions(+) create mode 100644 lib/duffel_api/resources/city.rb create mode 100644 lib/duffel_api/services/cities_service.rb create mode 100644 spec/duffel_api/services/cities_service_spec.rb create mode 100644 spec/fixtures/cities/list.json create mode 100644 spec/fixtures/cities/show.json diff --git a/lib/duffel_api.rb b/lib/duffel_api.rb index 5edbbcb..83fc1df 100644 --- a/lib/duffel_api.rb +++ b/lib/duffel_api.rb @@ -15,6 +15,7 @@ require_relative "duffel_api/resources/aircraft" require_relative "duffel_api/resources/airline" require_relative "duffel_api/resources/airport" +require_relative "duffel_api/resources/city" require_relative "duffel_api/resources/offer" require_relative "duffel_api/resources/offer_passenger" require_relative "duffel_api/resources/offer_request" @@ -33,6 +34,7 @@ require_relative "duffel_api/services/aircraft_service" require_relative "duffel_api/services/airlines_service" require_relative "duffel_api/services/airports_service" +require_relative "duffel_api/services/cities_service" require_relative "duffel_api/services/offer_passengers_service" require_relative "duffel_api/services/offer_requests_service" require_relative "duffel_api/services/offers_service" diff --git a/lib/duffel_api/client.rb b/lib/duffel_api/client.rb index c6fc401..68cce8f 100644 --- a/lib/duffel_api/client.rb +++ b/lib/duffel_api/client.rb @@ -30,6 +30,11 @@ def airports @airports ||= Services::AirportsService.new(@api_service) end + # @return [Services::CitiesService] + def cities + @cities ||= Services::CitiesService.new(@api_service) + end + # @return [Services::OfferPassengersService] def offer_passengers @offer_passengers ||= Services::OfferPassengersService.new(@api_service) diff --git a/lib/duffel_api/resources/city.rb b/lib/duffel_api/resources/city.rb new file mode 100644 index 0000000..73e9234 --- /dev/null +++ b/lib/duffel_api/resources/city.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 +# frozen_string_literal: true + +module DuffelAPI + module Resources + class City < BaseResource + # @return [String] + attr_reader :iata_code + + # @return [String] + attr_reader :iata_country_code + + # @return [String] + attr_reader :id + + # @return [String] + attr_reader :name + + def initialize(object, response = nil) + @object = object + + @iata_code = object["iata_code"] + @iata_country_code = object["iata_country_code"] + @id = object["id"] + @name = object["name"] + + super(object, response) + end + end + end +end diff --git a/lib/duffel_api/services/cities_service.rb b/lib/duffel_api/services/cities_service.rb new file mode 100644 index 0000000..51464e7 --- /dev/null +++ b/lib/duffel_api/services/cities_service.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module DuffelAPI + module Services + class CitiesService < BaseService + # Lists cities, returning a single page of results + # + # @option [Hash] :params Parameters to include in the HTTP querystring, including + # any filters + # @return [ListResponse] + # @raise [Errors::Error] when the Duffel API returns an error + def list(options = {}) + path = "/air/cities" + + response = make_request(:get, path, options) + + ListResponse.new( + response: response, + unenveloped_body: unenvelope_body(response.parsed_body), + resource_class: Resources::City, + ) + end + + # Returns an `Enumerator` which can automatically cycle through multiple + # pages of `Resources::City`s. + # + # By default, this will use pages of 200 results under the hood, but this + # can be customised by specifying the `:limit` option in the `:params`. + # + # @param options [Hash] options passed to `#list`, for example `:params` to + # send an HTTP querystring with filters + # @return [Enumerator] + # @raise [Errors::Error] when the Duffel API returns an error + def all(options = {}) + options[:params] = DEFAULT_ALL_PARAMS.merge(options[:params] || {}) + + DuffelAPI::Paginator.new( + service: self, + options: options, + ).enumerator + end + + # Retrieves a single city by ID + # + # @param id [String] + # @return [Resources::City] + # @raise [Errors::Error] when the Duffel API returns an error + def get(id, options = {}) + path = substitute_url_pattern("/air/cities/:id", "id" => id) + + response = make_request(:get, path, options) + + return if response.raw_body.nil? + + Resources::City.new(unenvelope_body(response.parsed_body), response) + end + end + end +end diff --git a/spec/duffel_api/services/cities_service_spec.rb b/spec/duffel_api/services/cities_service_spec.rb new file mode 100644 index 0000000..f9891ab --- /dev/null +++ b/spec/duffel_api/services/cities_service_spec.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe DuffelAPI::Services::CitiesService do + let(:client) do + DuffelAPI::Client.new( + access_token: "secret_token", + ) + end + + let(:response_headers) do + { + "content-type" => "application/json", + "x-request-id" => "FsJz79144I4JjDwAA6TB", + } + end + + describe "#list" do + describe "with no filters" do + subject(:get_list_response) { client.cities.list } + + let(:response_body) { load_fixture("cities/list.json") } + + let!(:stub) do + stub_request(:get, "https://api.duffel.com/air/cities").to_return( + body: response_body, + headers: response_headers, + ) + end + + it "makes the expected request to the Duffel API" do + get_list_response + expect(stub).to have_been_requested + end + + it "exposes valid City resources" do + expect(get_list_response.records.map(&:class).uniq). + to eq([DuffelAPI::Resources::City]) + + city = get_list_response.records.first + + expect(city.iata_code).to eq("ABI") + expect(city.iata_country_code).to eq("US") + expect(city.id).to eq("cit_abi_us") + expect(city.name).to eq("Abilene") + end + + it "exposes the cursors for before and after" do + expect(get_list_response.before).to be_nil + expect(get_list_response.after).to eq("g3QAAAACdwJpZG0AAAAKY2l0X2Nvc191c3" \ + "cEbmFtZW0AAAAQQ29sb3JhZG8gU3ByaW5ncw==") + end + + it "exposes the API response" do + api_response = get_list_response.api_response + + expect(api_response).to be_a(DuffelAPI::APIResponse) + + expect(api_response.body).to be_a(String) + expect(api_response.headers).to eq(response_headers) + expect(api_response.request_id).to eq(response_headers["x-request-id"]) + expect(api_response.status_code).to eq(200) + end + end + + describe "with parameters" do + subject(:get_list_response) { client.cities.list(params: { limit: 200 }) } + + let(:response_body) { load_fixture("cities/list.json") } + + let!(:stub) do + stub_request(:get, "https://api.duffel.com/air/cities"). + with(query: { "limit" => "200" }). + to_return( + body: response_body, + headers: response_headers, + ) + end + + it "makes the expected request to the Duffel API" do + get_list_response + expect(stub).to have_been_requested + end + end + end + + describe "#all" do + let(:first_page_response_body) { load_fixture("cities/list.json") } + + let(:last_page_response_body) do + convert_list_response_to_last_page(first_page_response_body) + end + + let(:expected_query_params) do + { limit: 200 } + end + + let!(:first_response_stub) do + stub_request(:get, "https://api.duffel.com/air/cities"). + with(query: expected_query_params). + to_return( + body: first_page_response_body, + headers: response_headers, + ) + end + + let!(:second_response_stub) do + stub_request(:get, "https://api.duffel.com/air/cities"). + with(query: expected_query_params.merge( + "after" => "g3QAAAACdwJpZG0AAAAKY2l0X2Nvc191c3cEbmFtZW0AAAAQQ29sb3JhZG8gU3B" \ + "yaW5ncw==", + )). + to_return( + body: last_page_response_body, + headers: response_headers, + ) + end + + it "automatically makes the requests to load all pages, 200 results at a time" do + expect(client.cities.all.to_a.length).to eq(100) + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested + end + + it "exposes valid City resources" do + records = client.cities.all.to_a + expect(records.map(&:class).uniq).to eq([DuffelAPI::Resources::City]) + city = records.first + + expect(city.iata_code).to eq("ABI") + expect(city.iata_country_code).to eq("US") + expect(city.id).to eq("cit_abi_us") + expect(city.name).to eq("Abilene") + end + + it "exposes the API response" do + records = client.cities.all.to_a + api_response = records.first.api_response + + expect(api_response).to be_a(DuffelAPI::APIResponse) + + expect(api_response.body).to be_a(String) + expect(api_response.headers).to eq(response_headers) + expect(api_response.request_id).to eq(response_headers["x-request-id"]) + expect(api_response.status_code).to eq(200) + end + + context "customising the limit per page" do + let(:expected_query_params) do + { limit: 33 } + end + + it "requests the requested number of items per page from the API" do + client.cities.all(params: { limit: 33 }). + to_a + + expect(first_response_stub).to have_been_requested + expect(second_response_stub).to have_been_requested + end + end + end + + describe "#get" do + subject(:get_response) { client.cities.get(id) } + + let(:id) { "cit_lon_gb" } + + let!(:stub) do + stub_request(:get, "https://api.duffel.com/air/cities/cit_lon_gb"). + to_return( + body: response_body, + headers: response_headers, + ) + end + + let(:response_body) { load_fixture("cities/show.json") } + + it "makes the expected request to the Duffel API" do + get_response + expect(stub).to have_been_requested + end + + it "returns an Airport resource" do + expect(get_response).to be_a(DuffelAPI::Resources::City) + + expect(get_response.iata_code).to eq("LON") + expect(get_response.iata_country_code).to eq("GB") + expect(get_response.id).to eq("cit_lon_gb") + expect(get_response.name).to eq("London") + + end + + it "exposes the API response" do + api_response = get_response.api_response + + expect(api_response).to be_a(DuffelAPI::APIResponse) + + expect(api_response.body).to be_a(String) + expect(api_response.headers).to eq(response_headers) + expect(api_response.request_id).to eq(response_headers["x-request-id"]) + expect(api_response.status_code).to eq(200) + end + end +end diff --git a/spec/fixtures/cities/list.json b/spec/fixtures/cities/list.json new file mode 100644 index 0000000..1da9d89 --- /dev/null +++ b/spec/fixtures/cities/list.json @@ -0,0 +1,1801 @@ +{ + "meta": { + "before": null, + "limit": 50, + "after": "g3QAAAACdwJpZG0AAAAKY2l0X2Nvc191c3cEbmFtZW0AAAAQQ29sb3JhZG8gU3ByaW5ncw==" + }, + "data": [ + { + "airports": [ + { + "city_name": "Abilene", + "iata_city_code": "ABI", + "iata_country_code": "US", + "icao_code": "KABI", + "iata_code": "ABI", + "latitude": 32.411237, + "longitude": -99.681822, + "time_zone": "America/Chicago", + "name": "Abilene Regional Airport", + "id": "arp_abi_us" + }, + { + "city_name": "Abilene", + "iata_city_code": "ABI", + "iata_country_code": "US", + "icao_code": "KDYS", + "iata_code": "DYS", + "latitude": 32.420799, + "longitude": -99.854599, + "time_zone": "America/Chicago", + "name": "Dyess Air Force Base", + "id": "arp_dys_us" + } + ], + "iata_country_code": "US", + "iata_code": "ABI", + "name": "Abilene", + "id": "cit_abi_us" + }, + { + "airports": [ + { + "city_name": "Abu Dhabi", + "iata_city_code": "AUH", + "iata_country_code": "AE", + "icao_code": "OMAM", + "iata_code": "DHF", + "latitude": 24.249302, + "longitude": 54.548251, + "time_zone": "Asia/Dubai", + "name": "Al Dhafra Air Base", + "id": "arp_dhf_ae" + }, + { + "city_name": "Abu Dhabi", + "iata_city_code": "AUH", + "iata_country_code": "AE", + "icao_code": null, + "iata_code": "AYM", + "latitude": 24.464725, + "longitude": 54.610182, + "time_zone": "Asia/Dubai", + "name": "Yas Island Seaplane Base Airport", + "id": "arp_aym_ae" + }, + { + "city_name": "Abu Dhabi", + "iata_city_code": "AUH", + "iata_country_code": "AE", + "icao_code": "OMAA", + "iata_code": "AUH", + "latitude": 24.437469, + "longitude": 54.650586, + "time_zone": "Asia/Dubai", + "name": "Abu Dhabi International Airport", + "id": "arp_auh_ae" + }, + { + "city_name": "Abu Dhabi", + "iata_city_code": "AUH", + "iata_country_code": "AE", + "icao_code": "OMAD", + "iata_code": "AZI", + "latitude": 24.428556, + "longitude": 54.457014, + "time_zone": "Asia/Dubai", + "name": "Bateen Airport", + "id": "arp_azi_ae" + } + ], + "iata_country_code": "AE", + "iata_code": "AUH", + "name": "Abu Dhabi", + "id": "cit_auh_ae" + }, + { + "airports": [ + { + "city_name": "Aitape", + "iata_city_code": "ATP", + "iata_country_code": "PG", + "icao_code": "AYAI", + "iata_code": "ATP", + "latitude": -3.143579, + "longitude": 142.346516, + "time_zone": "Pacific/Port_Moresby", + "name": "Aitape Airport", + "id": "arp_atp_pg" + }, + { + "city_name": "Aitape", + "iata_city_code": "ATP", + "iata_country_code": "PG", + "icao_code": null, + "iata_code": "TAJ", + "latitude": -3.197688, + "longitude": 142.429805, + "time_zone": "Pacific/Port_Moresby", + "name": "Tadji Airport", + "id": "arp_taj_pg" + } + ], + "iata_country_code": "PG", + "iata_code": "ATP", + "name": "Aitape", + "id": "cit_atp_pg" + }, + { + "airports": [ + { + "city_name": "Akron", + "iata_city_code": "CAK", + "iata_country_code": "US", + "icao_code": "KCAK", + "iata_code": "CAK", + "latitude": 40.915548, + "longitude": -81.440739, + "time_zone": "America/New_York", + "name": "Akron–Canton Airport", + "id": "arp_cak_us" + }, + { + "city_name": "Akron", + "iata_city_code": "CAK", + "iata_country_code": "US", + "icao_code": "KAKR", + "iata_code": "AKC", + "latitude": 41.035888, + "longitude": -81.467633, + "time_zone": "America/New_York", + "name": "Akron Fulton International Airport", + "id": "arp_akc_us" + } + ], + "iata_country_code": "US", + "iata_code": "CAK", + "name": "Akron/Canton", + "id": "cit_cak_us" + }, + { + "airports": [ + { + "city_name": "Alamogordo", + "iata_city_code": "ALM", + "iata_country_code": "US", + "icao_code": "KALM", + "iata_code": "ALM", + "latitude": 32.840108, + "longitude": -105.9919, + "time_zone": "America/Denver", + "name": "Alamogordo White Sands Regional Airport", + "id": "arp_alm_us" + }, + { + "city_name": "Alamogordo", + "iata_city_code": "ALM", + "iata_country_code": "US", + "icao_code": "KHMN", + "iata_code": "HMN", + "latitude": 32.852501, + "longitude": -106.107002, + "time_zone": "America/Denver", + "name": "Holloman Air Force Base", + "id": "arp_hmn_us" + } + ], + "iata_country_code": "US", + "iata_code": "ALM", + "name": "Alamogordo", + "id": "cit_alm_us" + }, + { + "airports": [ + { + "city_name": "Alexandria", + "iata_city_code": "AEX", + "iata_country_code": "US", + "icao_code": "KAEX", + "iata_code": "AEX", + "latitude": 31.326149, + "longitude": -92.546915, + "time_zone": "America/Chicago", + "name": "Alexandria International Airport", + "id": "arp_aex_us" + }, + { + "city_name": "Alexandria", + "iata_city_code": "AEX", + "iata_country_code": "US", + "icao_code": "KESF", + "iata_code": "ESF", + "latitude": 31.394614, + "longitude": -92.295764, + "time_zone": "America/Chicago", + "name": "Esler Regional Airport", + "id": "arp_esf_us" + } + ], + "iata_country_code": "US", + "iata_code": "AEX", + "name": "Alexandria", + "id": "cit_aex_us" + }, + { + "airports": [ + { + "city_name": "Alexandria", + "iata_city_code": "ALY", + "iata_country_code": "EG", + "icao_code": "HEAX", + "iata_code": "ALY", + "latitude": 31.191435, + "longitude": 29.952488, + "time_zone": "Africa/Cairo", + "name": "El Nouzha Airport", + "id": "arp_aly_eg" + }, + { + "city_name": "Alexandria", + "iata_city_code": "ALY", + "iata_country_code": "EG", + "icao_code": "HEBA", + "iata_code": "HBE", + "latitude": 30.931803, + "longitude": 29.69568, + "time_zone": "Africa/Cairo", + "name": "Borg El Arab Airport", + "id": "arp_hbe_eg" + } + ], + "iata_country_code": "EG", + "iata_code": "ALY", + "name": "Alexandria", + "id": "cit_aly_eg" + }, + { + "airports": [ + { + "city_name": "Altus", + "iata_city_code": "LTS", + "iata_country_code": "US", + "icao_code": "KAXS", + "iata_code": "AXS", + "latitude": 34.698416, + "longitude": -99.337992, + "time_zone": "America/Chicago", + "name": "Altus/Quartz Mountain Regional Airport", + "id": "arp_axs_us" + }, + { + "city_name": "Altus", + "iata_city_code": "LTS", + "iata_country_code": "US", + "icao_code": "KLTS", + "iata_code": "LTS", + "latitude": 34.667099, + "longitude": -99.266701, + "time_zone": "America/Chicago", + "name": "Altus Air Force Base", + "id": "arp_lts_us" + } + ], + "iata_country_code": "US", + "iata_code": "LTS", + "name": "Altus", + "id": "cit_lts_us" + }, + { + "airports": [ + { + "city_name": "Amarillo", + "iata_city_code": "AMA", + "iata_country_code": "US", + "icao_code": "KAMA", + "iata_code": "AMA", + "latitude": 35.219956, + "longitude": -101.706679, + "time_zone": "America/Chicago", + "name": "Rick Husband Amarillo International Airport", + "id": "arp_ama_us" + }, + { + "city_name": "Amarillo", + "iata_city_code": "AMA", + "iata_country_code": "US", + "icao_code": "KTDW", + "iata_code": "TDW", + "latitude": 35.169989, + "longitude": -101.826954, + "time_zone": "America/Chicago", + "name": "Tradewind Airport", + "id": "arp_tdw_us" + } + ], + "iata_country_code": "US", + "iata_code": "AMA", + "name": "Amarillo", + "id": "cit_ama_us" + }, + { + "airports": [ + { + "city_name": "Amman", + "iata_city_code": "AMM", + "iata_country_code": "JO", + "icao_code": "OJAM", + "iata_code": "ADJ", + "latitude": 31.973712, + "longitude": 35.987633, + "time_zone": "Asia/Amman", + "name": "Amman Civil Airport", + "id": "arp_adj_jo" + }, + { + "city_name": "Amman", + "iata_city_code": "AMM", + "iata_country_code": "JO", + "icao_code": "OJAI", + "iata_code": "AMM", + "latitude": 31.72266, + "longitude": 35.992224, + "time_zone": "Asia/Amman", + "name": "Queen Alia International Airport", + "id": "arp_amm_jo" + } + ], + "iata_country_code": "JO", + "iata_code": "AMM", + "name": "Amman", + "id": "cit_amm_jo" + }, + { + "airports": [ + { + "city_name": "Anchorage", + "iata_city_code": "ANC", + "iata_country_code": "US", + "icao_code": "PAFR", + "iata_code": "FRN", + "latitude": 61.264454, + "longitude": -149.653852, + "time_zone": "America/Anchorage", + "name": "Bryant Army Heliport", + "id": "arp_frn_us" + }, + { + "city_name": "Anchorage", + "iata_city_code": "ANC", + "iata_country_code": "US", + "icao_code": "PANC", + "iata_code": "ANC", + "latitude": 61.175143, + "longitude": -149.99304, + "time_zone": "America/Anchorage", + "name": "Ted Stevens Anchorage International Airport", + "id": "arp_anc_us" + }, + { + "city_name": "Anchorage", + "iata_city_code": "ANC", + "iata_country_code": "US", + "icao_code": "PAED", + "iata_code": "EDF", + "latitude": 61.251878, + "longitude": -149.808422, + "time_zone": "America/Anchorage", + "name": "Elmendorf Air Force Base", + "id": "arp_edf_us" + }, + { + "city_name": "Anchorage", + "iata_city_code": "ANC", + "iata_country_code": "US", + "icao_code": "PAMR", + "iata_code": "MRI", + "latitude": 61.214603, + "longitude": -149.843405, + "time_zone": "America/Anchorage", + "name": "Merrill Field Airport", + "id": "arp_mri_us" + } + ], + "iata_country_code": "US", + "iata_code": "ANC", + "name": "Anchorage", + "id": "cit_anc_us" + }, + { + "airports": [ + { + "city_name": "Ankara", + "iata_city_code": "ANK", + "iata_country_code": "TR", + "icao_code": "LTAD", + "iata_code": "ANK", + "latitude": 39.949798, + "longitude": 32.688598, + "time_zone": "Europe/Istanbul", + "name": "Etimesgut Air Base", + "id": "arp_ank_tr" + }, + { + "city_name": "Ankara", + "iata_city_code": "ANK", + "iata_country_code": "TR", + "icao_code": "LTAC", + "iata_code": "ESB", + "latitude": 40.12627, + "longitude": 32.993385, + "time_zone": "Europe/Istanbul", + "name": "Esenboğa International Airport", + "id": "arp_esb_tr" + } + ], + "iata_country_code": "TR", + "iata_code": "ANK", + "name": "Ankara", + "id": "cit_ank_tr" + }, + { + "airports": [ + { + "city_name": "Apia", + "iata_city_code": "APW", + "iata_country_code": "WS", + "icao_code": "NSFA", + "iata_code": "APW", + "latitude": -13.830224, + "longitude": -172.008052, + "time_zone": "Pacific/Apia", + "name": "Faleolo International Airport", + "id": "arp_apw_ws" + }, + { + "city_name": "Apia", + "iata_city_code": "APW", + "iata_country_code": "WS", + "icao_code": "NSFI", + "iata_code": "FGI", + "latitude": -13.84844, + "longitude": -171.740968, + "time_zone": "Pacific/Apia", + "name": "Fagali'i Airport", + "id": "arp_fgi_ws" + } + ], + "iata_country_code": "WS", + "iata_code": "APW", + "name": "Apia", + "id": "cit_apw_ws" + }, + { + "airports": [ + { + "city_name": "Ardmore", + "iata_city_code": "ADM", + "iata_country_code": "US", + "icao_code": null, + "iata_code": "AHD", + "latitude": 34.146973, + "longitude": -97.122742, + "time_zone": "America/Chicago", + "name": "Ardmore Downtown Executive Airport", + "id": "arp_ahd_us" + }, + { + "city_name": "Ardmore", + "iata_city_code": "ADM", + "iata_country_code": "US", + "icao_code": "KADM", + "iata_code": "ADM", + "latitude": 34.302418, + "longitude": -97.021157, + "time_zone": "America/Chicago", + "name": "Ardmore Municipal Airport", + "id": "arp_adm_us" + } + ], + "iata_country_code": "US", + "iata_code": "ADM", + "name": "Ardmore", + "id": "cit_adm_us" + }, + { + "airports": [ + { + "city_name": "Atlanta", + "iata_city_code": "ATL", + "iata_country_code": "US", + "icao_code": "KATL", + "iata_code": "ATL", + "latitude": 33.638714, + "longitude": -84.4279, + "time_zone": "America/New_York", + "name": "Hartsfield-Jackson Atlanta International Airport", + "id": "arp_atl_us" + }, + { + "city_name": "Atlanta", + "iata_city_code": "ATL", + "iata_country_code": "US", + "icao_code": "KPDK", + "iata_code": "PDK", + "latitude": 33.875583, + "longitude": -84.303619, + "time_zone": "America/New_York", + "name": "Dekalb Peachtree Airport", + "id": "arp_pdk_us" + }, + { + "city_name": "Atlanta", + "iata_city_code": "ATL", + "iata_country_code": "US", + "icao_code": "KFTY", + "iata_code": "FTY", + "latitude": 33.777896, + "longitude": -84.521638, + "time_zone": "America/New_York", + "name": "Fulton County Airport", + "id": "arp_fty_us" + } + ], + "iata_country_code": "US", + "iata_code": "ATL", + "name": "Atlanta", + "id": "cit_atl_us" + }, + { + "airports": [ + { + "city_name": "Auckland", + "iata_city_code": "AKL", + "iata_country_code": "NZ", + "icao_code": "NZAA", + "iata_code": "AKL", + "latitude": -37.008148, + "longitude": 174.789684, + "time_zone": "Pacific/Auckland", + "name": "Auckland Airport", + "id": "arp_akl_nz" + } + ], + "iata_country_code": "NZ", + "iata_code": "AKL", + "name": "Auckland", + "id": "cit_akl_nz" + }, + { + "airports": [ + { + "city_name": "Augusta", + "iata_city_code": "AGS", + "iata_country_code": "US", + "icao_code": "KDNL", + "iata_code": "DNL", + "latitude": 33.466454, + "longitude": -82.038534, + "time_zone": "America/New_York", + "name": "Daniel Field", + "id": "arp_dnl_us" + }, + { + "city_name": "Augusta", + "iata_city_code": "AGS", + "iata_country_code": "US", + "icao_code": "KAGS", + "iata_code": "AGS", + "latitude": 33.370604, + "longitude": -81.96675, + "time_zone": "America/New_York", + "name": "Augusta Regional Airport", + "id": "arp_ags_us" + } + ], + "iata_country_code": "US", + "iata_code": "AGS", + "name": "Augusta", + "id": "cit_ags_us" + }, + { + "airports": [ + { + "city_name": "Austin", + "iata_city_code": "AUS", + "iata_country_code": "US", + "icao_code": "KAUS", + "iata_code": "AUS", + "latitude": 30.196466, + "longitude": -97.667519, + "time_zone": "America/Chicago", + "name": "Austin-Bergstrom International Airport", + "id": "arp_aus_us" + } + ], + "iata_country_code": "US", + "iata_code": "AUS", + "name": "Austin", + "id": "cit_aus_us" + }, + { + "airports": [ + { + "city_name": "Baku", + "iata_city_code": "BAK", + "iata_country_code": "AZ", + "icao_code": "UBTT", + "iata_code": "ZXT", + "latitude": 40.495968, + "longitude": 49.97472, + "time_zone": "Asia/Baku", + "name": "Zabrat Airport", + "id": "arp_zxt_az" + }, + { + "city_name": "Baku", + "iata_city_code": "BAK", + "iata_country_code": "AZ", + "icao_code": "UBBB", + "iata_code": "GYD", + "latitude": 40.466321, + "longitude": 50.049476, + "time_zone": "Asia/Baku", + "name": "Heydar Aliyev International Airport", + "id": "arp_gyd_az" + } + ], + "iata_country_code": "AZ", + "iata_code": "BAK", + "name": "Baku", + "id": "cit_bak_az" + }, + { + "airports": [ + { + "city_name": "Baltimore", + "iata_city_code": "BWI", + "iata_country_code": "US", + "icao_code": "KMTN", + "iata_code": "MTN", + "latitude": 39.326578, + "longitude": -76.41708, + "time_zone": "America/New_York", + "name": "Martin State Airport", + "id": "arp_mtn_us" + }, + { + "city_name": "Baltimore", + "iata_city_code": "BWI", + "iata_country_code": "US", + "icao_code": "KBWI", + "iata_code": "BWI", + "latitude": 39.176402, + "longitude": -76.668345, + "time_zone": "America/New_York", + "name": "Baltimore Washington International Thurgood Marshall Airport", + "id": "arp_bwi_us" + } + ], + "iata_country_code": "US", + "iata_code": "BWI", + "name": "Baltimore", + "id": "cit_bwi_us" + }, + { + "airports": [ + { + "city_name": "Bangkok", + "iata_city_code": "BKK", + "iata_country_code": "TH", + "icao_code": "VTBS", + "iata_code": "BKK", + "latitude": 13.691187, + "longitude": 100.748128, + "time_zone": "Asia/Bangkok", + "name": "Suvarnabhumi Airport", + "id": "arp_bkk_th" + }, + { + "city_name": "Bangkok", + "iata_city_code": "BKK", + "iata_country_code": "TH", + "icao_code": "VTBD", + "iata_code": "DMK", + "latitude": 13.912929, + "longitude": 100.6056, + "time_zone": "Asia/Bangkok", + "name": "Don Mueang International Airport", + "id": "arp_dmk_th" + } + ], + "iata_country_code": "TH", + "iata_code": "BKK", + "name": "Bangkok", + "id": "cit_bkk_th" + }, + { + "airports": [ + { + "city_name": null, + "iata_city_code": "EAP", + "iata_country_code": "FR", + "icao_code": "LFSB", + "iata_code": "BSL", + "latitude": 47.59409, + "longitude": 7.527331, + "time_zone": "Europe/Paris", + "name": "EuroAirport Basel-Mulhouse-Freiburg", + "id": "arp_bsl_ch" + }, + { + "city_name": "Mulhouse/Basel", + "iata_city_code": "EAP", + "iata_country_code": "FR", + "icao_code": "LFSB", + "iata_code": "MLH", + "latitude": 47.59818, + "longitude": 7.525496, + "time_zone": "Europe/Paris", + "name": "EuroAirport Basel Mulhouse Freiburg", + "id": "arp_mlh_fr" + }, + { + "city_name": "Basel", + "iata_city_code": "EAP", + "iata_country_code": "FR", + "icao_code": "LFSB", + "iata_code": "BSL", + "latitude": 47.59409, + "longitude": 7.52733, + "time_zone": "Europe/Paris", + "name": "EuroAirport Basel-Mulhouse-Freiburg", + "id": "arp_bsl_fr" + } + ], + "iata_country_code": "CH", + "iata_code": "EAP", + "name": "Basel/Mulhouse", + "id": "cit_eap_ch" + }, + { + "airports": [ + { + "city_name": "Beaumont", + "iata_city_code": "BPT", + "iata_country_code": "US", + "icao_code": "KBMT", + "iata_code": "BMT", + "latitude": 30.07052, + "longitude": -94.213826, + "time_zone": "America/Chicago", + "name": "Beaumont Municipal Airport", + "id": "arp_bmt_us" + }, + { + "city_name": "Beaumont", + "iata_city_code": "BPT", + "iata_country_code": "US", + "icao_code": "KBPT", + "iata_code": "BPT", + "latitude": 29.95207, + "longitude": -94.021041, + "time_zone": "America/Chicago", + "name": "Jack Brooks Regional Airport", + "id": "arp_bpt_us" + } + ], + "iata_country_code": "US", + "iata_code": "BPT", + "name": "Beaumont/Port Arthur", + "id": "cit_bpt_us" + }, + { + "airports": [ + { + "city_name": "Beijing", + "iata_city_code": "BJS", + "iata_country_code": "CN", + "icao_code": "ZBAD", + "iata_code": "PKX", + "latitude": 39.509945, + "longitude": 116.41092, + "time_zone": "Asia/Shanghai", + "name": "Beijing Daxing International Airport", + "id": "arp_pkx_cn" + }, + { + "city_name": "Beijing", + "iata_city_code": "BJS", + "iata_country_code": "CN", + "icao_code": "ZBAA", + "iata_code": "PEK", + "latitude": 40.080613, + "longitude": 116.602404, + "time_zone": "Asia/Shanghai", + "name": "Beijing Capital International Airport", + "id": "arp_pek_cn" + }, + { + "city_name": "Beijing", + "iata_city_code": "BJS", + "iata_country_code": "CN", + "icao_code": "ZBNY", + "iata_code": "NAY", + "latitude": 39.782699, + "longitude": 116.387926, + "time_zone": "Asia/Shanghai", + "name": "Beijing Nanyuan Airport", + "id": "arp_nay_cn" + } + ], + "iata_country_code": "CN", + "iata_code": "BJS", + "name": "Beijing", + "id": "cit_bjs_cn" + }, + { + "airports": [ + { + "city_name": "Belfast", + "iata_city_code": "BFS", + "iata_country_code": "GB", + "icao_code": "EGAC", + "iata_code": "BHD", + "latitude": 54.617857, + "longitude": -5.872165, + "time_zone": "Europe/London", + "name": "George Best Belfast City Airport", + "id": "arp_bhd_gb" + }, + { + "city_name": "Belfast", + "iata_city_code": "BFS", + "iata_country_code": "GB", + "icao_code": "EGAA", + "iata_code": "BFS", + "latitude": 54.658279, + "longitude": -6.217177, + "time_zone": "Europe/London", + "name": "Belfast International Airport", + "id": "arp_bfs_gb" + } + ], + "iata_country_code": "GB", + "iata_code": "BFS", + "name": "Belfast", + "id": "cit_bfs_gb" + }, + { + "airports": [ + { + "city_name": "Belgrade", + "iata_city_code": "BEG", + "iata_country_code": "RS", + "icao_code": "LYBE", + "iata_code": "BEG", + "latitude": 44.820559, + "longitude": 20.292378, + "time_zone": "Europe/Belgrade", + "name": "Belgrade Nikola Tesla Airport", + "id": "arp_beg_rs" + }, + { + "city_name": "Batajnica", + "iata_city_code": "BEG", + "iata_country_code": "RS", + "icao_code": "LYBT", + "iata_code": "BJY", + "latitude": 44.935079, + "longitude": 20.257138, + "time_zone": "Europe/Belgrade", + "name": "Batajnica Air Base", + "id": "arp_bjy_rs" + } + ], + "iata_country_code": "RS", + "iata_code": "BEG", + "name": "Belgrade", + "id": "cit_beg_rs" + }, + { + "airports": [ + { + "city_name": "Belize City", + "iata_city_code": "BZE", + "iata_country_code": "BZ", + "icao_code": "MZBZ", + "iata_code": "BZE", + "latitude": 17.537595, + "longitude": -88.308539, + "time_zone": "America/Belize", + "name": "Philip S. W. Goldson International Airport", + "id": "arp_bze_bz" + }, + { + "city_name": "Belize City", + "iata_city_code": "BZE", + "iata_country_code": "BZ", + "icao_code": null, + "iata_code": "TZA", + "latitude": 17.516191, + "longitude": -88.194597, + "time_zone": "America/Belize", + "name": "Belize City Municipal Airport", + "id": "arp_tza_bz" + } + ], + "iata_country_code": "BZ", + "iata_code": "BZE", + "name": "Belize City", + "id": "cit_bze_bz" + }, + { + "airports": [ + { + "city_name": "Belo Horizonte", + "iata_city_code": "BHZ", + "iata_country_code": "BR", + "icao_code": "SBCF", + "iata_code": "CNF", + "latitude": -19.634271, + "longitude": -43.964763, + "time_zone": "America/Sao_Paulo", + "name": "Tancredo Neves International Airport", + "id": "arp_cnf_br" + }, + { + "city_name": "Belo Horizonte", + "iata_city_code": "BHZ", + "iata_country_code": "BR", + "icao_code": "SBBH", + "iata_code": "PLU", + "latitude": -19.850524, + "longitude": -43.952745, + "time_zone": "America/Sao_Paulo", + "name": "Pierce County Airport", + "id": "arp_plu_br" + } + ], + "iata_country_code": "BR", + "iata_code": "BHZ", + "name": "Belo Horizonte", + "id": "cit_bhz_br" + }, + { + "airports": [ + { + "city_name": "Berlin", + "iata_city_code": "BER", + "iata_country_code": "DE", + "icao_code": "EDDT", + "iata_code": "TXL", + "latitude": 52.558044, + "longitude": 13.288889, + "time_zone": "Europe/Berlin", + "name": "Berlin Tegel Airport", + "id": "arp_txl_de" + }, + { + "city_name": "Berlin", + "iata_city_code": "BER", + "iata_country_code": "DE", + "icao_code": null, + "iata_code": "BER", + "latitude": 52.362307, + "longitude": 13.504906, + "time_zone": "Europe/Berlin", + "name": "Berlin Brandenburg Airport", + "id": "arp_ber_de" + }, + { + "city_name": "Berlin", + "iata_city_code": "BER", + "iata_country_code": "DE", + "icao_code": "EDDB", + "iata_code": "SXF", + "latitude": 52.381814, + "longitude": 13.522205, + "time_zone": "Europe/Berlin", + "name": "Berlin-Schönefeld Airport", + "id": "arp_sxf_de" + } + ], + "iata_country_code": "DE", + "iata_code": "BER", + "name": "Berlin", + "id": "cit_ber_de" + }, + { + "airports": [ + { + "city_name": "Bethel", + "iata_city_code": "BET", + "iata_country_code": "US", + "icao_code": "PABE", + "iata_code": "JBT", + "latitude": 60.782013, + "longitude": -161.743063, + "time_zone": "America/Anchorage", + "name": "Bethel Seaplane Base", + "id": "arp_jbt_us" + }, + { + "city_name": "Bethel", + "iata_city_code": "BET", + "iata_country_code": "US", + "icao_code": "PABE", + "iata_code": "BET", + "latitude": 60.779761, + "longitude": -161.837887, + "time_zone": "America/Anchorage", + "name": "Bethel Airport", + "id": "arp_bet_us" + } + ], + "iata_country_code": "US", + "iata_code": "BET", + "name": "Bethel", + "id": "cit_bet_us" + }, + { + "airports": [ + { + "city_name": "Blytheville", + "iata_city_code": "BYH", + "iata_country_code": "US", + "icao_code": "KHKA", + "iata_code": "HKA", + "latitude": 35.93796, + "longitude": -89.83193, + "time_zone": "America/Chicago", + "name": "Blytheville Municipal Airport", + "id": "arp_hka_us" + }, + { + "city_name": "Blytheville", + "iata_city_code": "BYH", + "iata_country_code": "US", + "icao_code": "KBYH", + "iata_code": "BYH", + "latitude": 35.962747, + "longitude": -89.94529, + "time_zone": "America/Chicago", + "name": "Arkansas International Airport", + "id": "arp_byh_us" + } + ], + "iata_country_code": "US", + "iata_code": "BYH", + "name": "Blytheville", + "id": "cit_byh_us" + }, + { + "airports": [ + { + "city_name": "Boston", + "iata_city_code": "BOS", + "iata_country_code": "US", + "icao_code": null, + "iata_code": "BNH", + "latitude": 42.352509, + "longitude": -71.025732, + "time_zone": "America/New_York", + "name": "Boston Harbor Seaplane Base", + "id": "arp_bnh_us" + }, + { + "city_name": "Boston", + "iata_city_code": "BOS", + "iata_country_code": "US", + "icao_code": "KBOS", + "iata_code": "BOS", + "latitude": 42.364956, + "longitude": -71.007381, + "time_zone": "America/New_York", + "name": "General Edward Lawrence Logan International Airport", + "id": "arp_bos_us" + }, + { + "city_name": "Portsmouth", + "iata_city_code": "BOS", + "iata_country_code": "US", + "icao_code": "KPSM", + "iata_code": "PSM", + "latitude": 43.079053, + "longitude": -70.822681, + "time_zone": "America/New_York", + "name": "Portsmouth International Airport", + "id": "arp_psm_us" + } + ], + "iata_country_code": "US", + "iata_code": "BOS", + "name": "Boston", + "id": "cit_bos_us" + }, + { + "airports": [ + { + "city_name": "Brunswick", + "iata_city_code": "SSI", + "iata_country_code": "US", + "icao_code": "KSSI", + "iata_code": "SSI", + "latitude": 31.152689, + "longitude": -81.390179, + "time_zone": "America/New_York", + "name": "Malcolm McKinnon Airport", + "id": "arp_ssi_us" + }, + { + "city_name": "Brunswick", + "iata_city_code": "SSI", + "iata_country_code": "US", + "icao_code": "KBQK", + "iata_code": "BQK", + "latitude": 31.256802, + "longitude": -81.466759, + "time_zone": "America/New_York", + "name": "Brunswick Golden Isles Airport", + "id": "arp_bqk_us" + } + ], + "iata_country_code": "US", + "iata_code": "SSI", + "name": "Brunswick", + "id": "cit_ssi_us" + }, + { + "airports": [ + { + "city_name": "Brussels", + "iata_city_code": "BRU", + "iata_country_code": "BE", + "icao_code": "EBBR", + "iata_code": "BRU", + "latitude": 50.90042, + "longitude": 4.484052, + "time_zone": "Europe/Brussels", + "name": "Brussels Airport", + "id": "arp_bru_be" + }, + { + "city_name": "Brussels", + "iata_city_code": "BRU", + "iata_country_code": "BE", + "icao_code": "EBCI", + "iata_code": "CRL", + "latitude": 50.46129, + "longitude": 4.457859, + "time_zone": "Europe/Brussels", + "name": "Brussels South Charleroi Airport", + "id": "arp_crl_be" + } + ], + "iata_country_code": "BE", + "iata_code": "BRU", + "name": "Brussels", + "id": "cit_bru_be" + }, + { + "airports": [ + { + "city_name": "Bucharest", + "iata_city_code": "BUH", + "iata_country_code": "RO", + "icao_code": "LROP", + "iata_code": "OTP", + "latitude": 44.57092, + "longitude": 26.084706, + "time_zone": "Europe/Bucharest", + "name": "Henri Coandă International Airport", + "id": "arp_otp_ro" + }, + { + "city_name": "Bucharest", + "iata_city_code": "BUH", + "iata_country_code": "RO", + "icao_code": "LRBS", + "iata_code": "BBU", + "latitude": 44.502897, + "longitude": 26.101912, + "time_zone": "Europe/Bucharest", + "name": "Băneasa International Airport", + "id": "arp_bbu_ro" + } + ], + "iata_country_code": "RO", + "iata_code": "BUH", + "name": "Bucharest", + "id": "cit_buh_ro" + }, + { + "airports": [ + { + "city_name": "Buenos Aires", + "iata_city_code": "BUE", + "iata_country_code": "AR", + "icao_code": "SABE", + "iata_code": "AEP", + "latitude": -34.558615, + "longitude": -58.416304, + "time_zone": "America/Argentina/Buenos_Aires", + "name": "Jorge Newbery Airfield", + "id": "arp_aep_ar" + }, + { + "city_name": "Buenos Aires", + "iata_city_code": "BUE", + "iata_country_code": "AR", + "icao_code": "SAEZ", + "iata_code": "EZE", + "latitude": -34.818602, + "longitude": -58.535314, + "time_zone": "America/Argentina/Buenos_Aires", + "name": "Ministro Pistarini International Airport", + "id": "arp_eze_ar" + } + ], + "iata_country_code": "AR", + "iata_code": "BUE", + "name": "Buenos Aires", + "id": "cit_bue_ar" + }, + { + "airports": [ + { + "city_name": "Cairo", + "iata_city_code": "CAI", + "iata_country_code": "GP", + "icao_code": null, + "iata_code": "CCE", + "latitude": 18.100423, + "longitude": -63.049779, + "time_zone": "America/Marigot", + "name": "Grand Case-Espérance Airport", + "id": "arp_cce_gp" + }, + { + "city_name": null, + "iata_city_code": "CAI", + "iata_country_code": "GP", + "icao_code": null, + "iata_code": "CCE", + "latitude": 18.100423, + "longitude": -63.049779, + "time_zone": "America/Marigot", + "name": "Grand Case-Espérance Airport", + "id": "arp_cce_eg" + }, + { + "city_name": "Cairo", + "iata_city_code": "CAI", + "iata_country_code": "EG", + "icao_code": "HECA", + "iata_code": "CAI", + "latitude": 30.121954, + "longitude": 31.40751, + "time_zone": "Africa/Cairo", + "name": "Cairo International Airport", + "id": "arp_cai_eg" + } + ], + "iata_country_code": "EG", + "iata_code": "CAI", + "name": "Cairo", + "id": "cit_cai_eg" + }, + { + "airports": [ + { + "city_name": "Campbell River", + "iata_city_code": "YBL", + "iata_country_code": "CA", + "icao_code": "CYBL", + "iata_code": "YBL", + "latitude": 49.951375, + "longitude": -125.270212, + "time_zone": "America/Vancouver", + "name": "Campbell River Airport", + "id": "arp_ybl_ca" + }, + { + "city_name": "Campbell River", + "iata_city_code": "YBL", + "iata_country_code": "CA", + "icao_code": null, + "iata_code": "YHH", + "latitude": 49.949302, + "longitude": -125.269567, + "time_zone": "America/Vancouver", + "name": "Campbell River Harbour Airport", + "id": "arp_yhh_ca" + } + ], + "iata_country_code": "CA", + "iata_code": "YBL", + "name": "Campbell River", + "id": "cit_ybl_ca" + }, + { + "airports": [ + { + "city_name": "Cannes", + "iata_city_code": "CEQ", + "iata_country_code": "FR", + "icao_code": "LFMD", + "iata_code": "CEQ", + "latitude": 43.544633, + "longitude": 6.953192, + "time_zone": "Europe/Paris", + "name": "Cannes – Mandelieu Airport", + "id": "arp_ceq_fr" + } + ], + "iata_country_code": "FR", + "iata_code": "CEQ", + "name": "Cannes", + "id": "cit_ceq_fr" + }, + { + "airports": [ + { + "city_name": "Casablanca", + "iata_city_code": "CAS", + "iata_country_code": "MA", + "icao_code": "GMMN", + "iata_code": "CMN", + "latitude": 33.366389, + "longitude": -7.587202, + "time_zone": "Africa/Casablanca", + "name": "Mohammed V International Airport", + "id": "arp_cmn_ma" + }, + { + "city_name": "Casablanca", + "iata_city_code": "CAS", + "iata_country_code": "MA", + "icao_code": "GMMC", + "iata_code": "CAS", + "latitude": 33.554704, + "longitude": -7.661528, + "time_zone": "Africa/Casablanca", + "name": "Anfa Airport", + "id": "arp_cas_ma" + } + ], + "iata_country_code": "MA", + "iata_code": "CAS", + "name": "Casablanca", + "id": "cit_cas_ma" + }, + { + "airports": [ + { + "city_name": "Charlotte", + "iata_city_code": "CLT", + "iata_country_code": "US", + "icao_code": "KCLT", + "iata_code": "CLT", + "latitude": 35.214201, + "longitude": -80.945207, + "time_zone": "America/New_York", + "name": "Charlotte Douglas International Airport", + "id": "arp_clt_us" + }, + { + "city_name": "Charlotte", + "iata_city_code": "CLT", + "iata_country_code": "US", + "icao_code": null, + "iata_code": "QWG", + "latitude": 35.212095, + "longitude": -80.669294, + "time_zone": "America/New_York", + "name": "Wilgrove Air Park", + "id": "arp_qwg_us" + } + ], + "iata_country_code": "US", + "iata_code": "CLT", + "name": "Charlotte", + "id": "cit_clt_us" + }, + { + "airports": [ + { + "city_name": "Cheyenne", + "iata_city_code": "CYS", + "iata_country_code": "US", + "icao_code": "KCYS", + "iata_code": "CYS", + "latitude": 41.154498, + "longitude": -104.81374, + "time_zone": "America/Denver", + "name": "Cheyenne Regional Airport", + "id": "arp_cys_us" + } + ], + "iata_country_code": "US", + "iata_code": "CYS", + "name": "Cheyenne", + "id": "cit_cys_us" + }, + { + "airports": [ + { + "city_name": "Chicago", + "iata_city_code": "CHI", + "iata_country_code": "US", + "icao_code": "KORD", + "iata_code": "ORD", + "latitude": 41.976381, + "longitude": -87.90606, + "time_zone": "America/Chicago", + "name": "O'Hare International Airport", + "id": "arp_ord_us" + }, + { + "city_name": "Chicago", + "iata_city_code": "CHI", + "iata_country_code": "US", + "icao_code": "KMDW", + "iata_code": "MDW", + "latitude": 41.786336, + "longitude": -87.750029, + "time_zone": "America/Chicago", + "name": "Chicago Midway International Airport", + "id": "arp_mdw_us" + }, + { + "city_name": "Chicago", + "iata_city_code": "CHI", + "iata_country_code": "US", + "icao_code": "KRFD", + "iata_code": "RFD", + "latitude": 42.19732, + "longitude": -89.097969, + "time_zone": "America/Chicago", + "name": "Chicago Rockford International Airport", + "id": "arp_rfd_us" + }, + { + "city_name": "Chicago", + "iata_city_code": "CHI", + "iata_country_code": "US", + "icao_code": "KPWK", + "iata_code": "PWK", + "latitude": 42.11582, + "longitude": -87.901879, + "time_zone": "America/Chicago", + "name": "Chicago Executive Airport", + "id": "arp_pwk_us" + }, + { + "city_name": "Chicago", + "iata_city_code": "CHI", + "iata_country_code": "US", + "icao_code": "KDPA", + "iata_code": "DPA", + "latitude": 41.907279, + "longitude": -88.25084, + "time_zone": "America/Chicago", + "name": "DuPage Airport", + "id": "arp_dpa_us" + } + ], + "iata_country_code": "US", + "iata_code": "CHI", + "name": "Chicago", + "id": "cit_chi_us" + }, + { + "airports": [ + { + "city_name": "Chignik Lake", + "iata_city_code": "KCL", + "iata_country_code": "US", + "icao_code": null, + "iata_code": "KCQ", + "latitude": 56.254971, + "longitude": -158.772529, + "time_zone": "America/Anchorage", + "name": "Chignik Lake Airport", + "id": "arp_kcq_us" + }, + { + "city_name": "Chignik", + "iata_city_code": "KCL", + "iata_country_code": "US", + "icao_code": "PAJC", + "iata_code": "KCG", + "latitude": 56.310832, + "longitude": -158.373611, + "time_zone": "America/Anchorage", + "name": "Chignik Fisheries Airport", + "id": "arp_kcg_us" + }, + { + "city_name": "Chignik", + "iata_city_code": "KCL", + "iata_country_code": "US", + "icao_code": "PAJC", + "iata_code": "KCL", + "latitude": 56.311175, + "longitude": -158.535949, + "time_zone": "America/Anchorage", + "name": "Chignik Lagoon Airport", + "id": "arp_kcl_us" + }, + { + "city_name": "Chignik", + "iata_city_code": "KCL", + "iata_country_code": "US", + "icao_code": null, + "iata_code": "KBW", + "latitude": 56.295608, + "longitude": -158.40132, + "time_zone": "America/Anchorage", + "name": "Chignik Bay Airport", + "id": "arp_kbw_us" + } + ], + "iata_country_code": "US", + "iata_code": "KCL", + "name": "Chignik", + "id": "cit_kcl_us" + }, + { + "airports": [ + { + "city_name": "Cincinnati", + "iata_city_code": "CVG", + "iata_country_code": "US", + "icao_code": "KCVG", + "iata_code": "CVG", + "latitude": 39.051038, + "longitude": -84.665409, + "time_zone": "America/New_York", + "name": "Cincinnati/Northern Kentucky International Airport", + "id": "arp_cvg_us" + }, + { + "city_name": "Cincinnati", + "iata_city_code": "CVG", + "iata_country_code": "US", + "icao_code": "KLUK", + "iata_code": "LUK", + "latitude": 39.103433, + "longitude": -84.421367, + "time_zone": "America/New_York", + "name": "Cincinnati Municipal Airport", + "id": "arp_luk_us" + } + ], + "iata_country_code": "US", + "iata_code": "CVG", + "name": "Cincinnati", + "id": "cit_cvg_us" + }, + { + "airports": [ + { + "city_name": "Cleveland", + "iata_city_code": "CLE", + "iata_country_code": "US", + "icao_code": "KBKL", + "iata_code": "BKL", + "latitude": 41.514519, + "longitude": -81.686615, + "time_zone": "America/New_York", + "name": "Cleveland Burke Lakefront Airport", + "id": "arp_bkl_us" + }, + { + "city_name": "Cleveland", + "iata_city_code": "CLE", + "iata_country_code": "US", + "icao_code": "KCLE", + "iata_code": "CLE", + "latitude": 41.410408, + "longitude": -81.850364, + "time_zone": "America/New_York", + "name": "Cleveland Hopkins International Airport", + "id": "arp_cle_us" + }, + { + "city_name": "Cleveland", + "iata_city_code": "CLE", + "iata_country_code": "US", + "icao_code": "KCGF", + "iata_code": "CGF", + "latitude": 41.565789, + "longitude": -81.487719, + "time_zone": "America/New_York", + "name": "Cuyahoga County Airport", + "id": "arp_cgf_us" + } + ], + "iata_country_code": "US", + "iata_code": "CLE", + "name": "Cleveland", + "id": "cit_cle_us" + }, + { + "airports": [ + { + "city_name": "Clinton", + "iata_city_code": "CSM", + "iata_country_code": "US", + "icao_code": "KCSM", + "iata_code": "CSM", + "latitude": 35.347016, + "longitude": -99.198301, + "time_zone": "America/Chicago", + "name": "Clinton Sherman Airport", + "id": "arp_csm_us" + }, + { + "city_name": "Clinton", + "iata_city_code": "CSM", + "iata_country_code": "US", + "icao_code": "KCLK", + "iata_code": "CLK", + "latitude": 35.538317, + "longitude": -98.932739, + "time_zone": "America/Chicago", + "name": "Clinton Regional Airport", + "id": "arp_clk_us" + } + ], + "iata_country_code": "US", + "iata_code": "CSM", + "name": "Clinton", + "id": "cit_csm_us" + }, + { + "airports": [ + { + "city_name": "Clovis", + "iata_city_code": "CVN", + "iata_country_code": "US", + "icao_code": "KCVS", + "iata_code": "CVS", + "latitude": 34.385129, + "longitude": -103.320767, + "time_zone": "America/Denver", + "name": "Cannon Air Force Base", + "id": "arp_cvs_us" + }, + { + "city_name": "Clovis", + "iata_city_code": "CVN", + "iata_country_code": "US", + "icao_code": "KCVN", + "iata_code": "CVN", + "latitude": 34.425868, + "longitude": -103.078707, + "time_zone": "America/Denver", + "name": "Clovis Municipal Airport", + "id": "arp_cvn_us" + } + ], + "iata_country_code": "US", + "iata_code": "CVN", + "name": "Clovis", + "id": "cit_cvn_us" + }, + { + "airports": [ + { + "city_name": "Colombo", + "iata_city_code": "CMB", + "iata_country_code": "LK", + "icao_code": "VCBI", + "iata_code": "CMB", + "latitude": 7.180457, + "longitude": 79.884176, + "time_zone": "Asia/Colombo", + "name": "Bandaranaike International Airport", + "id": "arp_cmb_lk" + }, + { + "city_name": "Colombo", + "iata_city_code": "CMB", + "iata_country_code": "LK", + "icao_code": "VCCC", + "iata_code": "RML", + "latitude": 6.822141, + "longitude": 79.88773, + "time_zone": "Asia/Colombo", + "name": "Ratmalana Airport", + "id": "arp_rml_lk" + }, + { + "city_name": "Colombo", + "iata_city_code": "CMB", + "iata_country_code": "LK", + "icao_code": null, + "iata_code": "BYV", + "latitude": 6.92883, + "longitude": 79.854596, + "time_zone": "Asia/Colombo", + "name": "Beira Lake Seaplane Base Airport", + "id": "arp_byv_lk" + }, + { + "city_name": "Colombo", + "iata_city_code": "CMB", + "iata_country_code": "LK", + "icao_code": null, + "iata_code": "KEZ", + "latitude": 6.85, + "longitude": 80.55, + "time_zone": "Asia/Colombo", + "name": "Kelaniya River Airport", + "id": "arp_kez_lk" + }, + { + "city_name": "Colombo", + "iata_city_code": "CMB", + "iata_country_code": "LK", + "icao_code": null, + "iata_code": "DGM", + "latitude": 7.1079, + "longitude": 79.8721, + "time_zone": "Asia/Colombo", + "name": "Dandugama Seaplane Base", + "id": "arp_dgm_lk" + } + ], + "iata_country_code": "LK", + "iata_code": "CMB", + "name": "Colombo", + "id": "cit_cmb_lk" + }, + { + "airports": [ + { + "city_name": "Colorado Springs", + "iata_city_code": "COS", + "iata_country_code": "US", + "icao_code": "KAFF", + "iata_code": "AFF", + "latitude": 38.970442, + "longitude": -104.814704, + "time_zone": "America/Denver", + "name": "United States Air Force Academy Airfield", + "id": "arp_aff_us" + }, + { + "city_name": "Colorado Springs", + "iata_city_code": "COS", + "iata_country_code": "US", + "icao_code": "KCOS", + "iata_code": "COS", + "latitude": 38.803756, + "longitude": -104.701799, + "time_zone": "America/Denver", + "name": "Colorado Springs Airport", + "id": "arp_cos_us" + }, + { + "city_name": "Colorado Springs", + "iata_city_code": "COS", + "iata_country_code": "US", + "icao_code": "KFCS", + "iata_code": "FCS", + "latitude": 38.678398, + "longitude": -104.757004, + "time_zone": "America/Denver", + "name": "Butts Army Airfield", + "id": "arp_fcs_us" + } + ], + "iata_country_code": "US", + "iata_code": "COS", + "name": "Colorado Springs", + "id": "cit_cos_us" + } + ] +} diff --git a/spec/fixtures/cities/show.json b/spec/fixtures/cities/show.json new file mode 100644 index 0000000..5467f30 --- /dev/null +++ b/spec/fixtures/cities/show.json @@ -0,0 +1,94 @@ +{ + "data": { + "airports": [ + { + "city_name": "London", + "icao_code": "EGLL", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "LHR", + "latitude": 51.470311, + "longitude": -0.458118, + "time_zone": "Europe/London", + "name": "Heathrow Airport", + "id": "arp_lhr_gb" + }, + { + "city_name": "London", + "icao_code": "EGKB", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "BQH", + "latitude": 51.331399, + "longitude": 0.030782, + "time_zone": "Europe/London", + "name": "London Biggin Hill Airport", + "id": "arp_bqh_gb" + }, + { + "city_name": "London", + "icao_code": "EGGW", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "LTN", + "latitude": 51.875482, + "longitude": -0.37004, + "time_zone": "Europe/London", + "name": "London Luton Airport", + "id": "arp_ltn_gb" + }, + { + "city_name": "London", + "icao_code": "EGLC", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "LCY", + "latitude": 51.505071, + "longitude": 0.052398, + "time_zone": "Europe/London", + "name": "London City Airport", + "id": "arp_lcy_gb" + }, + { + "city_name": "London", + "icao_code": "EGMC", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "SEN", + "latitude": 51.571075, + "longitude": 0.697117, + "time_zone": "Europe/London", + "name": "London Southend Airport", + "id": "arp_sen_gb" + }, + { + "city_name": "London", + "icao_code": "EGKK", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "LGW", + "latitude": 51.150882, + "longitude": -0.18617, + "time_zone": "Europe/London", + "name": "Gatwick Airport", + "id": "arp_lgw_gb" + }, + { + "city_name": "London", + "icao_code": "EGSS", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "STN", + "latitude": 51.885508, + "longitude": 0.236933, + "time_zone": "Europe/London", + "name": "London Stansted Airport", + "id": "arp_stn_gb" + } + ], + "iata_country_code": "GB", + "iata_code": "LON", + "name": "London", + "id": "cit_lon_gb" + } +} From f0d3aed34277e47996da5910ac8b6d9ca68dded8 Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 12 Jul 2024 16:58:04 +0100 Subject: [PATCH 12/13] Support rendering embedded City resources --- lib/duffel_api/resources/airport.rb | 4 +-- .../services/airports_service_spec.rb | 20 +++++++------- spec/fixtures/airports/show.json | 26 ++++++++++++------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/duffel_api/resources/airport.rb b/lib/duffel_api/resources/airport.rb index 28c6873..5589cf5 100644 --- a/lib/duffel_api/resources/airport.rb +++ b/lib/duffel_api/resources/airport.rb @@ -4,7 +4,7 @@ module DuffelAPI module Resources class Airport < BaseResource - # @return [Hash, nil] + # @return [DuffelAPI::Resources::City, nil] attr_reader :city # @return [String] @@ -37,7 +37,7 @@ class Airport < BaseResource def initialize(object, response = nil) @object = object - @city = object["city"] + @city = object["city"].nil? ? object["city"] : City.new(object["city"]) @city_name = object["city_name"] @iata_code = object["iata_code"] @iata_country_code = object["iata_country_code"] diff --git a/spec/duffel_api/services/airports_service_spec.rb b/spec/duffel_api/services/airports_service_spec.rb index ca4ef1b..89b4834 100644 --- a/spec/duffel_api/services/airports_service_spec.rb +++ b/spec/duffel_api/services/airports_service_spec.rb @@ -197,16 +197,16 @@ it "returns an Airport resource" do expect(get_response).to be_a(DuffelAPI::Resources::Airport) - expect(get_response.city).to be_nil - expect(get_response.city_name).to eq("Mogadishu") - expect(get_response.iata_code).to eq("MGQ") - expect(get_response.iata_country_code).to eq("SO") - expect(get_response.icao_code).to eq("HCMM") - expect(get_response.id).to eq("arp_mgq_so") - expect(get_response.latitude).to eq(2.015817) - expect(get_response.longitude).to eq(45.304832) - expect(get_response.name).to eq("Aden Adde International Airport") - expect(get_response.time_zone).to eq("Africa/Mogadishu") + expect(get_response.city).to be_a(DuffelAPI::Resources::City) + expect(get_response.city_name).to eq("London") + expect(get_response.iata_code).to eq("LHR") + expect(get_response.iata_country_code).to eq("GB") + expect(get_response.icao_code).to eq("EGLL") + expect(get_response.id).to eq("arp_lhr_gb") + expect(get_response.latitude).to eq(51.470311) + expect(get_response.longitude).to eq(-0.458118) + expect(get_response.name).to eq("Heathrow Airport") + expect(get_response.time_zone).to eq("Europe/London") end it "exposes the API response" do diff --git a/spec/fixtures/airports/show.json b/spec/fixtures/airports/show.json index 04b8ce8..26dfe22 100644 --- a/spec/fixtures/airports/show.json +++ b/spec/fixtures/airports/show.json @@ -1,14 +1,20 @@ { "data": { - "time_zone": "Africa/Mogadishu", - "name": "Aden Adde International Airport", - "longitude": 45.304832, - "latitude": 2.015817, - "id": "arp_mgq_so", - "icao_code": "HCMM", - "iata_country_code": "SO", - "iata_code": "MGQ", - "city_name": "Mogadishu", - "city": null + "city_name": "London", + "icao_code": "EGLL", + "iata_city_code": "LON", + "iata_country_code": "GB", + "iata_code": "LHR", + "latitude": 51.470311, + "longitude": -0.458118, + "city": { + "iata_country_code": "GB", + "iata_code": "LON", + "name": "London", + "id": "cit_lon_gb" + }, + "time_zone": "Europe/London", + "name": "Heathrow Airport", + "id": "arp_lhr_gb" } } From 9ca56c8c2cca5bcdec5e6b8470cedabab1e56a9d Mon Sep 17 00:00:00 2001 From: Sasha Gerrand Date: Fri, 13 Sep 2024 15:46:31 +0100 Subject: [PATCH 13/13] Satisfy the RuboCop :cop: - Style/SuperArguments: Call super without arguments and parentheses when the signature is identical. - Layout/EmptyLinesAroundBlockBody: Extra empty line detected at block body end. (https://rubystyle.guide#empty-lines-around-bodies) --- lib/duffel_api/resources/city.rb | 2 +- spec/duffel_api/services/cities_service_spec.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/duffel_api/resources/city.rb b/lib/duffel_api/resources/city.rb index 73e9234..fc95633 100644 --- a/lib/duffel_api/resources/city.rb +++ b/lib/duffel_api/resources/city.rb @@ -24,7 +24,7 @@ def initialize(object, response = nil) @id = object["id"] @name = object["name"] - super(object, response) + super end end end diff --git a/spec/duffel_api/services/cities_service_spec.rb b/spec/duffel_api/services/cities_service_spec.rb index f9891ab..c33f04d 100644 --- a/spec/duffel_api/services/cities_service_spec.rb +++ b/spec/duffel_api/services/cities_service_spec.rb @@ -188,7 +188,6 @@ expect(get_response.iata_country_code).to eq("GB") expect(get_response.id).to eq("cit_lon_gb") expect(get_response.name).to eq("London") - end it "exposes the API response" do