feature flag state-change callbacks

to enable for your feature, in your feature definition, use
  after_state_change_proc: ->(context, old_state, new_state) { ... }

test plan:
 - specs pass
 - a future commit will make use of this feature
   and can be tested directly

fixes CNVS-10357

Change-Id: I5510545802fd181707bc217e179ba7102eec9d37
Reviewed-on: https://gerrit.instructure.com/28570
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
Product-Review: Bracken Mosbacker <bracken@instructure.com>
QA-Review: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
Jeremy Stanley 2014-01-14 16:34:59 -07:00
parent 4e1641b833
commit b0a789e25a
3 changed files with 42 additions and 3 deletions

View File

@ -244,6 +244,9 @@ class FeatureFlagsController < ApplicationController
end
if new_flag.save
if prior_state != new_flag.state && feature_def.after_state_change_proc.is_a?(Proc)
feature_def.after_state_change_proc.call(@context, prior_state, new_flag.state)
end
render json: feature_flag_json(new_flag, @context, @current_user, session)
else
render json: new_flag.errors.to_json, status: :bad_request

View File

@ -17,7 +17,7 @@
#
class Feature
ATTRS = [:feature, :display_name, :description, :applies_to, :state, :root_opt_in, :enable_at, :beta, :development, :release_notes_url, :custom_transition_proc]
ATTRS = [:feature, :display_name, :description, :applies_to, :state, :root_opt_in, :enable_at, :beta, :development, :release_notes_url, :custom_transition_proc, :after_state_change_proc]
attr_reader *ATTRS
def initialize(opts = {})
@ -63,6 +63,7 @@ class Feature
beta: false, # 'beta' tag shown in UI
development: false, # 'development' tag shown in UI
release_notes_url: 'http://example.com/',
# optional: you can supply a Proc to attach warning messages to and/or forbid certain transitions
# see lib/feature/draft_state.rb for example usage
custom_transition_proc: ->(user, context, from_state, transitions) do
@ -70,7 +71,11 @@ class Feature
transitions['on']['warning'] = I18n.t('features.automatic_essay_grading.enable_warning',
'Enabling this feature after some students have submitted essays may yield inconsistent grades.')
end
end
end,
# optional hook to be called before after a feature flag change
# queue a delayed_job to perform any nontrivial processing
after_state_change_proc: ->(context, old_state, new_state) { ... }
}
=end

View File

@ -471,5 +471,36 @@ describe "Feature Flags API", type: :request do
end
end
end
end
describe "after_state_change_proc" do
let(:t_state_changes) { [] }
before do
Feature.stubs(:definitions).returns({
'custom_feature' => Feature.new(feature: 'custom_feature', applies_to: 'Course', state: 'allowed',
after_state_change_proc: ->(context, from_state, to_state) do
t_state_changes << [context.id, from_state, to_state]
end
)
})
end
it "should fire when creating a feature flag to enable an allowed feature" do
expect {
api_call_as_user(t_root_admin, :put, "/api/v1/courses/#{t_course.id}/features/flags/custom_feature?state=on",
{ controller: 'feature_flags', action: 'update', format: 'json', course_id: t_course.to_param, feature: 'custom_feature', state: 'on' })
}.to change(t_state_changes, :size).by(1)
t_state_changes.last.should eql [t_course.id, 'allowed', 'on']
end
it "should fire when changing a feature flag's state" do
t_course.disable_feature! 'custom_feature'
expect {
api_call_as_user(t_root_admin, :put, "/api/v1/courses/#{t_course.id}/features/flags/custom_feature?state=on",
{ controller: 'feature_flags', action: 'update', format: 'json', course_id: t_course.to_param, feature: 'custom_feature', state: 'on' })
}.to change(t_state_changes, :size).by(1)
t_state_changes.last.should eql [t_course.id, 'off', 'on']
end
end
end