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
@@ -0,0 +1,70 @@
1
+ require 'multi_json'
2
+ require 'diesel/utils/inflections'
3
+ require 'diesel/swagger/specification'
4
+
5
+
6
+ module Diesel
7
+ module Swagger
8
+ class Parser
9
+ include Diesel::Utils::Inflections
10
+
11
+ def parse(spec)
12
+ build_specification(MultiJson.load(spec))
13
+ end
14
+
15
+ def build_specification(json)
16
+ specification = build_node(Specification, json)
17
+ specification.info = build_node(Info, json['info'])
18
+ specification.external_docs = build_node(ExternalDocs, json['externalDocs'])
19
+ specification.schemes = json['schemes']
20
+ specification.produces = json['produces'] || []
21
+ specification.security_definitions = build_node_hash(Swagger::SecurityDefinition, json, 'securityDefinitions')
22
+ specification.paths = build_node_hash(Path, json, 'paths') do |path, path_json|
23
+ [:get, :put, :post, :delete, :options, :head, :patch].each do |method|
24
+ if op_json = path_json[method.to_s]
25
+ op = build_node(Operation, op_json)
26
+ op.external_docs = build_node(ExternalDocs, op_json['externalDocs'])
27
+ op.parameters = build_node_list(Parameter, op_json, 'parameters')
28
+ path.send("#{method}=".to_sym, op)
29
+ end
30
+ end
31
+ end
32
+ specification.definitions = build_node_hash(Swagger::Definition, json, 'definitions') do |definition, def_json|
33
+ definition.properties = build_node_hash(Swagger::Property, def_json, 'properties') do |prop, prop_json|
34
+ prop.enum = prop_json['enum']
35
+ end
36
+ end
37
+ specification
38
+ end
39
+
40
+ protected
41
+
42
+ def build_node(model_class, json)
43
+ if json
44
+ model = model_class.new
45
+ model_class.attribute_names.each do |att|
46
+ model.send("#{att}=".to_sym, json[camelize(att.to_s, false)])
47
+ end
48
+ model
49
+ end
50
+ end
51
+
52
+ def build_node_list(model_class, json, json_list_key)
53
+ (json[json_list_key] || []).map do |node_json|
54
+ node = build_node(model_class, node_json)
55
+ yield(node, node_json) if block_given?
56
+ node
57
+ end
58
+ end
59
+
60
+ def build_node_hash(model_class, json, json_hash_key)
61
+ (json[json_hash_key] || {}).reduce({}) do |m, (k, v)|
62
+ m[k] = node = build_node(model_class, v)
63
+ yield(node, v) if block_given?
64
+ m
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ require 'diesel/swagger/node'
2
+ require 'diesel/swagger/operation'
3
+
4
+ module Diesel
5
+ module Swagger
6
+ class Path < Node
7
+ attribute :get, validate: true
8
+ attribute :put, validate: true
9
+ attribute :post, validate: true
10
+ attribute :delete, validate: true
11
+ attribute :options, validate: true
12
+ attribute :head, validate: true
13
+ attribute :patch, validate: true
14
+
15
+ list :parameters, validate: true
16
+
17
+ def operations_map
18
+ [:get, :put, :post, :delete, :options, :head, :patch].reduce({}) do |m, method|
19
+ if op = __send__(method)
20
+ m[method] = op
21
+ end
22
+ m
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'diesel/swagger/data_type_field'
2
+
3
+ module Diesel
4
+ module Swagger
5
+ class Property < DataTypeField
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require 'diesel/swagger/node'
2
+
3
+ module Diesel
4
+ module Swagger
5
+ class SecurityDefinition < Node
6
+ attribute :type
7
+ attribute :description
8
+ attribute :name
9
+ attribute :in, symbolize: true
10
+ attribute :flow
11
+ attribute :authorization_url
12
+ attribute :token_url
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'diesel/swagger/info'
2
+ require 'diesel/swagger/external_docs'
3
+ require 'diesel/swagger/security_definition'
4
+ require 'diesel/swagger/path'
5
+ require 'diesel/swagger/definition'
6
+ # require 'diesel/swagger/response'
7
+
8
+ module Diesel
9
+ module Swagger
10
+ class Specification < Node
11
+ attribute :swagger
12
+ attribute :info, validate: true
13
+ attribute :host
14
+ attribute :base_path
15
+ attribute :external_docs, validate: true
16
+
17
+ list :schemes
18
+ list :consumes
19
+ list :produces
20
+ list :tags, validate: true
21
+
22
+ hash :paths, validate: true
23
+ hash :definitions, validate: true
24
+ hash :parameters, validate: true
25
+ hash :responses, validate: true
26
+ hash :security_definitions, validate: true
27
+ hash :security
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ module Diesel
2
+ module Utils
3
+ module Inflections
4
+
5
+ def self.acronyms
6
+ @@acronyms ||= {}
7
+ end
8
+
9
+ def self.acronym_regex
10
+ @@acronym_regex ||= /(?=a)b/
11
+ end
12
+
13
+ def camelize(term, uppercase_first_letter = true)
14
+ string = term.to_s
15
+ if uppercase_first_letter
16
+ string = string.sub(/^[a-z\d]*/) { ::Diesel::Utils::Inflections.acronyms[$&] || $&.capitalize }
17
+ else
18
+ string = string.sub(/^(?:#{::Diesel::Utils::Inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
19
+ end
20
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{::Diesel::Utils::Inflections.acronyms[$2] || $2.capitalize}" }
21
+ string.gsub!('/', '::')
22
+ string
23
+ end
24
+
25
+ def underscore(camel_cased_word)
26
+ word = camel_cased_word.to_s.gsub('::', '/')
27
+ word.gsub!(/(?:([A-Za-z\d])|^)(#{::Diesel::Utils::Inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
28
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
29
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
30
+ word.tr!("-", "_")
31
+ word.downcase!
32
+ word
33
+ end
34
+
35
+ def constantize(camel_cased_word)
36
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
37
+ raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
38
+ end
39
+
40
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ Diesel::Utils::Inflections.extend(Diesel::Utils::Inflections)
@@ -1,3 +1,3 @@
1
1
  module Diesel
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'diesel'
3
+
4
+ describe Diesel::APIBuilder do
5
+
6
+ let(:specification) do
7
+ Diesel.parse_specification(File.join(File.dirname(__FILE__), '..', 'files', 'pivotal_tracker.json'))
8
+ end
9
+
10
+ it "should build a API class from specification" do
11
+ api = Diesel::APIBuilder.new(specification).build
12
+ end
13
+
14
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'diesel/swagger/parser'
3
+
4
+ describe Diesel::Swagger::Parser do
5
+ subject { Diesel::Swagger::Parser }
6
+
7
+ let(:specification_json) do
8
+ File.read(File.join(File.dirname(__FILE__), '..', '..', 'files', 'pivotal_tracker.json'))
9
+ end
10
+
11
+ it "should parse a diesel specification" do
12
+ specification = Diesel::Swagger::Parser.new.parse(specification_json)
13
+ expect(specification).to_not be_nil
14
+
15
+ expect(specification.host).to eq "www.pivotaltracker.com"
16
+ expect(specification.schemes).to include "https"
17
+ expect(specification.base_path).to eq "/services/v5"
18
+
19
+ expect(specification.info).to_not be_nil
20
+ expect(specification.info.title).to eq "PivotalTracker"
21
+ expect(specification.info.version).to eq "5"
22
+
23
+ api_token = specification.security_definitions['apiToken']
24
+ expect(api_token).to_not be_nil
25
+ expect(api_token.type).to eq "apiKey"
26
+ expect(api_token.in).to eq "header"
27
+ expect(api_token.name).to eq "X-TrackerToken"
28
+
29
+ expect(specification.produces).to include "application/json"
30
+
31
+ expect(specification.paths.count).to eq 1
32
+
33
+ path = specification.paths["/projects/{projectId}/stories"]
34
+ expect(path).to_not be_nil
35
+
36
+ expect(path.post).to_not be_nil
37
+ expect(path.post.operation_id).to eq "createStory"
38
+ expect(path.post.external_docs.url).to eq "https://www.pivotaltracker.com/help/api/rest/v5#projects_project_id_stories_post"
39
+
40
+ expect(path.post.parameters.count).to eq 2
41
+
42
+ param1 = path.post.parameters[0]
43
+ expect(param1).to_not be_nil
44
+ expect(param1.in).to eq :path
45
+ expect(param1.name).to eq "projectId"
46
+ expect(param1).to be_required
47
+
48
+ param2 = path.post.parameters[1]
49
+ expect(param2).to_not be_nil
50
+ expect(param2.in).to eq :body
51
+ expect(param2.name).to eq "body"
52
+ expect(param2).to be_required
53
+ expect(param2.schema).to eq "NewStory"
54
+
55
+ expect(specification.definitions.count).to eq 1
56
+
57
+ definition = specification.definitions["NewStory"]
58
+ expect(definition).to_not be_nil
59
+ expect(definition.required).to eq ["name"]
60
+
61
+ expect(definition.properties.count).to eq 15
62
+
63
+ prop1 = definition.properties["cl_numbers"]
64
+ expect(prop1).to_not be_nil
65
+ expect(prop1.type).to eq :string
66
+
67
+ prop2 = definition.properties["current_state"]
68
+ expect(prop2).to_not be_nil
69
+ expect(prop2.type).to eq :string
70
+ expect(prop2.enum).to eq [
71
+ "accepted",
72
+ "delivered",
73
+ "finished",
74
+ "started",
75
+ "rejected",
76
+ "unstarted",
77
+ "unscheduled"
78
+ ]
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'diesel'
3
+ require 'logger'
4
+
5
+ describe Diesel do
6
+
7
+ def load_api(name)
8
+ fn = File.join(File.dirname(__FILE__), 'files', "#{name}.json")
9
+ Diesel.load_api(fn)
10
+ end
11
+
12
+ it "should be able to build a PivotalTracker API client" do
13
+ PivotalTracker = load_api('pivotal_tracker')
14
+ client = PivotalTracker.new(api_token: 'afaketoken')
15
+ client.logger = Logger.new(STDOUT)
16
+ client.logger.level = Logger::DEBUG
17
+
18
+ VCR.use_cassette('pivotal_tracker_create_story') do
19
+ result = client.create_story(
20
+ project_id: '1053124',
21
+ story: {
22
+ name: 'Testing Pivotal API',
23
+ story_type: :chore
24
+ })
25
+ expect(result['kind']).to eq 'story'
26
+ expect(result).to have_key('id')
27
+ expect(result.response.code).to eq "200"
28
+ end
29
+ end
30
+
31
+ it "should be able to build a Honeybadger API client" do
32
+ Honeybadger = load_api('honeybadger')
33
+ client = Honeybadger.new(auth_token: 'afaketoken')
34
+ client.logger = Logger.new(STDOUT)
35
+ client.logger.level = Logger::DEBUG
36
+
37
+ VCR.use_cassette('honeybadger') do
38
+ results = client.get_faults(project_id: 3167)
39
+ expect(results['results'].first).to have_key('project_id')
40
+ expect(results.response.code).to eq "200"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,80 @@
1
+ {
2
+ "swagger": "2.0",
3
+
4
+ "info": {
5
+ "title": "Honeybadger",
6
+ "description": "Honeybadger Read API v1",
7
+ "version": "1"
8
+ },
9
+
10
+ "host": "api.honeybadger.io",
11
+
12
+ "basePath": "/v1",
13
+
14
+ "schemes": ["https"],
15
+
16
+ "produces": ["application/json"],
17
+
18
+ "paths": {
19
+ "/projects/{projectId}/faults": {
20
+ "get": {
21
+ "summary": "Returns a list of faults for the given project",
22
+ "externalDocs": {
23
+ "url": "https://www.honeybadger.io/documentation/read_api"
24
+ },
25
+ "operationId": "getFaults",
26
+ "parameters": [
27
+ {
28
+ "name": "projectId",
29
+ "in": "path",
30
+ "required": true,
31
+ "type": "string"
32
+ },
33
+ {
34
+ "name": "page",
35
+ "in": "query"
36
+ }
37
+ ]
38
+ }
39
+ },
40
+ "/projects/{projectId}/faults/{faultId}/notices": {
41
+ "get": {
42
+ "summary": "Returns a list of notices for the given fault",
43
+ "externalDocs": {
44
+ "url": "https://www.honeybadger.io/documentation/read_api"
45
+ },
46
+ "operationId": "getNotices",
47
+ "parameters": [
48
+ {
49
+ "name": "projectId",
50
+ "in": "path",
51
+ "required": true,
52
+ "type": "string"
53
+ },
54
+ {
55
+ "name": "faultId",
56
+ "in": "path",
57
+ "required": true,
58
+ "type": "string"
59
+ },
60
+ {
61
+ "name": "page",
62
+ "in": "query"
63
+ }
64
+ ]
65
+ }
66
+ }
67
+ },
68
+
69
+ "securityDefinitions": {
70
+ "authToken": {
71
+ "type": "apiKey",
72
+ "name": "auth_token",
73
+ "in": "query"
74
+ }
75
+ },
76
+
77
+ "security": {
78
+ "authToken": []
79
+ }
80
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "swagger": "2.0",
3
+
4
+ "info": {
5
+ "title": "PivotalTracker",
6
+ "description": "Agile project management tool for software teams",
7
+ "version": "5"
8
+ },
9
+
10
+ "host": "www.pivotaltracker.com",
11
+
12
+ "basePath": "/services/v5",
13
+
14
+ "schemes": ["https"],
15
+
16
+ "produces": ["application/json"],
17
+
18
+ "paths": {
19
+ "/projects/{projectId}/stories": {
20
+ "post": {
21
+ "summary": "Create a new story",
22
+ "externalDocs": {
23
+ "url": "https://www.pivotaltracker.com/help/api/rest/v5#projects_project_id_stories_post"
24
+ },
25
+ "operationId": "createStory",
26
+ "parameters": [
27
+ {
28
+ "name": "projectId",
29
+ "in": "path",
30
+ "required": true,
31
+ "type": "string"
32
+ },
33
+ {
34
+ "name": "story",
35
+ "in": "body",
36
+ "required": true,
37
+ "schema": "NewStory"
38
+ }
39
+ ]
40
+ }
41
+ }
42
+ },
43
+
44
+ "definitions": {
45
+ "NewStory": {
46
+ "title": "New Story",
47
+ "type": "object",
48
+ "properties": {
49
+ "cl_numbers": { "type": "string" },
50
+ "current_state": {
51
+ "type": "string",
52
+ "enum": [
53
+ "accepted",
54
+ "delivered",
55
+ "finished",
56
+ "started",
57
+ "rejected",
58
+ "unstarted",
59
+ "unscheduled"
60
+ ]
61
+ },
62
+ "external_id": { "type": "string" },
63
+ "deadline": { "type": "string" },
64
+ "description": { "type": "string" },
65
+ "estimate": { "type": "string" },
66
+ "integration_id": { "type": "string" },
67
+ "planned_iteration_number": { "type": "string" },
68
+ "requested_by_id": { "type": "string" },
69
+ "accepted_at": { "type": "string" },
70
+ "before_id": { "type": "string" },
71
+ "created_at": { "type": "string" },
72
+ "after_id": { "type": "string" },
73
+ "name": { "type": "string" },
74
+ "story_type": {
75
+ "type": "string",
76
+ "enum": [ "feature", "bug", "chore", "release" ]
77
+ }
78
+ },
79
+ "required": [ "name" ]
80
+ }
81
+ },
82
+
83
+ "securityDefinitions": {
84
+ "apiToken": {
85
+ "type": "apiKey",
86
+ "name": "X-TrackerToken",
87
+ "in": "header"
88
+ }
89
+ },
90
+
91
+ "security": {
92
+ "apiToken": []
93
+ }
94
+ }