apiture 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE +20 -0
  5. data/README.md +38 -0
  6. data/Rakefile +6 -0
  7. data/apiture.gemspec +30 -0
  8. data/lib/apiture.rb +24 -0
  9. data/lib/apiture/api_base.rb +27 -0
  10. data/lib/apiture/api_builder.rb +196 -0
  11. data/lib/apiture/api_error.rb +3 -0
  12. data/lib/apiture/api_group.rb +28 -0
  13. data/lib/apiture/data_model.rb +24 -0
  14. data/lib/apiture/endpoint.rb +25 -0
  15. data/lib/apiture/middleware/auth/api_key.rb +39 -0
  16. data/lib/apiture/middleware/auth/basic.rb +25 -0
  17. data/lib/apiture/middleware/auth/oauth2.rb +31 -0
  18. data/lib/apiture/middleware/convert_json_body.rb +15 -0
  19. data/lib/apiture/middleware/debug.rb +20 -0
  20. data/lib/apiture/middleware/set_body_parameter.rb +20 -0
  21. data/lib/apiture/middleware/set_header.rb +15 -0
  22. data/lib/apiture/middleware/set_parameter_base.rb +37 -0
  23. data/lib/apiture/middleware/set_path_parameter.rb +18 -0
  24. data/lib/apiture/middleware/set_query_parameter.rb +13 -0
  25. data/lib/apiture/middleware_builder.rb +15 -0
  26. data/lib/apiture/middleware_stack.rb +21 -0
  27. data/lib/apiture/request_context.rb +103 -0
  28. data/lib/apiture/swagger/data_type_field.rb +25 -0
  29. data/lib/apiture/swagger/definition.rb +20 -0
  30. data/lib/apiture/swagger/external_docs.rb +10 -0
  31. data/lib/apiture/swagger/info.rb +14 -0
  32. data/lib/apiture/swagger/node.rb +149 -0
  33. data/lib/apiture/swagger/operation.rb +32 -0
  34. data/lib/apiture/swagger/parameter.rb +35 -0
  35. data/lib/apiture/swagger/parser.rb +148 -0
  36. data/lib/apiture/swagger/path.rb +37 -0
  37. data/lib/apiture/swagger/property.rb +15 -0
  38. data/lib/apiture/swagger/security.rb +21 -0
  39. data/lib/apiture/swagger/security_definition.rb +23 -0
  40. data/lib/apiture/swagger/specification.rb +31 -0
  41. data/lib/apiture/uri.rb +36 -0
  42. data/lib/apiture/utils/inflections.rb +46 -0
  43. data/lib/apiture/version.rb +3 -0
  44. data/spec/apiture/api_builder_spec.rb +20 -0
  45. data/spec/apiture/swagger/parser_spec.rb +107 -0
  46. data/spec/apiture/swagger/specification_spec.rb +19 -0
  47. data/spec/apiture_spec.rb +228 -0
  48. data/spec/files/github.json +186 -0
  49. data/spec/files/harvest.json +75 -0
  50. data/spec/files/honeybadger.json +80 -0
  51. data/spec/files/mandrill.json +502 -0
  52. data/spec/files/pivotal_tracker.json +94 -0
  53. data/spec/files/slack.json +88 -0
  54. data/spec/files/uber.json +43 -0
  55. data/spec/fixtures/vcr_cassettes/github_checkAuthorization.yml +70 -0
  56. data/spec/fixtures/vcr_cassettes/github_getUser.yml +82 -0
  57. data/spec/fixtures/vcr_cassettes/harvest_invoiceList.yml +85 -0
  58. data/spec/fixtures/vcr_cassettes/honeybadger.yml +98 -0
  59. data/spec/fixtures/vcr_cassettes/mandrill_messageSend.yml +44 -0
  60. data/spec/fixtures/vcr_cassettes/mandrill_userPing.yml +44 -0
  61. data/spec/fixtures/vcr_cassettes/pivotal_tracker_create_story.yml +61 -0
  62. data/spec/fixtures/vcr_cassettes/slack.yml +43 -0
  63. data/spec/fixtures/vcr_cassettes/uber_getProducts.yml +60 -0
  64. data/spec/spec_helper.rb +11 -0
  65. metadata +241 -0
