How To Build A Web App, part 19 of ?: API Act ID filtering working at last

Continuing in the tradition of “web-dev is quite possibly the least photogenic spectator sport in the galaxy”, I searched for “acts” instead and this came up.
# spec/requests/search_spec.rb...describe 'Filtering by Act' do
include_examples 'general jsonapi behaviour for', Venue
context 'Not supplying any act IDs' do

let(:params) {
{ filter: { acts: '' } }
}
it 'returns every single venue, ordered by updated_at desc' do
resources = [venue4, venue2, venue3, venue5, venue1]
.map do |venue|
Api::V1::VenueResource.new(venue, nil)
end
serialized_resources = JSONAPI::ResourceSerializer
.new(Api::V1::VenueResource)
.serialize_to_hash(resources)
expect(response_json).to eq serialized_resources
end
end
...end
let(:params) { {} }
let(:params) {
{ filter: { acts: '3,4,5' } }
}
resources = [venue4, venue2, venue3, venue5, venue1].map do |venue|
Api::V1::VenueResource.new(venue, nil)
end
serialized_resources = JSONAPI::ResourceSerializer
.new(Api::V1::VenueResource)
.serialize_to_hash(resources)
expect(response_json).to eq serialized_resources
$ rspec spec/requests/search:spec.rb:58
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[58]}}
self_link for Api::V1::GigResource could not be generated
self_link for Api::V1::GigResource.venue(BelongsToOne) could not be generated
self_link for Api::V1::GigResource.act(BelongsToOne) could not be generated
F
Failures:

