3scale_toolbox 0.19.2 → 0.19.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/3scale_toolbox/commands/import_command/openapi/update_policies_step.rb +23 -10
  3. data/lib/3scale_toolbox/commands/plans_command/export_command.rb +52 -29
  4. data/lib/3scale_toolbox/commands/plans_command/import/import_backend_metrics_step.rb +37 -0
  5. data/lib/3scale_toolbox/commands/plans_command/import/import_plan_limits_step.rb +11 -2
  6. data/lib/3scale_toolbox/commands/plans_command/import/import_plan_metrics_step.rb +2 -2
  7. data/lib/3scale_toolbox/commands/plans_command/import/import_plan_pricing_rules_step.rb +12 -1
  8. data/lib/3scale_toolbox/commands/plans_command/import/step.rb +23 -8
  9. data/lib/3scale_toolbox/commands/plans_command/import/validate_plan_step.rb +126 -0
  10. data/lib/3scale_toolbox/commands/plans_command/import_command.rb +5 -1
  11. data/lib/3scale_toolbox/crds/limit_dump.rb +1 -1
  12. data/lib/3scale_toolbox/crds/pricing_rule_dump.rb +1 -1
  13. data/lib/3scale_toolbox/entities/application_plan.rb +64 -0
  14. data/lib/3scale_toolbox/entities/backend.rb +4 -0
  15. data/lib/3scale_toolbox/entities/backend_method.rb +16 -0
  16. data/lib/3scale_toolbox/entities/backend_metric.rb +16 -0
  17. data/lib/3scale_toolbox/entities/limit.rb +52 -7
  18. data/lib/3scale_toolbox/entities/method.rb +11 -0
  19. data/lib/3scale_toolbox/entities/metric.rb +12 -0
  20. data/lib/3scale_toolbox/entities/pricing_rule.rb +52 -7
  21. data/lib/3scale_toolbox/entities/service.rb +4 -0
  22. data/lib/3scale_toolbox/remote_cache.rb +42 -1
  23. data/lib/3scale_toolbox/version.rb +1 -1
  24. data/licenses.xml +4 -4
  25. metadata +4 -10
  26. data/lib/3scale_toolbox/commands/plans_command/export/read_app_plan_step.rb +0 -16
  27. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_features_step.rb +0 -16
  28. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_limits_step.rb +0 -19
  29. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_methods_step.rb +0 -47
  30. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_metrics_step.rb +0 -47
  31. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_pricing_rules_step.rb +0 -19
  32. data/lib/3scale_toolbox/commands/plans_command/export/step.rb +0 -85
  33. data/lib/3scale_toolbox/commands/plans_command/export/write_artifacts_file_step.rb +0 -84
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dee95ebde6b993418261f63857e80e18c8dd94bcfea9b92c6fa505cbbfdade99
4
- data.tar.gz: ff8c3c97adb47ad141b2af12f8d4481330f9f25ce8819ecb5162666c6eedc521
3
+ metadata.gz: d2a44bdf128bf6f8d4d0a592a4eee49c82c34415017e03366ff2bcc241fe18c0
4
+ data.tar.gz: 0e94df4747331e9092ae0c961709cfcc334a553eb54209378941b8239307eceb
5
5
  SHA512:
6
- metadata.gz: 41473b4f397a15743224e1848aecdd21abdf8a0246833522c2a3aadf6ed5c5cc58970e4761bb61801afa2e32b4326a33d15bf71db3f926c06a7620f7daa5e8b1
7
- data.tar.gz: 6d1396f584b308ba1f978f02981605023d346c2ff39d806e1e3b265b7d16233f72beb4f7fc896e99a6d62d5b765878d5e9c71ee4eeb444db91101decc0a16bae
6
+ metadata.gz: 8dd7a6efc6567598d87e4ebbc18c486eba1ea9e6766ca6c0241f78e73a4287408d9e18ac838788e81a073c8f3a8fd93d299fac4ea9f21a42571e3cff68f2b0bb
7
+ data.tar.gz: c4ed75a0b46414606208a5fb6e1374e72d5fe06fbb5a237b3793bc2471d20667c489acace51cdf93d4971bfe59b42dc2919631b90074a8af72bac1668c663d01
@@ -15,7 +15,7 @@ module ThreeScaleToolbox
15
15
  # do not update in-place, otherwise changes will not be detected
