diesel-api-dsl 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -13
  2. data/Rakefile +5 -0
  3. data/diesel.gemspec +4 -0
  4. data/lib/diesel.rb +13 -5
  5. data/lib/diesel/api_base.rb +27 -0
  6. data/lib/diesel/api_builder.rb +98 -34
  7. data/lib/diesel/api_error.rb +3 -0
  8. data/lib/diesel/api_group.rb +27 -0
  9. data/lib/diesel/data_model.rb +24 -0
  10. data/lib/diesel/endpoint.rb +15 -33
  11. data/lib/diesel/middleware/auth/api_key.rb +27 -0
  12. data/lib/diesel/middleware/debug.rb +20 -0
  13. data/lib/diesel/middleware/set_body_parameter.rb +11 -0
  14. data/lib/diesel/middleware/set_header.rb +15 -0
  15. data/lib/diesel/middleware/set_parameter_base.rb +33 -0
  16. data/lib/diesel/middleware/set_path_parameter.rb +17 -0
  17. data/lib/diesel/middleware/set_query_parameter.rb +13 -0
  18. data/lib/diesel/middleware_builder.rb +15 -0
  19. data/lib/diesel/middleware_stack.rb +21 -0
  20. data/lib/diesel/request_context.rb +40 -15
  21. data/lib/diesel/swagger/data_type_field.rb +25 -0
  22. data/lib/diesel/swagger/definition.rb +12 -0
  23. data/lib/diesel/swagger/external_docs.rb +10 -0
  24. data/lib/diesel/swagger/info.rb +14 -0
  25. data/lib/diesel/swagger/node.rb +140 -0
  26. data/lib/diesel/swagger/operation.rb +22 -0
  27. data/lib/diesel/swagger/parameter.rb +32 -0
  28. data/lib/diesel/swagger/parser.rb +70 -0
  29. data/lib/diesel/swagger/path.rb +27 -0
  30. data/lib/diesel/swagger/property.rb +8 -0
  31. data/lib/diesel/swagger/security_definition.rb +15 -0
  32. data/lib/diesel/swagger/specification.rb +30 -0
  33. data/lib/diesel/utils/inflections.rb +46 -0
  34. data/lib/diesel/version.rb +1 -1
  35. data/spec/diesel/api_builder_spec.rb +14 -0
  36. data/spec/diesel/swagger/parser_spec.rb +80 -0
  37. data/spec/diesel_spec.rb +43 -0
  38. data/spec/files/honeybadger.json +80 -0
  39. data/spec/files/pivotal_tracker.json +94 -0
  40. data/spec/fixtures/vcr_cassettes/honeybadger.yml +98 -0
  41. data/spec/fixtures/vcr_cassettes/pivotal_tracker_create_story.yml +61 -0
  42. data/spec/spec_helper.rb +10 -0
  43. metadata +110 -25
  44. data/apis/pivotal_tracker.rb +0 -55
  45. data/examples/create_pivotal_task.rb +0 -13
  46. data/lib/diesel/action/http.rb +0 -92
  47. data/lib/diesel/api.rb +0 -33
  48. data/lib/diesel/auth/api_key.rb +0 -39
  49. data/lib/diesel/auth/base.rb +0 -8
  50. data/lib/diesel/auth/oauth2.rb +0 -13
  51. data/lib/diesel/authenticator.rb +0 -35
  52. data/lib/diesel/complex_type_builder.rb +0 -24
  53. data/lib/diesel/container_builder.rb +0 -6
  54. data/lib/diesel/dsl.rb +0 -239
  55. data/lib/diesel/model_builder.rb +0 -9
  56. data/lib/diesel/profile.rb +0 -112
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YWYzZDNhNzk5OWE5MDgxOWM1MzE4MjZlNDg2YWFjMDUyNzlmNjNkOQ==
5
- data.tar.gz: !binary |-
6
- ODhkYmQ4ZDFjMTBjZjZiMTUxMDU4NTI4NjhkMzQ5NTUzODg5OGQxNA==
2
+ SHA1:
3
+ metadata.gz: 4c09f2251953d171164fa4883b9aad83e9a1d7d9
4
+ data.tar.gz: 7c885dc69abd71c3e3c897e20bfcbf5ca141c166
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- ZDViYzZlNzlkOGYyYWRiZDY1ZmNhZTRlM2U2OWQwNmJkZmQzZmI5OGEwMTZh
10
- OWZlZjExOTA4NjRiN2E0MjZiZmE5N2JiYzJjMjFkMGQ1NWI5ZTQ0OGM0YTZk
11
- M2M5MTk3ZWRiYzk1YTIxNTQ3ZDU2NzQ3MjQ2YjNmNDYyYWQ1ZmQ=
12
- data.tar.gz: !binary |-
13
- NjhlOGZkZTg2ODhiYmNkYjQ5MmYwZjVjYTRjOWZkNjhhZTI5NjZlMTUyYzhj
14
- NjlhMzkyNTljZDQ1Y2IzZWM1YmRmODVlNzdlNDBkMmNjZWYyZmJlNjdhMzU1
15
- MDJiYjk2NDFhNThhNTkzZTIwODRkYjE1Nzc4MTJhODgyZjQ4Yzc=
6
+ metadata.gz: 8347d51bde9ca019c77d813ce453199603736228b00e69294bc4af670959101b978218d39bf18ef1f7b04afdfe2483d7b2fae19eb9bde26f51c528592d442496
7
+ data.tar.gz: b67551247eab1cde0aa09fb14f34f60e1d6e4824d1d4016eae690d3f34d92662b1d2e738e032a81b33295151a59613e021ba43d5ae0d7135c857093f9d86f1ba
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/diesel.gemspec CHANGED
@@ -20,6 +20,10 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake", "~> 10.1"
23
+ spec.add_development_dependency "rspec", "~> 3.0.0"
24
+ spec.add_development_dependency "vcr", "~> 2.9.0"
25
+ spec.add_development_dependency "webmock", "~> 1.20.0"
23
26
 
