diff --git a/app/resources/decklist_resource.rb b/app/resources/decklist_resource.rb index 7ff51d34..10a3df1c 100644 --- a/app/resources/decklist_resource.rb +++ b/app/resources/decklist_resource.rb @@ -36,6 +36,15 @@ class DecklistResource < ApplicationResource end end + # Will return decklists that do NOT contain any of the specified cards. + filter :exclude_card_id, :string do + eq do |scope, card_ids| + scope.left_joins(:decklist_cards) + .group('decklists.id') + .having('COUNT(CASE WHEN decklists_cards.card_id IN (?) THEN 1 END) = 0', card_ids) + end + end + attribute :card_slots, :hash attribute :num_cards, :integer attribute :influence_spent, :integer diff --git a/spec/acceptance/decklists_spec.rb b/spec/acceptance/decklists_spec.rb index 6c036dc4..f441c412 100644 --- a/spec/acceptance/decklists_spec.rb +++ b/spec/acceptance/decklists_spec.rb @@ -50,6 +50,21 @@ end end + get '/api/v3/public/decklists?filter[exclude_card_id]=pennyshaver,stargate' do + parameter :exclude_card_id, type: :string, required: true + + example_request 'Filter - Get decklists excluding all supplied Card ids' do + explanation <<~EXPLANATION + The exclude_card_id filter can accept a single card_id or a comma-separated list of card ids. + + If multiple card ids are supplied, the decklist must NOT contain any of the + cards to be included in the results. + EXPLANATION + + expect(status).to eq 200 + end + end + get '/api/v3/public/decklists?filter[faction_id]=:faction_id' do parameter :nsg_rules_team_verified, type: :boolean, required: true diff --git a/spec/resources/decklist_resource_reads_spec.rb b/spec/resources/decklist_resource_reads_spec.rb index 45bb340a..bbf115fe 100644 --- a/spec/resources/decklist_resource_reads_spec.rb +++ b/spec/resources/decklist_resource_reads_spec.rb @@ -42,6 +42,40 @@ expect(d.map(&:id)).to eq([decklist.id]) end end + + context 'with exclude_card_id' do + let!(:corp_decklist) { Decklist.find('11111111-1111-1111-1111-111111111111') } + let!(:runner_decklist) { Decklist.find('22222222-2222-2222-2222-222222222222') } + + it 'excludes decklists with one specified card' do + params[:filter] = { exclude_card_id: { eq: 'pennyshaver' } } + + render + decklist_ids = d.map(&:id) + + expect(decklist_ids).to include(corp_decklist.id) + expect(decklist_ids).not_to include(runner_decklist.id) + end + + it 'excludes decklists with multiple specified cards' do + params[:filter] = + { exclude_card_id: { eq: 'pennyshaver,adonis_campaign' } } + render + decklist_ids = d.map(&:id) + + expect(decklist_ids).not_to include(corp_decklist.id) + expect(decklist_ids).not_to include(runner_decklist.id) + end + + it 'exludes no decklists when nonexistent card is specified' do + params[:filter] = { exclude_card_id: { eq: 'nonexistent_card' } } + render + decklist_ids = d.map(&:id) + + expect(decklist_ids).to include(corp_decklist.id) + expect(decklist_ids).to include(runner_decklist.id) + end + end end describe 'sideloading' do