3scale_toolbox 0.11.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -13,7 +13,14 @@ module ThreeScaleToolbox
13
13
  end
14
14
 
15
15
  existing_operations.each do |op|
16
- op.set(:metric_id, service_methods_index.fetch(op.method['system_name']))
16
+ method_attrs = methods_index.fetch(op.method['system_name'])
17
+ method = Entities::Method.new(
18
+ id: method_attrs.fetch('id'),
19
+ parent_id: hits_metric_id,
20
+ service: service
21
+ )
22
+ method.update(op.method)
23
+ op.set(:metric_id, method.id)
17
24
  end
18
25
  end
19
26
 
@@ -23,18 +30,18 @@ module ThreeScaleToolbox
23
30
  @hits_metric_id ||= service.hits['id']
24
31
  end
25
32
 
26
- def service_methods_index
27
- @service_methods_index ||= service.methods(hits_metric_id).each_with_object({}) do |method, acc|
28
- acc[method['system_name']] = method['id']
33
+ def methods_index
34
+ @methods_index ||= service.methods(hits_metric_id).each_with_object({}) do |method, acc|
35
+ acc[method['system_name']] = method
29
36
  end
30
37
  end
31
38
 
32
39
  def missing_operations
33
- operations.reject { |op| service_methods_index.key? op.method['system_name'] }
40
+ operations.reject { |op| methods_index.key? op.method['system_name'] }
34
41
  end
35
42
 
36
43
  def existing_operations
37
- operations.select { |op| service_methods_index.key? op.method['system_name'] }
44
+ operations.select { |op| methods_index.key? op.method['system_name'] }
38
45
  end
39
46
  end
40
47
  end
@@ -6,6 +6,7 @@ module ThreeScaleToolbox
6
6
  def method
7
7
  {
8
8
  'friendly_name' => friendly_name,
9
+ 'description' => description,
9
10
  'system_name' => system_name
10
11
  }
11
12
  end
@@ -21,6 +22,10 @@ module ThreeScaleToolbox
21
22
  def operation_id
22
23
  "#{operation[:verb]}#{operation[:path].gsub(/[^\w]/, '')}"
23
24
  end
25
+
26
+ def description
27
+ String(operation[:description])
28
+ end
24
29
  end
25
30
  end
26
31
  end
@@ -66,6 +66,10 @@ module ThreeScaleToolbox
66
66
  def staging_public_base_url
67
67
  context[:staging_public_base_url]
68
68
  end
69
+
70
+ def override_private_base_url
71
+ context[:override_private_base_url]
72
+ end
69
73
  end
70
74
  end
71
75
  end
@@ -51,7 +51,8 @@ module ThreeScaleToolbox
51
51
  public_base_path: public_base_path,
52
52
  path: op.path,
53
53
  verb: op.verb,
54
- operationId: op.operation_id
54
+ operationId: op.operation_id,
55
+ description: op.description,
55
56
  )
56
57
  end
57
58
  end
@@ -40,12 +40,9 @@ module ThreeScaleToolbox
40
40
  end
41
41
 
42
42
  def add_api_backend_settings(settings)
43
- return if api_spec.host.nil?
44
-
45
- scheme = api_spec.schemes.first || 'https'
46
- host = api_spec.host
43
+ return if private_base_url.nil?
47
44
 
48
- settings[:api_backend] = "#{scheme}://#{host}"
45
+ settings[:api_backend] = private_base_url
49
46
  end
50
47
 
51
48
  def add_security_proxy_settings(settings)
@@ -74,6 +71,16 @@ module ThreeScaleToolbox
74
71
  raise ThreeScaleToolbox::Error, "Unexpected security in_f field #{in_f}"
75
72
  end
76
73
  end
74
+
75
+ def private_base_url
76
+ override_private_base_url || private_base_url_from_openapi
77
+ end
78
+
79
+ def private_base_url_from_openapi
80
+ return if api_spec.host.nil?
81
+
82
+ "#{api_spec.schemes.first || 'https'}://#{api_spec.host}"
83
+ end
77
84
  end
78
85
  end
79
86
  end
@@ -33,18 +33,11 @@ module ThreeScaleToolbox
33
33
  end
34
34
 
35
35
  def print_data
36
- metrics.each do |metric|
36
+ service.metrics.each do |metric|
37
37
  puts FIELDS_TO_SHOW.map { |field| metric.fetch(field, '(empty)') }.join("\t")
38
38
  end
39
39
  end
40
40
 
41
- def metrics
42
- hits_id = service.hits['id']
43
- ThreeScaleToolbox::Helper.array_difference(service.metrics, service.methods(hits_id)) do |metric, method|
44
- ThreeScaleToolbox::Helper.compare_hashes(metric, method, %w[id])
45
- end
46
- end
47
-
48
41
  def service