24
27
  spec.add_dependency "httparty", "~> 0.12"
28
+ spec.add_dependency "multi_json", "~> 1.10.1"
25
29
  end
data/lib/diesel.rb CHANGED
@@ -1,10 +1,18 @@
1
- require "diesel/version"
2
- require 'diesel/dsl'
1
+ require 'diesel/version'
2
+ require 'diesel/swagger/parser'
3
3
  require 'diesel/api_builder'
4
4
 
5
+
5
6
  module Diesel
6
- def self.load_api(name)
7
- profile = DSL.load_profile(name, "#{name}.rb")
8
- Diesel::APIBuilder.new(profile).build
7
+ def self.parse_specification(path)
8
+ Diesel::Swagger::Parser.new.parse(File.read(path))
9
+ end
10
+
11
+ def self.build_api(specification)
12
+ Diesel::APIBuilder.new(specification).build
13
+ end
14
+
15
+ def self.load_api(path)
16
+ build_api(parse_specification(path))
9
17
  end
10
18
  end
@@ -0,0 +1,27 @@
1
+ require 'diesel/api_group'
2
+
3
+ module Diesel
4
+ class APIError < Exception; end
5
+ end
6
+
7
+ module Diesel
8
+ class APIBase
9
+ attr_accessor :logger, :options
10
+
11
+ def initialize(options = {})
12
+ @options = options
13
+ end
14
+
15
+ def logger=(logger)
16
+ @logger = logger
17
+ self.class.api_groups.each { |g| g.logger = logger }
18
+ end
19
+
20
+ class << self
21
+ def api_groups; @api_groups ||= []; end
22
+ def api_groups=(api_groups)
23
+ @api_groups = api_groups
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,61 +1,125 @@
1
- require 'diesel/action/http'
2
- require 'diesel/complex_type_builder'
3
- require 'diesel/container_builder'
1
+ require 'diesel/api_error'
2
+ require 'diesel/utils/inflections'
3
+ require 'diesel/api_base'
4
+ require 'diesel/data_model'
5
+
6
+ require 'diesel/middleware/debug'
7
+ require 'diesel/middleware/set_header'
4
8
 
5
9
  module Diesel
6
10
  class APIBuilder
7
- attr_reader :profile
11
+ include Diesel::Utils::Inflections
12
+
13
+ attr_reader :specification
8
14
 
9
- def initialize(profile)
10
- @profile = profile
15
+ def initialize(specification)
16
+ @specification = specification
11
17
  end
12
18
 
13
19
  def build
20
+ validate!
21
+
14
22
  create_api_class.tap do |klass|
