3scale_toolbox 0.11.0 → 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/3scale_toolbox.gemspec +42 -0
  3. data/LICENSE +201 -0
  4. data/NOTICE +15 -0
  5. data/README.md +8 -0
  6. data/lib/3scale_toolbox.rb +1 -0
  7. data/lib/3scale_toolbox/commands/application_command/apply_command.rb +21 -6
  8. data/lib/3scale_toolbox/commands/application_command/create_command.rb +3 -1
  9. data/lib/3scale_toolbox/commands/copy_command/copy_service.rb +5 -0
  10. data/lib/3scale_toolbox/commands/import_command/openapi.rb +5 -3
  11. data/lib/3scale_toolbox/commands/import_command/openapi/create_activedocs_step.rb +10 -2
  12. data/lib/3scale_toolbox/commands/import_command/openapi/create_method_step.rb +13 -6
  13. data/lib/3scale_toolbox/commands/import_command/openapi/method.rb +5 -0
  14. data/lib/3scale_toolbox/commands/import_command/openapi/step.rb +4 -0
  15. data/lib/3scale_toolbox/commands/import_command/openapi/threescale_api_spec.rb +2 -1
  16. data/lib/3scale_toolbox/commands/import_command/openapi/update_service_proxy_step.rb +12 -5
  17. data/lib/3scale_toolbox/commands/metrics_command/list_command.rb +1 -8
  18. data/lib/3scale_toolbox/commands/plans_command/apply_command.rb +8 -7
  19. data/lib/3scale_toolbox/commands/plans_command/create_command.rb +0 -2
  20. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_methods_step.rb +0 -3
  21. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_metrics_step.rb +0 -2
  22. data/lib/3scale_toolbox/commands/plans_command/export/read_plan_pricing_rules_step.rb +2 -1
  23. data/lib/3scale_toolbox/commands/plans_command/export/step.rb +0 -2
  24. data/lib/3scale_toolbox/commands/plans_command/import/create_or_update_app_plan_step.rb +8 -3
  25. data/lib/3scale_toolbox/commands/plans_command/import/import_plan_metrics_step.rb +0 -3
  26. data/lib/3scale_toolbox/commands/plans_command/import/import_plan_pricing_rules_step.rb +9 -2
  27. data/lib/3scale_toolbox/commands/plans_command/import/step.rb +5 -1
  28. data/lib/3scale_toolbox/commands/proxy_config_command/promote_command.rb +21 -5
  29. data/lib/3scale_toolbox/entities/account.rb +44 -4
  30. data/lib/3scale_toolbox/entities/activedocs.rb +1 -1
  31. data/lib/3scale_toolbox/entities/application.rb +21 -13
  32. data/lib/3scale_toolbox/entities/application_plan.rb +13 -12
  33. data/lib/3scale_toolbox/entities/base_entity.rb +1 -1
  34. data/lib/3scale_toolbox/entities/method.rb +1 -1
  35. data/lib/3scale_toolbox/entities/metric.rb +1 -1
  36. data/lib/3scale_toolbox/entities/service.rb +30 -15
  37. data/lib/3scale_toolbox/remotes.rb +1 -1
  38. data/lib/3scale_toolbox/resource_reader.rb +5 -2
  39. data/lib/3scale_toolbox/swagger/swagger.rb +5 -3
  40. data/lib/3scale_toolbox/tasks/copy_activedocs_task.rb +27 -9
  41. data/lib/3scale_toolbox/tasks/copy_limits_task.rb +4 -1
  42. data/lib/3scale_toolbox/tasks/copy_mapping_rules_task.rb +4 -1
  43. data/lib/3scale_toolbox/tasks/copy_methods_task.rb +20 -16
  44. data/lib/3scale_toolbox/tasks/copy_metrics_task.rb +14 -14
  45. data/lib/3scale_toolbox/tasks/copy_pricingrules_task.rb +4 -1
  46. data/lib/3scale_toolbox/tasks/copy_task.rb +40 -0
  47. data/lib/3scale_toolbox/version.rb +1 -1
  48. data/licenses.xml +303 -0
  49. metadata +8 -4
