Skip to content

Commit 17689cc

Browse files
authored
Merge pull request #4325 from 3scale/THREESCALE-11354-application-plan-name-sync
THREESCALE-11354: Sync application plan name to backend
2 parents 34b297e + cdb42e5 commit 17689cc

7 files changed

Lines changed: 183 additions & 9 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
module Backend
4+
module ModelExtensions
5+
module ApplicationPlan
6+
extend ActiveSupport::Concern
7+
8+
included do
9+
after_commit :sync_backend_application_plan_name, if: :saved_change_to_name?, on: :update
10+
end
11+
12+
private
13+
14+
def sync_backend_application_plan_name
15+
BackendUpdateApplicationPlanJob.perform_async(id)
16+
end
17+
end
18+
end
19+
end

app/lib/backend/model_extensions/cinstance.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,26 @@ def self.included(base)
2020

2121
def update_backend_application
2222
if plan && service
23-
state = self.state
24-
state = :active if live?
25-
26-
ThreeScale::Core::Application.save( :service_id => service.backend_id,
27-
:id => application_id,
28-
:state => state,
29-
:plan_id => plan.id,
30-
:plan_name => plan.name,
31-
:redirect_url => redirect_url )
23+
ThreeScale::Core::Application.save(backend_application_attributes(service, plan))
3224
end
3325

3426
true
3527
end
3628

29+
def backend_application_attributes(service, plan)
30+
state = self.state
31+
state = :active if live?
32+
33+
{
34+
:service_id => service.backend_id,
35+
:id => application_id,
36+
:state => state,
37+
:plan_id => plan.id,
38+
:plan_name => plan.name,
39+
:redirect_url => redirect_url
40+
}
41+
end
42+
3743
def delete_backend_application
3844
if service.present? && service.id.present? && application_id.present?
3945
ThreeScale::Core::Application.delete(service.backend_id, application_id)

app/models/application_plan.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
class ApplicationPlan < Plan
3+
include Backend::ModelExtensions::ApplicationPlan
34

45
#TODO: is the dependent :destroy working?
56
has_many :cinstances, :foreign_key => :plan_id, :dependent => :destroy, :inverse_of => :plan
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
class BackendUpdateApplicationPlanJob
4+
include Sidekiq::IterableJob
5+
include Sidekiq::Throttled::Job
6+
7+
sidekiq_options queue: :backend_sync
8+
9+
sidekiq_throttle concurrency: {
10+
limit: 1,
11+
key_suffix: ->(plan_id) { "plan:#{plan_id}" },
12+
ttl: 1.hour.to_i
13+
}
14+
15+
def build_enumerator(plan_id, cursor:)
16+
plan = ApplicationPlan.find_by(id: plan_id)
17+
return unless plan
18+
19+
active_record_batches_enumerator(plan.cinstances, cursor: cursor, batch_size: 10_000)
20+
end
21+
22+
# :reek:UtilityFunction :reek:TooManyStatements
23+
def each_iteration(batch, plan_id)
24+
plan = ApplicationPlan.find_by(id: plan_id)
25+
return unless plan
26+
27+
save_backend_applications(batch, plan)
28+
end
29+
30+
private
31+
32+
def save_backend_applications(batch, plan)
33+
service = plan.service
34+
applications = batch.map { |app| app.backend_application_attributes(service, plan) }
35+
ThreeScale::Core::Application.save_batch(service.backend_id, applications)
36+
end
37+
end

test/integration/admin/api/application_plans_controller_test.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'test_helper'
44

55
class Admin::Api::ApplicationPlansControllerTest < ActionDispatch::IntegrationTest
6+
include ActiveJob::TestHelper
67

78
def setup
89
Settings::Switch.any_instance.stubs(:allowed?).returns(true)
@@ -77,6 +78,20 @@ def test_index_json
7778
assert_equal 2, JSON.parse(response.body)['plans'].length
7879
end
7980

