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
Mikey Clarke

Mikey Clarke

533 Followers

Hi there! My snippets and postings here are either zeroth drafts from my larger novels, or web-app tutorials and other computery codey musings.