@@ -42,7 +42,7 @@ module ThreeScaleToolbox
42
42
  attr_reader :id, :remote
43
43
 
44
44
  def initialize(id:, remote:, attrs: nil)
45
- @id = id
45
+ @id = id.to_i
46
46
  @remote = remote
47
47
  @attrs = attrs
48
48
  end
@@ -16,37 +16,37 @@ module ThreeScaleToolbox
16
16
  # * User_key (API key)
17
17
  # * App_id (from app_id/app_key pair)
18
18
  # * Client ID (for OAuth and OpenID Connect authentication modes)
19
- def find(remote:, ref:)
20
- app = find_by_user_key(remote, ref)
19
+ def find(remote:, service_id: nil, ref:)
20
+ app = find_by_user_key(remote, service_id, ref)
21
21
  return app unless app.nil?
22
22
 
23
- app = find_by_app_id(remote, ref)
23
+ app = find_by_app_id(remote, service_id, ref)
24
24
  return app unless app.nil?
25
25
 
26
- app = find_by_id(remote, ref)
26
+ app = find_by_id(remote, service_id, ref)
27
27
  return app unless app.nil?
28
28
 
29
29
  nil
30
30
  end
31
31
 
32
- def find_by_id(remote, id)
33
- generic_find(remote, :id, id)
32
+ def find_by_id(remote, service_id, id)
33
+ generic_find(remote, service_id, :id, id)
34
34
  end
35
35
 
36
- def find_by_user_key(remote, user_key)
37
- generic_find(remote, :user_key, user_key)
36
+ def find_by_user_key(remote, service_id, user_key)
37
+ generic_find(remote, service_id, :user_key, user_key)
38
38
  end
39
39
 
40
- def find_by_app_id(remote, app_id)
41
- generic_find(remote, :application_id, app_id)
40
+ def find_by_app_id(remote, service_id, app_id)
41
+ generic_find(remote, service_id, :application_id, app_id)
42
42
  end
43
43
 
44
44
  private
45
45
 
46
- def generic_find(remote, type, ref)
46
+ def generic_find(remote, service_id, type, ref)
47
47
  # find_application criteria only accepts one parameter.
48
48
  # Otherwise unexpected behavior
49
- attrs = remote.find_application(type => ref)
49
+ attrs = remote.find_application(service_id: service_id, type => ref)
50
50
  if (errors = attrs['errors'])
51
51
  raise ThreeScaleToolbox::ThreeScaleApiError.new('Application find error', errors)
52
52
  end
@@ -60,7 +60,7 @@ module ThreeScaleToolbox
60
60
  attr_reader :id, :remote
61
61
 
62
62
  def initialize(id:, remote:, attrs: nil)
63
- @id = id
63
+ @id = id.to_i
64
64
  @remote = remote
65
65
  @attrs = attrs
66
66
  end
@@ -81,6 +81,8 @@ module ThreeScaleToolbox
81
81
  end
82
82
 
83
83
  def resume
84
+ return attrs if live?
85
+
84
86
  new_attrs = remote.resume_application(account_id, id)
85
87
  if (errors = new_attrs['errors'])
86
88
  raise ThreeScaleToolbox::ThreeScaleApiError.new('Application has not been resumed', errors)
@@ -92,6 +94,8 @@ module ThreeScaleToolbox
92
94
  end
93
95
 
94
96
  def suspend
97
+ return attrs unless live?
98
+
95
99
  new_attrs = remote.suspend_application(account_id, id)
96
100
  if (errors = new_attrs['errors'])
97
101
  raise ThreeScaleToolbox::ThreeScaleApiError.new('Application has not been suspended', errors)
