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
@@ -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