49
42
  @service ||= find_service
50
43
  end
@@ -22,7 +22,6 @@ module ThreeScaleToolbox
22
22
  option nil, 'cost-per-month', 'Cost per month', argument: :required, transform: method(:Float)
23
23
  option nil, 'setup-fee', 'Setup fee', argument: :required, transform: method(:Float)
24
24
  option nil, 'trial-period-days', 'Trial period days', argument: :required, transform: method(:Integer)
25
- option nil, 'end-user-required', 'End user required. true or false', argument: :required, transform: ThreeScaleToolbox::Helper::BooleanTransformer.new
26
25
  param :remote
27
26
  param :service_ref
28
27
  param :plan_ref
@@ -38,7 +37,7 @@ module ThreeScaleToolbox
38
37
  plan = Entities::ApplicationPlan.create(service: service,
39
38
  plan_attrs: create_plan_attrs)
40
39
  else
41
- plan.update(plan_attrs) unless plan_attrs.empty?
40
+ plan.update(new_plan_attrs) unless new_plan_attrs.empty?
42
41
  end
43
42
 
44
43
  plan.make_default if option_default
@@ -65,12 +64,15 @@ module ThreeScaleToolbox
65
64
  end
66
65
 
67
66
  def create_plan_attrs
68
- plan_attrs.merge('system_name' => plan_ref,
69
- 'name' => plan_ref) { |_key, oldval, _newval| oldval }
67
+ new_attrs = plan_basic_attrs.merge('name' => plan_ref) { |_key, oldval, _newval| oldval }
68
+ new_attrs.tap do |params|
69
+ params['system_name'] = plan_ref
70
+ params['state'] = 'published' if option_publish
71
+ end
70
72
  end
71
73
 
72
- def plan_attrs
73
- plan_basic_attrs.tap do |params|
74
+ def new_plan_attrs
75
+ plan_basic_attrs.clone.tap do |params|
74
76
  params['state'] = 'published' if option_publish
75
77
  params['state'] = 'hidden' if option_hide
76
78
  end