16
16
  policies_settings = source_policies_settings.dup
17
17
 
18
- add_anonymous_access_policy(policies_settings)
18
+ reconcile_anonymous_access_policy(policies_settings)
19
19
  add_rh_sso_keycloak_role_check_policy(policies_settings)
20
20
  add_url_rewritting_policy(policies_settings)
21
21
 
@@ -31,15 +31,28 @@ module ThreeScaleToolbox
31
31
 
32
32
  private
33
33
 
34
- def add_anonymous_access_policy(policies)
35
- # only on 'open api' security req
36
- return unless api_spec.security.nil?
37
-
38
- return if policies.any? { |policy| policy['name'] == 'default_credentials' }
39
-
40
- # Anonymous policy should be before apicast policy
41
- # hence, adding as a first element
42
- policies.insert(0, anonymous_policy)
34
+ def reconcile_anonymous_access_policy(policies)
35
+ idx = policies.find_index { |p| p['name'] == 'default_credentials' }
36
+
37
+ if api_spec.security.nil?
38
+ # only on 'open api' security req
39
+ # Update anonymous policy if exists
40
+ #
41
+ if idx.nil?
42
+ # Anonymous policy should be before apicast policy
43
+ # hence, adding as a first element
44
+ policies.insert(0, anonymous_policy)
45
+ else
46
+ # only update if different
47
+ if policies[idx].dig('configuration', 'user_key') != anonymous_policy.dig(:configuration, :user_key)
48
+ policies[idx] = anonymous_policy
49
+ end
50
+ end
51
+ else
52
+ unless idx.nil?
53
+ policies.slice!(idx)
54
+ end
55
+ end
43
56
  end
44
57
 
45
58
  def anonymous_policy
@@ -1,12 +1,3 @@
1
- require '3scale_toolbox/commands/plans_command/export/step'
2
- require '3scale_toolbox/commands/plans_command/export/read_app_plan_step'
3
- require '3scale_toolbox/commands/plans_command/export/read_plan_features_step'
4
- require '3scale_toolbox/commands/plans_command/export/read_plan_limits_step'
5
- require '3scale_toolbox/commands/plans_command/export/read_plan_pricing_rules_step'
6
- require '3scale_toolbox/commands/plans_command/export/read_plan_methods_step'
7
- require '3scale_toolbox/commands/plans_command/export/read_plan_metrics_step'
8
- require '3scale_toolbox/commands/plans_command/export/write_artifacts_file_step'
9
-
10
1
  module ThreeScaleToolbox
11
2
  module Commands
12
3
  module PlansCommand
@@ -31,32 +22,64 @@ module ThreeScaleToolbox
31
22
  end
32
23
 
33
24
  def run
34
- tasks = []
35
- tasks << ReadAppPlanStep.new(context)
36
- tasks << ReadPlanLimitsStep.new(context)
37
- tasks << ReadPlanPricingRulesStep.new(context)
38
- tasks << ReadPlanFeaturesStep.new(context)
39
- tasks << ReadPlanMethods.new(context)
40
- tasks << ReadPlanMetrics.new(context)
41
- tasks << WriteArtifactsStep.new(context)
42
-
43
- # run tasks
44
- tasks.each(&:call)
25
+ select_output do |output|
26
+ plan_object = application_plan.to_hash.merge(
27
+ 'created_at' => Time.now.utc.iso8601,
28
+ 'toolbox_version' => ThreeScaleToolbox::VERSION
29
+ )
30
+ output.write(plan_object.to_yaml)
31
+ end
45
32
  end
46
33
 
47
34
  private
48
35
 
49
- def context
50
- @context ||= create_context
36
+ def remote
37
+ @remote ||= threescale_client(arguments[:remote])
38
+ end
39
+
40
+ def select_output
41
+ ios = if file
42
+ File.open(file, 'w')
43
+ else
44
+ $stdout
45
+ end
46
+ begin
47
+ yield(ios)
48
+ ensure
49
+ ios.close
50
+ end
51
+ end
52
+
53
+ def application_plan
54
+ @application_plan ||= find_application_plan
55
+ end
56
+
57
+ def find_application_plan
58
+ Entities::ApplicationPlan.find(service: product, ref: plan_system_name).tap do |p|
59
+ raise ThreeScaleToolbox::Error, "Application plan #{plan_system_name} does not exist" if p.nil?
60
+ end
61
+ end
62
+
63
+ def product
64
+ @product ||= find_product
65
+ end
66
+
67
+ def product_ref
68
+ arguments[:service_system_name]
69
+ end
70
+
71
+ def plan_system_name
72
+ arguments[:plan_system_name]
73
+ end
74
+
75
+ def find_product
76
+ Entities::Service.find(remote: remote, ref: product_ref).tap do |prd|
77
+ raise ThreeScaleToolbox::Error, "Product #{product_ref} does not exist" if prd.nil?
78
+ end
51
79
  end