15
- klass.base_path = profile.base_path
16
- unless profile.authorizations.empty?
17
- klass.authenticator = Diesel::Authenticator.build(profile.authorizations)
18
- end
19
- build_endpoints(klass)
20
- build_models(klass)
23
+ klass.api_groups = [
24
+ APIGroup.new.tap do |group|
25
+ group.endpoints = build_endpoints
26
+ group.endpoints.each do |endpoint|
27
+ klass.send(:define_method, endpoint.name.to_sym) do |parameters|
28
+ group.execute(self.options, endpoint, parameters)
29
+ end
30
+ end
31
+ end
32
+ ]
33
+
21
34
  #Diesel.const_set create_class_name(name), klass
22
35
  end
23
36
  end
24
37
 
25
38
  protected
26
- def global_filters
27
- @global_filters ||= profile.parameters.map { |p| Diesel::Action::HTTP::ParameterFilter.new(p) }
39
+
40
+ def validate!
41
+ api_errors = specification.collect_errors
42
+ if api_errors.any?
43
+ raise APIError, "API specification is invalid: \n #{api_errors.join("\n")}"
44
+ end
28
45
  end
29
46
 
30
47
  def create_api_class
31
- Class.new(Diesel::API)
48
+ Class.new(Diesel::APIBase)
49
+ end
50
+
51
+ def build_endpoint_url(specification, path)
52
+ base_path = specification.base_path.chomp('/')
53
+ resource_path = path.sub(/^([^\/])/, '/\1').chomp('/')
54
+ [
55
+ specification.schemes.first,
56
+ '://',
57
+ specification.host,
58
+ base_path,
59
+ resource_path
60
+ ].join('')
32
61
  end
33
62
 
34
- def build_endpoints(klass)
35
- klass.endpoints = profile.resources.map do |resource|
36
- resource.operations.map do |operation|
37
- Diesel::Endpoint.new(resource.path).tap do |endpoint|
38
- action = Diesel::Action::HTTP.new
39
- action.request_method = operation.method
40
- action.filters.concat(global_filters)
41
- action.filters.concat(operation.parameters.map { |p| Diesel::Action::HTTP::ParameterFilter.new(p) })
42
- endpoint.action = action
43
- klass.__send__(:define_method, operation.nickname) do |parameters|
44
- execute(endpoint, parameters)
63
+ def build_endpoints
64
+ spec = specification
65
+ data_models = self.data_models
66
+
67
+ spec.paths.map do |path, path_model|
68
+ path_model.operations_map.map do |(method, operation)|
69
+ endpoint_name = underscore(operation.operation_id)
70
+ endpoint_url = build_endpoint_url(spec, path)
71
+
72
+ Diesel::Endpoint.new(endpoint_name, endpoint_url, method).tap do |endpoint|
73
+ endpoint.config_middleware do
74
+ use Diesel::Middleware::Debug
75
+
76
+ if spec.produces.any?
77
+ use Diesel::Middleware::SetHeader, 'Content-Type' => spec.produces.first
78
+ end
79
+
80
+ spec.security_definitions.each_pair do |name, security_def|
81
+ case security_def.type
82
+ when 'apiKey'
83
+ require 'diesel/middleware/auth/api_key'
84
+ use Diesel::Middleware::Auth::APIKey,
85
+ id: Diesel::Utils::Inflections.underscore(name).to_sym,
86
+ in: security_def.in,
87
+ name: security_def.name
88
+ else
89
+ raise APIError, "Unsupported security definition: #{security_def.type}"
90
+ end
91
+ end
92
+
93
+ operation.parameters.each do |parameter|
94
+ param_class_path = "diesel/middleware/set_#{parameter.in}_parameter"
95
+ require param_class_path
96
+ param_class_name = Diesel::Utils::Inflections.camelize(param_class_path)
97
+ param_class = Diesel::Utils::Inflections.constantize(param_class_name)
98
+ middleware_opts = {name: parameter.name}
99
+ if parameter.schema?
100
+ unless schema = data_models[parameter.schema]
101
+ raise APIError, "Unspecified schema: #{parameter.schema}"
102
+ end
103
+ middleware_opts[:schema] = schema
104
+ end
105
+ use param_class, middleware_opts
106
+ end
107
+
45
108
  end
46
109
  end
47
110
  end
48
111
  end.flatten.compact
49
112
  end
50
113
 
51
- def build_models(klass)
52
- klass.models = profile.models.map do |model|
53
- if model.is_a? Diesel::Profile::Container
54
- Diesel::ContainerBuilder.new(model)
55
- else
56
- Diesel::ComplexTypeBuilder.new(model)
57
- end
58
- end.reduce({}) { |m,b| m[b.model.name] = b; m }
114
+ def data_models
115
+ @data_models ||= build_data_models
116
+ end
117
+
118
+ def build_data_models
119
+ specification.definitions.reduce({}) do |m, (name, definition)|
120
+ m[name] = DataModel.new(definition)
121
+ m
122
+ end
59
123
  end
