-
Notifications
You must be signed in to change notification settings - Fork 3
User dependent validation
You may have noticed policies are always run before the operation's contract is synced with the record. This has two practical outcomes:
- The
#create?
action of your policies will always receive an empty record, so you don't have access to the parameters set by the user. - The
#update?
action of your policies will always receive the record as it was before the update, so you don't have access to the new parameters set by the user.
In other words, you can't write code like this:
# app/resources/api/v1/article/policy.rb
module API
module V1
module Article
class Policy < Pragma::Policy::Base
def create?
# Only admins are allowed to publish articles, everyone else can only submit drafts.
user.admin? || record.status.blank? || record.status == 'draft'
end
def update?
# Same as for creation.
create?
end
end
end
end
end
This will not work. You can test it by trying to create or update an article with a non-draft status as a non-admin user - you'll see that Pragma doesn't raise the slightest complain.
While this may sound inconvenient, it makes sense when you think about policies as having the only job of determining whether the user can perform the given operation on the record, irrespectively of what the operation's parameters actually are. Ensuring that the parameters are appropriate for that user and record is a validation concern and should be in the contract.
Let's try to rewrite the code above and put it in the contract instead:
# app/resources/api/v1/article/contract/base.rb
module API
module V1
module Article
module Contract
class Base < Pragma::Contract::Base
configure do
def self.messages
super.merge(en: {
errors: {
acceptable_status: 'status is not acceptable'
}
})
end
end
property :status, type: coercible(:string)
validation do
optional(:current_user).maybe
required(:status).filled(included_in?: %w(draft published))
validate(acceptable_status: %i[current_user status]) do |current_user, status|
current_user.admin? || status == 'draft'
end
end
end
end
end
end
end
This contract uses a custom validation block
to check that the status is acceptable for the user's role. current_user
is a virtual attribute
that Pragma operations will automatically pass to the contract, and the optional
rule is
required to trigger the custom validation block.