diesel-api-dsl 0.0.1 → 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.
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