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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +2 -0
  3. data/.gitignore +24 -0
  4. data/.rubocop.yml +57 -0
  5. data/.travis.yml +10 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +13 -0
  9. data/LICENSE +22 -0
  10. data/README.md +75 -0
  11. data/Rakefile +6 -0
  12. data/bin/_guard-core +17 -0
  13. data/bin/console +12 -0
  14. data/bin/guard +17 -0
  15. data/bin/rake +17 -0
  16. data/bin/rspec +17 -0
  17. data/bin/setup +6 -0
  18. data/exe/.keep +0 -0
  19. data/lib/marketo_api.rb +45 -0
  20. data/lib/marketo_api/abstract_client.rb +15 -0
  21. data/lib/marketo_api/api/activities.rb +64 -0
  22. data/lib/marketo_api/api/base.rb +63 -0
  23. data/lib/marketo_api/api/campaigns.rb +70 -0
  24. data/lib/marketo_api/api/leads.rb +86 -0
  25. data/lib/marketo_api/api/sales.rb +19 -0
  26. data/lib/marketo_api/api/stats.rb +54 -0
  27. data/lib/marketo_api/client.rb +8 -0
  28. data/lib/marketo_api/concerns/authentication.rb +20 -0
  29. data/lib/marketo_api/concerns/base.rb +49 -0
  30. data/lib/marketo_api/concerns/caching.rb +24 -0
  31. data/lib/marketo_api/concerns/connection.rb +75 -0
  32. data/lib/marketo_api/concerns/verbs.rb +60 -0
  33. data/lib/marketo_api/config.rb +131 -0
  34. data/lib/marketo_api/middleware.rb +27 -0
  35. data/lib/marketo_api/middleware/authentication.rb +64 -0
  36. data/lib/marketo_api/middleware/authentication/token.rb +12 -0
  37. data/lib/marketo_api/middleware/authorization.rb +16 -0
  38. data/lib/marketo_api/middleware/caching.rb +26 -0
  39. data/lib/marketo_api/middleware/logger.rb +40 -0
  40. data/lib/marketo_api/middleware/raise_error.rb +47 -0
  41. data/lib/marketo_api/version.rb +3 -0
  42. data/marketo_api.gemspec +37 -0
  43. 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