active_campaign_rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCampaignRb
4
+ #
5
+ # Base class error for almost all exceptions
6
+ #
7
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
8
+ #
9
+ class Error < RuntimeError
10
+ def initialize(response = nil, exception = nil)
11
+ self.response = response
12
+ @exception = exception
13
+ super
14
+ end
15
+
16
+ def message
17
+ if response.nil?
18
+ super
19
+ else
20
+ <<~MESSAGE
21
+ STATUS: #{response.status}
22
+ URL: #{env.url}
23
+ REQUEST HEADERS: #{env.request_headers}
24
+ RESPONSE_HEADERS: #{env.response_headers}
25
+ REQUEST_BODY: #{env.request_body}\n\n"
26
+ RESPONSE_BODY: #{response.body}\n\n"
27
+ MESSAGE
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def env
34
+ @env ||= response.env
35
+ end
36
+
37
+ attr_accessor :response
38
+ end
39
+
40
+ #
41
+ # Proxy an exception to allow nil to be passed in
42
+ #
43
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
44
+ #
45
+ class ErrorProxy < RuntimeError
46
+ def initialize(error = nil)
47
+ self.error = error
48
+ super
49
+ end
50
+
51
+ def message
52
+ error ? error.message : super
53
+ end
54
+
55
+ private
56
+
57
+ attr_accessor :error
58
+ end
59
+
60
+ #
61
+ # Wraps Faraday::ParsingError
62
+ #
63
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
64
+ #
65
+ class ParsingError < ErrorProxy
66
+ end
67
+
68
+ #
69
+ # Wraps Faraday::ClientError
70
+ #
71
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
72
+ #
73
+ class ClientError < Error
74
+ end
75
+
76
+ class InvalidHTTPMethod < StandardError
77
+ end
78
+
79
+ #
80
+ # Wraps Faraday::BadRequestError
81
+ #
82
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
83
+ #
84
+ class BadRequestError < ClientError
85
+ end
86
+
87
+ #
88
+ # Wraps Faraday::UnauthorizedError
89
+ #
90
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
91
+ #
92
+ class UnauthorizedError < ClientError
93
+ end
94
+
95
+ #
96
+ # Wraps Faraday::ForbiddenError
97
+ #
98
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
99
+ #
100
+ class ForbiddenError < ClientError
101
+ end
102
+
103
+ #
104
+ # Wraps Faraday::ResourceNotFound
105
+ #
106
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
107
+ #
108
+ class ResourceNotFound < ClientError
109
+ end
110
+
111
+ #
112
+ # Wraps Faraday::ProxyAuthError
113
+ #
114
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
115
+ #
116
+ class ProxyAuthError < ClientError
117
+ end
118
+
119
+ #
120
+ # Wraps Faraday::ConflictError
121
+ #
122
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
123
+ #
124
+ class ConflictError < ClientError
125
+ end
126
+
127
+ #
128
+ # Wraps Faraday::UnprocessableEntityError
129
+ #
130
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
131
+ #
132
+ class UnprocessableEntityError < ClientError
133
+ end
134
+
135
+ #
136
+ # Wraps Faraday::ServerError
137
+ #
138
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
139
+ #
140
+ class ServerError < Error
141
+ end
142
+
143
+ #
144
+ # Wraps Faraday::TimeoutError
145
+ #
146
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
147
+ #
148
+ class TimeoutError < ErrorProxy
149
+ def initialize(exc = "timeout", response = nil)
150
+ super(response, exc)
151
+ end
152
+ end
153
+
154
+ #
155
+ # Wraps Faraday::NilStatusError
156
+ #
157
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
158
+ #
159
+ class NilStatusError < ServerError
160
+ def initialize(exc, response = nil)
161
+ exc_msg_and_response!(exc, response)
162
+ super("http status could not be derived from the server response")
163
+ end
164
+ end
165
+
166
+ #
167
+ # Wraps Faraday::ConnectionFailed
168
+ #
169
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
170
+ #
171
+ class ConnectionFailed < Error
172
+ end
173
+
174
+ #
175
+ # Wraps Faraday::SSLError
176
+ #
177
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
178
+ #
179
+ class SSLError < Error
180
+ end
181
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+
5
+ module ActiveCampaignRb
6
+ module Faraday
7
+ module Middleware
8
+ #
9
+ # Gem specific request middleware for Faraday
10
+ #
11
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
12
+ #
13
+ class Request < ::Faraday::Middleware
14
+ dependency "oj"
15
+
16
+ include TransformHash
17
+
18
+ def initialize(app, headers)
19
+ super(app)
20
+ @headers = headers
21
+ end
22
+
23
+ def call(env)
24
+ case env.method
25
+ when :post, :patch, :put
26
+ normalize_body(env)
27
+ end
28
+
29
+ env[:request_headers].merge!(@headers)
30
+
31
+ @app.call(env)
32
+ end
33
+
34
+ def normalize_body(env)
35
+ body = env[:request_body]
36
+ if body.is_a?(Array)
37
+ body = transform_array(body.flatten, :camelcase, :lower)
38
+ elsif body.key?(:group)
39
+ logger.debug("Using body as is because group #{body}")
40
+ elsif body.key?(:list)
41
+ logger.debug("Using body as is because list #{body}")
42
+ else
43
+ body = transform_keys(body, :camelcase, :lower)
44
+ end
45
+
46
+ env[:request_body] = ::Oj.dump(body, mode: :compat)
47
+ end
48
+
49
+ def logger
50
+ @logger ||= Logger.new($stdout)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Faraday::Request.register_middleware active_campaign: ActiveCampaignRb::Faraday::Middleware::Request
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "faraday"
5
+ require "faraday/logging/formatter"
6
+
7
+ module ActiveCampaignRb
8
+ LOGGER = ::Logger.new($stdout)
9
+ module Faraday
10
+ module Middleware
11
+ #
12
+ # Gem specific response middleware for Faraday
13
+ #
14
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
15
+ #
16
+ class Response < ::Faraday::Response::Middleware
17
+ dependency "oj"
18
+
19
+ include TransformHash
20
+
21
+ # Override this to modify the environment after the response has finished.
22
+ # Calls the `parse` method if defined
23
+ def on_complete(env)
24
+ env.body = parse(env.body)
25
+ debug(env) if ActiveCampaignRb.config.debug?
26
+ end
27
+
28
+ def parse(body)
29
+ return body if body.to_s.empty?
30
+
31
+ body = ::Oj.load(body, mode: :compat)
32
+ transform_keys(body, :underscore)
33
+ end
34
+
35
+ def debug(env)
36
+ formatter.request(env)
37
+ formatter.response(env)
38
+ end
39
+
40
+ def formatter
41
+ @formatter ||= ::Faraday::Logging::Formatter.new(
42
+ logger: LOGGER,
43
+ options: { headers: true, bodies: true }
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Faraday::Response.register_middleware active_campaign: ActiveCampaignRb::Faraday::Middleware::Response
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "middleware/request"
4
+ require_relative "middleware/response"
5
+
6
+ module ActiveCampaignRb
7
+ module Faraday
8
+ #
9
+ # Utility functions for managing Faraday middlewares
10
+ #
11
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
12
+ #
13
+ module Middleware
14
+ def self.add_request_middleware(faraday, middleware = {})
15
+ faraday.request :active_campaign, {}
16
+
17
+ return if middleware.none?
18
+
19
+ middleware.each do |key, options|
20
+ faraday.request key, options unless key.to_s.empty?
21
+ end
22
+ end
23
+
24
+ def self.add_response_middleware(faraday, middleware = {})
25
+ faraday.response :active_campaign
26
+
27
+ return if middleware.none?
28
+
29
+ middleware.each do |key, **options|
30
+ next if key.to_s.empty?
31
+
32
+ add_middleware(faraday, key, options)
33
+ end
34
+ end
35
+
36
+ def self.add_middleware(faraday, key, options)
37
+ if options.is_a?(Hash) && options.any?
38
+ faraday.response key, options
39
+ else
40
+ faraday.response key
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCampaignRb
4
+ #
5
+ # HTTP Client wrapper
6
+ #
7
+ class HttpClient
8
+ attr_reader :connection
9
+
10
+ def initialize(opts = {})
11
+ @connection = ::Faraday.new(url: opts.api_endpoint) do |f|
12
+ ActiveCampaignRb::Faraday::Middleware.add_request_middleware(f, opts.request_middleware)
13
+ ActiveCampaignRb::Faraday::Middleware.add_response_middleware(f, opts.response_middleware)
14
+ f.adapter opts.adapter
15
+
16
+ f.headers = {
17
+ "Accept" => "application/json",
18
+ "Content-Type" => "application/json",
19
+ "Api-Token" => opts.api_key,
20
+ "User-Agent" => "ActiveCampaignRb Client (v#{ActiveCampaignRb::VERSION})"
21
+ }
22
+ end
23
+ end
24
+
25
+ def post(*args)
26
+ safe_http_call do
27
+ connection.post(*args)
28
+ end
29
+ end
30
+
31
+ def patch(*args)
32
+ safe_http_call do
33
+ connection.patch(*args)
34
+ end
35
+ end
36
+
37
+ def put(*args)
38
+ safe_http_call do
39
+ connection.put(*args)
40
+ end
41
+ end
42
+
43
+ def delete(*args)
44
+ safe_http_call do
45
+ connection.delete(*args)
46
+ end
47
+ end
48
+
49
+ def get(*args)
50
+ safe_http_call do
51
+ connection.get(*args)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # rubocop:disable Metrics/MethodLength
58
+ # rubocop:disable Metrics/AbcSize
59
+ def safe_http_call
60
+ response = yield
61
+ response.body
62
+ rescue ::Oj::ParseError => e
63
+ raise ParsingError, e
64
+ rescue ::Faraday::BadRequestError => e
65
+ raise BadRequestError, e
66
+ rescue ::Faraday::UnauthorizedError => e
67
+ raise UnauthorizedError, e
68
+ rescue ::Faraday::ForbiddenError => e
69
+ raise ForbiddenError, e
70
+ rescue ::Faraday::ResourceNotFound => e
71
+ raise ResourceNotFound, e
72
+ rescue ::Faraday::ProxyAuthError => e
73
+ raise ProxyAuthError, e
74
+ rescue ::Faraday::ConflictError => e
75
+ raise ConflictError, e
76
+ rescue ::Faraday::UnprocessableEntityError => e
77
+ raise UnprocessableEntityError, e
78
+ rescue ::Faraday::ServerError => e
79
+ raise ServerError, e
80
+ rescue ::Faraday::TimeoutError => e
81
+ raise TimeoutError, e
82
+ rescue ::Faraday::NilStatusError => e
83
+ raise NilStatusError, e
84
+ rescue ::Faraday::ConnectionFailed => e
85
+ raise ConnectionFailed, e
86
+ rescue ::Faraday::SSLError => e
87
+ raise SSLError, e
88
+ end
89
+ # rubocop:enable Metrics/MethodLength
90
+ # rubocop:enable Metrics/AbcSize
91
+ end
92
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string"
4
+
5
+ module ActiveCampaignRb
6
+ #
7
+ # Utility methods
8
+ #
9
+ module TransformHash
10
+ module_function
11
+
12
+ def transform_keys(hash, *new_case)
13
+ hash.each_with_object({}) do |(key, value), memo|
14
+ memo[transform_key(key, *new_case)] = transform_value(value, *new_case)
15
+ end
16
+ end
17
+
18
+ def transform_key(key, *new_case)
19
+ key.to_s.public_send(*new_case).to_sym
20
+ end
21
+
22
+ def transform_value(value, *new_case)
23
+ case value
24
+ when Hash
25
+ transform_keys(value, *new_case)
26
+ when Array
27
+ transform_array(value, *new_case)
28
+ else
29
+ value
30
+ end
31
+ end
32
+
33
+ def transform_array(collection, *new_case)
34
+ collection.map do |element|
35
+ case element
36
+ when Hash
37
+ transform_keys(element, *new_case)
38
+ else
39
+ element
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCampaignRb
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ require_relative "active_campaign_rb/version"
6
+ require_relative "active_campaign_rb/errors"
7
+ require_relative "active_campaign_rb/transform_hash"
8
+ require_relative "active_campaign_rb/faraday/middleware"
9
+ require_relative "active_campaign_rb/config"
10
+ require_relative "active_campaign_rb/http_client"
11
+
12
+ #
13
+ # API Client for the Active Campaign API v3
14
+ #
15
+ #
16
+ module ActiveCampaignRb
17
+ module_function
18
+
19
+ ENDPOINTS = YAML.load_file(File.join(File.dirname(__FILE__), "active_campaign_rb", "endpoints.yaml"))
20
+
21
+ def config
22
+ @config ||= Config.new
23
+ end
24
+
25
+ def configure
26
+ yield config if block_given?
27
+ end
28
+
29
+ def http_client
30
+ @http_client ||= HttpClient.new(config)
31
+ end
32
+
33
+ ENDPOINTS.each do |name, config|
34
+ define_method(name) do |**args|
35
+ http_methods = config.dig(:link, :method)
36
+ request_path = config.dig(:link, :href)
37
+
38
+ case http_methods
39
+ when :GET, :get
40
+ http_client.get(request_path % args, args)
41
+ when :POST, :post
42
+ http_client.post(request_path % args, args)
43
+ when :PUT, :put
44
+ http_client.put(request_path % args, args)
45
+ when :PATCH, :patch
46
+ http_client.path(request_path % args, args)
47
+ when :DELETE, :delete
48
+ http_client.delete(request_path % args, args)
49
+ else
50
+ raise InvalidHTTPMethod, "Invalid http_method specified"
51
+ end
52
+ rescue ::KeyError => e
53
+ raise ArgumentError, e
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_campaign_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - spidergears
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: oj
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.13'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ description:
84
+ email:
85
+ - deepak@eloquent.studio
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gem_release.yml"
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".rubocop.yml"
94
+ - CHANGELOG.md
95
+ - CODE_OF_CONDUCT.md
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - active_campaign_rb.gemspec
102
+ - bin/console
103
+ - bin/setup
104
+ - lib/active_campaign_rb.rb
105
+ - lib/active_campaign_rb/config.rb
106
+ - lib/active_campaign_rb/endpoints.yaml
107
+ - lib/active_campaign_rb/errors.rb
108
+ - lib/active_campaign_rb/faraday/middleware.rb
109
+ - lib/active_campaign_rb/faraday/middleware/request.rb
110
+ - lib/active_campaign_rb/faraday/middleware/response.rb
111
+ - lib/active_campaign_rb/http_client.rb
112
+ - lib/active_campaign_rb/transform_hash.rb
113
+ - lib/active_campaign_rb/version.rb
114
+ homepage: https://github.com/EloquentStudio/active_campaign_rb/blob/master/CHANGELOG.md
115
+ licenses:
116
+ - MIT
117
+ metadata:
118
+ allowed_push_host: https://rubygems.org
119
+ homepage_uri: https://github.com/EloquentStudio/active_campaign_rb/blob/master/CHANGELOG.md
120
+ source_code_uri: https://github.com/EloquentStudio/active_campaign_rb
121
+ changelog_uri: https://github.com/EloquentStudio/active_campaign_rb/blob/master/CHANGELOG.md
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 2.5.0
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 3.2.15
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Ruby wrapper around active campaign api
141
+ test_files: []