active_campaign 0.0.12 → 0.1.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 +4 -4
- data/.ruby-version +1 -0
- data/Gemfile +26 -0
- data/active_campaign.gemspec +6 -11
- data/lib/active_campaign.rb +26 -17
- data/lib/active_campaign/arguments.rb +14 -0
- data/lib/active_campaign/authentication.rb +68 -0
- data/lib/active_campaign/client.rb +201 -14
- data/lib/active_campaign/configurable.rb +84 -0
- data/lib/active_campaign/default.rb +135 -0
- data/lib/active_campaign/error.rb +93 -27
- data/lib/active_campaign/response/debugger.rb +42 -0
- data/lib/active_campaign/response/instrumentation.rb +33 -0
- data/lib/active_campaign/response/json_normalizer.rb +49 -0
- data/lib/active_campaign/response/mashify.rb +40 -0
- data/lib/active_campaign/response/parse_json.rb +83 -0
- data/lib/active_campaign/response/raise_error.rb +24 -0
- data/lib/active_campaign/version.rb +1 -1
- data/spec/active_campaign_spec.rb +3 -3
- data/spec/helper.rb +1 -1
- metadata +32 -84
- data/lib/active_campaign/configuration.rb +0 -74
- data/lib/active_campaign/connection.rb +0 -52
- data/lib/active_campaign/param_validation.rb +0 -12
- data/lib/active_campaign/request.rb +0 -84
- data/lib/faraday/response/body_logger.rb +0 -37
- data/lib/faraday/response/json_normalizer.rb +0 -46
- data/lib/faraday/response/raise_active_campaign_error.rb +0 -25
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'active_campaign/version'
|
2
|
+
require 'active_campaign/response/raise_error'
|
3
|
+
require 'active_campaign/response/debugger'
|
4
|
+
require 'active_campaign/response/json_normalizer'
|
5
|
+
require 'active_campaign/response/mashify'
|
6
|
+
require 'active_campaign/response/instrumentation'
|
7
|
+
require 'active_campaign/response/parse_json'
|
8
|
+
|
9
|
+
module ActiveCampaign
|
10
|
+
|
11
|
+
# Default configuration options for {Client}
|
12
|
+
module Default
|
13
|
+
|
14
|
+
# Default API endpoint
|
15
|
+
API_ENDPOINT = "https://subdomain.activehosted.com".freeze
|
16
|
+
|
17
|
+
API_PATH = "admin/api.php".freeze
|
18
|
+
|
19
|
+
# Default User Agent header string
|
20
|
+
USER_AGENT = "ActiveCampaign Ruby Gem #{ActiveCampaign::VERSION}".freeze
|
21
|
+
|
22
|
+
# Default media type
|
23
|
+
MEDIA_TYPE = "text/html"
|
24
|
+
|
25
|
+
# Default media type
|
26
|
+
API_OUTPUT = "json"
|
27
|
+
|
28
|
+
# Default WEB endpoint
|
29
|
+
WEB_ENDPOINT = "http://www.activecampaign.com/".freeze
|
30
|
+
|
31
|
+
# Default Faraday middleware stack
|
32
|
+
MIDDLEWARE = Faraday::Builder.new do |builder|
|
33
|
+
builder.request :url_encoded
|
34
|
+
builder.response :mashify
|
35
|
+
builder.response :json_normalizer
|
36
|
+
builder.response :parse_json
|
37
|
+
builder.use :debugger
|
38
|
+
builder.use :instrumentation
|
39
|
+
builder.adapter Faraday.default_adapter
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
|
44
|
+
# Configuration options
|
45
|
+
# @return [Hash]
|
46
|
+
def options
|
47
|
+
Hash[ActiveCampaign::Configurable.keys.map{|key| [key, send(key)]}]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Default API endpoint from ENV or {API_ENDPOINT}
|
51
|
+
# @return [String]
|
52
|
+
def api_endpoint
|
53
|
+
ENV['ACTIVE_CAMPAIGN_API_ENDPOINT'] || "#{API_ENDPOINT}/#{api_path}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Default API endpoint from ENV or {API_ENDPOINT}
|
57
|
+
# @return [String]
|
58
|
+
def api_path
|
59
|
+
API_PATH
|
60
|
+
end
|
61
|
+
|
62
|
+
# Default pagination preference from ENV
|
63
|
+
# @return [String]
|
64
|
+
def auto_paginate
|
65
|
+
ENV['ACTIVE_CAMPAIGN_AUTO_PAGINATE']
|
66
|
+
end
|
67
|
+
|
68
|
+
# Default options for Faraday::Connection
|
69
|
+
# @return [Hash]
|
70
|
+
def connection_options
|
71
|
+
{
|
72
|
+
:headers => {
|
73
|
+
:accept => default_media_type,
|
74
|
+
:user_agent => user_agent
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
# Default media type from ENV or {MEDIA_TYPE}
|
80
|
+
# @return [String]
|
81
|
+
def default_media_type
|
82
|
+
ENV['ACTIVE_CAMPAIGN_DEFAULT_MEDIA_TYPE'] || MEDIA_TYPE
|
83
|
+
end
|
84
|
+
|
85
|
+
# Default GitHub username for Basic Auth from ENV
|
86
|
+
# @return [String]
|
87
|
+
def api_key
|
88
|
+
ENV['ACTIVE_CAMPAIGN_API_KEY']
|
89
|
+
end
|
90
|
+
|
91
|
+
def debug
|
92
|
+
false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Default middleware stack for Faraday::Connection
|
96
|
+
# from {MIDDLEWARE}
|
97
|
+
# @return [String]
|
98
|
+
def middleware
|
99
|
+
MIDDLEWARE
|
100
|
+
end
|
101
|
+
|
102
|
+
# Default media api_output from ENV or {API_OUTPUT}
|
103
|
+
# @return [String]
|
104
|
+
def api_output
|
105
|
+
ENV['ACTIVE_CAMPAIGN_API_OUTPUT'] || API_OUTPUT
|
106
|
+
end
|
107
|
+
|
108
|
+
# Default pagination page size from ENV
|
109
|
+
# @return [Fixnum] Page size
|
110
|
+
def per_page
|
111
|
+
page_size = ENV['ACTIVE_CAMPAIGN_PER_PAGE']
|
112
|
+
|
113
|
+
page_size.to_i if page_size
|
114
|
+
end
|
115
|
+
|
116
|
+
# Default proxy server URI for Faraday connection from ENV
|
117
|
+
# @return [String]
|
118
|
+
def proxy
|
119
|
+
ENV['ACTIVE_CAMPAIGN_PROXY']
|
120
|
+
end
|
121
|
+
|
122
|
+
# Default User-Agent header string from ENV or {USER_AGENT}
|
123
|
+
# @return [String]
|
124
|
+
def user_agent
|
125
|
+
ENV['ACTIVE_CAMPAIGN_USER_AGENT'] || USER_AGENT
|
126
|
+
end
|
127
|
+
|
128
|
+
# Default web endpoint from ENV or {WEB_ENDPOINT}
|
129
|
+
# @return [String]
|
130
|
+
def web_endpoint
|
131
|
+
ENV['ACTIVE_CAMPAIGN_WEB_ENDPOINT'] || WEB_ENDPOINT
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -1,16 +1,54 @@
|
|
1
1
|
module ActiveCampaign
|
2
|
-
# Custom error class for rescuing from all
|
2
|
+
# Custom error class for rescuing from all GitHub errors
|
3
3
|
class Error < StandardError
|
4
|
+
|
5
|
+
# Returns the appropriate ActiveCampaign::Error sublcass based
|
6
|
+
# on status and response message
|
7
|
+
#
|
8
|
+
# @param [Hash]
|
9
|
+
# @returns [ActiveCampaign::Error]
|
10
|
+
def self.from_response(response)
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
|
14
|
+
if klass = case status
|
15
|
+
when 400 then ActiveCampaign::BadRequest
|
16
|
+
when 401 then ActiveCampaign::Unauthorized
|
17
|
+
when 403
|
18
|
+
if body =~ /rate limit exceeded/i
|
19
|
+
ActiveCampaign::TooManyRequests
|
20
|
+
elsif body =~ /login attempts exceeded/i
|
21
|
+
ActiveCampaign::TooManyLoginAttempts
|
22
|
+
else
|
23
|
+
ActiveCampaign::Forbidden
|
24
|
+
end
|
25
|
+
when 404 then ActiveCampaign::NotFound
|
26
|
+
when 406 then ActiveCampaign::NotAcceptable
|
27
|
+
when 422 then ActiveCampaign::UnprocessableEntity
|
28
|
+
when 500 then ActiveCampaign::InternalServerError
|
29
|
+
when 501 then ActiveCampaign::NotImplemented
|
30
|
+
when 502 then ActiveCampaign::BadGateway
|
31
|
+
when 503 then ActiveCampaign::ServiceUnavailable
|
32
|
+
end
|
33
|
+
klass.new(response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
4
37
|
def initialize(response=nil)
|
5
38
|
@response = response
|
6
39
|
super(build_error_message)
|
7
40
|
end
|
8
41
|
|
9
|
-
|
10
|
-
|
42
|
+
private
|
43
|
+
|
44
|
+
def data
|
45
|
+
@data ||=
|
11
46
|
if (body = @response[:body]) && !body.empty?
|
12
|
-
if body.is_a?(String)
|
13
|
-
|
47
|
+
if body.is_a?(String) &&
|
48
|
+
@response[:response_headers] &&
|
49
|
+
@response[:response_headers][:content_type] =~ /json/
|
50
|
+
|
51
|
+
Sawyer::Agent.serializer.decode(body)
|
14
52
|
else
|
15
53
|
body
|
16
54
|
end
|
@@ -19,50 +57,78 @@ module ActiveCampaign
|
|
19
57
|
end
|
20
58
|
end
|
21
59
|
|
22
|
-
|
60
|
+
def response_message
|
61
|
+
case data
|
62
|
+
when Hash
|
63
|
+
data[:message]
|
64
|
+
when String
|
65
|
+
data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def response_error
|
70
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
71
|
+
end
|
72
|
+
|
73
|
+
def response_error_summary
|
74
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
75
|
+
|
76
|
+
summary = "\nError summary:\n"
|
77
|
+
summary << data[:errors].map do |hash|
|
78
|
+
hash.map { |k,v| " #{k}: #{v}" }
|
79
|
+
end.join("\n")
|
80
|
+
|
81
|
+
summary
|
82
|
+
end
|
23
83
|
|
24
84
|
def build_error_message
|
25
|
-
return nil
|
85
|
+
return nil if @response.nil?
|
26
86
|
|
27
|
-
message =
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
"#{@response[:method].to_s.upcase} #{@response[:url].to_s}: #{@response[:status]}#{message}#{errors}"
|
87
|
+
message = "#{@response[:method].to_s.upcase} "
|
88
|
+
message << "#{@response[:url].to_s}: "
|
89
|
+
message << "#{@response[:status]} - "
|
90
|
+
message << "#{response_message}" unless response_message.nil?
|
91
|
+
message << "#{response_error}" unless response_error.nil?
|
92
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
93
|
+
message
|
36
94
|
end
|
37
95
|
end
|
38
96
|
|
39
|
-
# Raised when
|
97
|
+
# Raised when GitHub returns a 400 HTTP status code
|
40
98
|
class BadRequest < Error; end
|
41
99
|
|
42
|
-
# Raised when
|
100
|
+
# Raised when GitHub returns a 401 HTTP status code
|
43
101
|
class Unauthorized < Error; end
|
44
102
|
|
45
|
-
# Raised when
|
103
|
+
# Raised when GitHub returns a 403 HTTP status code
|
46
104
|
class Forbidden < Error; end
|
47
105
|
|
48
|
-
# Raised when
|
106
|
+
# Raised when GitHub returns a 403 HTTP status code
|
107
|
+
# and body matches 'rate limit exceeded'
|
108
|
+
class TooManyRequests < Forbidden; end
|
109
|
+
|
110
|
+
# Raised when GitHub returns a 403 HTTP status code
|
111
|
+
# and body matches 'login attempts exceeded'
|
112
|
+
class TooManyLoginAttempts < Forbidden; end
|
113
|
+
|
114
|
+
# Raised when GitHub returns a 404 HTTP status code
|
49
115
|
class NotFound < Error; end
|
50
116
|
|
51
|
-
# Raised when
|
117
|
+
# Raised when GitHub returns a 406 HTTP status code
|
52
118
|
class NotAcceptable < Error; end
|
53
119
|
|
54
|
-
# Raised when
|
120
|
+
# Raised when GitHub returns a 422 HTTP status code
|
55
121
|
class UnprocessableEntity < Error; end
|
56
122
|
|
57
|
-
# Raised when
|
123
|
+
# Raised when GitHub returns a 500 HTTP status code
|
58
124
|
class InternalServerError < Error; end
|
59
125
|
|
60
|
-
# Raised when
|
126
|
+
# Raised when GitHub returns a 501 HTTP status code
|
61
127
|
class NotImplemented < Error; end
|
62
128
|
|
63
|
-
# Raised when
|
129
|
+
# Raised when GitHub returns a 502 HTTP status code
|
64
130
|
class BadGateway < Error; end
|
65
131
|
|
66
|
-
# Raised when
|
132
|
+
# Raised when GitHub returns a 503 HTTP status code
|
67
133
|
class ServiceUnavailable < Error; end
|
68
|
-
end
|
134
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module ActiveCampaign
|
5
|
+
module Response
|
6
|
+
class Debugger < Faraday::Response::Middleware
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def initialize(app, logger = nil)
|
10
|
+
super(app)
|
11
|
+
@logger = logger || begin
|
12
|
+
require 'logger'
|
13
|
+
::Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
info("#{env[:method]} #{env[:url].to_s}")
|
21
|
+
debug('request') { dump_headers(env[:request_headers]) + "\nbody: #{env[:body]}" }
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_complete(env)
|
26
|
+
info('Status') { env[:status].to_s }
|
27
|
+
debug('response-head') { dump_headers env[:response_headers] }
|
28
|
+
debug('response-body') { env[:body] }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def dump_headers(headers)
|
34
|
+
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Faraday.register_middleware :middleware, debugger: lambda {
|
41
|
+
ActiveCampaign::Response::Debugger
|
42
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveCampaign
|
2
|
+
module Response
|
3
|
+
# Public: Instruments requests using Active Support.
|
4
|
+
#
|
5
|
+
# Measures time spent only for synchronous requests.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env|
|
10
|
+
# url = env[:url]
|
11
|
+
# http_method = env[:method].to_s.upcase
|
12
|
+
# duration = ends - starts
|
13
|
+
# $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
|
14
|
+
# end
|
15
|
+
class Instrumentation < Faraday::Middleware
|
16
|
+
dependency 'active_support/notifications'
|
17
|
+
|
18
|
+
def initialize(app, options = {})
|
19
|
+
super(app)
|
20
|
+
@name = options.fetch(:name, 'request.faraday')
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
ActiveSupport::Notifications.instrument(@name, env) do
|
25
|
+
@app.call(env)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
Faraday.register_middleware :middleware, instrumentation: lambda {
|
32
|
+
ActiveCampaign::Response::Instrumentation
|
33
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday/response'
|
3
|
+
|
4
|
+
module ActiveCampaign
|
5
|
+
module Response
|
6
|
+
class JsonNormalizer < Faraday::Response::Middleware
|
7
|
+
def initialize(app, logger = nil)
|
8
|
+
super(app)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(environment)
|
12
|
+
@app.call(environment).on_complete do |env|
|
13
|
+
if env[:body].is_a?(Hash)
|
14
|
+
env[:body] = normalize(env[:body])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def normalize(response)
|
22
|
+
keys, values = keys_values(response)
|
23
|
+
|
24
|
+
if keys.all?{|key| is_numeric?(key) }
|
25
|
+
response[:results] = values
|
26
|
+
keys.each do |key|
|
27
|
+
response.delete(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_numeric?(string)
|
35
|
+
string.to_s.match(/\A[+-]?\d+\Z/) == nil ? false : true
|
36
|
+
end
|
37
|
+
|
38
|
+
def keys_values(response)
|
39
|
+
results = results(response)
|
40
|
+
[results.keys, results.values]
|
41
|
+
end
|
42
|
+
|
43
|
+
def results(response)
|
44
|
+
response.reject{|k,v| %w(result_code result_message result_output).include?(k) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
Faraday.register_middleware :response, json_normalizer: lambda { ActiveCampaign::Response::JsonNormalizer }
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module ActiveCampaign
|
4
|
+
module Response
|
5
|
+
|
6
|
+
# Public: Converts parsed response bodies to a Hashie::Mash if they were of
|
7
|
+
# Hash or Array type.
|
8
|
+
class Mashify < Faraday::Response::Middleware
|
9
|
+
attr_accessor :mash_class
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :mash_class
|
13
|
+
end
|
14
|
+
|
15
|
+
dependency do
|
16
|
+
require 'hashie/mash'
|
17
|
+
self.mash_class = ::Hashie::Mash
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(app = nil, options = {})
|
21
|
+
super(app)
|
22
|
+
self.mash_class = options[:mash_class] || self.class.mash_class
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(body)
|
26
|
+
case body
|
27
|
+
when Hash
|
28
|
+
mash_class.new(body)
|
29
|
+
when Array
|
30
|
+
body.map { |item| parse(item) }
|
31
|
+
else
|
32
|
+
body
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
Faraday.register_middleware :response, mashify: lambda {
|
39
|
+
ActiveCampaign::Response::Mashify
|
40
|
+
}
|