discourse/lib/service/base/lock_step.rb
Loïc Guitaut 08df9a7ebb
DEV: Fix the lock step when using contracts (#38019)
Recently, the `lock` step from our Ruby service framework has been
extended to be able to work with keys from the global context when the
key isn’t present in the `params`.

The implementation is flawed because we usually get a contract object in
the `params` key of the context, and currently we call
`public_send(key)` on it. This actually raises an error when the key
doesn’t exist on the contract.

The fix is rather simple, we just use `try` instead, that way if the
method doesn’t exist, it returns `nil`, allowing us to get the key from
the global context as expected.
2026-02-24 12:30:38 +01:00

48 lines
1 KiB
Ruby

# frozen_string_literal: true
module Service
module Base
# @!visibility private
class LockStep < Step
include StepsHelpers
attr_reader :steps, :keys
def initialize(*keys, &block)
super(keys.join(":"))
@keys = keys
@steps = []
instance_exec(&block)
end
def run_step
success =
begin
DistributedMutex.synchronize(lock_name) do
steps.each { |step| step.call(instance, context) }
:success
end
rescue Discourse::ReadOnly
:read_only
end
if success != :success
context[result_key].fail(lock_not_acquired: true)
context.fail!
end
end
private
def lock_name
[
context.__service_class__.to_s.underscore,
*keys.flat_map do |key|
value = context[:params].try(key) || context[key]
[key, value.try(:id) || value]
end,
].join(":")
end
end
end
end