52
80
 
53
- def create_context
54
- {
55
- file: options[:file],
56
- threescale_client: threescale_client(arguments[:remote]),
57
- service_system_name: arguments[:service_system_name],
58
- plan_system_name: arguments[:plan_system_name],
59
- }
81
+ def file
82
+ options[:file]
60
83
  end
61
84
  end
62
85
  end
@@ -0,0 +1,37 @@
1
+ module ThreeScaleToolbox
2
+ module Commands
3
+ module PlansCommand
4
+ module Import
5
+ class ImportBackendMetricsStep
6
+ include Step
7
+ ##
8
+ # Writes Plan metrics and methods
9
+ def call
10
+ resource_backend_metrics.each(&method(:create_metric))
11
+ resource_backend_methods.each(&method(:create_method))
12
+ end
13
+
14
+ private
15
+
16
+ def create_metric(metric_attrs)
17
+ backend = find_backend(metric_attrs.fetch('backend_system_name'))
18
+
19
+ unless backend.metrics.any? { |m| m.system_name == metric_attrs.fetch('system_name') }
20
+ Entities::BackendMetric.create(backend: backend, attrs: metric_attrs)
21
+ puts "Created backend metric: #{metric_attrs.fetch('system_name')}; backend: #{backend.system_name}"
22
+ end
23
+ end
24
+
25
+ def create_method(method_attrs)
26
+ backend = find_backend(method_attrs.fetch('backend_system_name'))
27
+
28
+ unless backend.methods.any? { |m| m.system_name == method_attrs.fetch('system_name') }
29
+ Entities::BackendMethod.create(backend: backend, attrs: method_attrs)
30
+ puts "Created backend method: #{method_attrs.fetch('system_name')}; backend: #{backend.system_name}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,7 +2,7 @@ module ThreeScaleToolbox
2
2
  module Commands
3
3
  module PlansCommand
4
4
  module Import
5
- class ImportMetricLimitsStep
5
+ class ImportLimitsStep
6
6
  include Step
7
7
  ##
8
8
  # Writes Plan limits
@@ -26,9 +26,18 @@ module ThreeScaleToolbox
26
26
 
27
27
  def resource_limits_processed
28
28
  resource_limits.map do |limit|
29
- metric = find_metric_by_system_name(limit.delete('metric_system_name'))
29
+ metric_system_name = limit.delete('metric_system_name')
30
+ backend_system_name = limit.delete('metric_backend_system_name')
31
+ metric_owner = if backend_system_name.nil?
32
+ service
33
+ else
34
+ find_backend(backend_system_name)
35
+ end
36
+ metric = metric_owner.find_metric_or_method(metric_system_name)
30
37
  # this ImportMetricLimitsStep step is assuming all metrics/methods have been created
31
38
  # in previous step, so finding metric should always succeed.
39
+ raise ThreeScaleToolbox::Error, "metric [#{metric_system_name}, #{backend_system_name}] not found" if metric.nil?
40
+
32
41
  limit.merge('metric_id' => metric.id)
33
42
  end
34
43
  end
@@ -14,13 +14,13 @@ module ThreeScaleToolbox
14
14
  private
15
15
 
16
16
  def missing_metrics
17
- ThreeScaleToolbox::Helper.array_difference(resource_metrics, service.metrics) do |a, b|
17
+ ThreeScaleToolbox::Helper.array_difference(resource_product_metrics, service.metrics) do |a, b|
18
18
  a['system_name'] == b.system_name
19
19
  end
20
20
  end
21
21
 
22
22
  def missing_methods
23
- ThreeScaleToolbox::Helper.array_difference(resource_methods, service.methods) do |a, b|
23
+ ThreeScaleToolbox::Helper.array_difference(resource_product_methods, service.methods) do |a, b|
24
24
  a['system_name'] == b.system_name