@@ -0,0 +1,25 @@
1
+ require 'apiture/middleware_builder'
2
+ require 'apiture/middleware_stack'
3
+
4
+ module Apiture
5
+ class Endpoint
6
+ attr_reader :name, :url, :request_method
7
+
8
+ def initialize(name, url, request_method)
9
+ @name, @url, @request_method = name, url, request_method
10
+ end
11
+
12
+ def middlewares
13
+ @middlewares ||= []
14
+ end
15
+
16
+ def middleware_stack
17
+ @middleware_stack ||= MiddlewareStack.new(middlewares)
18
+ end
19
+
20
+ def config_middleware(&block)
21
+ builder = MiddlewareBuilder.new(middlewares)
22
+ builder.build(&block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ module Apiture
2
+ module Middleware
3
+ module Auth
4
+ class APIKey
5
+
6
+ def initialize(app, options)
7
+ @app = app
8
+ @id = options[:id]
9
+ @in = options[:in]
10
+ @name = options[:name]
11
+ @format = options[:format]
12
+ end
13
+
14
+ def call(env)
15
+ context = env[:context]
16
+ value = format_value(context.options[@id])
17
+ if @in == :header
18
+ env[:request_headers][@name] = value
19
+ elsif @in == :query
20
+ env[:params][@name] = value
21
+ elsif @in == :body
22
+ env[:body] = if body = env[:body]
23
+ body.merge(@name => value)
24
+ else
25
+ { @name => value }
26
+ end
27
+ end
28
+ @app.call(env)
29
+ end
30
+
31
+ protected
32
+ def format_value(val)
33
+ return val unless @format
34
+ @format % val
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Apiture
2
+ module Middleware
3
+ module Auth
4
+ class Basic
5
+
6
+ AUTHORIZATION_HEADER = 'Authorization'.freeze
7
+
8
+ def initialize(app, options)
9
+ @app = app
10
+ @id = options[:id]
11
+ end
12
+
13
+ def call(env)
14
+ auth_options = env[:context].options[@id]
15
+ env[:basic_auth] = {
16
+ username: auth_options[:username],
17
+ password: auth_options[:password]
18
+ }
19
+ @app.call(env)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module Apiture
2
+ module Middleware
3
+ module Auth
4
+ class OAuth2
5
+
6
+ AUTHORIZATION_HEADER = 'Authorization'.freeze
7
+ AUTHORIZATION_HEADER_FORMAT = 'Bearer %s'.freeze
8
+
9
+ def initialize(app, options)
10
+ @app = app
11
+ @id = options[:id]
12
+ @in = options[:in]
13
+ @name = options[:name]
14
+ end
15
+
16
+ def call(env)
17
+ context = env[:context]
18
+ auth_options = context.options[@id]
19
+ token = auth_options[:token]
20
+ if @in == :query
21
+ env[:params][@name] = token
22
+ else
23
+ env[:request_headers][AUTHORIZATION_HEADER] = AUTHORIZATION_HEADER_FORMAT % token
24
+ end
25
+ @app.call(env)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module Apiture
2
+ module Middleware
3
+ class ConvertJSONBody
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+ def call(env)
8
+ @app.call(env)
9
+ if body = env[:body]
10
+ env[:body] = MultiJson.dump(body)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module Apiture
2
+ module Middleware
3
+ class Debug
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+ def call(env)
8
+ @app.call(env)
9
+ logger = env[:logger]
10
+ if logger && logger.debug?
11
+ logger.debug("Request Method: #{env[:method]}")
12
+ logger.debug("URL: #{env[:url]}")
13
+ logger.debug("Request Headers: #{env[:request_headers].inspect}")
14
+ logger.debug("Params: #{env[:params].inspect}")
15
+ logger.debug("Body: #{env[:body]}")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'apiture/middleware/set_parameter_base'
2
+
3
+ module Apiture
4
+ module Middleware
5
+ class SetBodyParameter < SetParameterBase
6
+ def apply_parameter_value(env, value)
7
+ if value
8
+ env[:body] = if body = env[:body]
9
+ body.merge(value)
10
+ else
11
+ value
12
+ end
13
+ if env[:request_headers]["Content-Type"].nil?
14
+ env[:request_headers]["Content-Type"] = "application/json"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module Apiture
2
+ module Middleware
3
+ class SetHeader
4
+ def initialize(app, headers)
5
+ @app = app
6
+ @headers = headers
7
+ end
8
+
9
+ def call(env)
10
+ env[:request_headers].merge!(@headers)
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ module Apiture
2
+ module Middleware
3
+ class SetParameterBase
4
+
5
+ def initialize(app, options)
6
+ @app = app
7
+ @name = options[:name]
8
+ @schema = options[:schema]
9
+ @default = options[:default]
10
+ end
11
+
12
+ def call(env)
13
+ value = find_parameter_value(env)
14
+ if value == nil && @default
15
+ value = @default
16
+ end
17
+ apply_parameter_value(env, value)
18
+ @app.call(env)
19
+ end
20
+
21
+ protected
22
+
23
+ def find_parameter_value(env)
24
+ if @schema
25
+ @schema.build(@name, env)
26
+ else
27
+ context = env[:context]
28
+ context.get_attribute(@name)
29
+ end
30
+ end
31
+
32
+ def apply_parameter_value(env, value)
33
+ raise NotImplementedError
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require 'apiture/middleware/set_parameter_base'
2
+
3
+ module Apiture
4
+ module Middleware
5
+ class SetPathParameter < SetParameterBase
6
+
7
+ def initialize(app, options)
8
+ super
9
+ @regex = Regexp.new(Regexp.quote("{#{@name}}"))
10
+ end
11
+
12
+ def apply_parameter_value(env, value)
13
+ uri = env[:url]
14
+ uri.resource_path = uri.resource_path.gsub(@regex, value.to_s)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'apiture/middleware/set_parameter_base'
2
+
3
+ module Apiture
4
+ module Middleware
5
+ class SetQueryParameter < SetParameterBase
6
+ def apply_parameter_value(env, value)
7
+ unless value.nil?
8
+ env[:params][@name] = value
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Apiture
2
+ class MiddlewareBuilder
3
+ def initialize(middlewares)
4
+ @middlewares = middlewares
5
+ end
6
+
7
+ def build(&block)
8
+ instance_eval(&block)
9
+ end
10
+
11
+ def use(middleware_klass, options = {})
12
+ @middlewares << [middleware_klass, options]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Apiture
2
+ class MiddlewareStack
3
+
4
+ def initialize(middlewares)
5
+ cur = ->(env){}
6
+ middlewares.reverse.each do |(klass, options)|
7
+ cur = if klass.instance_method(:initialize).arity == 1
8
+ klass.new(cur)
9
+ else
10
+ klass.new(cur, options)
11
+ end
12
+ end
13
+ @first = cur
14
+ end
15
+
16
+ def call(env)
17
+ @first.call(env)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,103 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'apiture/utils/inflections'
4
+
5
+ module FaradayMiddleware
6
+ class ParseJsonWithQuirksMode < ParseJson
7
+ define_parser do |body|
8
+ ::JSON.parse(body, quirks_mode: true) unless body.strip.empty?
9
+ end
10
+ end
11
+ end
12
+ Faraday::Response.register_middleware json_quirks: lambda { FaradayMiddleware::ParseJsonWithQuirksMode }
13
+
14
+ module Apiture
15
+ class RequestContext
16
+ include Apiture::Utils::Inflections
17
+
18
+ attr_reader :options, :group, :endpoint, :attributes
19
+
20
+ def initialize(options, group, endpoint, attributes)
21
+ @options, @group, @endpoint, @attributes = options, group, endpoint, attributes
22
+ end
23
+
24
+ def perform
25
+ url = endpoint.url.dup
26
+
27
+ if url.base_host
28
+ url.subdomain = options[:subdomain]
29
+ end
30
+
31
+ env = {
32
+ method: endpoint.request_method,
33
+ url: url,
34
+ params: {},
35
+ request_headers: {},
36
+ logger: logger,
37
+ context: self
38
+ }
39
+
40
+ endpoint.middleware_stack.call(env)
41
+ perform_request(env)
42
+ end
43
+
44
+ def authenticator
45
+ group.authenticator
46
+ end
47
+
48
+ def logger
49
+ group.logger
50
+ end
51
+
52
+ def get_attribute(name)
53
+ name = name.to_sym
54
+ unless attributes.has_key?(name)
55
+ name = underscore(name).to_sym
56
+ end
57
+ attributes[name]
58
+ end
59
+
60
+ protected
61
+ def perform_request(env)
62
+ request_method = env[:method] ? env[:method].to_sym : :get
63
+ headers = env[:request_headers]
64
+
65
+ conn = Faraday.new do |faraday|
66
+ faraday.adapter Faraday.default_adapter
67
+ configure_format_handlers(faraday, headers)
68
+ end
69
+
70
+ if auth = env[:basic_auth]
71
+ conn.basic_auth(auth[:username], auth[:password])
72
+ end
73
+
74
+ conn.__send__(request_method) do |req|
75
+ req.url env[:url].to_s, env[:params]
76
+ req.headers.merge!(headers) if headers
77
+ if body = env[:body]
78
+ req.body = body
79
+ end
80
+ end
81
+ end
82
+
83
+ def configure_format_handlers(conn, headers)
84
+ if headers && accept = headers["Accept"]
85
+ content_types = accept.split(",").map { |s| s.split(";").first.strip }
86
+ content_types.each do |type|
87
+ content_type_mappings.each do |(parser, type_match)|
88
+ if type.match(type_match)
89
+ conn.response(parser, content_type: type_match, preserve_raw: true)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def content_type_mappings
97
+ [
98
+ [:xml, /\bxml$/],
99
+ [:json_quirks, /\bjson$/]
100
+ ]
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,25 @@
1
+ require 'apiture/swagger/node'
2
+
3
+ module Apiture
4
+ module Swagger
5
+ class DataTypeField < Node
6
+ attribute :type, symbolize: true
7
+ attribute :format
8
+ attribute :default_value
9
+ attribute :multiple_of
10
+ attribute :maximum
11
+ attribute :exclusive_maximum, type: :boolean
12
+ attribute :minimum
13
+ attribute :exclusive_minimum, type: :boolean
14
+ attribute :max_length
15
+ attribute :min_length
16
+ attribute :pattern
17
+ attribute :max_items
18
+ attribute :min_items
19
+ attribute :unique_items, type: :boolean
20
+ attribute :items
21
+
22
+ list :enum
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ require 'apiture/swagger/node'
2
+ require 'apiture/swagger/property'
3
+
4
+ module Apiture
5
+ module Swagger
6
+ class Definition < Node
7
+ attr_reader :id
8
+
9
+ attribute :required
10
+ attribute :discriminator
11
+
12
+ hash :properties
13
+
14
+ def initialize(id = "<Inline>")
15
+ super()
16
+ @id = id
17
+ end
18
+ end
19
+ end
20
+ end