Skip to content
Open
77 changes: 77 additions & 0 deletions app/controllers/supplejack_api/concerns/stories/multiple.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

module SupplejackApi
module Concerns
module Stories
module Multiple
def multiple_add
stories = multiple_params['stories'].each_with_object([]) do |story, stories_array|
set = SupplejackApi::UserSet.custom_find(story['id'])
return render_error_with(I18n.t('errors.story_not_found', id: story['id']), :not_found) unless set

authorize(set)
stories_array.push(set)
end

changes = multiple_params['stories'].each_with_object([]) do |story_params, changes_array|
set = stories.find { |s| s.id.to_s == story_params['id'] }

item_ids = story_params['items'].each_with_object([]) do |item, ids|
item = set.set_items.build(item)

return render_error_with(item.errors.messages.values.join(', '), :bad_request) unless item.valid?

ids.push(item.id)
end

set.save!

changes_array.push({
story_id: story_params['id'],
item_ids: item_ids
})
end

render json: changes
end

def multiple_remove
stories = multiple_params['stories'].each_with_object([]) do |story, stories_array|
set = SupplejackApi::UserSet.custom_find(story['id'])
return render_error_with(I18n.t('errors.story_not_found', id: story['id']), :not_found) unless set

authorize(set)
stories_array.push(set)
end

multiple_params['stories'].each do |story_params|
set = stories.find { |s| s.id.to_s == story_params['id'] }

story_params['items'].each do |item|
set_item = set.set_items.find_by_id(item[:id])

return render json: { errors: I18n.t('errors.record_not_found', id: item[:id]) },
status: :not_found unless set_item

set_item.destroy!
end

set.save!
end

head :no_content
end

private

def multiple_params
params.permit(:api_key,
stories: [:id,
{ items: [:id, :position, :type, :sub_type, :image_url,
:display_collection, :category, :meta, :record_id,
{ content: %i[value image_url display_collection category] }] }])
end
end
end
end
end
3 changes: 1 addition & 2 deletions app/controllers/supplejack_api/stories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module SupplejackApi
class StoriesController < SupplejackApplicationController
include Pundit
include Concerns::IgnoreMetrics
include Concerns::Stories::Multiple

rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

Expand Down Expand Up @@ -69,8 +70,6 @@ def reposition_items
end
end

private

def story_params
fields = [:name, :description, :privacy, :copyright, :cover_thumbnail, { tags: [], subjects: [] }]

Expand Down
2 changes: 2 additions & 0 deletions app/policies/supplejack_api/user_set_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ def show?
end

alias update? admin_or_owner?
alias multiple_add? admin_or_owner?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specs for this new policies?

alias multiple_remove? admin_or_owner?
end
end
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@
# Stories
namespace 'stories' do
resources :featured, only: [:index]
post :multiple_add
post :multiple_remove
end

resources :stories, except: [:new, :edit] do
post :reposition_items


resources :items, controller: :story_items, except: [:new, :edit]
end
end
Expand Down
267 changes: 267 additions & 0 deletions spec/controllers/supplejack_api/stories_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -365,5 +365,272 @@ module SupplejackApi
end
end
end

describe 'POST multiple_add' do
let(:user) { create(:user) }
let(:user_two) { create(:user) }
let!(:story_one) { create(:user_set, user_id: user.id, privacy: 'public') }
let!(:story_two) { create(:user_set, user_id: user.id, privacy: 'hidden') }

let(:story_item_one) { attributes_for(:story_item) }
let(:story_item_two) { attributes_for(:story_item) }
let(:story_item_three) { attributes_for(:story_item) }
let(:story_item_four) { attributes_for(:story_item) }
let(:story_item_five) { attributes_for(:story_item) }

context 'valid' do
before do
post :multiple_add,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: story_one.id,
items: [
story_item_one,
story_item_two
]
},
{
id: story_two.id,
items: [
story_item_three,
story_item_four,
story_item_five
]
}
]
}
end

it 'adds multiple story items to multiple stories' do
expect(story_one.reload.set_items.count).to eq 2
expect(story_two.reload.set_items.count).to eq 3
end

it 'returns a serialized response with the stories and new items' do
expect(JSON.parse(response.body).count).to eq 2

expect(JSON.parse(response.body).first).to have_key 'story_id'
expect(JSON.parse(response.body).first).to have_key 'item_ids'