81+
def test_update_syncs_plan_name_to_backend
82+
application_plan = FactoryBot.create(:application_plan, name: 'old name', issuer: service)
83+
cinstances = FactoryBot.create_list(:simple_cinstance, 2, plan: application_plan)
84+
85+
ThreeScale::Core::Application.expects(:save_batch)
86+
.with(service.backend_id, expected_backend_applications(cinstances, application_plan, 'new name'))
87+
88+
Sidekiq::Testing.inline! do
89+
put admin_api_service_application_plan_path(application_plan, service_id: service.id, format: :json, access_token: @token,
90+
application_plan: { name: 'new name' })
91+
assert_response :success
92+
end
93+
end
94+
8095
def test_approval_required
8196
assert_difference service.application_plans.method(:count) do
8297
post admin_api_service_application_plans_path(application_plan_params(approval_required: true))
@@ -160,6 +175,15 @@ def current_account
160175

161176
attr_reader :service
162177

178+
def expected_backend_applications(cinstances, plan, plan_name)
179+
cinstances.map do |cinstance|
180+
state = cinstance.state
181+
state = :active if cinstance.live?
182+
{ service_id: plan.service.backend_id, id: cinstance.application_id, state: state,
183+
plan_id: plan.id, plan_name: plan_name, redirect_url: cinstance.redirect_url }
184+
end
185+
end
186+
163187
def application_plan_params(state_event: 'publish', approval_required: 0)
164188
@application_plan_params ||= {
165189
service_id: service.id,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
require 'test_helper'
4+
5+
class Backend::ModelExtensions::ApplicationPlanTest < ActiveSupport::TestCase
6+
def setup
7+
@plan = FactoryBot.create(:application_plan)
8+
end
9+
10+
attr_reader :plan
11+
12+
test 'enqueues worker when plan name is updated' do
13+
BackendUpdateApplicationPlanJob.jobs.clear
14+
plan.update!(name: 'New Plan Name')
15+
assert_equal 1, BackendUpdateApplicationPlanJob.jobs.size
16+
assert_equal [plan.id], BackendUpdateApplicationPlanJob.jobs.first['args']
17+
end
18+
19+
test 'does not enqueue worker when other attributes are updated' do
20+
BackendUpdateApplicationPlanJob.jobs.clear
21+
plan.update!(description: 'Updated description')
22+
assert_equal 0, BackendUpdateApplicationPlanJob.jobs.size
23+
end
24+
25+
test 'does not enqueue worker when plan is created' do
26+
BackendUpdateApplicationPlanJob.jobs.clear
27+
FactoryBot.create(:application_plan)
28+
assert_equal 0, BackendUpdateApplicationPlanJob.jobs.size
29+
end
30+
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
require 'test_helper'
4+
5+
class BackendUpdateApplicationPlanJobTest < ActiveSupport::TestCase
6+
include NPlusOneControl::MinitestHelper
7+
8+
def setup
9+
@plan = FactoryBot.create(:application_plan)
10+
end
11+
12+
attr_reader :plan
13+
14+
test 'batch syncs plan name to backend for all cinstances' do
15+
cinstances = FactoryBot.create_list(:simple_cinstance, 2, plan: plan)
16+
17+
expected_applications = cinstances.map do |cinstance|
18+
state = cinstance.state
19+
state = :active if cinstance.live?
20+
21+
{
22+
service_id: plan.service.backend_id,
23+
id: cinstance.application_id,
24+
state: state,
25+
plan_id: plan.id,
26+
plan_name: plan.name,
27+
redirect_url: cinstance.redirect_url
28+
}
29+
end
30+
31+
ThreeScale::Core::Application.expects(:save_batch).with(plan.service.backend_id, expected_applications)
32+
33+
BackendUpdateApplicationPlanJob.new.perform(plan.id)
34+
end
35+
36+
test 'no n+1 queries' do
37+
ThreeScale::Core::Application.stubs(:save_batch)
38+
39+
populate = ->(count) { FactoryBot.create_list(:simple_cinstance, count, plan: plan) }
40+
41+
assert_perform_constant_number_of_queries(populate: populate) do
42+
BackendUpdateApplicationPlanJob.new.perform(plan.id)
43+
end
44+
end
45+
46+
test 'does nothing when plan does not exist' do
47+
ThreeScale::Core::Application.expects(:save_batch).never
48+
49+
BackendUpdateApplicationPlanJob.new.perform(0)
50+
end
51+
52+
test 'does nothing when plan has no cinstances' do
53+
ThreeScale::Core::Application.expects(:save_batch).never
54+
55+
BackendUpdateApplicationPlanJob.new.perform(plan.id)
56+
end
57+
end

0 commit comments

Comments
 (0)