25
25
  end
26
26
  end
@@ -26,7 +26,18 @@ module ThreeScaleToolbox
26
26
 
27
27
  def resource_pr_processed
28
28
  resource_pricing_rules.map do |pr|
29
- metric = find_metric_by_system_name(pr.delete('metric_system_name'))
29
+ metric_system_name = pr.delete('metric_system_name')
30
+ backend_system_name = pr.delete('metric_backend_system_name')
31
+ metric_owner = if backend_system_name.nil?
32
+ service
33
+ else
34
+ find_backend(backend_system_name)
35
+ end
36
+ metric = metric_owner.find_metric_or_method(metric_system_name)
37
+ # this ImportMetricLimitsStep step is assuming all metrics/methods have been created
38
+ # in previous step, so finding metric should always succeed.
39
+ raise ThreeScaleToolbox::Error, "metric [#{metric_system_name}, #{backend_system_name}] not found" if metric.nil?
40
+
30
41
  pr.merge('metric_id' => metric.id,
31
42
  'cost_per_unit' => pr.fetch('cost_per_unit').to_f)
32
43
  end
@@ -53,6 +53,22 @@ module ThreeScaleToolbox
53
53
  artifacts_resource['methods'] || []
54
54
  end
55
55
 
56
+ def resource_product_metrics
57
+ resource_metrics.reject{ |m| m.has_key? 'backend_system_name' }
58
+ end
59
+
60
+ def resource_product_methods
61
+ resource_methods.reject{ |mth| mth.has_key? 'backend_system_name' }
62
+ end
63
+
64
+ def resource_backend_metrics
65
+ resource_metrics.select{ |m| m.has_key? 'backend_system_name' }
66
+ end
67
+
68
+ def resource_backend_methods
69
+ resource_methods.select{ |mth| mth.has_key? 'backend_system_name' }
70
+ end
71
+
56
72
  def resource_limits
57
73
  artifacts_resource['limits'] || []
58
74
  end
@@ -65,10 +81,6 @@ module ThreeScaleToolbox
65
81
  artifacts_resource['plan_features'] || []
66
82
  end
67
83
 
68
- def service_metrics_and_methods
69
- service.metrics + service.methods
70
- end
71
-
72
84
  def service_features
73
85
  context[:service_features] ||= service.features
74
86
  end
@@ -82,10 +94,6 @@ module ThreeScaleToolbox
82
94
  service_features.find { |feature| feature['system_name'] == system_name }
83
95
  end
84
96
 
85
- def find_metric_by_system_name(system_name)
86
- service_metrics_and_methods.find { |metric| metric.system_name == system_name }
87
- end
88
-
89
97
  private
90
98
 
91
99
  def find_service
@@ -100,6 +108,13 @@ module ThreeScaleToolbox
100
108
  raise ThreeScaleToolbox::Error, "Application plan #{plan_system_name} does not exist" if p.nil?
101
109
  end
102
110
  end
111
+
112
+ def find_backend(backend_system_name)
113
+ Entities::Backend.find_by_system_name(remote: threescale_client,
114
+ system_name: backend_system_name).tap do |backend|
115
+ raise ThreeScaleToolbox::Error, "Backend #{backend_system_name} does not exist" if backend.nil?
116
+ end
117
+ end
103
118
  end
104
119
  end
105
120
  end
