From 035910bc3fb0f04e321218f4045111c8fd0a7c80 Mon Sep 17 00:00:00 2001 From: Matt Langston Date: Wed, 4 Jan 2023 17:33:55 -0600 Subject: [PATCH] Switch back to default Faraday encoder and change list-records call to POST. Fixes #95 --- lib/airrecord/client.rb | 6 +-- lib/airrecord/query_string.rb | 45 ---------------------- lib/airrecord/table.rb | 4 +- test/associations_test.rb | 8 ++-- test/query_string_test.rb | 70 ----------------------------------- test/table_test.rb | 24 ++++++------ test/test_helper.rb | 6 +-- 7 files changed, 22 insertions(+), 141 deletions(-) delete mode 100644 lib/airrecord/query_string.rb delete mode 100644 test/query_string_test.rb diff --git a/lib/airrecord/client.rb b/lib/airrecord/client.rb index f27a08b..12bc864 100644 --- a/lib/airrecord/client.rb +++ b/lib/airrecord/client.rb @@ -1,5 +1,3 @@ -require 'uri' -require_relative 'query_string' require_relative 'faraday_rate_limiter' module Airrecord @@ -21,9 +19,7 @@ def connection headers: { "Authorization" => "Bearer #{api_key}", "User-Agent" => "Airrecord/#{Airrecord::VERSION}", - "X-API-VERSION" => "0.1.0", }, - request: { params_encoder: Airrecord::QueryString } ) do |conn| if Airrecord.throttle? conn.request :airrecord_rate_limiter, requests_per_second: AIRTABLE_RPS_LIMIT @@ -33,7 +29,7 @@ def connection end def escape(*args) - QueryString.escape(*args) + CGI.escape(*args) end def parse(body) diff --git a/lib/airrecord/query_string.rb b/lib/airrecord/query_string.rb deleted file mode 100644 index 5f65c69..0000000 --- a/lib/airrecord/query_string.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'erb' - -module Airrecord - # Airtable expects that arrays in query strings be encoded with indices. - # Faraday follows Rack conventions and encodes arrays _without_ indices. - # - # Airrecord::QueryString is a Faraday-compliant params_encoder that follows - # the Airtable spec. - module QueryString - def self.encode(params) - params.map { |key, val| Encodings[val].call(key, val) }.join('&') - end - - def self.decode(query) - Faraday::NestedParamsEncoder.decode(query) - end - - def self.escape(*query) - query.map { |qs| ERB::Util.url_encode(qs) }.join('') - end - - module Encodings - def self.[](value) - TYPES.fetch(value.class, DEFAULT) - end - - TYPES = { - Array => lambda do |prefix, array| - array.each_with_index.map do |value, index| - self[value].call("#{prefix}[#{index}]", value) - end - end, - Hash => lambda do |prefix, hash| - hash.map do |key, value| - self[value].call("#{prefix}[#{key}]", value) - end - end - }.freeze - - DEFAULT = lambda do |key, value| - "#{QueryString.escape(key)}=#{QueryString.escape(value)}" - end - end - end -end diff --git a/lib/airrecord/table.rb b/lib/airrecord/table.rb index aebf473..9f2e5b2 100644 --- a/lib/airrecord/table.rb +++ b/lib/airrecord/table.rb @@ -76,8 +76,8 @@ def records(filter: nil, sort: nil, view: nil, offset: nil, paginate: true, fiel options[:maxRecords] = max_records if max_records options[:pageSize] = page_size if page_size - path = "/v0/#{base_key}/#{client.escape(table_name)}" - response = client.connection.get(path, options) + path = "/v0/#{base_key}/#{client.escape(table_name)}/listRecords" + response = client.connection.post(path, options.to_json, { 'Content-Type' => 'application/json' }) parsed_response = client.parse(response.body) if response.success? diff --git a/test/associations_test.rb b/test/associations_test.rb index b22a821..bda0757 100644 --- a/test/associations_test.rb +++ b/test/associations_test.rb @@ -41,7 +41,7 @@ def test_has_many_associations { "id" => "rec2", "Name" => "Good brew" }, { "id" => "rec1", "Name" => "Decent brew" } ] - stub_request(brews, table: Brew) + stub_records_request(brews, table: Brew) assert_equal 2, tea.brews.size assert_kind_of Airrecord::Table, tea.brews.first @@ -50,7 +50,7 @@ def test_has_many_associations def test_has_many_handles_empty_associations tea = Tea.new("Name" => "Gunpowder") - stub_request([{ "id" => "brew1", "Name" => "unrelated" }], table: Brew) + stub_records_request([{ "id" => "brew1", "Name" => "unrelated" }], table: Brew) assert_equal 0, tea.brews.size end @@ -82,7 +82,7 @@ def test_build_association_from_strings tea.create - stub_request([{ id: "rec2" }, { id: "rec1" }], table: Brew) + stub_records_request([{ id: "rec2" }, { id: "rec1" }], table: Brew) assert_equal 2, tea.brews.count end @@ -110,7 +110,7 @@ def test_build_has_many_association_from_setter tea.brews = brews brew_fields = brews.map { |brew| brew.fields.merge("id" => brew.id) } - stub_request(brew_fields, table: Brew) + stub_records_request(brew_fields, table: Brew) assert_equal 2, tea.brews.size assert_kind_of Airrecord::Table, tea.brews.first diff --git a/test/query_string_test.rb b/test/query_string_test.rb deleted file mode 100644 index 64ad994..0000000 --- a/test/query_string_test.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'test_helper' - -class QueryStringTest < Minitest::Test - def setup - @params = { maxRecords: 50, view: "Master" } - @query = "maxRecords=3&pageSize=1&sort%5B0%5D%5Bfield%5D=Quality&sort%5B0%5D%5Bdirection%5D=asc" - @qs = Airrecord::QueryString - end - - def test_encoding_simple_params_matches_faraday - expected = Faraday::NestedParamsEncoder.encode(@params) - result = @qs.encode(@params) - - assert_equal(result, expected) - end - - def test_decode_matches_faraday - assert_equal( - Faraday::NestedParamsEncoder.decode(@query), - @qs.decode(@query), - ) - end - - def test_encoding_arrays_uses_indices - params = @params.merge(fields: %w[Quality Price]) - - expected = "maxRecords=50&view=Master&fields%5B0%5D=Quality&fields%5B1%5D=Price" - result = @qs.encode(params) - - assert_equal(result, expected) - end - - def test_encoding_arrays_of_objects - params = { sort: [ - { field: 'Quality', direction: 'desc' }, - { field: 'Price', direction: 'asc' } - ]} - - expected = "sort%5B0%5D%5Bfield%5D=Quality&sort%5B0%5D%5Bdirection%5D=desc&sort%5B1%5D%5Bfield%5D=Price&sort%5B1%5D%5Bdirection%5D=asc" - result = @qs.encode(params) - - assert_equal(result, expected) - end - - def test_params_fuzzing - params = { - "an explicit nil" => nil, - horror: [1, 2, [{ mic: "check" }, { one: "two" }]], - view: "A name with spaces", - } - - expected = { - "an explicit nil" => "", - "horror" => ["1", "2", [{ "mic" => "check" }, { "one" => "two" }]], - "view" => "A name with spaces", - } - result = Faraday::NestedParamsEncoder.decode(@qs.encode(params)) - - assert_equal(result, expected) - end - - def test_escaping_one_string - assert_equal(@qs.escape("test string"), "test%20string") - end - - def test_escaping_many_strings - strings = ['test', 'string'] - assert_equal(@qs.escape(*strings), 'teststring') - end -end diff --git a/test/table_test.rb b/test/table_test.rb index 0c44a61..be19607 100644 --- a/test/table_test.rb +++ b/test/table_test.rb @@ -25,7 +25,7 @@ def setup builder.adapter :test, @stubs } - stub_request([{"Name" => "omg", "Notes" => "hello world"}, {"Name" => "more", "Notes" => "walrus"}]) + stub_records_request([{"Name" => "omg", "Notes" => "hello world"}, {"Name" => "more", "Notes" => "walrus"}]) end def test_table_overrides_key @@ -48,14 +48,14 @@ def test_different_clients_with_different_api_keys end def test_filter_records - stub_request([{"Name" => "yes"}, {"Name" => "no"}]) + stub_records_request([{"Name" => "yes"}, {"Name" => "no"}]) records = @table.records(filter: "Name") assert_equal "yes", records[0]["Name"] end def test_sort_records - stub_request([{"Name" => "a"}, {"Name" => "b"}]) + stub_records_request([{"Name" => "a"}, {"Name" => "b"}]) records = @table.records(sort: { "Name" => 'asc' }) assert_equal "a", records[0]["Name"] @@ -63,7 +63,7 @@ def test_sort_records end def test_view_records - stub_request([{"Name" => "a"}, {"Name" => "a"}]) + stub_records_request([{"Name" => "a"}, {"Name" => "a"}]) records = @table.records(view: 'A') assert_equal "a", records[0]["Name"] @@ -71,18 +71,18 @@ def test_view_records end def test_follow_pagination_by_default - stub_request([{"Name" => "1"}, {"Name" => "2"}], offset: 'dasfuhiu') - stub_request([{"Name" => "3"}, {"Name" => "4"}], offset: 'odjafio', clear: false) - stub_request([{"Name" => "5"}, {"Name" => "6"}], clear: false) + stub_records_request([{"Name" => "1"}, {"Name" => "2"}], offset: 'dasfuhiu') + stub_records_request([{"Name" => "3"}, {"Name" => "4"}], offset: 'odjafio', clear: false) + stub_records_request([{"Name" => "5"}, {"Name" => "6"}], clear: false) records = @table.records assert_equal 6, records.size end def test_dont_follow_pagination_if_disabled - stub_request([{"Name" => "1"}, {"Name" => "2"}], offset: 'dasfuhiu') - stub_request([{"Name" => "3"}, {"Name" => "4"}], offset: 'odjafio', clear: false) - stub_request([{"Name" => "5"}, {"Name" => "6"}], clear: false) + stub_records_request([{"Name" => "1"}, {"Name" => "2"}], offset: 'dasfuhiu') + stub_records_request([{"Name" => "3"}, {"Name" => "4"}], offset: 'odjafio', clear: false) + stub_records_request([{"Name" => "5"}, {"Name" => "6"}], clear: false) records = @table.records(paginate: false) assert_equal 2, records.size @@ -314,7 +314,7 @@ def test_find_many end def test_find_many_makes_no_network_call_when_ids_are_empty - stub_request([], status: 500) + stub_records_request([], status: 500) assert_equal([], @table.find_many([])) end @@ -353,7 +353,7 @@ def test_error_handles_errors_without_body end def test_dates_are_not_type_casted - stub_request([{"Name" => "omg", "Created" => Time.now.to_s}]) + stub_records_request([{"Name" => "omg", "Created" => Time.now.to_s}]) record = first_record assert_instance_of String, record["Created"] diff --git a/test/test_helper.rb b/test/test_helper.rb index 083b872..ab65f75 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -45,7 +45,7 @@ def stub_patch_request(record, updated_keys, table: @table, status: 200, headers end # TODO: Problem, can't stub on params. - def stub_request(records, table: @table, status: 200, headers: {}, offset: nil, clear: true) + def stub_records_request(records, table: @table, status: 200, headers: {}, offset: nil, clear: true) @stubs.instance_variable_set(:@stack, {}) if clear body = { @@ -59,7 +59,7 @@ def stub_request(records, table: @table, status: 200, headers: {}, offset: nil, offset: offset, }.to_json - @stubs.get("/v0/#{table.base_key}/#{table.table_name}") do |env| + @stubs.post("/v0/#{table.base_key}/#{table.table_name}/listRecords") do |env| [status, headers, body] end end @@ -87,7 +87,7 @@ def stub_error_request(type:, message:, status: 401, headers: {}, table: @table) } }.to_json - @stubs.get("/v0/#{table.base_key}/#{table.table_name}") do |env| + @stubs.post("/v0/#{table.base_key}/#{table.table_name}/listRecords") do |env| [status, headers, body] end end