@@ -106,6 +110,10 @@ module ThreeScaleToolbox
106
110
  remote.delete_application account_id, id
107
111
  end
108
112
 
113
+ def live?
114
+ attrs.fetch('state') == 'live'
115
+ end
116
+
109
117
  private
110
118
 
111
119
  def account_id
@@ -39,7 +39,7 @@ module ThreeScaleToolbox
39
39
  attr_reader :id, :service, :remote
40
40
 
41
41
  def initialize(id:, service:, attrs: nil)
42
- @id = id
42
+ @id = id.to_i
43
43
  @service = service
44
44
  @remote = service.remote
45
45
  @attrs = attrs
@@ -50,6 +50,8 @@ module ThreeScaleToolbox
50
50
  end
51
51
 
52
52
  def update(plan_attrs)
53
+ return attrs if update_plan_attrs(plan_attrs).empty?
54
+
53
55
  new_attrs = remote.update_application_plan(service.id, id,
54
56
  update_plan_attrs(plan_attrs))
55
57
  if (errors = new_attrs['errors'])
@@ -170,6 +172,10 @@ module ThreeScaleToolbox
170
172
  end
171
173
  end
172
174
 
175
+ def published?
176
+ attrs.fetch('state') == 'published'
177
+ end
178
+
173
179
  private
174
180
 
175
181
  def read_plan_attrs
@@ -181,17 +187,12 @@ module ThreeScaleToolbox
181
187
  plan_attrs
182
188
  end
183
189
 
184
- def update_plan_attrs(source_attrs)
185
- # shallow copy is enough
186
- source_attrs.clone.tap do |new_plan_attrs|
187
- new_plan_attrs.delete('id')
188
- new_plan_attrs.delete('links')
189
- new_plan_attrs.delete('system_name')
190
- # plans are created by default in hidden state
191
- # If published is required, 'state_event' attr has to be added
192
- state = new_plan_attrs.delete('state')
193
- new_plan_attrs['state_event'] = 'publish' if state == 'published'
194
- new_plan_attrs['state_event'] = 'hide' if state == 'hidden'
190
+ def update_plan_attrs(update_attrs)
191
+ new_attrs = update_attrs.reject { |key, _| %w[id links system_name].include? key }
192
+ new_attrs.tap do |params|
193
+ state = params.delete('state')
194
+ params['state_event'] = 'publish' if state == 'published' && !published?
195
+ params['state_event'] = 'hide' if state == 'hidden' && published?
195
196
  end
196
197
  end
197
198
 
@@ -16,7 +16,7 @@ module ThreeScaleToolbox
16
16
  attr_reader :id, :attrs, :remote
17
17
 
18
18
  def initialize(id:, remote:, attrs: nil, verbose: false)
19
- @id = id
19
+ @id = id.to_i
20
20
  @remote = remote
21
21
  @attrs = attrs
22
22
  @verbose = verbose
@@ -30,7 +30,7 @@ module ThreeScaleToolbox
30
30
  attr_reader :id, :parent_id, :service, :remote
31
31
 
32
32
  def initialize(id:, parent_id:, service:, attrs: nil)
33
- @id = id
33
+ @id = id.to_i
34
34
  @service = service
35
35
  @parent_id = parent_id
36
36
  @remote = service.remote
@@ -29,7 +29,7 @@ module ThreeScaleToolbox
29
29
  attr_reader :id, :service, :remote
30
30
 
31
31
  def initialize(id:, service:, attrs: nil)
32
- @id = id
32
+ @id = id.to_i
33
33
  @service = service
34
34
  @remote = service.remote
35
35
  @attrs = attrs
@@ -66,7 +66,7 @@ module ThreeScaleToolbox
66
66
  attr_reader :id, :remote
67
67
 
68
68
  def initialize(id:, remote:, attrs: nil)
69
- @id = id
69
+ @id = id.to_i
70
70
  @remote = remote
