diesel-api-dsl 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/apis/pivotal_tracker.rb +55 -0
- data/apis/slack.rb +36 -0
- data/diesel.gemspec +25 -0
- data/examples/create_pivotal_task.rb +13 -0
- data/examples/post_slack_message.rb +8 -0
- data/lib/diesel/action/http.rb +92 -0
- data/lib/diesel/api.rb +33 -0
- data/lib/diesel/api_builder.rb +68 -0
- data/lib/diesel/auth/api_key.rb +39 -0
- data/lib/diesel/auth/base.rb +8 -0
- data/lib/diesel/auth/oauth2.rb +13 -0
- data/lib/diesel/authenticator.rb +35 -0
- data/lib/diesel/complex_type_builder.rb +24 -0
- data/lib/diesel/container_builder.rb +6 -0
- data/lib/diesel/dsl.rb +239 -0
- data/lib/diesel/endpoint.rb +43 -0
- data/lib/diesel/model_builder.rb +9 -0
- data/lib/diesel/profile.rb +112 -0
- data/lib/diesel/request_context.rb +30 -0
- data/lib/diesel/version.rb +3 -0
- data/lib/diesel.rb +10 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YWYzZDNhNzk5OWE5MDgxOWM1MzE4MjZlNDg2YWFjMDUyNzlmNjNkOQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ODhkYmQ4ZDFjMTBjZjZiMTUxMDU4NTI4NjhkMzQ5NTUzODg5OGQxNA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZDViYzZlNzlkOGYyYWRiZDY1ZmNhZTRlM2U2OWQwNmJkZmQzZmI5OGEwMTZh
|
10
|
+
OWZlZjExOTA4NjRiN2E0MjZiZmE5N2JiYzJjMjFkMGQ1NWI5ZTQ0OGM0YTZk
|
11
|
+
M2M5MTk3ZWRiYzk1YTIxNTQ3ZDU2NzQ3MjQ2YjNmNDYyYWQ1ZmQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NjhlOGZkZTg2ODhiYmNkYjQ5MmYwZjVjYTRjOWZkNjhhZTI5NjZlMTUyYzhj
|
14
|
+
NjlhMzkyNTljZDQ1Y2IzZWM1YmRmODVlNzdlNDBkMmNjZWYyZmJlNjdhMzU1
|
15
|
+
MDJiYjk2NDFhNThhNTkzZTIwODRkYjE1Nzc4MTJhODgyZjQ4Yzc=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Calvin Yu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Diesel
|
2
|
+
|
3
|
+
Create API Clients From an DSL
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'diesel'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install diesel
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
apis do
|
2
|
+
description "PivotalTracker API v5"
|
3
|
+
api_version "5"
|
4
|
+
base_path "https://www.pivotaltracker.com/services/v5"
|
5
|
+
|
6
|
+
api_key "X-TrackerToken" do
|
7
|
+
nickname :api_token
|
8
|
+
pass_as :header
|
9
|
+
end
|
10
|
+
|
11
|
+
header "Content-Type" => "application/json"
|
12
|
+
|
13
|
+
resource "projects/{project_id}/stories" do
|
14
|
+
description "Operations on stories"
|
15
|
+
|
16
|
+
operation :create_story do
|
17
|
+
reference_url "https://www.pivotaltracker.com/help/api/rest/v5#projects_project_id_stories_post"
|
18
|
+
method :post
|
19
|
+
|
20
|
+
parameter "project_id" do
|
21
|
+
param_type :path
|
22
|
+
required
|
23
|
+
end
|
24
|
+
|
25
|
+
body "story" do
|
26
|
+
data_type "NewStory"
|
27
|
+
required
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
complex_type "NewStory" do
|
33
|
+
required :name
|
34
|
+
property :cl_numbers
|
35
|
+
property :current_state do
|
36
|
+
enum :accepted, :delivered, :finished, :started, :rejected, :unstarted, :unscheduled
|
37
|
+
end
|
38
|
+
property :external_id
|
39
|
+
property :deadline
|
40
|
+
property :description
|
41
|
+
property :estimate
|
42
|
+
property :integration_id
|
43
|
+
property :planned_iteration_number
|
44
|
+
property :requested_by_id
|
45
|
+
property :accepted_at
|
46
|
+
property :before_id
|
47
|
+
property :created_at
|
48
|
+
property :after_id
|
49
|
+
property :name
|
50
|
+
property :story_type do
|
51
|
+
enum :feature, :bug, :chore, :release
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/apis/slack.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
apis do
|
2
|
+
description "Slack"
|
3
|
+
api_version "1"
|
4
|
+
base_path "https://scoutmob.slack.com/services/hooks"
|
5
|
+
|
6
|
+
api_key "token" do
|
7
|
+
nickname :api_token
|
8
|
+
pass_as :query_parameter
|
9
|
+
end
|
10
|
+
|
11
|
+
header "Content-Type" => "application/json"
|
12
|
+
|
13
|
+
resource "incoming-webhook" do
|
14
|
+
description "Incoming Webhook"
|
15
|
+
|
16
|
+
operation :send_message do
|
17
|
+
reference_url "https://my.slack.com/services/new/incoming-webhook"
|
18
|
+
method :post
|
19
|
+
|
20
|
+
body "payload" do
|
21
|
+
data_type "Payload"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
complex_type "Payload" do
|
27
|
+
required :text
|
28
|
+
property :text
|
29
|
+
property :channel
|
30
|
+
property :username
|
31
|
+
property :icon_url
|
32
|
+
property :icon_emoji
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
data/diesel.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'diesel/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "diesel-api-dsl"
|
8
|
+
spec.version = Diesel::VERSION
|
9
|
+
spec.authors = ["Calvin Yu"]
|
10
|
+
spec.email = ["me@sourcebender.com"]
|
11
|
+
spec.description = %q{Create API Clients From an DSL}
|
12
|
+
spec.summary = %q{Create API Clients From an DSL}
|
13
|
+
spec.homepage = "http://github.com/cyu/diesel"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.1"
|
23
|
+
|
24
|
+
spec.add_dependency "httparty", "~> 0.12"
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'diesel'
|
2
|
+
require 'logger'
|
3
|
+
PivotalTracker = Diesel.load_api 'apis/pivotal_tracker'
|
4
|
+
api = PivotalTracker.new
|
5
|
+
api.logger = Logger.new(STDOUT)
|
6
|
+
api.logger.level = Logger::DEBUG
|
7
|
+
api.api_token = YOUR_API_TOKEN
|
8
|
+
p api.create_story(
|
9
|
+
project_id: YOUR_PROJECT_ID,
|
10
|
+
story: {
|
11
|
+
name: 'Testing Pivotal API',
|
12
|
+
:story_type => :chore
|
13
|
+
})
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Diesel
|
4
|
+
module Action
|
5
|
+
class HTTP
|
6
|
+
attr_accessor :filters, :request_method
|
7
|
+
|
8
|
+
def filters
|
9
|
+
@filters ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform(context)
|
13
|
+
req = Request.new(request_method, context.endpoint_url)
|
14
|
+
[context.api.class.authenticator].concat(filters).each do |filter|
|
15
|
+
filter.apply_filter(req, context)
|
16
|
+
end
|
17
|
+
|
18
|
+
if context.logger && context.logger.debug?
|
19
|
+
context.logger.debug("Request Method: #{request_method}")
|
20
|
+
context.logger.debug("URL: #{req.url}")
|
21
|
+
context.logger.debug("Options: #{req.http_options.inspect}")
|
22
|
+
end
|
23
|
+
|
24
|
+
req.perform
|
25
|
+
end
|
26
|
+
|
27
|
+
class Request
|
28
|
+
attr_reader :method, :headers, :query
|
29
|
+
attr_accessor :url, :body
|
30
|
+
|
31
|
+
def initialize(method, url)
|
32
|
+
@method = method
|
33
|
+
@url = url
|
34
|
+
@headers = {}
|
35
|
+
@query = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def perform
|
39
|
+
HTTParty.send(method, url, http_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def http_options
|
43
|
+
{headers: headers, query: query, body: body}
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_query_parameter(name, value)
|
47
|
+
@url = if url.index('?')
|
48
|
+
"#{url}&#{URI.escape(name)}=#{URI.escape(value)}"
|
49
|
+
else
|
50
|
+
"#{url}?#{URI.escape(name)}=#{URI.escape(value)}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class ParameterFilter
|
56
|
+
attr_reader :parameter
|
57
|
+
|
58
|
+
def initialize(parameter)
|
59
|
+
@parameter = parameter
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_filter(request, context)
|
63
|
+
v = find_parameter_value(context)
|
64
|
+
case parameter.param_type
|
65
|
+
when :query
|
66
|
+
request.query[parameter.name] = v
|
67
|
+
when :path
|
68
|
+
request.url = request.url.gsub(path_regex, v.to_s)
|
69
|
+
when :body
|
70
|
+
request.body = v
|
71
|
+
when :header
|
72
|
+
request.headers[parameter.name] = v
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_parameter_value(context)
|
77
|
+
if parameter.complex?
|
78
|
+
model = context.api.class.models[parameter.data_type]
|
79
|
+
model.build(parameter, context)
|
80
|
+
else
|
81
|
+
parameter.value || context.get_attribute(parameter.name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
def path_regex
|
87
|
+
Regexp.new(Regexp.quote("{#{parameter.name}}"))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/diesel/api.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Diesel
|
2
|
+
class API
|
3
|
+
attr_accessor :logger
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def base_path; @base_path; end
|
7
|
+
def base_path=(base_path)
|
8
|
+
@base_path = base_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def endpoints; @endpoints ||= []; end
|
12
|
+
def endpoints=(endpoints)
|
13
|
+
@endpoints = endpoints
|
14
|
+
end
|
15
|
+
|
16
|
+
def authenticator; @authenticator; end
|
17
|
+
def authenticator=(auth)
|
18
|
+
@authenticator = auth
|
19
|
+
@authenticator.activate(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def models; @models ||= {}; end
|
23
|
+
def models=(models)
|
24
|
+
@models = models
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def execute(endpoint, options)
|
30
|
+
RequestContext.new(self, endpoint, options).perform
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'diesel/action/http'
|
2
|
+
require 'diesel/complex_type_builder'
|
3
|
+
require 'diesel/container_builder'
|
4
|
+
|
5
|
+
module Diesel
|
6
|
+
class APIBuilder
|
7
|
+
attr_reader :profile
|
8
|
+
|
9
|
+
def initialize(profile)
|
10
|
+
@profile = profile
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
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)
|
21
|
+
#Diesel.const_set create_class_name(name), klass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def global_filters
|
27
|
+
@global_filters ||= profile.parameters.map { |p| Diesel::Action::HTTP::ParameterFilter.new(p) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_api_class
|
31
|
+
Class.new(Diesel::API)
|
32
|
+
end
|
33
|
+
|
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)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end.flatten.compact
|
49
|
+
end
|
50
|
+
|
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 }
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_class_name(name)
|
62
|
+
string = name.to_s.sub(/.*\./, '')
|
63
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
64
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'diesel/auth/base'
|
2
|
+
|
3
|
+
module Diesel
|
4
|
+
module Auth
|
5
|
+
class APIKey < Base
|
6
|
+
attr_accessor :nickname, :pass_as, :name
|
7
|
+
|
8
|
+
def self.build(authorization)
|
9
|
+
auth = new
|
10
|
+
auth.name = authorization.name
|
11
|
+
auth.nickname = authorization.nickname
|
12
|
+
auth.pass_as = authorization.pass_as
|
13
|
+
auth
|
14
|
+
end
|
15
|
+
|
16
|
+
def activate(api_class)
|
17
|
+
header_name = name
|
18
|
+
api_class.__send__(:define_method, "#{nickname}=".to_sym) do |header_value|
|
19
|
+
(@_auth_http_headers ||= {})[header_name] = header_value
|
20
|
+
end
|
21
|
+
api_class.send(:include, InstanceMethods)
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_filter(request, context)
|
25
|
+
if pass_as.to_sym == :header
|
26
|
+
request.headers[name] = context.api.auth_http_headers[name]
|
27
|
+
elsif pass_as.to_sym == :query_parameter
|
28
|
+
request.add_query_parameter(name, context.api.auth_http_headers[name])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
def auth_http_headers
|
34
|
+
@_auth_http_headers
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Diesel
|
2
|
+
module Auth
|
3
|
+
class OAuth2
|
4
|
+
def self.activate(api_class, options)
|
5
|
+
api_class.send :attr_accessor, :access_token
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.append_authentication(context, http_options)
|
9
|
+
(http_options[:headers] ||= {})['Authorization'] = "Bearer #{context.api.access_token}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Diesel
|
2
|
+
class Authenticator
|
3
|
+
attr_reader :strategies
|
4
|
+
|
5
|
+
def self.build(authorizations)
|
6
|
+
strategies = authorizations.map do |auth|
|
7
|
+
case auth.type
|
8
|
+
when :oauth2
|
9
|
+
require 'diesel/auth/oauth2'
|
10
|
+
Diesel::Auth::OAuth2.build(auth)
|
11
|
+
when :api_key
|
12
|
+
require 'diesel/auth/api_key'
|
13
|
+
Diesel::Auth::APIKey.build(auth)
|
14
|
+
else
|
15
|
+
raise "Unsupported authentication: #{auth.type}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
new(strategies)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(strategies)
|
23
|
+
@strategies = strategies
|
24
|
+
end
|
25
|
+
|
26
|
+
def activate(api_class)
|
27
|
+
@strategies.each { |s| s.activate(api_class) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply_filter(request, context)
|
31
|
+
@strategies.each { |s| s.apply_filter(request, context) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'diesel/model_builder'
|
3
|
+
|
4
|
+
module Diesel
|
5
|
+
class ComplexTypeBuilder < ModelBuilder
|
6
|
+
def build(parameter, context)
|
7
|
+
h = context.get_attribute(parameter.name)
|
8
|
+
unless h
|
9
|
+
if parameter.required?
|
10
|
+
raise "Missing required parameter #{parameter.name}"
|
11
|
+
else
|
12
|
+
return nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
if model.required.detect { |r| h[r].nil? }
|
16
|
+
raise "Missing required value #{r} in #{parameter.name}"
|
17
|
+
end
|
18
|
+
json = model.properties.reduce({}) do |m, property|
|
19
|
+
m[property.name] = h[property.name] if h[property.name]; m
|
20
|
+
end
|
21
|
+
MultiJson.dump(json)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/diesel/dsl.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'diesel/profile'
|
2
|
+
require 'diesel/endpoint'
|
3
|
+
require 'diesel/request_context'
|
4
|
+
|
5
|
+
module Diesel
|
6
|
+
module DSL
|
7
|
+
def self.load_profile(name, filename)
|
8
|
+
loader = DSL::Loader.new(name)
|
9
|
+
loader.instance_eval File.read(filename), filename
|
10
|
+
loader.profile
|
11
|
+
end
|
12
|
+
|
13
|
+
class LoaderBase
|
14
|
+
attr_reader :object
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def attribute(name)
|
18
|
+
define_method(name) do |val|
|
19
|
+
@object.__send__("#{name}=".to_sym, val)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attribute :description
|
25
|
+
end
|
26
|
+
|
27
|
+
module Parameters
|
28
|
+
def header(*args, &block)
|
29
|
+
ParameterLoader.load_parameters(@object, :header, *args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def body(*args, &block)
|
33
|
+
ParameterLoader.load_parameters(@object, :body, *args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def parameter(*args, &block)
|
37
|
+
ParameterLoader.load_parameters(@object, nil, *args, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#class PartLoader
|
42
|
+
# attr_reader :part
|
43
|
+
|
44
|
+
# def initialize(*args)
|
45
|
+
# @part = Part.new(*args)
|
46
|
+
# end
|
47
|
+
|
48
|
+
# def header name, value
|
49
|
+
# @part.set_header(name, value)
|
50
|
+
# end
|
51
|
+
|
52
|
+
# def body value
|
53
|
+
# @part.body = value
|
54
|
+
# end
|
55
|
+
#end
|
56
|
+
|
57
|
+
#class ActionLoader < PartLoader
|
58
|
+
# attr_reader :action
|
59
|
+
|
60
|
+
# def initialize(endpoint, method, options)
|
61
|
+
# @part = @action = Action.new(endpoint, method, options)
|
62
|
+
# end
|
63
|
+
|
64
|
+
# def multipart(options = {}, &block)
|
65
|
+
# loader = MultipartLoader.new(options)
|
66
|
+
# loader.instance_eval(&block)
|
67
|
+
# action.multipart = loader.multipart
|
68
|
+
# end
|
69
|
+
#end
|
70
|
+
|
71
|
+
#class MultipartLoader
|
72
|
+
# attr_reader :multipart
|
73
|
+
|
74
|
+
# def initialize(options)
|
75
|
+
# @multipart = Multipart.new(options)
|
76
|
+
# end
|
77
|
+
|
78
|
+
# def part(*args, &block)
|
79
|
+
# loader = PartLoader.new(*args)
|
80
|
+
# loader.instance_eval(&block) if block
|
81
|
+
# multipart.add_part(loader.part)
|
82
|
+
# end
|
83
|
+
#end
|
84
|
+
|
85
|
+
class AuthorizationLoader < LoaderBase
|
86
|
+
attribute :name
|
87
|
+
attribute :nickname
|
88
|
+
attribute :pass_as
|
89
|
+
attribute :type
|
90
|
+
|
91
|
+
def initialize
|
92
|
+
@object = Diesel::Profile::Authorization.new
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class PropertyLoader < LoaderBase
|
97
|
+
attribute :name
|
98
|
+
|
99
|
+
def initialize
|
100
|
+
@object = Diesel::Profile::Property.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def enum(*values)
|
104
|
+
@object.enum = values
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class ComplexTypeLoader < LoaderBase
|
109
|
+
attribute :name
|
110
|
+
|
111
|
+
def initialize
|
112
|
+
@object = Diesel::Profile::ComplexType.new
|
113
|
+
end
|
114
|
+
|
115
|
+
def required(*required_properties)
|
116
|
+
@object.required = required_properties
|
117
|
+
end
|
118
|
+
|
119
|
+
def property(name, &block)
|
120
|
+
l = PropertyLoader.new
|
121
|
+
l.name(name)
|
122
|
+
l.instance_eval(&block) if block
|
123
|
+
@object.properties << l.object
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class OperationLoader < LoaderBase
|
128
|
+
include Parameters
|
129
|
+
|
130
|
+
attribute :nickname
|
131
|
+
attribute :reference_url
|
132
|
+
attribute :method
|
133
|
+
|
134
|
+
def initialize
|
135
|
+
@object = Diesel::Profile::Operation.new
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class ResourceLoader < LoaderBase
|
140
|
+
def initialize(path)
|
141
|
+
@object = Diesel::Profile::Resource.new(path)
|
142
|
+
end
|
143
|
+
|
144
|
+
def operation(nickname, &block)
|
145
|
+
l = OperationLoader.new
|
146
|
+
l.nickname(nickname)
|
147
|
+
l.instance_eval(&block)
|
148
|
+
@object.operations << l.object
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class ParameterLoader < LoaderBase
|
153
|
+
attribute :param_type
|
154
|
+
attribute :name
|
155
|
+
attribute :value
|
156
|
+
attribute :required
|
157
|
+
attribute :data_type
|
158
|
+
|
159
|
+
def initialize
|
160
|
+
@object = Diesel::Profile::Parameter.new
|
161
|
+
end
|
162
|
+
|
163
|
+
def required(required = true)
|
164
|
+
@object.required = required
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.load_parameters(target, param_type, *args, &block)
|
168
|
+
if args.first.is_a?(::Hash)
|
169
|
+
args.shift.each_pair do |name, value|
|
170
|
+
l = new
|
171
|
+
l.param_type(param_type)
|
172
|
+
l.name(name)
|
173
|
+
l.value(value)
|
174
|
+
target.parameters << l.object
|
175
|
+
end
|
176
|
+
else
|
177
|
+
l = new
|
178
|
+
l.param_type(param_type)
|
179
|
+
l.name(args.first)
|
180
|
+
l.instance_eval(&block) if block
|
181
|
+
target.parameters << l.object
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class APILoader < LoaderBase
|
187
|
+
include Parameters
|
188
|
+
|
189
|
+
attribute :api_version
|
190
|
+
attribute :base_path
|
191
|
+
|
192
|
+
def initialize(api_profile)
|
193
|
+
@object = api_profile
|
194
|
+
end
|
195
|
+
|
196
|
+
def resource(path, &block)
|
197
|
+
l = ResourceLoader.new(path)
|
198
|
+
l.instance_eval(&block)
|
199
|
+
@object.resources << l.object
|
200
|
+
end
|
201
|
+
|
202
|
+
def complex_type(name, &block)
|
203
|
+
l = ComplexTypeLoader.new
|
204
|
+
l.name(name)
|
205
|
+
l.instance_eval(&block)
|
206
|
+
@object.models << l.object
|
207
|
+
end
|
208
|
+
|
209
|
+
def container_type(name, &block)
|
210
|
+
l = ContainerTypeLoader.new
|
211
|
+
l.name(name)
|
212
|
+
l.instance_eval(&block)
|
213
|
+
@object.models << l.object
|
214
|
+
end
|
215
|
+
|
216
|
+
def api_key(name, &block)
|
217
|
+
l = AuthorizationLoader.new
|
218
|
+
l.name(name)
|
219
|
+
l.type(:api_key)
|
220
|
+
l.instance_eval(&block)
|
221
|
+
@object.authorizations << l.object
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class Loader
|
226
|
+
attr_reader :name
|
227
|
+
attr_reader :profile
|
228
|
+
|
229
|
+
def initialize(name)
|
230
|
+
@name = name
|
231
|
+
end
|
232
|
+
|
233
|
+
def apis(&block)
|
234
|
+
@profile = Profile::API.new(name)
|
235
|
+
APILoader.new(@profile).instance_eval(&block)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Diesel
|
2
|
+
class Endpoint
|
3
|
+
attr_reader :path
|
4
|
+
attr_accessor :action
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
8
|
+
end
|
9
|
+
|
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)
|
20
|
+
end
|
21
|
+
|
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
|
35
|
+
|
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
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'diesel/api'
|
2
|
+
require 'diesel/authenticator'
|
3
|
+
|
4
|
+
module Diesel
|
5
|
+
module Profile
|
6
|
+
class Base
|
7
|
+
attr_accessor :description
|
8
|
+
end
|
9
|
+
|
10
|
+
class Authorization < Base
|
11
|
+
attr_accessor :name, :nickname, :pass_as, :type
|
12
|
+
end
|
13
|
+
|
14
|
+
class Property < Base
|
15
|
+
attr_accessor :name, :enum
|
16
|
+
end
|
17
|
+
|
18
|
+
class Model < Base
|
19
|
+
attr_accessor :name
|
20
|
+
end
|
21
|
+
|
22
|
+
class ComplexType < Model
|
23
|
+
attr_accessor :required, :properties
|
24
|
+
|
25
|
+
def required
|
26
|
+
@required ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def properties
|
30
|
+
@properties ||= []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Container < Model
|
35
|
+
attr_accessor :type
|
36
|
+
end
|
37
|
+
|
38
|
+
class Parameter < Base
|
39
|
+
PRIMITIVE_TYPES = [
|
40
|
+
:integer,
|
41
|
+
:long,
|
42
|
+
:float,
|
43
|
+
:double,
|
44
|
+
:string,
|
45
|
+
:byte,
|
46
|
+
:boolean,
|
47
|
+
:date,
|
48
|
+
:date_time
|
49
|
+
]
|
50
|
+
|
51
|
+
attr_accessor :param_type, :name, :value, :data_type, :required
|
52
|
+
|
53
|
+
def complex?
|
54
|
+
!data_type.nil? && !primitive?
|
55
|
+
end
|
56
|
+
|
57
|
+
def primitive?
|
58
|
+
PRIMITIVE_TYPES.include?(data_type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def required?
|
62
|
+
!!required
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Operation < Base
|
67
|
+
attr_accessor :nickname, :reference_url, :method, :parameters
|
68
|
+
|
69
|
+
def parameters
|
70
|
+
@parameters ||= []
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Resource < Base
|
75
|
+
attr_accessor :path, :operations
|
76
|
+
|
77
|
+
def initialize(path)
|
78
|
+
@path = path
|
79
|
+
end
|
80
|
+
|
81
|
+
def operations
|
82
|
+
@operations ||= []
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class API < Base
|
87
|
+
attr_reader :name
|
88
|
+
attr_accessor :api_version, :base_path, :auth_type, :auth_options, :resources, :parameters, :authorizations
|
89
|
+
|
90
|
+
def initialize(name)
|
91
|
+
@name = name
|
92
|
+
end
|
93
|
+
|
94
|
+
def parameters
|
95
|
+
@parameters ||= []
|
96
|
+
end
|
97
|
+
|
98
|
+
def resources
|
99
|
+
@resources ||= []
|
100
|
+
end
|
101
|
+
|
102
|
+
def models
|
103
|
+
@models ||= []
|
104
|
+
end
|
105
|
+
|
106
|
+
def authorizations
|
107
|
+
@authorizations ||= []
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Diesel
|
2
|
+
class RequestContext
|
3
|
+
attr_reader :api, :endpoint, :attributes
|
4
|
+
|
5
|
+
def initialize(api, endpoint, attributes)
|
6
|
+
@api, @endpoint, @attributes = api, endpoint, attributes
|
7
|
+
end
|
8
|
+
|
9
|
+
def logger
|
10
|
+
api.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_attribute(name)
|
14
|
+
name = name.to_sym
|
15
|
+
attributes[name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def endpoint_url
|
19
|
+
url = api.class.base_path.dup
|
20
|
+
url << "/" unless url =~ /\/$/
|
21
|
+
endpoint_path = endpoint.path
|
22
|
+
url << (endpoint_path =~ /^\// ? endpoint_path[1..-1] : endpoint_path)
|
23
|
+
url
|
24
|
+
end
|
25
|
+
|
26
|
+
def perform
|
27
|
+
endpoint.perform(self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/diesel.rb
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: diesel-api-dsl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Calvin Yu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: httparty
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.12'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.12'
|
55
|
+
description: Create API Clients From an DSL
|
56
|
+
email:
|
57
|
+
- me@sourcebender.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- apis/pivotal_tracker.rb
|
68
|
+
- apis/slack.rb
|
69
|
+
- diesel.gemspec
|
70
|
+
- examples/create_pivotal_task.rb
|
71
|
+
- examples/post_slack_message.rb
|
72
|
+
- lib/diesel.rb
|
73
|
+
- lib/diesel/action/http.rb
|
74
|
+
- lib/diesel/api.rb
|
75
|
+
- lib/diesel/api_builder.rb
|
76
|
+
- lib/diesel/auth/api_key.rb
|
77
|
+
- lib/diesel/auth/base.rb
|
78
|
+
- lib/diesel/auth/oauth2.rb
|
79
|
+
- lib/diesel/authenticator.rb
|
80
|
+
- lib/diesel/complex_type_builder.rb
|
81
|
+
- lib/diesel/container_builder.rb
|
82
|
+
- lib/diesel/dsl.rb
|
83
|
+
- lib/diesel/endpoint.rb
|
84
|
+
- lib/diesel/model_builder.rb
|
85
|
+
- lib/diesel/profile.rb
|
86
|
+
- lib/diesel/request_context.rb
|
87
|
+
- lib/diesel/version.rb
|
88
|
+
homepage: http://github.com/cyu/diesel
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Create API Clients From an DSL
|
112
|
+
test_files: []
|