expect(JSON.parse(response.body).last).to have_key 'story_id'
expect(JSON.parse(response.body).last).to have_key 'item_ids'
end
end

context 'invalid' do
it 'returns an error when given an invalid story id' do
post :multiple_add,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: 'a',
items: [
story_item_one,
story_item_two
]
},
{
id: story_two.id,
items: [
story_item_three,
story_item_four,
story_item_five
]
}
]
}

expect(response.status).to eq 404
end

it 'returns an error when given an invalid item' do
post :multiple_add,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: story_one.id,
items: [
story_item_one,
{
type: 'text',
sub_type: 'heading'
}
]
}
]
}

expect(response.status).to eq 400
expect(JSON.parse(response.body)['errors']).to eq 'Content value is missing: content must contain value field'
end

it 'returns an error when trying to update stories that you do not have access too' do
post :multiple_add,
params: {
api_key: user_two.api_key,
user_key: user_two.api_key,
stories: [
{
id: story_one.id,
items: [
story_item_one,
story_item_two
]
},
{
id: story_two.id,
items: [
story_item_three,
story_item_four,
story_item_five
]
}
]
}

expect(response.status).to eq 401
expect(JSON.parse(response.body)['errors']).to eq 'Provided user key is not authorized to access this story'
end
end
end

describe 'POST multiple_delete' do
let(:user) { create(:user) }
let(:user_two) { create(:user) }

let!(:story_one) { create(:story_with_dnz_story_items, user_id: user.id) }
let!(:story_two) { create(:story_with_dnz_story_items, user_id: user.id) }

context 'valid' do
it 'deletes multiple story items from multiple stories' do
expect(story_one.reload.set_items.count).to eq 4
expect(story_two.reload.set_items.count).to eq 4

post :multiple_remove,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: story_one.id,
items: [
{ id: story_one.set_items.first.id.to_s },
{ id: story_one.set_items.last.id.to_s }
]
},
{
id: story_two.id,
items: [
{ id: story_two.set_items.first.id.to_s },
{ id: story_two.set_items.last.id.to_s }
]
}
]
}

expect(story_one.reload.set_items.count).to eq 2
expect(story_two.reload.set_items.count).to eq 2
end

it 'returns a success response' do
post :multiple_remove,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: story_one.id,
items: [
{ id: story_one.set_items.first.id.to_s },
{ id: story_one.set_items.last.id.to_s }
]
},
{
id: story_two.id,
items: [
{ id: story_two.set_items.first.id.to_s },
{ id: story_two.set_items.last.id.to_s }
]
}
]
}

expect(response.status).to eq 204
end
end

context 'invalid' do
it 'returns an error for stories that do not belong to the user' do
expect(story_one.reload.set_items.count).to eq 4
expect(story_two.reload.set_items.count).to eq 4

post :multiple_remove,
params: {
api_key: user_two.api_key,
user_key: user_two.api_key,
stories: [
{
id: story_one.id,
items: [
{ id: story_one.set_items.first.id.to_s },
{ id: story_one.set_items.last.id.to_s }
]
},
{
id: story_two.id,
items: [
{ id: story_two.set_items.first.id.to_s },
{ id: story_two.set_items.last.id.to_s }
]
}
]
}

expect(response.status).to eq 401
expect(JSON.parse(response.body)['errors']).to eq 'Provided user key is not authorized to access this story'
expect(story_one.reload.set_items.count).to eq 4
expect(story_two.reload.set_items.count).to eq 4
end

it 'returns an error for invalid story items' do
expect(story_one.reload.set_items.count).to eq 4
expect(story_two.reload.set_items.count).to eq 4

post :multiple_remove,
params: {
api_key: user.api_key,
user_key: user.api_key,
stories: [
{
id: story_one.id,
items: [
{ id: story_one.set_items.first.id.to_s },
{ id: 'a' }
]
},
{
id: story_two.id,
items: [
{ id: story_two.set_items.first.id.to_s },
{ id: story_two.set_items.last.id.to_s }
]
}
]
}

expect(response.status).to eq 404
expect(JSON.parse(response.body)['errors']).to eq 'Record with ID a was not found'
expect(story_one.reload.set_items.count).to eq 3
expect(story_two.reload.set_items.count).to eq 4
end
end
end
end
end
Loading