71
71
  @attrs = attrs
72
72
  end
@@ -95,21 +95,18 @@ module ThreeScaleToolbox
95
95
  end
96
96
 
97
97
  def metrics
98
- service_metrics = remote.list_metrics id
99
- if service_metrics.respond_to?(:has_key?) && (errors = service_metrics['errors'])
100
- raise ThreeScaleToolbox::ThreeScaleApiError.new('Service metrics not read', errors)
101
- end
98
+ # cache result to reuse
99
+ metric_and_method_list = metrics_and_methods
100
+ hits_metric_obj = hits_metric(metric_and_method_list)
101
+ hits_id = hits_metric_obj.fetch('id')
102
102
 
103
- service_metrics
103
+ ThreeScaleToolbox::Helper.array_difference(metric_and_method_list, methods(hits_id)) do |metric, method|
104
+ ThreeScaleToolbox::Helper.compare_hashes(metric, method, %w[id])
105
+ end
104
106
  end
105
107
 
106
108
  def hits
107
- hits_metric = metrics.find do |metric|
108
- metric['system_name'] == 'hits'
109
- end
110
- raise ThreeScaleToolbox::Error, 'missing hits metric' if hits_metric.nil?
111
-
112
- hits_metric
109
+ hits_metric(metrics_and_methods)
113
110
  end
114
111
 
115
112
  def methods(parent_metric_id)
@@ -121,6 +118,15 @@ module ThreeScaleToolbox
121
118
  service_methods
122
119
  end
123
120
 
121
+ def metrics_and_methods
122
+ m_m = remote.list_metrics id
123
+ if m_m.respond_to?(:has_key?) && (errors = m_m['errors'])
124
+ raise ThreeScaleToolbox::ThreeScaleApiError.new('Service metrics not read', errors)
125
+ end
126
+
127
+ m_m
128
+ end
129
+
124
130
  def plans
125
131
  service_plans = remote.list_service_application_plans id
126
132
  if service_plans.respond_to?(:has_key?) && (errors = service_plans['errors'])
@@ -174,9 +180,7 @@ module ThreeScaleToolbox
174
180
  end
175
181
 
176
182
  tenant_activedocs.select do |activedoc|
177
- # service_id is optional attr. It would return nil and would not match
178
- # activedocs endpoints return service_id as integers
179
- activedoc['service_id'] == id.to_i
183
+ activedoc['service_id'] == id
180
184
  end
181
185
  end
182
186
 
@@ -245,8 +249,19 @@ module ThreeScaleToolbox
245
249
  end
246
250
  end
247
251
 
252
+ def ==(other)
253
+ remote.http_client.endpoint == other.remote.http_client.endpoint && id == other.id
254
+ end
255
+
248
256
  private
249
257
 
258
+ def hits_metric(metric_list)
259
+ hits_metric = metric_list.find { |metric| metric['system_name'] == 'hits' }
260
+ raise ThreeScaleToolbox::Error, 'missing hits metric' if hits_metric.nil?
261
+
262
+ hits_metric
263
+ end
264
+
250
265
  def service_attrs
251
266
  svc = remote.show_service id
252
267
  if (errors = svc['errors'])
@@ -52,7 +52,7 @@ module ThreeScaleToolbox
52
52
  end
53
53
 
54
54
  def fetch(name)
55
- all.fetch name
55
+ all.fetch(name) { raise_not_found(name) }
56
56
  end
57
57
 
58
58
  private
@@ -27,8 +27,11 @@ module ThreeScaleToolbox
27
27
  end
28
28
 
29
29
  # Detect format from file extension
30
- def read_file(resource)
31
- File.read(resource)
30
+ def read_file(filename)
31
+ raise ThreeScaleToolbox::Error, "File not found: #{filename} " unless File.file?(filename)
32
+ raise ThreeScaleToolbox::Error, "File not readable: #{filename} " unless File.readable?(filename)
33
+
34
+ File.read(filename)
32
35
  end
