Continuing my previous question at: Active Model Serializer and Pundit deleting records during a Show CRUD action
I have a situation where a User
should not be able to view another user's unpublished chapters belonging to a Story
an author created.
E.g. If UserA
creates a story called Targon
and provides 2 published chapters and 2 unpublished chapters, then UserB
should only see published chapters for Targon
story.
Normally with Pundit policy scoping, it scopes the index
CRUD action.
What I need to scope however, are Chapters
belonging to a Story
during the render json line:
render json: story, include: [:user, :chapters], status: :ok
I have tried:
# ---------------------------------------------------------------------------
# ActiveRecord auto-save will kick in and delete all unpublished chapters
# ---------------------------------------------------------------------------
story.chapters = policy_scope(story.chapters)
render json: story, include: [:user, :chapters], status: :ok
According to https://gist.github.com/demisx/9896113 (has_many section) the above code will delete all the unpublished chapters belonging to Targon
when I reassign story.chapters
:
story.chapters = policy_scope(story.chapters) # BAD
I am hoping there is some way I can do something like this:
render json: story, include: [:user, policy_scope(:chapters)], status: :ok
At the moment, without scoping the story.chapters
any users who fetch for Story with ID 16
(Targon) will get back JSONAPI:
{
"data": {
"id": "16",
"type": "stories",
"attributes": {
"title": "Mount Targon",
"summary": "Mount Targon is the mightiest peak in Runeterra, a towering peak of sun-baked rock amid a range of summits unmatched in scale anywhere else in the world. Located far from civilization, Mount Targon is utterly remote and all but impossible to reach save by the most determined seeker. Many legends cling to Mount Targon, and, like any place of myth, it is a beacon to dreamers, madmen and questors of adventure. Some of these brave souls attempt to scale the impossible mountain, perhaps seeking wisdom or enlightenment, perhaps chasing glory or some soul-deep yearning to witness its summit. The ascent is all but impossible, and those hardy few who somehow survive to reach the top almost never speak of what they have seen. Some return with a haunted, empty look in their eyes, others changed beyond all recognition, imbued by an Aspect of unearthly, inhuman power with a destiny few mortals can comprehend.",
"published": true,
"published-date": "2017-11-02T10:35:33.184Z",
"created-at": "2017-11-02T10:35:33.184Z",
"updated-at": "2017-11-04T07:35:04.083Z",
"cover": {
"url": "http://res.cloudinary.com/chewedon/image/upload/v1509780931/c8ubn3tfivxziyxwynsa.png",
"standard": {
"url": "http://res.cloudinary.com/chewedon/image/upload/c_fill,g_north,h_300,w_200/c8ubn3tfivxziyxwynsa.png"
}
}
},
"relationships": {
"user": {
"data": {
"id": "1",
"type": "users"
}
},
"chapters": {
"data": [{
"id": "26",
"type": "chapters"
}, {
"id": "27",
"type": "chapters"
}, {
"id": "37",
"type": "chapters"
}, {
"id": "38",
"type": "chapters"
}]
}
}
},
"included": [{
"id": "1",
"type": "users",
"attributes": {
"username": "Chewedon",
"photo": {
"url": "http://res.cloudinary.com/chewedon/image/upload/v1509857442/nx1tqlcdxrhz6r3kjx87.jpg",
"standard": {
"url": "http://res.cloudinary.com/chewedon/image/upload/c_fill,g_north,h_150,w_150/nx1tqlcdxrhz6r3kjx87.jpg"
}
}
},
"relationships": {
"stories": {
"data": [{
"id": "1",
"type": "stories"
}, {
"id": "2",
"type": "stories"
}, {
"id": "3",
"type": "stories"
}, {
"id": "4",
"type": "stories"
}, {
"id": "5",
"type": "stories"
}, {
"id": "6",
"type": "stories"
}, {
"id": "8",
"type": "stories"
}, {
"id": "9",
"type": "stories"
}, {
"id": "10",
"type": "stories"
}, {
"id": "11",
"type": "stories"
}, {
"id": "12",
"type": "stories"
}, {
"id": "13",
"type": "stories"
}, {
"id": "14",
"type": "stories"
}, {
"id": "15",
"type": "stories"
}, {
"id": "16",
"type": "stories"
}]
}
}
}]
}
Here in the relationship section, chapters 37
and 38
are unpublished, leading to a 403 Forbidden on my Ember frontend.
Ideally the server should have scoped out these before returning the records but due to the bug I described above and in my previous Stackoverflow question, I am stuck on how to scope included fields with Pundit.
Any ideas?
Thanks to user oowowaee from previous linked question, who suggested overriding the Story
serializer's chapters
field (which I didn't know you could do that), the code is working now and the records don't get deleted from database.
class StorySerializer < ActiveModel::Serializer
include Pundit
attributes :id, :title, :summary, :published, :published_date, :created_at, :updated_at, :cover
belongs_to :user
has_many :chapters
# ------------------------------------------------------------------------
# Note: need to use 'object.chapters' not 'self.chapters` below.
# ------------------------------------------------------------------------
def chapters
policy_scope(object.chapters)
end
end