@@ -0,0 +1,126 @@
1
+ module ThreeScaleToolbox
2
+ module Commands
3
+ module PlansCommand
4
+ module Import
5
+ class ValidatePlanStep
6
+ include Step
7
+ ##
8
+ # Creates if it does not exist, updates otherwise
9
+ def call
10
+ validate_product_metric_method_uniqueness!
11
+
12
+ validate_backend_metric_method_uniqueness!
13
+
14
+ validate_product_backend_usage_references!
15
+
16
+ validate_limit_backend_references!
17
+
18
+ validate_limit_product_references!
19
+
20
+ validate_pricingrule_backend_references!
21
+
22
+ validate_pricingrule_product_references!
23
+ end
24
+
25
+ private
26
+
27
+ def validate_product_metric_method_uniqueness!
28
+ system_name_list = (resource_product_metrics + resource_product_methods).map do |m|
29
+ m.fetch('system_name')
30
+ end
31
+ if system_name_list.length != system_name_list.uniq.length
32
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
33
+ "Product metrics and method system names must be unique."
34
+ end
35
+ end
36
+
37
+ def validate_backend_metric_method_uniqueness!
38
+ metric_list = resource_backend_metrics + resource_backend_methods
39
+ backend_list = metric_list.map { |m| m.fetch('backend_system_name') }.uniq
40
+ backend_list.each do |backend_system_name|
41
+ backend_metric_list = metric_list.select do |m|
42
+ m.fetch('backend_system_name') == backend_system_name
43
+ end.map { |m| m.fetch('system_name') }
44
+
45
+ if backend_metric_list.length != backend_metric_list.uniq.length
46
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
47
+ "Backend #{backend_system_name} contains metrics and method system names that are not unique"
48
+ end
49
+ end
50
+ end
51
+
52
+ def validate_product_backend_usage_references!
53
+ metric_list = resource_backend_metrics + resource_backend_methods
54
+ backend_list = metric_list.map { |m| m.fetch('backend_system_name') }.uniq
55
+ backend_usages_list = service.backend_usage_list.map(&:backend).map(&:system_name)
56
+
57
+ backend_list.each do |backend_system_name|
58
+ unless backend_usages_list.include?(backend_system_name)
59
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
60
+ "Backend usage reference to backend #{backend_system_name} has not been found"
61
+ end
62
+ end
63
+ end
64
+
65
+ def validate_limit_backend_references!
66
+ metric_list = resource_backend_metrics + resource_backend_methods
67
+ limits_with_backend_ref = resource_limits.select{ |limit| limit.has_key? 'metric_backend_system_name'}
68
+ limits_with_backend_ref.each do |limit|
69
+ none = metric_list.none? do |m|
70
+ m.fetch('system_name') == limit.fetch('metric_system_name') &&
71
+ m.fetch('backend_system_name') == limit.fetch('metric_backend_system_name')
72
+ end
73
+
74
+ if none
75
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
76
+ "Limit with backend metric [#{limit.fetch('metric_system_name')}, #{limit.fetch('metric_backend_system_name')}] " \
77
+ "has not been found in metric or method list"
78
+ end
79
+ end
80
+ end
81
+
82
+ def validate_limit_product_references!
83
+ metric_list = resource_product_metrics + resource_product_methods
84
+ limits = resource_limits.reject { |limit| limit.has_key? 'metric_backend_system_name'}
85
+ limits.each do |limit|
86
+ if metric_list.none? { |m| m.fetch('system_name') == limit.fetch('metric_system_name') }
87
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
88
+ "Limit with product metric [#{limit.fetch('metric_system_name')}] " \
89
+ "has not been found in metric or method list"
90
+ end
91
+ end
92
+ end
93
+
94
+ def validate_pricingrule_backend_references!
95
+ metric_list = resource_backend_metrics + resource_backend_methods
96
+ pr_with_backend_ref = resource_pricing_rules.select{ |pr| pr.has_key? 'metric_backend_system_name'}
97
+ pr_with_backend_ref.each do |pr|
98
+ none = metric_list.none? do |m|
99
+ m.fetch('system_name') == pr.fetch('metric_system_name') &&
100
+ m.fetch('backend_system_name') == pr.fetch('metric_backend_system_name')
101
+ end
102
+
103
+ if none
104
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
105
+ "PricingRule with backend metric [#{pr.fetch('metric_system_name')}, #{pr.fetch('metric_backend_system_name')}] " \
106
+ "has not been found in metric or method list"
107
+ end
108
+ end
109
+ end
110
+
111
+ def validate_pricingrule_product_references!
112
+ metric_list = resource_product_metrics + resource_product_methods
113
+ prs = resource_pricing_rules.reject { |pr| pr.has_key? 'metric_backend_system_name'}
114
+ prs.each do |pr|
115
+ if metric_list.none? { |m| m.fetch('system_name') == pr.fetch('metric_system_name') }
116
+ raise ThreeScaleToolbox::Error, "Invalid content. " \
117
+ "PricingRule with product metric [#{pr.fetch('metric_system_name')}] " \
118
+ "has not been found in metric or method list"
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -1,9 +1,11 @@
1
1
  require '3scale_toolbox/commands/plans_command/import/step'
