rapis 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Apifile +262 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +408 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/rapis +8 -0
- data/lib/rapis.rb +7 -0
- data/lib/rapis/actions.rb +102 -0
- data/lib/rapis/cli.rb +64 -0
- data/lib/rapis/client.rb +58 -0
- data/lib/rapis/converter.rb +124 -0
- data/lib/rapis/logger.rb +86 -0
- data/lib/rapis/utils.rb +38 -0
- data/lib/rapis/version.rb +3 -0
- data/rapis.gemspec +31 -0
- metadata +178 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rapis"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/rapis
ADDED
data/lib/rapis.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'rapis/client'
|
4
|
+
require 'rapis/converter'
|
5
|
+
require 'rapis/logger'
|
6
|
+
require 'rapis/utils'
|
7
|
+
|
8
|
+
module Rapis
|
9
|
+
class Actions
|
10
|
+
include Rapis::Logger::Helper
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@client = Rapis::Client.new
|
14
|
+
@converter = Rapis::Converter.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(name, options)
|
18
|
+
ret = @client.create(name, options['description'])
|
19
|
+
info("API id: #{ret.id}")
|
20
|
+
info("API name: #{ret.name}")
|
21
|
+
info("API description: #{ret.description}")
|
22
|
+
ret.warnings.each {|w| warn("WARNING: #{w}") } unless ret.warnings.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert(options)
|
26
|
+
output = @converter.to_h(File.read(options['file']))
|
27
|
+
case options['format']
|
28
|
+
when 'json'
|
29
|
+
output = JSON.pretty_generate(output)
|
30
|
+
Rapis::Utils.print_json(output)
|
31
|
+
when 'yaml'
|
32
|
+
output = YAML.dump(output)
|
33
|
+
Rapis::Utils.print_yaml(output)
|
34
|
+
else
|
35
|
+
raise "\"#{options['format']}\" format is not supported."
|
36
|
+
end
|
37
|
+
File.write(options['output'], output) unless options['output'].empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def list(options)
|
41
|
+
@client.get_apis.each do |a|
|
42
|
+
if options['verbose']
|
43
|
+
api = Rapis::Utils.struct_to_hash(a)
|
44
|
+
api['stages'] = []
|
45
|
+
else
|
46
|
+
a_key = "#{a.id} (#{a.name})"
|
47
|
+
api = {a_key => []}
|
48
|
+
end
|
49
|
+
|
50
|
+
@client.get_stages(a.id).each do |s|
|
51
|
+
if options['verbose']
|
52
|
+
api['stages'] << Rapis::Utils.struct_to_hash(s)
|
53
|
+
else
|
54
|
+
api[a_key] = s.stage_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
Rapis::Utils.print_yaml(YAML.dump(api))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def export(options)
|
62
|
+
dsl = @converter.to_dsl(
|
63
|
+
@client.export_api(options['rest_api'], options['stage'])
|
64
|
+
)
|
65
|
+
Rapis::Utils.print_ruby(dsl)
|
66
|
+
File.write(options['file'], dsl) if options['write']
|
67
|
+
end
|
68
|
+
|
69
|
+
def diff(options)
|
70
|
+
puts Rapis::Utils.diff(
|
71
|
+
@client.export_api(options['rest_api'], options['stage']),
|
72
|
+
@converter.to_h(File.read(options['file']))
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def apply(options)
|
77
|
+
ret = @client.put_api(
|
78
|
+
options['rest_api'],
|
79
|
+
@converter.to_h(File.read(options['file']))
|
80
|
+
)
|
81
|
+
info("Applied the REST API configuration to \"#{ret.id}\" (#{ret.name})")
|
82
|
+
ret.warnings.each {|w| warn("WARNING: #{w}") } unless ret.warnings.nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
def deploy(options)
|
86
|
+
args = {}
|
87
|
+
args[:rest_api_id] = options['rest_api']
|
88
|
+
args[:stage_name] = options['stage']
|
89
|
+
args[:description] = options['description'] unless options['description'].empty?
|
90
|
+
args[:stage_description] = options['stage_description'] unless options['description'].empty?
|
91
|
+
args[:cache_cluster_enabled] = (options['cache'].to_f > 0.0)
|
92
|
+
args[:cache_cluster_size] = options['cache'] if args[:cache_cluster_enabled]
|
93
|
+
args[:variables] = options['variables'] unless options['variables'].empty?
|
94
|
+
|
95
|
+
ret = @client.deploy(args)
|
96
|
+
summary = YAML.dump(Rapis::Utils.struct_to_hash(ret.api_summary))
|
97
|
+
info("Deployment id: #{ret.id}")
|
98
|
+
info("Deployment description: #{ret.description}")
|
99
|
+
info("API summary :\n#{summary}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/rapis/cli.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'rapis/actions'
|
3
|
+
|
4
|
+
module Rapis
|
5
|
+
class Cli < Thor
|
6
|
+
class_option :file, aliases: '-f', desc: 'Configuration file', type: :string, default: 'Apifile'
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@actions = Rapis::Actions.new
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "create", "Create REST API"
|
14
|
+
option :description, aliases: '-d', desc: 'Description', type: :string, default: ''
|
15
|
+
def create(name)
|
16
|
+
@actions.create(name, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "convert", "Convert the REST API configuration to the specified format"
|
20
|
+
option :format, aliases: '-F', desc: 'Output format # accepts json, yaml', type: :string, required: true
|
21
|
+
option :output, aliases: '-o', desc: 'Output path', type: :string, default: ''
|
22
|
+
def convert
|
23
|
+
@actions.convert(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "list", "List the REST APIs and the stages"
|
27
|
+
option :verbose, aliases: '-V', desc: 'Verbose mode', type: :boolean, default: false
|
28
|
+
def list
|
29
|
+
@actions.list(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "export", "Export the configuration as Ruby DSL"
|
33
|
+
option :rest_api, aliases: '-r', desc: 'The id of the REST API', type: :string, required: true
|
34
|
+
option :stage, aliases: '-s', desc: 'The name of the stage', type: :string, required: true
|
35
|
+
option :write, aliases: '-w', desc: 'Write the configuration to the file', type: :boolean, default: false
|
36
|
+
def export
|
37
|
+
@actions.export(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "diff", "Diff the local configuration and the remote configuration"
|
41
|
+
option :rest_api, aliases: '-r', desc: 'The id of the REST API', type: :string, required: true
|
42
|
+
option :stage, aliases: '-s', desc: 'The name of the stage', type: :string, required: true
|
43
|
+
def diff
|
44
|
+
@actions.diff(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "apply", "Apply the REST API configuration"
|
48
|
+
option :rest_api, aliases: '-r', desc: 'The id of the REST API', type: :string, required: true
|
49
|
+
def apply
|
50
|
+
@actions.apply(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "deploy", "Deploy the current REST API configuration to the stage"
|
54
|
+
option :rest_api, aliases: '-r', desc: 'The id of the REST API', type: :string, required: true
|
55
|
+
option :stage, aliases: '-s', desc: 'The name of the stage', type: :string, required: true
|
56
|
+
option :description, aliases: '-d', desc: 'The description for the deployment', type: :string, default: ''
|
57
|
+
option :stage_description, aliases: '-D', desc: 'The description of the stage', type: :string, default: ''
|
58
|
+
option :cache, aliases: '-c', desc: 'Size of the cache cluster # accepts 0.5, 1.6, 6.1, 13.5, 28.4, 58.2, 118, 237', type: :string, default: '0.0'
|
59
|
+
option :variables, aliases: '-v', desc: 'A map that defines the stage variables', type: :hash, default: {}
|
60
|
+
def deploy
|
61
|
+
@actions.deploy(options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/rapis/client.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
require 'rapis/utils'
|
5
|
+
|
6
|
+
module Rapis
|
7
|
+
class Client
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@api = Aws::APIGateway::Client.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def create(name, desc)
|
14
|
+
@api.create_rest_api({
|
15
|
+
name: name,
|
16
|
+
description: desc
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_apis(pos=nil)
|
21
|
+
opt = {}
|
22
|
+
opt[:limit] = 500
|
23
|
+
opt[:position] = pos unless pos.nil?
|
24
|
+
|
25
|
+
ret = @api.get_rest_apis(opt)
|
26
|
+
ret.items.concat(get_apis(ret.posision)) unless ret.position.nil?
|
27
|
+
ret.items
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_stages(api_id, pos=nil)
|
31
|
+
@api.get_stages({rest_api_id: api_id}).item
|
32
|
+
end
|
33
|
+
|
34
|
+
def export_api(api_id, stage_name)
|
35
|
+
ret = @api.get_export({
|
36
|
+
rest_api_id: api_id,
|
37
|
+
stage_name: stage_name,
|
38
|
+
export_type: 'swagger',
|
39
|
+
parameters: { extensions: 'integrations' }
|
40
|
+
})
|
41
|
+
|
42
|
+
JSON.load(ret.body.read)
|
43
|
+
end
|
44
|
+
|
45
|
+
def put_api(api_id, data)
|
46
|
+
@api.put_rest_api({
|
47
|
+
rest_api_id: api_id,
|
48
|
+
mode: 'overwrite',
|
49
|
+
parameters: { extensions: 'integrations' },
|
50
|
+
body: JSON.dump(data)
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
def deploy(args)
|
55
|
+
@api.create_deployment(args)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'dslh'
|
2
|
+
require 'rapis/utils'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
CHANGE_SETS = {
|
6
|
+
'x-amazon-apigateway-integration' => 'amazon_apigateway_integration',
|
7
|
+
'$ref' => 'ref'
|
8
|
+
}
|
9
|
+
|
10
|
+
module Rapis
|
11
|
+
class Converter
|
12
|
+
def to_dsl(hash)
|
13
|
+
exclude_key = proc do |k|
|
14
|
+
if ['in'].include?(k)
|
15
|
+
true
|
16
|
+
elsif (k.include?('/') and not k =~ /^\//)
|
17
|
+
true
|
18
|
+
elsif ['-', '.'].any? {|i| k.include?(i) } and k != 'x-amazon-apigateway-integration'
|
19
|
+
true
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
key_conv = proc do |k|
|
26
|
+
k = k.to_s
|
27
|
+
if k =~ /^\//
|
28
|
+
proc do |v, nested|
|
29
|
+
if nested
|
30
|
+
"path #{k.inspect} #{v}"
|
31
|
+
else
|
32
|
+
"path #{k.inspect}, #{v}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
elsif k =~ /^\d{3}$/
|
36
|
+
proc do |v, nested|
|
37
|
+
if nested
|
38
|
+
"code #{k} #{v}"
|
39
|
+
else
|
40
|
+
"code #{k}, #{v}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
else
|
44
|
+
CHANGE_SETS.each { |f,t| k = k.gsub(f,t) }
|
45
|
+
k
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
value_conv = proc do |v|
|
50
|
+
if v.kind_of?(String) and v =~ /\A(?:0|[1-9]\d*)\Z/
|
51
|
+
v.to_i
|
52
|
+
else
|
53
|
+
v
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
dsl = Dslh.deval(
|
58
|
+
hash,
|
59
|
+
exclude_key: exclude_key,
|
60
|
+
key_conv: key_conv,
|
61
|
+
value_conv: value_conv)
|
62
|
+
dsl.gsub!(/^/, ' ').strip!
|
63
|
+
<<-EOS
|
64
|
+
rest_api do
|
65
|
+
#{dsl}
|
66
|
+
end
|
67
|
+
EOS
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_h(dsl)
|
71
|
+
instance_eval(dsl)
|
72
|
+
@apis
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def rest_api(value = nil, &block)
|
78
|
+
exclude_key = proc do |k|
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
key_conv = proc do |k|
|
83
|
+
k = k.to_s
|
84
|
+
CHANGE_SETS.each { |f,t| k = k.gsub(t,f) }
|
85
|
+
k
|
86
|
+
end
|
87
|
+
|
88
|
+
value_conv = proc do |v|
|
89
|
+
case v
|
90
|
+
when Hash, Array
|
91
|
+
v
|
92
|
+
else
|
93
|
+
v.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@apis = Dslh.eval(
|
98
|
+
exclude_key: exclude_key,
|
99
|
+
key_conv: key_conv,
|
100
|
+
value_conv: value_conv,
|
101
|
+
allow_empty_args: true,
|
102
|
+
scope_hook: proc {|scope| define_template_func(scope)},
|
103
|
+
&block
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def define_template_func(scope)
|
108
|
+
scope.instance_eval(<<-EOS)
|
109
|
+
def path(key, value = nil, &block)
|
110
|
+
if block
|
111
|
+
value = Dslh::ScopeBlock.nest(binding, 'block', key)
|
112
|
+
end
|
113
|
+
@__hash__[key] = value
|
114
|
+
end
|
115
|
+
def code(key, value = nil, &block)
|
116
|
+
if block
|
117
|
+
value = Dslh::ScopeBlock.nest(binding, 'block', key)
|
118
|
+
end
|
119
|
+
@__hash__[key] = value
|
120
|
+
end
|
121
|
+
EOS
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/rapis/logger.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Rapis
|
5
|
+
class TermColor
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def green(msg)
|
9
|
+
colorize 32, msg
|
10
|
+
end
|
11
|
+
|
12
|
+
def yellow(msg)
|
13
|
+
colorize 33, msg
|
14
|
+
end
|
15
|
+
|
16
|
+
def red(msg)
|
17
|
+
colorize 31, msg
|
18
|
+
end
|
19
|
+
|
20
|
+
def colorize(num, msg)
|
21
|
+
"\e[#{num}m#{msg}\e[0m"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Logger < Logger
|
28
|
+
include Singleton
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super(STDERR)
|
32
|
+
|
33
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
34
|
+
"#{msg}\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
self.level = Logger::INFO
|
38
|
+
end
|
39
|
+
|
40
|
+
def debug(progname = nil, method_name = nil, msg)
|
41
|
+
super(progname) { { method_name: method_name, message: msg } }
|
42
|
+
end
|
43
|
+
|
44
|
+
def info(msg)
|
45
|
+
super { Rapis::TermColor.green(msg) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def warn(msg)
|
49
|
+
super { Rapis::TermColor.yellow(msg) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def fatal(msg)
|
53
|
+
super { Rapis::TermColor.red(msg) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def error(progname = nil, method_name = nil, msg, backtrace)
|
57
|
+
super(progname) { { method_name: method_name, message: msg, backtrace: backtrace } }
|
58
|
+
end
|
59
|
+
|
60
|
+
module Helper
|
61
|
+
|
62
|
+
def log(level, message)
|
63
|
+
logger = Rapis::Logger.instance
|
64
|
+
logger.send(level, message)
|
65
|
+
end
|
66
|
+
|
67
|
+
def info(msg)
|
68
|
+
log(:info, msg)
|
69
|
+
end
|
70
|
+
|
71
|
+
def warn(msg)
|
72
|
+
log(:warn, msg)
|
73
|
+
end
|
74
|
+
|
75
|
+
def fatal(msg)
|
76
|
+
log(:error, msg)
|
77
|
+
end
|
78
|
+
|
79
|
+
def debug(msg)
|
80
|
+
log(:debug, msg)
|
81
|
+
end
|
82
|
+
|
83
|
+
module_function :log, :info, :warn, :fatal, :debug
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|