33
36
 
34
37
  def read_stdin(_resource)
@@ -23,12 +23,13 @@ module ThreeScaleToolbox
23
23
  end
24
24
 
25
25
  class Operation
26
- attr_reader :verb, :operation_id, :path
26
+ attr_reader :verb, :operation_id, :path, :description
27
27
 
28
- def initialize(verb:, operation_id:, path:)
28
+ def initialize(verb:, operation_id:, path:, description:)
29
29
  @verb = verb
30
30
  @operation_id = operation_id
31
31
  @path = path
32
+ @description = description
32
33
  end
33
34
  end
34
35
 
@@ -83,7 +84,8 @@ module ThreeScaleToolbox
83
84
  path_obj.flat_map do |method, operation|
84
85
  next unless %w[get head post put patch delete trace options].include? method
85
86
 
86
- Operation.new(verb: method, path: path, operation_id: operation['operationId'])
87
+ Operation.new(verb: method, path: path, description: operation['description'],
88
+ operation_id: operation['operationId'])
87
89
  end.compact
88
90
  end
89
91
  end
@@ -6,17 +6,35 @@ module ThreeScaleToolbox
6
6
  def call
7
7
  puts 'copying all service ActiveDocs'
8
8
 
9
- target_activedocs = source.activedocs.map do |source_activedoc|
10
- source_activedoc.clone.tap do |target_activedoc|
11
- target_activedoc.delete('id')
12
- target_activedoc['service_id'] = target.id
13
- target_activedoc['system_name'] = "#{target_activedoc['system_name']}#{target.id}"
14
- end
9
+ source.activedocs.each(&method(:apply_target_activedoc))
10
+ end
11
+
12
+ private
13
+
14
+ def apply_target_activedoc(attrs)
15
+ activedocs = Entities::ActiveDocs.find_by_system_name(remote: target.remote,
16
+ system_name: attrs['system_name'])
17
+ if activedocs.nil?
18
+ Entities::ActiveDocs.create(remote: target.remote, attrs: create_attrs(attrs))
19
+ elsif activedocs.attrs.fetch('service_id') == target.id
20
+ activedocs.update(update_attrs(attrs))
21
+ else
22
+ # activedocs with same system_name exists, but now owned by target service
23
+ new_attrs = create_attrs(attrs)
24
+ new_attrs['system_name'] = "#{attrs['system_name']}#{target.id}"
25
+ Entities::ActiveDocs.create(remote: target.remote, attrs: new_attrs)
15
26
  end
27
+ end
28
+
29
+ def update_attrs(old_attrs)
30
+ create_attrs(old_attrs)
31
+ end
16
32
 
17
- target_activedocs.each do |ad|
18
- res = target.remote.create_activedocs(ad)
19
- raise ThreeScaleToolbox::Error, "ActiveDocs has not been created. Errors: #{res['errors']}" unless res['errors'].nil?
33
+ def create_attrs(old_attrs)
34
+ # keep same system_name
35
+ new_attrs = old_attrs.reject { |key, _| %w[id created_at updated_at].include? key }
36
+ new_attrs.tap do |attrs|
37
+ attrs['service_id'] = target.id
20
38
  end
21
39
  end
22
40
  end
@@ -5,7 +5,6 @@ module ThreeScaleToolbox
5
5
  include Helper
6
6
 
7
7
  def call
8
- metrics_map = metrics_mapping(source.metrics, target.metrics)
9
8
  plan_mapping = application_plan_mapping(source.plans, target.plans)
10
9
  plan_mapping.each do |plan_id, target_plan|
11
10
  source_plan = ThreeScaleToolbox::Entities::ApplicationPlan.new(id: plan_id, service: source)
@@ -22,6 +21,10 @@ module ThreeScaleToolbox
22
21
 
23
22
  private