2
+ require '3scale_toolbox/commands/plans_command/import/validate_plan_step'
2
3
  require '3scale_toolbox/commands/plans_command/import/create_or_update_app_plan_step'
3
4
  require '3scale_toolbox/commands/plans_command/import/import_plan_features_step'
4
5
  require '3scale_toolbox/commands/plans_command/import/import_plan_metrics_step'
5
6
  require '3scale_toolbox/commands/plans_command/import/import_plan_limits_step'
6
7
  require '3scale_toolbox/commands/plans_command/import/import_plan_pricing_rules_step'
8
+ require '3scale_toolbox/commands/plans_command/import/import_backend_metrics_step'
7
9
 
8
10
  module ThreeScaleToolbox
9
11
  module Commands
@@ -31,9 +33,11 @@ module ThreeScaleToolbox
31
33
 
32
34
  def run
33
35
  tasks = []
36
+ tasks << ValidatePlanStep.new(context)
34
37
  tasks << CreateOrUpdateAppPlanStep.new(context)
35
38
  tasks << ImportMetricsStep.new(context)
36
- tasks << ImportMetricLimitsStep.new(context)
39
+ tasks << ImportBackendMetricsStep.new(context)
40
+ tasks << ImportLimitsStep.new(context)
37
41
  tasks << ImportPricingRulesStep.new(context)
38
42
  tasks << ImportPlanFeaturesStep.new(context)
39
43
 
@@ -18,7 +18,7 @@ module ThreeScaleToolbox
18
18
  { 'systemName' => method.system_name }
19
19
  elsif (metric = plan.service.metrics.find { |m| m.id == metric_id })
20
20
  { 'systemName' => metric.system_name }
21
- elsif (backend = backend_from_metric)
21
+ elsif (backend = backend_from_metric_link)
22
22
  if (backend_metric = backend.metrics.find { |m| m.id == metric_id })
23
23
  { 'systemName' => backend_metric.system_name, 'backend' => backend.system_name }
24
24
  elsif (backend_method = backend.methods.find { |m| m.id == metric_id })
@@ -19,7 +19,7 @@ module ThreeScaleToolbox
19
19
  { 'systemName' => method.system_name }
20
20
  elsif (metric = plan.service.metrics.find { |m| m.id == metric_id })
21
21
  { 'systemName' => metric.system_name }
22
- elsif (backend = backend_from_metric)
22
+ elsif (backend = backend_from_metric_link)
23
23
  if (backend_metric = backend.metrics.find { |m| m.id == metric_id })
24
24
  { 'systemName' => backend_metric.system_name, 'backend' => backend.system_name }
25
25
  elsif (backend_method = backend.methods.find { |m| m.id == metric_id })
@@ -3,6 +3,9 @@ module ThreeScaleToolbox
3
3
  class ApplicationPlan
4
4
  include CRD::ApplicationPlanSerializer
5
5
 
6
+ APP_PLANS_BLACKLIST = %w[id links default custom created_at updated_at].freeze
7
+ PLAN_FEATURE_BLACKLIST = %w[id links created_at updated_at].freeze
8
+
6
9
  class << self
7
10
  def create(service:, plan_attrs:)
8
11
  plan = service.remote.create_application_plan service.id, create_plan_attrs(plan_attrs)
@@ -209,8 +212,69 @@ module ThreeScaleToolbox
209
212
  attrs.fetch('cost_per_month', 0.0)
210
213
  end
211
214
 
215
+ def to_hash
216
+ {
217
+ 'plan' => attrs.reject { |key, _| APP_PLANS_BLACKLIST.include? key },
218
+ 'limits' => limits.map(&:to_hash),
219
+ 'pricingrules' => pricing_rules.map(&:to_hash),
220
+ 'plan_features' => features.map do |feature|
221
+ feature.reject { |key, _| PLAN_FEATURE_BLACKLIST.include? key }
222
+ end,
223
+ 'metrics' => uniq_metrics_to_hash,
224
+ 'methods' => uniq_methods_to_hash,
225
+ }
226
+ end
227
+
212
228
  private
213
229
 
