marketo_api 0.0.7.pre.alpha
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 +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
|