Dry-validation: validate optional parameter dependent on the value of another?

I am hoping to validate the integrity of the data structure below, where the event_key entry is necessary only for event_kind of conference

data = {"event_info"=>
          {"event_kind"=>"conference", # when 'conference', the event_key is required.
           "event_id"=>"1234567",
           "event_key"=>"123456789012345678901234567890AB" # required for "conference" type events
         }}

The validation contains a rule on the optional event_key which rule does not run in the absence of the optional key:

class EventSettingsContract < Dry::Validation::Contract

  params do
    optional(:event_info).hash do
      required(:event_kind).value(included_in?: ['webinar', 'conference'])
      required(:event_id).value(:string, format?: /\A[0-9]{7}\z/)
      optional(:event_key).value(:string, format?: /\A[A-Z0-9]{32}\z/)
    end
  end
 
  # this doesn't run when event_key is missing
  rule(event_info: [:event_key, :event_id]) do
    key.failure 'event_key must be present for conferences' if values[:event_kind] == 'conference' && values[:event_key].nil?
  end

end

When run with the event_key definition commented out from the data hash:

result = EventSettingsContract.new.call data
=> #<Dry::Validation::Result{:event_info=>{:event_kind=>"conference", 
    :event_id=>"1234567"}} errors={}>

When run with the event_key included:

=> #<Dry::Validation::Result{:event_info=>{:event_kind=>"conference", 
:event_id=>"1234567", :event_key=>"123456789012345678901234567890AB"}} errors={}>

All of this is as per documentation. Seems like it is impossible, by design, to run a rule on an optional parameter and check for its presence or absence?

How can I validate the presence of the optional event_key parameter when and only when event_kind is conference?

I found an error in my addressing the values in the rule. This fixed the issue:

  rule(event_info: [:event_kind, :event_key]) do
    if values[:event_info]
      key.failure "event_key must be present for conferences" if values[:event_info][:event_kind] == 'conference' && values[:event_info][:event_key].nil?
    end
  end

@andreimoment you can do rule(:event_info) ... and just check if both event_kind and event_key are valid and then apply your extra, conditional check. In the future (probably in 2.0.0, maybe earlier), there will be a nice feature based on pattern-matched values so that use cases like your will be simpler to handle.

ps. sorry for late reply

1 Like

Thank you! Hope you’re doing well.

1 Like