rapis 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|