3scale_toolbox 0.5.1 → 0.6.0
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.
- checksums.yaml +4 -4
- data/README.md +143 -23
- data/exe/3scale +10 -3
- data/lib/3scale_toolbox.rb +18 -0
- data/lib/3scale_toolbox/3scale_client_factory.rb +33 -0
- data/lib/3scale_toolbox/base_command.rb +52 -14
- data/lib/3scale_toolbox/cli.rb +26 -5
- data/lib/3scale_toolbox/cli/error_handler.rb +120 -0
- data/lib/3scale_toolbox/commands.rb +3 -9
- data/lib/3scale_toolbox/commands/3scale_command.rb +8 -6
- data/lib/3scale_toolbox/commands/copy_command.rb +4 -4
- data/lib/3scale_toolbox/commands/copy_command/copy_service.rb +40 -193
- data/lib/3scale_toolbox/commands/help_command.rb +1 -1
- data/lib/3scale_toolbox/commands/import_command.rb +6 -4
- data/lib/3scale_toolbox/commands/import_command/import_csv.rb +15 -41
- data/lib/3scale_toolbox/commands/import_command/openapi.rb +70 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_mapping_rule_step.rb +18 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_method_step.rb +39 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_service_step.rb +69 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/mapping_rule.rb +35 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/method.rb +25 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/operation.rb +22 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/resource_reader.rb +49 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/step.rb +45 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/threescale_api_spec.rb +33 -0
- data/lib/3scale_toolbox/commands/remote_command.rb +36 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_add.rb +47 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_list.rb +29 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_remove.rb +26 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_rename.rb +42 -0
- data/lib/3scale_toolbox/commands/update_command.rb +4 -4
- data/lib/3scale_toolbox/commands/update_command/update_service.rb +45 -235
- data/lib/3scale_toolbox/configuration.rb +35 -0
- data/lib/3scale_toolbox/entities.rb +1 -0
- data/lib/3scale_toolbox/entities/service.rb +113 -0
- data/lib/3scale_toolbox/error.rb +8 -0
- data/lib/3scale_toolbox/helper.rb +37 -0
- data/lib/3scale_toolbox/remotes.rb +93 -0
- data/lib/3scale_toolbox/tasks.rb +10 -0
- data/lib/3scale_toolbox/tasks/copy_app_plans_task.rb +31 -0
- data/lib/3scale_toolbox/tasks/copy_limits_task.rb +36 -0
- data/lib/3scale_toolbox/tasks/copy_mapping_rules_task.rb +29 -0
- data/lib/3scale_toolbox/tasks/copy_methods_task.rb +29 -0
- data/lib/3scale_toolbox/tasks/copy_metrics_task.rb +33 -0
- data/lib/3scale_toolbox/tasks/copy_service_proxy_task.rb +12 -0
- data/lib/3scale_toolbox/tasks/copy_task.rb +26 -0
- data/lib/3scale_toolbox/tasks/destroy_mapping_rules_task.rb +22 -0
- data/lib/3scale_toolbox/tasks/helper_task.rb +25 -0
- data/lib/3scale_toolbox/tasks/update_service_settings_task.rb +32 -0
- data/lib/3scale_toolbox/version.rb +1 -1
- metadata +87 -11
@@ -1,20 +1,22 @@
|
|
1
1
|
require 'cri'
|
2
2
|
require '3scale_toolbox/base_command'
|
3
3
|
require '3scale_toolbox/commands/import_command/import_csv'
|
4
|
+
require '3scale_toolbox/commands/import_command/openapi'
|
4
5
|
|
5
6
|
module ThreeScaleToolbox
|
6
7
|
module Commands
|
7
8
|
module ImportCommand
|
8
|
-
|
9
|
+
include ThreeScaleToolbox::Command
|
9
10
|
def self.command
|
10
11
|
Cri::Command.define do
|
11
12
|
name 'import'
|
12
|
-
usage 'import <command> [options]'
|
13
|
-
summary '
|
14
|
-
description '3scale
|
13
|
+
usage 'import <sub-command> [options]'
|
14
|
+
summary 'import super command'
|
15
|
+
description 'Importing 3scale entities'
|
15
16
|
end
|
16
17
|
end
|
17
18
|
add_subcommand(ImportCsvSubcommand)
|
19
|
+
add_subcommand(OpenAPI::OpenAPISubcommand)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -7,45 +7,24 @@ require '3scale_toolbox/base_command'
|
|
7
7
|
module ThreeScaleToolbox
|
8
8
|
module Commands
|
9
9
|
module ImportCommand
|
10
|
-
|
11
|
-
|
10
|
+
class ImportCsvSubcommand < Cri::CommandRunner
|
11
|
+
include ThreeScaleToolbox::Command
|
12
|
+
|
12
13
|
def self.command
|
13
14
|
Cri::Command.define do
|
14
15
|
name 'csv'
|
15
16
|
usage 'csv [opts] -d <dst> -f <file>'
|
16
|
-
summary '
|
17
|
+
summary 'import csv file'
|
17
18
|
description 'Create new services, metrics, methods and mapping rules from CSV formatted file'
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
option :d, :destination, '3scale target instance. Url or remote name', argument: :required
|
21
|
+
option :f, 'file', 'CSV formatted file', argument: :required
|
21
22
|
|
22
|
-
|
23
|
-
ImportCsvSubcommand.run opts, args
|
24
|
-
end
|
23
|
+
runner ImportCsvSubcommand
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
puts message
|
30
|
-
exit 1
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.fetch_required_option(options, key)
|
34
|
-
options.fetch(key) { exit_with_message "error: Missing argument #{key}" }
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.provider_key_from_url(url)
|
38
|
-
URI(url).user
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.endpoint_from_url(url)
|
42
|
-
uri = URI(url)
|
43
|
-
uri.user = nil
|
44
|
-
|
45
|
-
uri.to_s
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.auth_app_key_according_service(service)
|
27
|
+
def auth_app_key_according_service(service)
|
49
28
|
case service['backend_version']
|
50
29
|
when '1'
|
51
30
|
'user_key'
|
@@ -56,14 +35,9 @@ module ThreeScaleToolbox
|
|
56
35
|
end
|
57
36
|
end
|
58
37
|
|
59
|
-
def
|
60
|
-
|
61
|
-
provider_key = provider_key_from_url destination
|
38
|
+
def import_csv(destination, file_path)
|
39
|
+
client = threescale_client(destination)
|
62
40
|
|
63
|
-
client = ThreeScale::API.new(endpoint: endpoint,
|
64
|
-
provider_key: provider_key,
|
65
|
-
verify_ssl: !insecure
|
66
|
-
)
|
67
41
|
data = CSV.read file_path
|
68
42
|
headings = data.shift
|
69
43
|
services = {}
|
@@ -163,11 +137,11 @@ module ThreeScaleToolbox
|
|
163
137
|
puts "#{stats[:mapping_rules]} mapping rules have been created"
|
164
138
|
end
|
165
139
|
|
166
|
-
def
|
167
|
-
destination = fetch_required_option(
|
168
|
-
file_path = fetch_required_option(
|
169
|
-
|
170
|
-
import_csv(destination, file_path
|
140
|
+
def run
|
141
|
+
destination = fetch_required_option(:destination)
|
142
|
+
file_path = fetch_required_option(:file)
|
143
|
+
|
144
|
+
import_csv(destination, file_path)
|
171
145
|
end
|
172
146
|
end
|
173
147
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'swagger'
|
2
|
+
require '3scale_toolbox/commands/import_command/openapi/method'
|
3
|
+
require '3scale_toolbox/commands/import_command/openapi/mapping_rule'
|
4
|
+
require '3scale_toolbox/commands/import_command/openapi/operation'
|
5
|
+
require '3scale_toolbox/commands/import_command/openapi/step'
|
6
|
+
require '3scale_toolbox/commands/import_command/openapi/resource_reader'
|
7
|
+
require '3scale_toolbox/commands/import_command/openapi/threescale_api_spec'
|
8
|
+
require '3scale_toolbox/commands/import_command/openapi/create_method_step'
|
9
|
+
require '3scale_toolbox/commands/import_command/openapi/create_mapping_rule_step'
|
10
|
+
require '3scale_toolbox/commands/import_command/openapi/create_service_step'
|
11
|
+
|
12
|
+
module ThreeScaleToolbox
|
13
|
+
module Commands
|
14
|
+
module ImportCommand
|
15
|
+
module OpenAPI
|
16
|
+
class OpenAPISubcommand < Cri::CommandRunner
|
17
|
+
include ThreeScaleToolbox::Command
|
18
|
+
include ResourceReader
|
19
|
+
|
20
|
+
def self.command
|
21
|
+
Cri::Command.define do
|
22
|
+
name 'openapi'
|
23
|
+
usage 'openapi [opts] -d <dst> <spec>'
|
24
|
+
summary 'Import API defintion in OpenAPI specification'
|
25
|
+
description 'Using an API definition format like OpenAPI, import to your 3scale API'
|
26
|
+
|
27
|
+
option :d, :destination, '3scale target instance. Format: "http[s]://<authentication>@3scale_domain"', argument: :required
|
28
|
+
option :t, 'target_system_name', 'Target system name', argument: :required
|
29
|
+
param :openapi_resource
|
30
|
+
|
31
|
+
runner OpenAPISubcommand
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
context = create_context
|
37
|
+
|
38
|
+
tasks = []
|
39
|
+
tasks << CreateServiceStep.new(context)
|
40
|
+
tasks << CreateMethodsStep.new(context)
|
41
|
+
tasks << ThreeScaleToolbox::Tasks::DestroyMappingRulesTask.new(context)
|
42
|
+
tasks << CreateMappingRulesStep.new(context)
|
43
|
+
|
44
|
+
# run tasks
|
45
|
+
tasks.each(&:call)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def create_context
|
51
|
+
{
|
52
|
+
api_spec: ThreeScaleApiSpec.new(load_openapi),
|
53
|
+
threescale_client: threescale_client(fetch_required_option(:destination)),
|
54
|
+
target_system_name: options[:target_system_name]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_openapi
|
59
|
+
Swagger.build(load_resource(arguments[:openapi_resource]))
|
60
|
+
# Disable validation step because https://petstore.swagger.io/v2/swagger.json
|
61
|
+
# does not pass validation. Maybe library's schema is outdated?
|
62
|
+
# openapi.tap(&:validate)
|
63
|
+
rescue Swagger::InvalidDefinition, Hashie::CoercionError, Psych::SyntaxError => e
|
64
|
+
raise ThreeScaleToolbox::Error, "OpenAPI schema validation failed: #{e.message}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
class CreateMappingRulesStep
|
6
|
+
include Step
|
7
|
+
|
8
|
+
def call
|
9
|
+
operations.each do |op|
|
10
|
+
service.create_mapping_rule(op.mapping_rule)
|
11
|
+
puts "Created #{op.http_method} #{op.pattern} endpoint"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
class CreateMethodsStep
|
6
|
+
include Step
|
7
|
+
|
8
|
+
def call
|
9
|
+
hits_metric_id = service.hits['id']
|
10
|
+
operations.each do |op|
|
11
|
+
res = service.create_method(hits_metric_id, op.method)
|
12
|
+
metric_id = res['id']
|
13
|
+
# if method system_name exists, ignore error and get metric_id
|
14
|
+
# Make operation indempotent
|
15
|
+
unless res['errors'].nil?
|
16
|
+
if !res['errors']['system_name'].nil? \
|
17
|
+
&& res['errors']['system_name'][0] =~ /has already been taken/
|
18
|
+
metric_id = method_id_by_system_name[op.method['system_name']]
|
19
|
+
else
|
20
|
+
raise Error, "Metohd has not been saved. Errors: #{res['errors']}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
op.set(:metric_id, metric_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def method_id_by_system_name
|
31
|
+
@method_id_by_system_name ||= service.methods.each_with_object({}) do |method, acc|
|
32
|
+
acc[method['system_name']] = method['id']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
class CreateServiceStep
|
6
|
+
include Step
|
7
|
+
|
8
|
+
##
|
9
|
+
# Creates service with a given system_name
|
10
|
+
# If service already exists, update basic settings like name and description
|
11
|
+
def call
|
12
|
+
# Create service and update context
|
13
|
+
self.service = Entities::Service.create(remote: threescale_client,
|
14
|
+
service: service_settings,
|
15
|
+
system_name: service_system_name)
|
16
|
+
puts "Created service id: #{service.id}, name: #{service_name}"
|
17
|
+
rescue ThreeScaleToolbox::Error => e
|
18
|
+
raise unless e.message =~ /"system_name"=>\["has already been taken"\]/
|
19
|
+
|
20
|
+
# Update service and update context
|
21
|
+
self.service = Entities::Service.new(id: service_id, remote: threescale_client)
|
22
|
+
service.update_service(service_settings)
|
23
|
+
puts "Updated service id: #{service.id}, name: #{service_name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def service_system_name
|
29
|
+
target_system_name || service_name.downcase.tr(' ', '_')
|
30
|
+
end
|
31
|
+
|
32
|
+
def service_id
|
33
|
+
@service_id ||= fetch_service_id
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch_service_id
|
37
|
+
# figure out service by system_name
|
38
|
+
service_found = threescale_client.list_services.find do |svc|
|
39
|
+
svc['system_name'] == service_system_name
|
40
|
+
end
|
41
|
+
# It should exist
|
42
|
+
raise ThreeScaleToolbox::Error, "Service with system_name: #{service_system_name}, should exist" if service_found.nil?
|
43
|
+
|
44
|
+
service_found['id']
|
45
|
+
end
|
46
|
+
|
47
|
+
def service_settings
|
48
|
+
default_service_settings.tap do |svc|
|
49
|
+
svc['name'] = service_name
|
50
|
+
svc['description'] = service_description
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_service_settings
|
55
|
+
{}
|
56
|
+
end
|
57
|
+
|
58
|
+
def service_name
|
59
|
+
api_spec.title
|
60
|
+
end
|
61
|
+
|
62
|
+
def service_description
|
63
|
+
api_spec.description
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
module MappingRule
|
6
|
+
def mapping_rule
|
7
|
+
{
|
8
|
+
'pattern' => pattern,
|
9
|
+
'http_method' => http_method,
|
10
|
+
'delta' => delta,
|
11
|
+
'metric_id' => metric_id
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def http_method
|
16
|
+
operation[:verb].upcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def pattern
|
20
|
+
# apply strict matching
|
21
|
+
operation[:path] + '$'
|
22
|
+
end
|
23
|
+
|
24
|
+
def delta
|
25
|
+
1
|
26
|
+
end
|
27
|
+
|
28
|
+
def metric_id
|
29
|
+
operation[:metric_id]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
module Method
|
6
|
+
|
7
|
+
def method
|
8
|
+
{
|
9
|
+
'friendly_name' => friendly_name,
|
10
|
+
'system_name' => system_name
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def friendly_name
|
15
|
+
operation[:operationId]
|
16
|
+
end
|
17
|
+
|
18
|
+
def system_name
|
19
|
+
friendly_name.downcase
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
class Operation
|
6
|
+
include Method
|
7
|
+
include MappingRule
|
8
|
+
|
9
|
+
attr_reader :operation
|
10
|
+
|
11
|
+
def initialize(operation)
|
12
|
+
@operation = operation
|
13
|
+
end
|
14
|
+
|
15
|
+
def set(key, val)
|
16
|
+
operation[key] = val
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module ThreeScaleToolbox
|
5
|
+
module Commands
|
6
|
+
module ImportCommand
|
7
|
+
module OpenAPI
|
8
|
+
module ResourceReader
|
9
|
+
##
|
10
|
+
# Load resource from different types of sources.
|
11
|
+
# Supported types are: file, URL, stdin
|
12
|
+
# Loaded content is returned
|
13
|
+
def load_resource(resource)
|
14
|
+
# Json format is parsed as well
|
15
|
+
YAML.safe_load(read_content(resource))
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Reads resources from different types of sources.
|
20
|
+
# Supported types are: file, URL, stdin
|
21
|
+
# Resource raw content is returned
|
22
|
+
def read_content(resource)
|
23
|
+
case resource
|
24
|
+
when '-'
|
25
|
+
method(:read_stdin)
|
26
|
+
when /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
|
27
|
+
method(:read_url)
|
28
|
+
else
|
29
|
+
method(:read_file)
|
30
|
+
end.call(resource)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Detect format from file extension
|
34
|
+
def read_file(resource)
|
35
|
+
File.read(resource)
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_stdin(_resource)
|
39
|
+
STDIN.read
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_url(resource)
|
43
|
+
Net::HTTP.get(URI.parse(resource))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|