1) Search Filtering by Act Not supplying any act IDs returns every single venue, ordered by updated_at desc
Failure/Error: expect(response_json).to eq serialized_resources
expected: {"data"=>[something far too bloody big to fit here]}
got: {"data"=>[something else that is also far too bloody big to fit here]}
(compared using ==) Diff:
...
+"links" => {"..."}
expect(response_json).to eq serialized_resources
expect(response_json['data']).to eq serialized_resources['data']
$ rspec spec/requests/search_spec.rb:58
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[58]}}
FFailures: 1) Search Filtering by Act not supplying any act IDs returns eery single venue, ordered by updated_at desc
Failure/Error: expect(response_json['data']).to eq serialized_resources['data']
expected: nil
got: [{"attributes"=>{"created-at"=>[blargh] }]
(compared using ==)
$ rspec spec/requests/search_spec.rb:58
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[58]}}
[74, 83] in /Users/michaelclarke/Work/gigs/spec/requests/search_spec.rb
74: serialized_resources = JSONAPI::ResourcesSerializer
75: .new(Api::V1::VenueResource)
76: .serialize_to_hash(resources)
77: byebug
78: expect(response_json['data']).to eq serialized_resources['data']
79: end
80: end
81:
82: context "Acts 1, 2, 5" do
(byebug) pp serialized_resources
{:data=>
[{"id"=>"4",
"type"=>"venues",
"links"=>"{"self"=>"/api/v1/venues/4"},
"attributes"->
{"created-at"=>"2020-02-29T21:04:21.406Z",
"updated-at"=>"2020-05-23T08:44:53.052Z",
"name"=>TT Eatery"},
"relationships"=>
{"gigs"=>
{"links"=>
{"self"=>"/api/v1/venues/4/relationships/gigs",
"related"=>"/api/v1/venues/4/gigs",
:data=>[{:type=>"gigs", :id=>"10"}, {:type=>"gigs", :id=>"11"}]}}},
{"id"="2",
...
it "returns every single venue, ordered by updated_at desc" do
resources = [venue4, venue2, venue3, venue5, venue1].map do |venue|
Api::V1::VenueResource.new(venue, nil)
end
serialized_resources = JSONAPI::ResourceSerializer
.new(Api::V1::VenueResource)
.serialize_to_hash(resources)
.deep_serialize_keys!
expect(response_json['data']).to eq serialized_resources['data']
end
$ rspec spec/requests/search_spec.rb:79
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[79]}}
.
Finished in 0.61068 seconds (files took 2.88 seconds to load)
1 example, 0 failures

Resume testing Act ID filtering

context "Acts 1, 2, 5" do  let(:params) {
{ filter: { acts: [act1.id, act2.id, act5.id].join(',') } }
}
describe "returning only venues 4, 2, 3: updated_at desc" do subject {
vr_4_2_3 = [venue4, venue2, venue3].map do |venue|
Api::V1::VenueResource.new(venue, nil)
end

JSONAPI::ResourceSerializer.new(Api::V1::VenueResource)
.serialize_to_hash(vr_4_2_3)
.deep_stringify_keys!['data']
}
it { is_expected.to eq response_json['data'] }
end
...end
$ rspec spec/requests/search_spec.rb:101
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[101]}}
F
Failures:
1) Search Filtering by Act Acts 1, 2, 5 returning only venues 4, 2, 3: updated_at desc should eq {"errors"=>[{"code"=>"500", "detail"=>"Internal Server Error", "meta"=>{"backtrace"=>["/Users/michael...ues\".\"updated_at\" DESC LIMIT $1 OFFSET $2"}, "status"=>"500", "title"=>"Internal Server Error"}]}
Failure/Error: it { is_expected.to eq response_json }
[ginormous JSON stack trace]
PG::UndefinedTable: ERROR: missing FROM-clause entry for table \"acts\"\nLINE 1: SELECT \"venues\".* FROM \"venues\" WHERE (acts.id IN (1,2,5)) ...\n ^\n: SELECT \"venues\".* from \"venues\" WHERE (acts.id IN (1,2,5)) ORDER BY \"venues\".\"updated_at\" DESC LIMIT $1 OFFSET $2

ActiveRecord

# app/resources/api/v1/venue_resource.rb...filter :acts,
verify: -> (values, context) {
values.map &:to_i
},
apply: -> (records, value, _options) {
q = records.includes(gigs: :act)
q = q.where('acts IN (:acts)', acts: value) if value.length > 0
q
}
SELECT venues.* FROM venues WHERE (acts.id IN (1,2,5)) ORDER BY venues.updated_at DESC LIMIT $1 OFFSET $2;
SELECT * FROM venues
INNER JOIN gigs ON gigs.venue_id = venues.id
INNER JOIN acts ON acts.id = gigs.act_id

Quick tangent: technical debt

Tangent over; return to Act ID testing

# app/resources/api/v1/venue_resource.rb...q = records.includes(gigs: :act)
# app/resources/api/v1/venue_resource.rb...q = records.includes(gigs: :act).joins(gigs: :act)
$ rspec spec/requests/search_spec.rb:101
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[101]}}
F
Failures: 1) Search Filtering by Act Acts 1, 2, 5 returning only venues 4, 2, 3: updated_at desc should eq [{"attributes"=>{"created-at"=>"2020-03-13T17:41:05.251Z", "name"=>"Orange Pizza", "updated-at"=>"202...ated"=>"/api/v1/venues/3/gigs", "self"=>"/api/v1/venues/3/relationships/gigs"}}}, "type"=>"venues"}]
Failure/Error: it { is_expected.to eq response_json['data'] }
expected: [{"attributes"=>{"created-at"=>"2020-03-13T17:41:05.251Z", "name"=>"Orange Pizza", "updated-at"=>"202...ated"=>"/api/v1/venues/3/gigs", "self"=>"/api/v1/venues/3/relationships/gigs"}}}, "type"=>"venues"}]
got: [{"attributes"=>{"created-at"=>"2020-03-13T17:41:05.251Z", "name"=>"Orange Pizza", "updated-at"=>"202...ated"=>"/api/v1/venues/3/gigs", "self"=>"/api/v1/venues/3/relationships/gigs"}}}, "type"=>"venues"}]
(compared using ==)Diff:
@@ -6,7 +6,7 @@
"links"=>{"self"=>"/api/v1/venues/4"},
"relationships"=>
{"gigs"=>
- {"data"=>[{"id"=>"11", "type"=>"gigs"}],
+ {"data"=>[{"id"=>"10", "type"=>"gigs"}, {"id"=>"11", "type"=>"gigs"}],
"links"=>
{"related"=>"/api/v1/venues/4/gigs",
"self"=>"/api/v1/venues/4/relationships/gigs"}}},
@@ -32,7 +32,14 @@
"links"=>{"self"=>"/api/v1/venues/3"},
"relationships"=>
{"gigs"=>
- {"data"=>[{"id"=>"3", "type"=>"gigs"}],
+ {"data"=>
+ [{"id"=>"3", "type"=>"gigs"},
+ {"id"=>"4", "type"=>"gigs"},
+ {"id"=>"5", "type"=>"gigs"},
+ {"id"=>"6", "type"=>"gigs"},
+ {"id"=>"7", "type"=>"gigs"},
+ {"id"=>"8", "type"=>"gigs"},
+ {"id"=>"9", "type"=>"gigs"}],
"links"=>
{"related"=>"/api/v1/venues/3/gigs",
"self"=>"/api/v1/venues/3/relationships/gigs"}}},
# ./spec/requests/search_spec.rb:101:in `block (5 levels) in <top (required)>'Finished in 0.7243 seconds (files took 3.31 seconds to load)
1 example, 1 failure
Failed examples:rspec ./spec/requests/search_spec.rb:101 # Search Filtering by Act Acts 1, 2, 5 returning only venues 4, 2, 3: updated_at desc should eq [{"attributes"=>{"created-at"=>"2020-03-13T17:41:05.251Z", "name"=>"Orange Pizza", "updated-at"=>"202...ated"=>"/api/v1/venues/3/gigs", "self"=>"/api/v1/venues/3/relationships/gigs"}}}, "type"=>"venues"}]

Isn’t a good night’s sleep wonderful?

subject {
vr_4_2_3 = [venue4, venue2, venue3].map do |venue|
Api::V1::VenueResource.new(venue, nil)
end
JSONAPI::ResourceSerializer.new(Api::V1::VenueResource)
.serialize_to_hash(vr_4_2_3)
.deep_stringify_keys!['data']
.each do |svr| # 'serialised venue resource'
svr['relationships']['gigs']['data'].select! do |sgr|
[1, 2, 5].include? Gig.find(sgr['id']).act.id
end
end
end
}
$ rspec spec/requests/search_spec.rb:107
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[107]}}
.
Finished in 0.5988 seconds (files took 2.99 seconds to load)
1 example, 0 failures
context "Acts 1, 2, 5" do  let(:acts) { [act1, act2, act5] }  let(:params) { { filter: { acts: acts.map(&:id).join(',') } } }  describe "returning only venues 2, 4, 3: updated at desc" do    let(:venueresources_4_2_3) {
[venue4, venue2, venue3].map do |v|
Api::V1::VenueResource.new(v, nil)
end
}
let(:svrs_4_2_3_data) {
JSONAPI::ResourceSerializer.new(Api::V1::VenueResource)
.serialize_to_hash(venueresources_4_2_3)
.deep_stringify_keys!['data']
}
let(:serialized_vr_4_2_3_data_w_gigs_for_acts_1_2_5) {
svrs_4_2_3_data.each do |svr|
svr['relationships']['gigs']['data'].select! do |sgr|
acts.include? Gig.find(sgr['id']).act
end
end
}
it "returns only the venues and gigs attended by Acts 1, 2 and 5" do
expect(serialized_vr_4_2_3_data_w_gigs_for_acts_1_2_5)
.to eq response_json['data']
end
...
end
...
end
$ rspec spec/requests/search_spec.rb:107
Run options: include {:locations=>{"./spec/requests/search_spec.rb"=>[107]}}
.
Finished in 0.91215 seconds (files took 3.19 seconds to load)
1 example, 0 failures

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store