230
+ def uniq_metrics_to_hash
231
+ limit_metrics = limits.flat_map do |limit|
232
+ # one of them (or both) could be nil
233
+ [
234
+ limit.product_metric, limit.backend_metric
235
+ ]
236
+ end.compact
237
+
238
+ pr_metrics = pricing_rules.flat_map do |pr|
239
+ # one of them (or both) could be nil
240
+ [
241
+ pr.product_metric, pr.backend_metric
242
+ ]
243
+ end.compact
244
+
245
+ # multiple limits can reference the same metric
246
+ # multiple pricing rules can reference the same metric
247
+ uniq_metrics = limit_metrics.concat(pr_metrics).each_with_object({}) do |metric, acc|
248
+ acc[metric.enriched_key] = metric
249
+ end.values
250
+
251
+ uniq_metrics.map { |metric| metric.to_hash }
252
+ end
253
+
254
+ def uniq_methods_to_hash
255
+ limit_methods = limits.flat_map do |limit|
256
+ # one of them (or both) could be nil
257
+ [
258
+ limit.product_method, limit.backend_method
259
+ ]
260
+ end.compact
261
+
262
+ pr_methods = pricing_rules.flat_map do |pr|
263
+ # one of them (or both) could be nil
264
+ [
265
+ pr.product_method, pr.backend_method
266
+ ]
267
+ end.compact
268
+
269
+ # multiple limits can reference the same method
270
+ # multiple pricing rules can reference the same method
271
+ uniq_methods = limit_methods.concat(pr_methods).each_with_object({}) do |method, acc|
272
+ acc[method.enriched_key] = method
273
+ end.values
274
+
275
+ uniq_methods.map { |method| method.to_hash }
276
+ end
277
+
214
278
  def read_plan_attrs
215
279
  raise ThreeScaleToolbox::InvalidIdError if id.zero?
216
280
 
@@ -161,6 +161,10 @@ module ThreeScaleToolbox
161
161
  remote.http_client.endpoint == other.remote.http_client.endpoint && id == other.id
162
162
  end
163
163
 
164
+ def find_metric_or_method(system_name)
165
+ (metrics + methods).find { |m| m.system_name == system_name }
166
+ end
167
+
164
168
  private
165
169
 
166
170
  def metrics_and_methods
@@ -6,6 +6,8 @@ module ThreeScaleToolbox
6
6
  VALID_PARAMS = %w[friendly_name system_name description].freeze
7
7
  public_constant :VALID_PARAMS
8
8
 
9
+ METHOD_BLACKLIST = %w[id links created_at updated_at parent_id].freeze
10
+
9
11
  class << self
10
12
  def create(backend:, attrs:)
11
13
  method = backend.remote.create_backend_method(backend.id, backend.hits.id,
@@ -71,6 +73,20 @@ module ThreeScaleToolbox
71
73
  remote.delete_backend_method backend.id, hits_id, id
72
74
  end
73
75
 
76
+ # enriched_key returns a metric key that will be unique for all
77
+ # metrics/methods from products and backends
78
+ def enriched_key
79
+ "backend.#{backend.id}.#{id}"
80
+ end
81
+
82
+ def to_hash
83
+ extra_attrs = {
84
+ 'backend_system_name' => backend.system_name
85
+ }
86
+
87
+ attrs.merge(extra_attrs).reject { |key, _| METHOD_BLACKLIST.include? key }
88
+ end
89
+
74
90
  private
75
91
 
76
92
  def hits_id
@@ -6,6 +6,8 @@ module ThreeScaleToolbox
6
6
  VALID_PARAMS = %w[friendly_name system_name unit description].freeze
7
7
  public_constant :VALID_PARAMS
8
8
 
9
+ METRIC_BLACKLIST = %w[id links created_at updated_at].freeze
10
+
9
11
  class << self
10
12
  def create(backend:, attrs:)
11
13
  metric = backend.remote.create_backend_metric(backend.id,
@@ -74,6 +76,20 @@ module ThreeScaleToolbox
74
76
  remote.delete_backend_metric backend.id, id
75
77
  end
76
78
 
79
+ # enriched_key returns a metric key that will be unique for all
80
+ # metrics from products and backends
81
+ def enriched_key
82
+ "backend.#{backend.id}.#{id}"
83
+ end
84
+
85
+ def to_hash
86
+ extra_attrs = {
87
+ 'backend_system_name' => backend.system_name
88
+ }
89
+
90
+ attrs.merge(extra_attrs).reject { |key, _| METRIC_BLACKLIST.include? key }
91
+ end
92
+
77
93
  private
78
94
 
79
95
  def process_attrs(metric_attrs)