rapis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/rapis ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'rapis/cli'
6
+ Version = Rapis::VERSION
7
+
8
+ Rapis::Cli.start(ARGV)
data/lib/rapis.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "rapis/version"
2
+ require "rapis/actions"
3
+ require "rapis/cli"
4
+ require "rapis/client"
5
+ require "rapis/converter"
6
+ require "rapis/logger"
7
+ require "rapis/utils"
@@ -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
@@ -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
@@ -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