@@ -80,7 +82,6 @@ module ThreeScaleToolbox
80
82
  {
81
83
  'name' => options[:name],
82
84
  'approval_required' => options[:'approval-required'],
83
- 'end_user_required' => options[:'end-user-required'],
84
85
  'cost_per_month' => options[:'cost-per-month'],
85
86
  'setup_fee' => options[:'setup-fee'],
86
87
  'trial_period_days' => options[:'trial-period-days']
@@ -20,7 +20,6 @@ module ThreeScaleToolbox
20
20
  option nil, 'cost-per-month', 'Cost per month', argument: :required, transform: method(:Float)
21
21
  option nil, 'setup-fee', 'Setup fee', argument: :required, transform: method(:Float)
22
22
  option nil, 'trial-period-days', 'Trial period days', argument: :required, transform: method(:Integer)
23
- option nil, 'end-user-required', 'End user required. true or false', argument: :required, transform: ThreeScaleToolbox::Helper::BooleanTransformer.new
24
23
  param :remote
25
24
  param :service_ref
26
25
  param :plan_name
@@ -56,7 +55,6 @@ module ThreeScaleToolbox
56
55
  'name' => arguments[:plan_name],
57
56
  'system_name' => options[:'system-name'],
58
57
  'approval_required' => options[:'approval-required'],
59
- 'end_user_required' => options[:'end-user-required'],
60
58
  'cost_per_month' => options[:'cost-per-month'],
61
59
  'setup_fee' => options[:'setup-fee'],
62
60
  'trial_period_days' => options[:'trial-period-days']
@@ -26,8 +26,6 @@ module ThreeScaleToolbox
26
26
  end
27
27
 
28
28
  def filtered_limit_methods
29
- # has to be filtered this way
30
- # looking up in metrics list does not work. Metric list includes methods and metrics
31
29
  result[:limits].select { |limit| limit.dig('metric', 'type') == 'method' }
32
30
  end
33
31
 
@@ -40,7 +38,6 @@ module ThreeScaleToolbox
40
38
  end
41
39
 
42
40
  def filtered_pricing_rule_methods
43
- # looking up in metrics list does not work. Metric list includes methods and metrics
44
41
  result[:pricingrules].select { |limit| limit.dig('metric', 'type') == 'method' }
45
42
  end
46
43
  end
@@ -26,7 +26,6 @@ module ThreeScaleToolbox
26
26
  end
27
27
 
28
28
  def filtered_limit_metrics
29
- # looking up in metrics list does not work. Metric list includes methods and metrics
30
29
  result[:limits].select { |limit| limit.dig('metric', 'type') == 'metric' }
31
30
  end
32
31
 
@@ -39,7 +38,6 @@ module ThreeScaleToolbox
39
38
  end
40
39
 
41
40
  def filtered_pricing_rule_metrics
42
- # looking up in metrics list does not work. Metric list includes methods and metrics
43
41
  result[:pricingrules].select { |limit| limit.dig('metric', 'type') == 'metric' }
44
42
  end
45
43
  end
@@ -9,7 +9,8 @@ module ThreeScaleToolbox
9
9
  # add metric system_name out of metric_id
10
10
  def call
11
11
  result[:pricingrules] = plan.pricing_rules.map do |pr|
12
- pr.tap { |e| e['metric'] = metric_info(e, 'PricingRule') }
12
+ pr.merge('metric' => metric_info(pr, 'PricingRule'),
13
+ 'cost_per_unit' => pr.fetch('cost_per_unit').to_f)
13
14
  end
14
15
  end
15
16
  end
@@ -54,8 +54,6 @@ module ThreeScaleToolbox
54
54
  end
55
55
 
56
56
  def metric_info(elem, elem_name)
57
- # Methods are included in metrics.
58
- # First methods must be checked, otherwise it could be considered as a false metric
59
57
  if (method = find_method(elem.fetch('metric_id')))
60
58
  { 'type' => 'method', 'system_name' => method.fetch('system_name') }
61
59
  elsif (metric = find_metric(elem.fetch('metric_id')))
@@ -9,10 +9,11 @@ module ThreeScaleToolbox
9
9
  def call
10
10
  plan_obj = Entities::ApplicationPlan.find(service: service, ref: plan_system_name)
11
11
  if plan_obj.nil?
12
- plan_obj = Entities::ApplicationPlan.create(service: service, plan_attrs: plan_attrs)
12
+ plan_obj = Entities::ApplicationPlan.create(service: service,
13
+ plan_attrs: create_plan_attrs)
13
14
  puts "Application plan created: #{plan_obj.id}"
14
15
  else
15
- res = plan_obj.update(plan_attrs)
16
+ res = plan_obj.update(update_plan_attrs)
16
17
  if (errors = res['errors'])
17
18
  raise ThreeScaleToolbox::Error, "Could not update application plan #{plan_system_name}. Errors: #{errors}"
18
19
  end
@@ -23,9 +24,13 @@ module ThreeScaleToolbox
23
24
 
24
25
  private
25
26
 
26
- def plan_attrs
27
+ def create_plan_attrs
27
28
  resource_plan.merge('system_name' => plan_system_name)
28
29
  end
30
+
31
+ def update_plan_attrs
32
+ resource_plan.reject { |key, _| %w[system_name].include? key }
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -18,9 +18,6 @@ module ThreeScaleToolbox
18
18
  private
19
19
 
20
20
  def missing_metrics
21
- # service metrics list includes methods
22
- # this array_difference method computes elements in resource_metrics not included in service_metrics
23
- # So methods will not be in the "missing_metrics" list, as long as array diff semantics are kept.
24
21
  ThreeScaleToolbox::Helper.array_difference(resource_metrics, service_metrics) do |a, b|
25
22
  ThreeScaleToolbox::Helper.compare_hashes(a, b, ['system_name'])
26
23
  end
@@ -21,15 +21,22 @@ module ThreeScaleToolbox
21
21
  private
22
22
 
23
23
  def missing_pricing_rules
24
- ThreeScaleToolbox::Helper.array_difference(resource_pr_processed, plan.pricing_rules) do |a, b|
24
+ ThreeScaleToolbox::Helper.array_difference(resource_pr_processed, remote_pr_processed) do |a, b|
25
25
  ThreeScaleToolbox::Helper.compare_hashes(a, b, %w[metric_id cost_per_unit min max])
26
26
  end
27
27
  end
28
28
 
29
+ def remote_pr_processed
30
+ plan.pricing_rules.map do |pr|
31
+ pr.merge('cost_per_unit' => pr.fetch('cost_per_unit').to_f)
32
+ end
33
+ end
34
+
29
35
  def resource_pr_processed
30
36
  resource_pricing_rules.map do |pr|
31
37
  metric = find_metric_by_system_name(pr.delete('metric_system_name'))
32
- pr.merge('metric_id' => metric.fetch('id'))
38
+ pr.merge('metric_id' => metric.fetch('id'),
39
+ 'cost_per_unit' => pr.fetch('cost_per_unit').to_f)
33
40
  end
34
41
  end
35
42
  end
@@ -81,6 +81,10 @@ module ThreeScaleToolbox
81
81
  context[:service_methods] ||= service.methods(service_hits['id'])
82
82
  end
83
83
 
84
+ def service_metrics_and_methods
85
+ service_metrics + service_methods
86
+ end
87
+
84
88
  def invalidate_service_methods
85
89
  context[:service_methods] = nil
86
90
  end
@@ -99,7 +103,7 @@ module ThreeScaleToolbox
99
103
  end
100
104
 
101
105
  def find_metric_by_system_name(system_name)
102
- service_metrics.find { |metric| metric['system_name'] == system_name }
106
+ service_metrics_and_methods.find { |metric| metric['system_name'] == system_name }
103
107
  end
104
108
 
105
109
  private
@@ -19,8 +19,12 @@ module ThreeScaleToolbox
19
19
  end
20
20
 
21
21
  def run
22
- latest_proxy_config.promote(to: to_env)
23
- puts "Proxy Configuration promoted to '#{to_env}'"
22
+ if promotable?
23
+ latest_proxy_config_from.promote(to: to_env)
24
+ puts "Proxy Configuration version #{latest_proxy_config_from.version} promoted to '#{to_env}'"
25
+ else
26
+ warn "warning: Nothing to promote. Proxy Configuration version #{latest_proxy_config_from.version} already promoted to production"
27
+ end
24
28
  end
25
29
 
26
30
  private
@@ -29,16 +33,28 @@ module ThreeScaleToolbox
29
33
  @remote ||= threescale_client(arguments[:remote])
30
34
  end
31
35
 
32
- def latest_proxy_config
33
- @proxy_config ||= find_proxy_config_latest
36
+ def latest_proxy_config_from
37
+ @proxy_config_from ||= find_proxy_config_latest_from
38
+ end
39
+
40
+ def latest_proxy_config_to
41
+ @proxy_config_to ||= find_proxy_config_latest_to
34
42
  end
35
43
 
36
- def find_proxy_config_latest
44
+ def promotable?
45
+ return latest_proxy_config_to.nil? || latest_proxy_config_from.version != latest_proxy_config_to.version
46
+ end
47
+
48
+ def find_proxy_config_latest_from
37
49
  Entities::ProxyConfig.find_latest(service: service, environment: from_env).tap do |pc|
38
50
  raise ThreeScaleToolbox::Error, "ProxyConfig #{from_env} in service #{service.id} does not exist" if pc.nil?
39
51
  end
40
52
  end
41
53
 
54
+ def find_proxy_config_latest_to
55
+ Entities::ProxyConfig.find_latest(service: service, environment: to_env)
56
+ end
57
+
42
58
  def service_ref
43
59
  arguments[:service_ref]
44
60
  end
@@ -20,15 +20,55 @@ module ThreeScaleToolbox
20
20
  find_by_text(ref, remote)
21
21
  end
22
22
 
23
- def find_by_text(text, client)
24
- account = client.find_account(email: text, buyer_provider_key: text,
25
- buyer_service_token: text)
23
+ # ref can be
24
+ # * Email of the account user.
25
+ # * Username of the account user.
26
+ # * ID of the account user.
27
+ # * [Master API] Provider key of the account
28
+ # * [Master API] Service token of the account service.
29
+ #
30
+ # email, username or user_id fields search with AND logic. Therefore separate requests.
31
+ # buyer_provider_key, buyer_service_token fields search with OR logic. Same request.
32
+ def find_by_text(ref, remote)
33
+ account = find_by_email(remote, ref)
34
+ return account unless account.nil?
35
+
36
+ account = find_by_username(remote, ref)
37
+ return account unless account.nil?
38
+
39
+ account = find_by_user_id(remote, ref)
40
+ return account unless account.nil?
41
+
42
+ account = find_by_provider_or_service_token(remote, ref)
43
+ return account unless account.nil?
44
+
45
+ nil
46
+ end
47
+
48
+ def find_by_email(remote, email)
49
+ generic_find(remote, email: email)
50
+ end
51
+
52
+ def find_by_username(remote, username)
53
+ generic_find(remote, username: username)
54
+ end
55
+
56
+ def find_by_user_id(remote, user_id)
57
+ generic_find(remote, user_id: user_id)
58
+ end
59
+
60
+ def find_by_provider_or_service_token(remote, text)
61
+ generic_find(remote, buyer_provider_key: text, buyer_service_token: text)
62
+ end
63
+
64
+ def generic_find(remote, criteria)
65
+ account = remote.find_account(criteria)
26
66
  if (errors = account['errors'])
27
67
  raise ThreeScaleToolbox::ThreeScaleApiError.new(
28
68
  'Account find returned errors', errors
29
69
  )
30
70
  end
31
- new(id: account['id'], remote: client, attrs: account)
71
+ new(id: account['id'], remote: remote, attrs: account)
32
72
  rescue ThreeScale::API::HttpClient::NotFoundError
33
73
  nil
34
74
  end