60
124
 
61
125
  def create_class_name(name)
@@ -0,0 +1,3 @@
1
+ module Diesel
2
+ class APIError < Exception; end
3
+ end
@@ -0,0 +1,27 @@
1
+ require 'diesel/request_context'
2
+ require 'diesel/endpoint'
3
+
4
+ module Diesel
5
+ class APIGroup
6
+ attr_accessor :logger
7
+
8
+ def endpoints; @endpoints ||= []; end
9
+ def endpoints=(endpoints)
10
+ @endpoints = endpoints
11
+ end
12
+
13
+ def authenticators; @authenticators; end
14
+ def authenticators=(auth)
15
+ @authenticators = auth
16
+ end
17
+
18
+ def data_models; @data_models ||= {}; end
19
+ def data_models=(data_models)
20
+ @data_models = data_models
21
+ end
22
+
23
+ def execute(options, endpoint, parameters)
24
+ RequestContext.new(options, self, endpoint, parameters).perform
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'multi_json'
2
+
3
+ module Diesel
4
+ class DataModel
5
+ attr_reader :definition
6
+
7
+ def initialize(definition)
8
+ @definition = definition
9
+ end
10
+
11
+ def build(parameter_name, env)
12
+ context = env[:context]
13
+ h = context.get_attribute(parameter_name)
14
+ return nil unless h
15
+
16
+ json = definition.properties.reduce({}) do |m, (name, property)|
17
+ name = name.to_sym
18
+ m[name] = h[name] if h[name]; m
19
+ end
20
+
21
+ MultiJson.dump(json)
22
+ end
23
+ end
24
+ end
@@ -1,43 +1,25 @@
1
+ require 'diesel/middleware_builder'
2
+ require 'diesel/middleware_stack'
3
+
1
4
  module Diesel
2
5
  class Endpoint
3
- attr_reader :path
4
- attr_accessor :action
6
+ attr_reader :name, :url, :request_method
5
7
 
6
- def initialize(path)
7
- @path = path
8
+ def initialize(name, url, request_method)
9
+ @name, @url, @request_method = name, url, request_method
8
10
  end
9
11
 
10
- def perform(context)
11
- raise 'action required for endpoint' unless action
12
-
13
- assert_attributes(context)
14
-
15
- #unless action.accept? context
16
- # raise 'endpoint does not support this context'
17
- #end
18
-
19
- action.perform(context)
12
+ def middlewares
13
+ @middlewares ||= []
20
14
  end
21
15
 
22
- private
23
- def assert_attributes(context)
24
- #if attributes.nil?
25
- # unless context.attributes.empty?
26
- # raise ArgumentError, "invalid attributes #{atts.keys.join(', ')}"
27
- # end
28
- # return
29
- #end
30
-
31
- #invalid_atts = context.attributes.keys.map!(&:intern) - attributes.keys
32
- #unless invalid_atts.empty?
33
- # raise ArgumentError, "invalid attributes #{invalid_atts.join(', ')}"
34
- #end
16
+ def middleware_stack
17
+ @middleware_stack ||= MiddlewareStack.new(middlewares)
18
+ end
35
19
 
36
- #attributes.each do |attr_name, options|
37
- # if options[:required] and context.attributes[attr_name].nil?
38
- # raise ArgumentError, "require attribute: #{attr_name}"
39
- # end
40
- #end
41
- end
20
+ def config_middleware(&block)
21
+ builder = MiddlewareBuilder.new(middlewares)
22
+ builder.build(&block)
23
+ end
42
24
  end
43
25
  end
@@ -0,0 +1,27 @@
1
+ module Diesel
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
+ end
12
+
13
+ def call(env)
14
+ context = env[:context]
15
+ value = context.options[@id]
16
+ if @in == :header
17
+ env[:request_headers][@name] = value
18
+ elsif @in == :query
19
+ env[:params][@name] = value
20
+ end
21
+ @app.call(env)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module Diesel
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,11 @@
1
+ require 'diesel/middleware/set_parameter_base'
2
+
3
+ module Diesel
4
+ module Middleware
5
+ class SetBodyParameter < SetParameterBase
6
+ def apply_parameter_value(env, value)
7
+ env[:body] = value
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Diesel
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