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
@@ -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
+ }