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