24
23
 
24
+ def metrics_map
25
+ @metrics_map ||= metrics_mapping(source_metrics_and_methods, target_metrics_and_methods)
26
+ end
27
+
25
28
  def missing_limits(source_limits, target_limits, metrics_map)
26
29
  ThreeScaleToolbox::Helper.array_difference(source_limits, target_limits) do |limit, target|
27
30
  ThreeScaleToolbox::Helper.compare_hashes(limit, target, ['period']) &&
@@ -5,7 +5,6 @@ module ThreeScaleToolbox
5
5
  include Helper
6
6
 
7
7
  def call
8
- metrics_map = metrics_mapping(source.metrics, target.metrics)
9
8
  missing_rules = missing_mapping_rules(source.mapping_rules,
10
9
  target.mapping_rules, metrics_map)
11
10
  missing_rules.each do |mapping_rule|
@@ -18,6 +17,10 @@ module ThreeScaleToolbox
18
17
 
19
18
  private
20
19
 
20
+ def metrics_map
21
+ @metrics_map ||= metrics_mapping(source_metrics_and_methods, target_metrics_and_methods)
22
+ end
23
+
21
24
  def missing_mapping_rules(source_rules, target_rules, metrics_map)
22
25
  ThreeScaleToolbox::Helper.array_difference(source_rules, target_rules) do |source_rule, target_rule|
23
26
  ThreeScaleToolbox::Helper.compare_hashes(source_rule, target_rule, %w[pattern http_method delta]) &&
@@ -4,26 +4,30 @@ module ThreeScaleToolbox
4
4
  include CopyTask
5
5
 
6
6
  def call
7
- source_hits_id = source.hits['id']
8
- target_hits_id = target.hits['id']
9
- source_methods = source.methods source_hits_id
10
- target_methods = target.methods target_hits_id
11
- puts "original service hits metric #{source_hits_id} has #{source_methods.size} methods"
12
- puts "target service hits metric #{target_hits_id} has #{target_methods.size} methods"
13
- missing = missing_methods(source_methods, target_methods).each do |method|
14
- Entities::Method.create(
15
- service: target,
16
- parent_id: target_hits_id,
17
- attrs: ThreeScaleToolbox::Helper.filter_params(%w[friendly_name system_name], method)
18
- )
19
- end
20
- puts "created #{missing.size} missing methods on target service"
7
+ puts "original service hits metric #{source_hits.fetch('id')} has #{source_methods.size} methods"
8
+ puts "target service hits metric #{target_hits.fetch('id')} has #{target_methods.size} methods"
9
+ missing_methods.each(&method(:create_method))
10
+ puts "created #{missing_methods.size} missing methods on target service"
11
+ invalidate_target_methods if missing_methods.size.positive?
21
12
  end
22
13
 
23
14
  private
24
15
 
25
- def missing_methods(source_methods, target_methods)
26
- ThreeScaleToolbox::Helper.array_difference(source_methods, target_methods) do |method, target|
16
+ def create_method(method)
17
+ Entities::Method.create(
18
+ service: target,
19
+ parent_id: target_hits.fetch('id'),
20
+ attrs: ThreeScaleToolbox::Helper.filter_params(%w[friendly_name system_name], method)
21
+ )
22
+ rescue ThreeScaleToolbox::ThreeScaleApiError => e
23
+ raise e unless ThreeScaleToolbox::Helper.system_name_already_taken_error?(e.apierrors)
24
+
25
+ warn "[WARN] method #{method.fetch('system_name')} not created. " \
26
+ 'Metric with the same system_name exists.'
27
+ end
28
+
29
+ def missing_methods
30
+ @missing_methods ||= ThreeScaleToolbox::Helper.array_difference(source_methods, target_methods) do |method, target|
27
31
  ThreeScaleToolbox::Helper.compare_hashes(method, target, ['system_name'])
28
32
  end
29
33
  end