active_campaign_rb 0.1.0

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.
@@ -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: []