marketo_api 0.0.7.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +2 -0
- data/.gitignore +24 -0
- data/.rubocop.yml +57 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +6 -0
- data/bin/_guard-core +17 -0
- data/bin/console +12 -0
- data/bin/guard +17 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/setup +6 -0
- data/exe/.keep +0 -0
- data/lib/marketo_api.rb +45 -0
- data/lib/marketo_api/abstract_client.rb +15 -0
- data/lib/marketo_api/api/activities.rb +64 -0
- data/lib/marketo_api/api/base.rb +63 -0
- data/lib/marketo_api/api/campaigns.rb +70 -0
- data/lib/marketo_api/api/leads.rb +86 -0
- data/lib/marketo_api/api/sales.rb +19 -0
- data/lib/marketo_api/api/stats.rb +54 -0
- data/lib/marketo_api/client.rb +8 -0
- data/lib/marketo_api/concerns/authentication.rb +20 -0
- data/lib/marketo_api/concerns/base.rb +49 -0
- data/lib/marketo_api/concerns/caching.rb +24 -0
- data/lib/marketo_api/concerns/connection.rb +75 -0
- data/lib/marketo_api/concerns/verbs.rb +60 -0
- data/lib/marketo_api/config.rb +131 -0
- data/lib/marketo_api/middleware.rb +27 -0
- data/lib/marketo_api/middleware/authentication.rb +64 -0
- data/lib/marketo_api/middleware/authentication/token.rb +12 -0
- data/lib/marketo_api/middleware/authorization.rb +16 -0
- data/lib/marketo_api/middleware/caching.rb +26 -0
- data/lib/marketo_api/middleware/logger.rb +40 -0
- data/lib/marketo_api/middleware/raise_error.rb +47 -0
- data/lib/marketo_api/version.rb +3 -0
- data/marketo_api.gemspec +37 -0
- metadata +269 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
module Concerns
|
3
|
+
module Caching
|
4
|
+
# Public: Runs the block with caching disabled.
|
5
|
+
#
|
6
|
+
# block - A query/describe/etc.
|
7
|
+
#
|
8
|
+
# Returns the result of the block
|
9
|
+
def without_caching(&block)
|
10
|
+
options[:use_cache] = false
|
11
|
+
block.call
|
12
|
+
ensure
|
13
|
+
options.delete(:use_cache)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Internal: Cache to use for the caching middleware
|
19
|
+
def cache
|
20
|
+
options[:cache]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
module Concerns
|
3
|
+
module Connection
|
4
|
+
# Public: The Faraday::Builder instance used for the middleware stack. This
|
5
|
+
# can be used to insert an custom middleware.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# # Add the instrumentation middleware for Rails.
|
10
|
+
# client.middleware.use FaradayMiddleware::Instrumentation
|
11
|
+
#
|
12
|
+
# Returns the Faraday::Builder for the Faraday connection.
|
13
|
+
def middleware
|
14
|
+
connection.builder
|
15
|
+
end
|
16
|
+
alias_method :builder, :middleware
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Internal: Internal faraday connection where all requests go through
|
21
|
+
def connection
|
22
|
+
@connection ||= Faraday.new(options[:instance_url], connection_options) do |builder|
|
23
|
+
# Converts the request into JSON.
|
24
|
+
builder.request(:json)
|
25
|
+
|
26
|
+
# Handles reauthentication for 403 responses.
|
27
|
+
if authentication_middleware
|
28
|
+
builder.use(authentication_middleware, self, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets the oauth token in the headers.
|
32
|
+
builder.use(MarketoApi::Middleware::Authorization, self, options)
|
33
|
+
|
34
|
+
# Ensures the instance url is set.
|
35
|
+
# builder.use(MarketoApi::Middleware::InstanceURL, self, options)
|
36
|
+
|
37
|
+
# Caches GET requests.
|
38
|
+
builder.use(MarketoApi::Middleware::Caching, cache, options) if cache
|
39
|
+
|
40
|
+
# Follows 30x redirects.
|
41
|
+
builder.use(FaradayMiddleware::FollowRedirects)
|
42
|
+
|
43
|
+
# Raises errors for 40x responses.
|
44
|
+
builder.use(MarketoApi::Middleware::RaiseError)
|
45
|
+
|
46
|
+
# Parses returned JSON response into a hash.
|
47
|
+
builder.response(:json, content_type: /\bjson$/)
|
48
|
+
|
49
|
+
# Inject custom headers into requests
|
50
|
+
# builder.use(MarketoApi::Middleware::CustomHeaders, self, options)
|
51
|
+
|
52
|
+
# Log request/responses
|
53
|
+
builder.use(MarketoApi::Middleware::Logger, MarketoApi.configuration.logger, options) if MarketoApi.log?
|
54
|
+
|
55
|
+
builder.adapter(adapter)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def adapter
|
60
|
+
options[:adapter]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Internal: Faraday Connection options
|
64
|
+
def connection_options
|
65
|
+
{
|
66
|
+
request: {
|
67
|
+
timeout: options[:timeout],
|
68
|
+
open_timeout: options[:timeout]
|
69
|
+
},
|
70
|
+
ssl: options[:ssl]
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
module Concerns
|
3
|
+
module Verbs
|
4
|
+
# Internal: Define methods to handle a verb.
|
5
|
+
#
|
6
|
+
# verbs - A list of verbs to define methods for.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# define_verbs :get, :post
|
11
|
+
#
|
12
|
+
# Returns nil.
|
13
|
+
def define_verbs(*verbs)
|
14
|
+
verbs.each do |verb|
|
15
|
+
define_verb(verb)
|
16
|
+
define_api_verb(verb)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Internal: Defines a method to handle HTTP requests with the passed in
|
21
|
+
# verb.
|
22
|
+
#
|
23
|
+
# verb - Symbol name of the verb (e.g. :get).
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
#
|
27
|
+
# define_verb :get
|
28
|
+
# # => get '/rest/v1/leads'
|
29
|
+
#
|
30
|
+
# Returns nil.
|
31
|
+
def define_verb(verb)
|
32
|
+
define_method verb do |*args, &block|
|
33
|
+
begin
|
34
|
+
connection.send(verb, *args, &block)
|
35
|
+
rescue MarketoApi::UnauthorizedError
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal: Defines a method to handle HTTP requests with the passed in
|
42
|
+
# verb to a marketo api endpoint.
|
43
|
+
#
|
44
|
+
# verb - Symbol name of the verb (e.g. :get).
|
45
|
+
#
|
46
|
+
# Examples
|
47
|
+
#
|
48
|
+
# define_api_verb :get
|
49
|
+
# # => api_get 'leads'
|
50
|
+
#
|
51
|
+
# Returns nil.
|
52
|
+
def define_api_verb(verb)
|
53
|
+
define_method :"api_#{verb}" do |*args, &block|
|
54
|
+
args[0] = api_path(args[0])
|
55
|
+
send(verb, *args, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# Totally took this from Restforce.
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module MarketoApi
|
5
|
+
class << self
|
6
|
+
attr_writer :log
|
7
|
+
|
8
|
+
# Returns the current Configuration
|
9
|
+
#
|
10
|
+
# Example
|
11
|
+
#
|
12
|
+
# MarketoApi.configuration.api_version = "1.0"
|
13
|
+
# MarketoApi.configuration.cache = "ActiveSupport::Cache.lookup_store :redis_store"
|
14
|
+
def configuration
|
15
|
+
@configuration ||= Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Yields the Configuration
|
19
|
+
#
|
20
|
+
# Example
|
21
|
+
#
|
22
|
+
# MarketoApi.configure do |config|
|
23
|
+
# config.api_version = "1.0"
|
24
|
+
# config.cache = "ActiveSupport::Cache.lookup_store :redis_store"
|
25
|
+
# end
|
26
|
+
def configure
|
27
|
+
yield configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
def log?
|
31
|
+
@log ||= false
|
32
|
+
end
|
33
|
+
|
34
|
+
def log(message)
|
35
|
+
return unless MarketoApi.log?
|
36
|
+
configuration.logger.send(configuration.log_level, message)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Configuration
|
41
|
+
class Option
|
42
|
+
attr_reader :configuration, :name, :options
|
43
|
+
|
44
|
+
def self.define(*args)
|
45
|
+
new(*args).define
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(configuration, name, options = {})
|
49
|
+
@configuration = configuration
|
50
|
+
@name = name
|
51
|
+
@options = options
|
52
|
+
@default = options.fetch(:default, nil)
|
53
|
+
end
|
54
|
+
|
55
|
+
def define
|
56
|
+
write_attribute
|
57
|
+
define_method if default_provided?
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_reader :default
|
64
|
+
alias_method :default_provided?, :default
|
65
|
+
|
66
|
+
def write_attribute
|
67
|
+
configuration.send :attr_accessor, name
|
68
|
+
end
|
69
|
+
|
70
|
+
def define_method
|
71
|
+
our_default = default
|
72
|
+
our_name = name
|
73
|
+
configuration.send :define_method, our_name do
|
74
|
+
instance_variable_get(:"@#{our_name}") ||
|
75
|
+
instance_variable_set(
|
76
|
+
:"@#{our_name}",
|
77
|
+
our_default.respond_to?(:call) ? our_default.call : our_default
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self
|
84
|
+
attr_accessor :options
|
85
|
+
|
86
|
+
def option(*args)
|
87
|
+
option = Option.define(self, *args)
|
88
|
+
(self.options ||= []) << option.name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
option :api_version, default: lambda { ENV['MARKETO_API_VERSION'] || '1' }
|
93
|
+
|
94
|
+
# The OAuth client id
|
95
|
+
option :client_id, default: lambda { ENV['MARKETO_CLIENT_ID'] }
|
96
|
+
|
97
|
+
# The OAuth client secret
|
98
|
+
option :client_secret, default: lambda { ENV['MARKETO_CLIENT_SECRET'] }
|
99
|
+
|
100
|
+
option :oauth_token
|
101
|
+
|
102
|
+
# The url to make all requests
|
103
|
+
option :instance_url
|
104
|
+
|
105
|
+
# Set this to an object that responds to read, write and fetch and all GET
|
106
|
+
# requests will be cached.
|
107
|
+
option :cache
|
108
|
+
|
109
|
+
# Faraday request read/open timeout.
|
110
|
+
option :timeout
|
111
|
+
|
112
|
+
# Faraday adapter to use. Defaults to Faraday.default_adapter.
|
113
|
+
option :adapter, default: lambda { Faraday.default_adapter }
|
114
|
+
|
115
|
+
# Set SSL options
|
116
|
+
option :ssl, default: {}
|
117
|
+
|
118
|
+
# A Hash that is converted to HTTP headers
|
119
|
+
option :request_headers
|
120
|
+
|
121
|
+
# Set a logger for when MarketoApi.log is set to true, defaulting to STDOUT
|
122
|
+
option :logger, default: ::Logger.new(STDOUT)
|
123
|
+
|
124
|
+
# Set a log level for logging when MarketoApi.log is set to true, defaulting to :debug
|
125
|
+
option :log_level, default: :debug
|
126
|
+
|
127
|
+
def options
|
128
|
+
self.class.options
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
# Base class that all middleware can extend. Provides some convenient helper
|
3
|
+
# functions.
|
4
|
+
class Middleware < Faraday::Middleware
|
5
|
+
autoload :RaiseError, 'marketo_api/middleware/raise_error'
|
6
|
+
autoload :Authentication, 'marketo_api/middleware/authentication'
|
7
|
+
autoload :Authorization, 'marketo_api/middleware/authorization'
|
8
|
+
autoload :Caching, 'marketo_api/middleware/caching'
|
9
|
+
autoload :Logger, 'marketo_api/middleware/logger'
|
10
|
+
|
11
|
+
def initialize(app, client, options)
|
12
|
+
@app = app
|
13
|
+
@client = client
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
# Internal: Proxy to the client.
|
18
|
+
def client
|
19
|
+
@client
|
20
|
+
end
|
21
|
+
|
22
|
+
# Internal: Proxy to the client's faraday connection.
|
23
|
+
def connection
|
24
|
+
client.send(:connection)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
# Faraday middleware that allows for on the fly authentication of requests.
|
3
|
+
# When a request fails (a status of 401 is returned), the middleware
|
4
|
+
# will attempt to either reauthenticate (request new access_token).
|
5
|
+
class Middleware::Authentication < MarketoApi::Middleware
|
6
|
+
autoload :Token, 'marketo_api/middleware/authentication/token'
|
7
|
+
|
8
|
+
# Rescue from 401's, authenticate then raise the error again so the client
|
9
|
+
# can reissue the request.
|
10
|
+
def call(env)
|
11
|
+
@app.call(env)
|
12
|
+
rescue MarketoApi::UnauthorizedError
|
13
|
+
authenticate!
|
14
|
+
raise
|
15
|
+
end
|
16
|
+
|
17
|
+
# Internal: Performs the authentication and returns the response body.
|
18
|
+
def authenticate!
|
19
|
+
encoded_params = URI.encode_www_form(params)
|
20
|
+
token_url = @options[:instance_url] + '/identity/oauth/token'
|
21
|
+
url = "#{token_url}?#{encoded_params}"
|
22
|
+
response = connection.get(url)
|
23
|
+
|
24
|
+
if response.status >= 500
|
25
|
+
raise MarketoApi::ServerError, error_message(response)
|
26
|
+
elsif response.status != 200
|
27
|
+
raise MarketoApi::AuthenticationError, error_message(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
@options[:oauth_token] = response.body['access_token']
|
31
|
+
@options[:expires_in] = response.body['expires_in']
|
32
|
+
|
33
|
+
response.body
|
34
|
+
end
|
35
|
+
|
36
|
+
# Internal: The params to post to the OAuth service.
|
37
|
+
def params
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal: Faraday connection to use when sending an authentication request.
|
42
|
+
def connection
|
43
|
+
@connection ||= Faraday.new(faraday_options) do |builder|
|
44
|
+
builder.use(Faraday::Request::UrlEncoded)
|
45
|
+
builder.response(:json)
|
46
|
+
|
47
|
+
builder.use(MarketoApi::Middleware::Logger, MarketoApi.configuration.logger, @options) if MarketoApi.log?
|
48
|
+
|
49
|
+
builder.adapter(@options[:adapter])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Internal: The parsed error response.
|
54
|
+
def error_message(response)
|
55
|
+
"#{response.body['error']}: #{response.body['error_description']}"
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def faraday_options
|
61
|
+
{ url: @options[:instance_url], ssl: @options[:ssl] }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
# Authentication middleware used to fetch the access_token
|
3
|
+
class Middleware::Authentication::Token < MarketoApi::Middleware::Authentication
|
4
|
+
def params
|
5
|
+
{
|
6
|
+
grant_type: 'client_credentials',
|
7
|
+
client_id: @options[:client_id],
|
8
|
+
client_secret: @options[:client_secret]
|
9
|
+
}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
# Piece of middleware that simply injects the OAuth token into the request
|
3
|
+
# headers.
|
4
|
+
class Middleware::Authorization < MarketoApi::Middleware
|
5
|
+
AUTH_HEADER = 'Authorization'.freeze
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
env[:request_headers][AUTH_HEADER] = %(Bearer #{token})
|
9
|
+
@app.call(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
def token
|
13
|
+
@options[:oauth_token]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MarketoApi
|
2
|
+
class Middleware::Caching < FaradayMiddleware::Caching
|
3
|
+
def call(env)
|
4
|
+
expire(cache_key(env)) unless use_cache?
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def expire(key)
|
9
|
+
cache.delete(key) if cache
|
10
|
+
end
|
11
|
+
|
12
|
+
# We don't want to cache requests for different clients, so append the
|
13
|
+
# oauth token to the cache key.
|
14
|
+
def cache_key(env)
|
15
|
+
super(env) + hashed_auth_header(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
def use_cache?
|
19
|
+
@options.fetch(:use_cache, true)
|
20
|
+
end
|
21
|
+
|
22
|
+
def hashed_auth_header(env)
|
23
|
+
Digest::SHA1.hexdigest(env[:request_headers][MarketoApi::Middleware::Authorization::AUTH_HEADER])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module MarketoApi
|
4
|
+
class Middleware::Logger < Faraday::Response::Middleware
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def initialize(app, logger, options)
|
8
|
+
super(app)
|
9
|
+
@options = options
|
10
|
+
@logger = logger || begin
|
11
|
+
require 'logger'
|
12
|
+
::Logger.new(STDOUT)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
debug('request') do
|
20
|
+
dump url: env[:url].to_s,
|
21
|
+
method: env[:method],
|
22
|
+
headers: env[:request_headers],
|
23
|
+
body: env[:body]
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_complete(env)
|
29
|
+
debug('response') do
|
30
|
+
dump status: env[:status].to_s,
|
31
|
+
headers: env[:response_headers],
|
32
|
+
body: env[:body]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def dump(hash)
|
37
|
+
"\n" + hash.map { |k, v| " #{k}: #{v.inspect}" }.join("\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|