mailchimp-rest-api 0.0.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 +7 -0
- data/README.md +154 -0
- data/VERSION +1 -0
- data/lib/mailchimp-api/batch_request.rb +32 -0
- data/lib/mailchimp-api/client/api_methods.rb +18 -0
- data/lib/mailchimp-api/client/batch_methods.rb +44 -0
- data/lib/mailchimp-api/client/pagination_methods.rb +51 -0
- data/lib/mailchimp-api/client.rb +55 -0
- data/lib/mailchimp-api/config.rb +30 -0
- data/lib/mailchimp-api/error.rb +118 -0
- data/lib/mailchimp-api/failed_request_error_builder.rb +48 -0
- data/lib/mailchimp-api/network_error_builder.rb +35 -0
- data/lib/mailchimp-api/request.rb +93 -0
- data/lib/mailchimp-api/request_executor.rb +84 -0
- data/lib/mailchimp-api/resource.rb +23 -0
- data/lib/mailchimp-api/resources/audience/member_tags.rb +24 -0
- data/lib/mailchimp-api/resources/audience/members.rb +49 -0
- data/lib/mailchimp-api/resources/audience/utils/subscriber_hash.rb +21 -0
- data/lib/mailchimp-api/response.rb +115 -0
- data/lib/mailchimp-api/uri_builder.rb +37 -0
- data/lib/mailchimp-api/version.rb +9 -0
- data/lib/mailchimp-api.rb +65 -0
- data/lib/mailchimp-rest-api.rb +3 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f7356505754644055f03ee2a25a23910247fc87fb350672e6a1fa3f3ead64626
|
4
|
+
data.tar.gz: f95ac89a0685693ed4be9afb6f5d78fb2948b724b96ac7d7a130e151ae275573
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d40b3c60e6ab469604cbca9c7d10da2a91483e9c9653d461d5c9485c84243162b2996ce17e1466607d25ae970a6da3d2075dcbe39c870b6c13ed5f12603444b6
|
7
|
+
data.tar.gz: ace08ae4987164126a36da7879ae75a7f4acb571b2f1ca8b61df51a5788410636b5c7d105bae535e51e973f21b67984aece570b221e9f951a1d03f0f400c0bb8
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# Mailchimp REST API (Marketing)
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
```bash
|
6
|
+
bundle add mailchimp-rest-api
|
7
|
+
```
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
- Supported Ruby Versions - *(2.6 .. 3.3), head, jruby-9.4, truffleruby-24*
|
12
|
+
- No dependencies;
|
13
|
+
- Auto-retries (configured);
|
14
|
+
- Pagination methods
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
# Setup client
|
20
|
+
MailchimpAPI.client = MailchimpAPI::Client.new(api_key: ENV['MAILCHIMP_API_KEY'])
|
21
|
+
|
22
|
+
# Call any HTTP API
|
23
|
+
MailchimpAPI.post(path, query: query, body: body, headers: headers)
|
24
|
+
MailchimpAPI.get(path, query: query, body: body, headers: headers)
|
25
|
+
MailchimpAPI.patch(path, query: query, body: body, headers: headers)
|
26
|
+
MailchimpAPI.put(path, query: query, body: body, headers: headers)
|
27
|
+
MailchimpAPI.delete(path, query: query, body: body, headers: headers)
|
28
|
+
|
29
|
+
# Call any defined REST API (defined in lib/resources)
|
30
|
+
MailchimpAPI::Audience::Members.show(audience_id, email, query: query)
|
31
|
+
MailchimpAPI::Audience::Members.create(audience_id, body: body)
|
32
|
+
|
33
|
+
# Or call it via shortcuts (defined in lib/client/api_methods.rb)
|
34
|
+
MailchimpAPI.audience_members.show(audience_id, email, query: query)
|
35
|
+
MailchimpAPI.audience_members.create(audience_id, body: body)
|
36
|
+
```
|
37
|
+
|
38
|
+
### Response
|
39
|
+
|
40
|
+
`Response` object is returned after each `API` request.
|
41
|
+
|
42
|
+
#### Original HTTP response data
|
43
|
+
|
44
|
+
- `response.http_status` - response HTTP status as Integer
|
45
|
+
- `response.http_body` - response body as String
|
46
|
+
- `response.http_headers` - response headers as Hash with String keys
|
47
|
+
- `response.http_response` - original Net::HTTP::Response object
|
48
|
+
- `response.request` - Request object that was used to get this response
|
49
|
+
|
50
|
+
#### Parsed JSON body methods
|
51
|
+
|
52
|
+
- `response.body` - parsed JSON body, keys are Symbols
|
53
|
+
- `response[:field]` - gets `:field` attribute from parsed body,
|
54
|
+
returns nil if response have no such key
|
55
|
+
- `response.fetch(:field)` - gets `:field` attribute from parsed body,
|
56
|
+
raises KeyError if response has no such key
|
57
|
+
|
58
|
+
#### Error check methods
|
59
|
+
|
60
|
+
- `response.success?` - checks HTTP status code is 2xx
|
61
|
+
- `response.failed?` - checks HTTP status code is not 2xx
|
62
|
+
|
63
|
+
## Configuration options
|
64
|
+
|
65
|
+
MailchimpAPI client accepts this additional options:
|
66
|
+
|
67
|
+
- `:retries`
|
68
|
+
- `:http_opts`
|
69
|
+
|
70
|
+
### Option `:retries`
|
71
|
+
|
72
|
+
This is a Hash with retries configuration.
|
73
|
+
By default retries are enabled, 4 retries with 0, 0.25, 0.75, 1.5 seconds delay.
|
74
|
+
Default config: `{enabled: true, count: 4, sleep: [0, 0.25, 0.75, 1.5]}`.
|
75
|
+
New options are merged with defaults.
|
76
|
+
Please keep `sleep` array same size as `count`.
|
77
|
+
|
78
|
+
Retries happen on any network error, on 409, 429, 5xx response status code.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
client = MailchimpAPI::Client.new(
|
82
|
+
retries: {enabled: !Rails.env.test?, count: 5, sleep: [0, 0.25, 0.75, 1.5, 2]}
|
83
|
+
# ...
|
84
|
+
)
|
85
|
+
```
|
86
|
+
|
87
|
+
### Option `:http_opts`
|
88
|
+
|
89
|
+
This are the options that are provided to the `Net::HTTP.start` method,
|
90
|
+
like `:read_timeout`, `:write_timeout`, etc.
|
91
|
+
|
92
|
+
You can find full list of available options here <https://docs.ruby-lang.org/en/master/Net/HTTP.html#method-c-start>
|
93
|
+
(Please choose you version of ruby).
|
94
|
+
|
95
|
+
By default it is an empty hash.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
client = MailchimpAPI::Client.new(
|
99
|
+
http_opts: {read_timeout: 30, write_timeout: 30, open_timeout: 30}
|
100
|
+
# ...
|
101
|
+
)
|
102
|
+
```
|
103
|
+
|
104
|
+
## Pagination
|
105
|
+
|
106
|
+
We have two specific methods:
|
107
|
+
|
108
|
+
- `MailchimpAPI#each_page(response)` - iterates over current and each next page
|
109
|
+
- `MailchimpAPI#each_page_item(response, items_field_name)` - iterates over
|
110
|
+
items on each page
|
111
|
+
|
112
|
+
Example:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
first_page = MailchimpAPI.audience_members.list(list_id, query: { count: 100 }
|
116
|
+
|
117
|
+
MailchimpAPI.each_page(first_page) do |response|
|
118
|
+
puts response.body
|
119
|
+
end
|
120
|
+
|
121
|
+
MailchimpAPI.each_page_item(first_page, :members) do |item|
|
122
|
+
puts item[:email_address]
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
## Request
|
127
|
+
|
128
|
+
...
|
129
|
+
|
130
|
+
## Errors
|
131
|
+
|
132
|
+
...
|
133
|
+
|
134
|
+
## APIs
|
135
|
+
|
136
|
+
...
|
137
|
+
|
138
|
+
## Development
|
139
|
+
|
140
|
+
```bash
|
141
|
+
rubocop
|
142
|
+
rspec
|
143
|
+
mdl README.md CHANGELOG.md RELEASE.md
|
144
|
+
```
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/aglushkov/mailchimp-rest-api>.
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
153
|
+
|
154
|
+
[pagination]: #pagination
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class BatchRequest
|
5
|
+
API_VERSION_PREFIX = "/#{MailchimpAPI::API_VERSION}"
|
6
|
+
|
7
|
+
attr_reader :request, :operation_id
|
8
|
+
|
9
|
+
def initialize(request, operation_id: nil)
|
10
|
+
@request = request
|
11
|
+
@operation_id = operation_id
|
12
|
+
end
|
13
|
+
|
14
|
+
# Make a batch operation request
|
15
|
+
# https://mailchimp.com/developer/marketing/guides/run-async-requests-batch-endpoint/#make-a-batch-operations-request
|
16
|
+
def operation
|
17
|
+
operation = {
|
18
|
+
method: request.method, # "GET", "POST", "PUT", "PATCH", "DELETE"
|
19
|
+
path: request.path.delete_prefix(API_VERSION_PREFIX) # Path relative to version prefix
|
20
|
+
}
|
21
|
+
|
22
|
+
body = request.body
|
23
|
+
operation[:body] = body if body
|
24
|
+
|
25
|
+
params = request.query
|
26
|
+
operation[:params] = params unless params.empty?
|
27
|
+
|
28
|
+
operation[:operation_id] = operation_id if operation_id
|
29
|
+
operation
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class Client
|
5
|
+
#
|
6
|
+
# Methods to access API resources
|
7
|
+
#
|
8
|
+
module APIMethods
|
9
|
+
def audience_members
|
10
|
+
Audience::Members.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def audience_member_tags
|
14
|
+
Audience::MemberTags.new(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class Client
|
5
|
+
#
|
6
|
+
# Methods to create and run batch requests
|
7
|
+
#
|
8
|
+
module BatchMethods
|
9
|
+
def batch_get_request(path, query: nil, body: nil, headers: nil, operation_id: nil)
|
10
|
+
new_batch_request(Net::HTTP::Get, path, query: query, body: body, headers: headers, operation_id: operation_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
def batch_post_request(path, query: nil, body: nil, headers: nil, operation_id: nil)
|
14
|
+
new_batch_request(Net::HTTP::Post, path, query: query, body: body, headers: headers, operation_id: operation_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def batch_put_request(path, query: nil, body: nil, headers: nil, operation_id: nil)
|
18
|
+
new_batch_request(Net::HTTP::Put, path, query: query, body: body, headers: headers, operation_id: operation_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def batch_patch_request(path, query: nil, body: nil, headers: nil, operation_id: nil)
|
22
|
+
new_batch_request(Net::HTTP::Patch, path, query: query, body: body, headers: headers, operation_id: operation_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def batch_delete_request(path, query: nil, body: nil, headers: nil, operation_id: nil)
|
26
|
+
new_batch_request(Net::HTTP::Delete, path, query: query, body: body, headers: headers, operation_id: operation_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def batch(batch_requests, query: nil)
|
30
|
+
operations = batch_requests.map(&:operation)
|
31
|
+
post("batches", query: query, body: {operations: operations})
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# rubocop:disable Metrics/ParameterLists
|
37
|
+
def new_batch_request(http_method, path, query: nil, body: nil, headers: nil, operation_id: nil)
|
38
|
+
request = new_request(http_method, path: path, query: query, body: body, headers: headers)
|
39
|
+
BatchRequest.new(request, operation_id: operation_id)
|
40
|
+
end
|
41
|
+
# rubocop:enable Metrics/ParameterLists
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class Client
|
5
|
+
module PaginationMethods
|
6
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
7
|
+
def next_page(response)
|
8
|
+
unless response.body.key?(:total_items)
|
9
|
+
raise Error, "Please ensure `total_items` field is not excluded from response"
|
10
|
+
end
|
11
|
+
|
12
|
+
request = response.request
|
13
|
+
query = request.query
|
14
|
+
|
15
|
+
count = query.key?(:count) ? query[:count].to_i : 10
|
16
|
+
offset = query.key?(:offset) ? query[:offset].to_i : 0
|
17
|
+
|
18
|
+
body = response.body
|
19
|
+
total_items = body[:total_items].to_i
|
20
|
+
is_final_page = ((offset + count) >= total_items)
|
21
|
+
return if is_final_page
|
22
|
+
|
23
|
+
new_query = query.merge(offset: offset + count, count: count)
|
24
|
+
|
25
|
+
get(
|
26
|
+
request.path,
|
27
|
+
query: new_query,
|
28
|
+
body: request.body,
|
29
|
+
headers: request.headers
|
30
|
+
)
|
31
|
+
end
|
32
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
33
|
+
|
34
|
+
def each_page(response, &block)
|
35
|
+
return enum_for(:each_page, response) unless block
|
36
|
+
|
37
|
+
page = response
|
38
|
+
yield(page)
|
39
|
+
yield(page) while (page = next_page(page))
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_page_item(response, items_field_name, &block)
|
43
|
+
return enum_for(:each_page_item, response, items_field_name) unless block
|
44
|
+
|
45
|
+
each_page(response) do |page|
|
46
|
+
page.body.fetch(items_field_name).each(&block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class Client
|
5
|
+
API_VERSION = MailchimpAPI::API_VERSION
|
6
|
+
|
7
|
+
include APIMethods
|
8
|
+
include BatchMethods
|
9
|
+
include PaginationMethods
|
10
|
+
|
11
|
+
attr_reader :api_key, :api_url, :api_version, :authorization_token, :config
|
12
|
+
|
13
|
+
def initialize(api_key:, http_opts: nil, retries: nil)
|
14
|
+
raise ArgumentError, "Invalid api_key" unless /\w+-\w+/.match?(api_key) # <token>-<dc>
|
15
|
+
dc = api_key.split("-", 2).last # initial api_key must have format
|
16
|
+
|
17
|
+
@api_key = api_key
|
18
|
+
@api_url = "https://#{dc}.api.mailchimp.com/#{API_VERSION}/"
|
19
|
+
@authorization_token = "Bearer #{api_key}"
|
20
|
+
@config = Config.new(http_opts: http_opts, retries: retries)
|
21
|
+
end
|
22
|
+
|
23
|
+
def post(path, query: nil, body: nil, headers: nil)
|
24
|
+
execute(Net::HTTP::Post, path, query: query, body: body, headers: headers)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(path, query: nil, body: nil, headers: nil)
|
28
|
+
execute(Net::HTTP::Get, path, query: query, body: body, headers: headers)
|
29
|
+
end
|
30
|
+
|
31
|
+
def patch(path, query: nil, body: nil, headers: nil)
|
32
|
+
execute(Net::HTTP::Patch, path, query: query, body: body, headers: headers)
|
33
|
+
end
|
34
|
+
|
35
|
+
def put(path, query: nil, body: nil, headers: nil)
|
36
|
+
execute(Net::HTTP::Put, path, query: query, body: body, headers: headers)
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(path, query: nil, body: nil, headers: nil)
|
40
|
+
execute(Net::HTTP::Delete, path, query: query, body: body, headers: headers)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def execute(http_method, path, query:, body:, headers:)
|
46
|
+
headers = {"authorization" => authorization_token}.merge!(headers || {})
|
47
|
+
request = new_request(http_method, path: path, query: query, body: body, headers: headers)
|
48
|
+
RequestExecutor.new(request, http_opts: config.http_opts, retries: config.retries).call
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_request(http_method, path:, query:, body:, headers:)
|
52
|
+
Request.new(http_method, url: api_url, path: path, query: query, body: body, headers: headers)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
#
|
5
|
+
# Stores client requests configuration
|
6
|
+
#
|
7
|
+
class Config
|
8
|
+
# Default config options
|
9
|
+
DEFAULTS = {
|
10
|
+
http_opts: {}.freeze,
|
11
|
+
retries: {enabled: true, count: 4, sleep: [0, 0.25, 0.75, 1.5].freeze}.freeze
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
attr_reader :http_opts, :retries
|
15
|
+
|
16
|
+
# Initializes Config
|
17
|
+
#
|
18
|
+
# @param http_opts [Hash] Net::Http opts for all requests
|
19
|
+
# @param retries [Hash] Retries configuration
|
20
|
+
#
|
21
|
+
# @return [Client] Initialized config object
|
22
|
+
#
|
23
|
+
def initialize(http_opts: nil, retries: {})
|
24
|
+
@http_opts = http_opts.dup.freeze || DEFAULTS[:http_opts]
|
25
|
+
@retries = DEFAULTS[:retries].merge(retries || {}).freeze
|
26
|
+
|
27
|
+
freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
#
|
5
|
+
# Common interface for all errors
|
6
|
+
#
|
7
|
+
class Error < StandardError
|
8
|
+
# @return [Response, nil] Returned response with non-200 status code
|
9
|
+
attr_reader :response
|
10
|
+
|
11
|
+
# @return [Request] Sent request
|
12
|
+
attr_reader :request
|
13
|
+
|
14
|
+
# @return [String, nil] Error type returned by Mailchimp
|
15
|
+
attr_reader :error_type
|
16
|
+
|
17
|
+
# @return [String] Error title returned by Mailchimp or response error class name
|
18
|
+
attr_reader :error_title
|
19
|
+
|
20
|
+
# @return [String] Error status returned by Mailchimp or response HTTP status
|
21
|
+
attr_reader :error_status
|
22
|
+
|
23
|
+
# @return [String, nil] Error description returned by Mailchimp
|
24
|
+
attr_reader :error_detail
|
25
|
+
|
26
|
+
# @return [String, nil] Error ID returned by Mailchimp
|
27
|
+
attr_reader :error_instance
|
28
|
+
|
29
|
+
# @return [Hash, nil] Fields specific errors returned by Mailchimp
|
30
|
+
attr_reader :error_fields
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Namespace for specific MailchimpAPI errors
|
35
|
+
#
|
36
|
+
module Errors
|
37
|
+
#
|
38
|
+
# Raised when Mailchimp responds with any status code except 200, 201, 202, 204
|
39
|
+
#
|
40
|
+
class FailedRequest < Error
|
41
|
+
def initialize(message = nil, request:, response:)
|
42
|
+
@request = request
|
43
|
+
@response = response
|
44
|
+
|
45
|
+
body = response.body
|
46
|
+
data = body.is_a?(Hash) ? body : {}
|
47
|
+
@error_type = data[:type]
|
48
|
+
@error_title = data[:title] || response.http_response.class.name
|
49
|
+
@error_detail = data[:detail]
|
50
|
+
@error_status = data[:status] || response.http_status
|
51
|
+
@error_fields = data[:errors]
|
52
|
+
@error_instance = data[:instance]
|
53
|
+
|
54
|
+
message += "\n #{response.http_body}" unless data.empty?
|
55
|
+
super(message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Raised when a network raised when executing the request
|
61
|
+
# List of network errors can be found in errors/network_error_builder.rb
|
62
|
+
#
|
63
|
+
class NetworkError < Error
|
64
|
+
def initialize(message = nil, request:, error:)
|
65
|
+
super(message)
|
66
|
+
@request = request
|
67
|
+
@response = nil
|
68
|
+
|
69
|
+
@error_type = nil
|
70
|
+
@error_title = error.class.name
|
71
|
+
@error_detail = error.message
|
72
|
+
@error_status = nil
|
73
|
+
@error_fields = nil
|
74
|
+
@error_instance = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# 400
|
79
|
+
class BadRequest < FailedRequest
|
80
|
+
end
|
81
|
+
|
82
|
+
# 401
|
83
|
+
class Unauthorized < FailedRequest
|
84
|
+
end
|
85
|
+
|
86
|
+
# 403
|
87
|
+
class Forbidden < FailedRequest
|
88
|
+
end
|
89
|
+
|
90
|
+
# 404
|
91
|
+
class NotFound < FailedRequest
|
92
|
+
end
|
93
|
+
|
94
|
+
# 405
|
95
|
+
class MethodNotAllowed < FailedRequest
|
96
|
+
end
|
97
|
+
|
98
|
+
# 414
|
99
|
+
class RequestURITooLong < FailedRequest
|
100
|
+
end
|
101
|
+
|
102
|
+
# 422
|
103
|
+
class UnprocessableEntity < FailedRequest
|
104
|
+
end
|
105
|
+
|
106
|
+
# 426
|
107
|
+
class UpgradeRequired < FailedRequest
|
108
|
+
end
|
109
|
+
|
110
|
+
# 429
|
111
|
+
class TooManyRequests < FailedRequest
|
112
|
+
end
|
113
|
+
|
114
|
+
# 5xx
|
115
|
+
class ServerError < FailedRequest
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module MailchimpAPI
|
6
|
+
#
|
7
|
+
# Builds MailchimpAPI::FailedRequest error
|
8
|
+
#
|
9
|
+
class FailedRequestErrorBuilder
|
10
|
+
# Matchings for Net::HTTP response class to MailchimpAPI::Error class
|
11
|
+
RESPONSE_ERROR_MAP = {
|
12
|
+
Net::HTTPBadRequest => Errors::BadRequest, # 400
|
13
|
+
Net::HTTPUnauthorized => Errors::Unauthorized, # 401
|
14
|
+
Net::HTTPForbidden => Errors::Forbidden, # 403
|
15
|
+
Net::HTTPNotFound => Errors::NotFound, # 404
|
16
|
+
Net::HTTPMethodNotAllowed => Errors::MethodNotAllowed, # 405
|
17
|
+
Net::HTTPRequestURITooLong => Errors::RequestURITooLong, # 414
|
18
|
+
Net::HTTPUnprocessableEntity => Errors::UnprocessableEntity, # 422
|
19
|
+
Net::HTTPUpgradeRequired => Errors::UpgradeRequired, # 426
|
20
|
+
Net::HTTPTooManyRequests => Errors::TooManyRequests, # 429
|
21
|
+
Net::HTTPServerError => Errors::ServerError # 5xx
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Builds FailedRequestError instance
|
26
|
+
#
|
27
|
+
# @param request [Request] Original request
|
28
|
+
# @param response [Response] Original response
|
29
|
+
#
|
30
|
+
# @return [Errors::FailedRequestError] error object
|
31
|
+
#
|
32
|
+
def call(request:, response:)
|
33
|
+
http_response = response.http_response
|
34
|
+
error_message = "#{http_response.code} #{http_response.message}"
|
35
|
+
error_class = find_error_class(http_response)
|
36
|
+
|
37
|
+
error_class.new(error_message, response: response, request: request)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def find_error_class(http_response)
|
43
|
+
found_class = RESPONSE_ERROR_MAP.keys.find { |http_error_class| http_response.is_a?(http_error_class) }
|
44
|
+
found_class ? RESPONSE_ERROR_MAP[found_class] : Errors::FailedRequest
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
#
|
5
|
+
# Builds MailchimpAPI::Errors::NetowrkError error
|
6
|
+
#
|
7
|
+
class NetworkErrorBuilder
|
8
|
+
# List of possible Network errors
|
9
|
+
ERRORS = [
|
10
|
+
IOError,
|
11
|
+
Errno::ECONNABORTED,
|
12
|
+
Errno::ECONNREFUSED,
|
13
|
+
Errno::ECONNRESET,
|
14
|
+
Errno::EHOSTUNREACH,
|
15
|
+
Errno::EPIPE,
|
16
|
+
Errno::ETIMEDOUT,
|
17
|
+
OpenSSL::SSL::SSLError,
|
18
|
+
SocketError,
|
19
|
+
Timeout::Error # Net::OpenTimeout, Net::ReadTimeout
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Builds NetworkError instance
|
24
|
+
#
|
25
|
+
# @param request [Request] Original request
|
26
|
+
# @param error [StandardError] Original error
|
27
|
+
#
|
28
|
+
# @return [Errors::NetworkError] Built NetworkError
|
29
|
+
#
|
30
|
+
def call(request:, error:)
|
31
|
+
Errors::NetworkError.new(error.message, request: request, error: error)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module MailchimpAPI
|
6
|
+
class Request
|
7
|
+
attr_reader \
|
8
|
+
:init_http_method,
|
9
|
+
:init_base_url,
|
10
|
+
:init_path,
|
11
|
+
:init_query,
|
12
|
+
:init_body,
|
13
|
+
:init_headers,
|
14
|
+
:http_request
|
15
|
+
|
16
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
17
|
+
def initialize(http_method, url:, path:, query: nil, body: nil, headers: nil)
|
18
|
+
@init_http_method = http_method
|
19
|
+
@init_url = url
|
20
|
+
@init_path = path
|
21
|
+
@init_query = query
|
22
|
+
@init_body = body
|
23
|
+
@init_headers = headers
|
24
|
+
|
25
|
+
uri = prepare_uri(url, path, query)
|
26
|
+
headers = prepare_headers(headers)
|
27
|
+
body = prepare_body(body)
|
28
|
+
http_method = prepare_http_method(http_method, headers)
|
29
|
+
|
30
|
+
@http_request = build_http_request(http_method, uri: uri, body: body, headers: headers)
|
31
|
+
end
|
32
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
|
33
|
+
|
34
|
+
# @return [String] HTTP request method name
|
35
|
+
def method
|
36
|
+
http_request.method # "GET", "POST", "PUT", "PATCH", "DELETE"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] HTTP request full path
|
40
|
+
def path
|
41
|
+
http_request.path
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [URI] HTTP request URI
|
45
|
+
def uri
|
46
|
+
http_request.uri
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String, nil] HTTP request body
|
50
|
+
def body
|
51
|
+
http_request.body
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash] HTTP request query params
|
55
|
+
def query
|
56
|
+
@query ||= uri.query ? URI.decode_www_form(uri.query).to_h.transform_keys!(&:to_sym) : {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Hash] HTTP request headers
|
60
|
+
def headers
|
61
|
+
http_request.each_header.to_h
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def build_http_request(http_method, uri:, body:, headers:)
|
67
|
+
http_request = http_method.new(uri, "accept-encoding" => nil, "content-type" => "application/json")
|
68
|
+
|
69
|
+
headers.each { |key, value| http_request[key] = value }
|
70
|
+
http_request.body = body
|
71
|
+
|
72
|
+
http_request
|
73
|
+
end
|
74
|
+
|
75
|
+
def prepare_uri(url, path, query)
|
76
|
+
URIBuilder.call(url: url, path: path, query: query)
|
77
|
+
end
|
78
|
+
|
79
|
+
def prepare_headers(headers)
|
80
|
+
return {} unless headers
|
81
|
+
|
82
|
+
headers.transform_keys { |key| key.to_s.downcase }
|
83
|
+
end
|
84
|
+
|
85
|
+
def prepare_body(body)
|
86
|
+
JSON.dump(body) unless body.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
def prepare_http_method(default_http_method, headers)
|
90
|
+
headers["x-http-method-override"] ? Net::HTTP::Post : default_http_method
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class RequestExecutor
|
5
|
+
attr_reader :request, :http_opts, :retries
|
6
|
+
|
7
|
+
def initialize(request, http_opts: {}, retries: {})
|
8
|
+
@request = request
|
9
|
+
@http_opts = http_opts
|
10
|
+
@retries = retries
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# @return [Response] Response
|
15
|
+
#
|
16
|
+
def call
|
17
|
+
response = execute_request
|
18
|
+
raise FailedRequestErrorBuilder.call(request: request, response: response) if response.failed?
|
19
|
+
|
20
|
+
response
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def execute_request(retry_number: 0)
|
26
|
+
response = execute_net_http_request
|
27
|
+
rescue => error
|
28
|
+
unknown_network_error?(error) ? handle_unknown_error(error) : handle_network_error(error, retry_number)
|
29
|
+
else
|
30
|
+
response.success? ? response : handle_failed_response(response, retry_number)
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_network_error(error, retry_number)
|
34
|
+
will_retry = retries[:enabled] && !retries_limit_reached?(retry_number)
|
35
|
+
raise NetworkErrorBuilder.call(request: request, error: error) unless will_retry
|
36
|
+
|
37
|
+
retry_request(retry_number)
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_failed_response(response, retry_number)
|
41
|
+
will_retry = retries[:enabled] && retryable?(response, retry_number)
|
42
|
+
will_retry ? retry_request(retry_number) : response
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute_net_http_request
|
46
|
+
uri = request.uri
|
47
|
+
|
48
|
+
http_response =
|
49
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, **http_opts) do |http|
|
50
|
+
http.max_retries = 0 # we have custom retries logic
|
51
|
+
http.request(request.http_request)
|
52
|
+
end
|
53
|
+
|
54
|
+
Response.new(http_response, request: request)
|
55
|
+
end
|
56
|
+
|
57
|
+
def retry_request(current_retry_number)
|
58
|
+
sleep_time = retry_sleep_seconds(current_retry_number)
|
59
|
+
sleep(sleep_time) if sleep_time.positive?
|
60
|
+
execute_request(retry_number: current_retry_number + 1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def retries_limit_reached?(retry_number)
|
64
|
+
retry_number >= retries[:count]
|
65
|
+
end
|
66
|
+
|
67
|
+
def retry_sleep_seconds(current_retry_number)
|
68
|
+
seconds_per_retry = retries[:sleep]
|
69
|
+
seconds_per_retry[current_retry_number] || seconds_per_retry.last || 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def retryable?(response, retry_number)
|
73
|
+
response.failed? && response.retryable? && !retries_limit_reached?(retry_number)
|
74
|
+
end
|
75
|
+
|
76
|
+
def unknown_network_error?(error)
|
77
|
+
NetworkErrorBuilder::ERRORS.none? { |network_error_class| error.is_a?(network_error_class) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_unknown_error(error)
|
81
|
+
raise error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class Resource
|
5
|
+
# @return current client
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
# Initializes Resource
|
9
|
+
#
|
10
|
+
# @param client [Client] current client
|
11
|
+
#
|
12
|
+
# @return [Collection] APIs collection
|
13
|
+
#
|
14
|
+
def initialize(client)
|
15
|
+
@client = client
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return global client
|
19
|
+
def self.client
|
20
|
+
MailchimpAPI.client
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
module Audience
|
5
|
+
class MemberTags < Resource
|
6
|
+
module APIs
|
7
|
+
def list(audience_id, email, query: nil, body: nil, headers: nil)
|
8
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}/tags"
|
9
|
+
client.get(path, query: query, body: body, headers: headers)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(audience_id, email, query: nil, body: nil, headers: nil)
|
13
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}/tags"
|
14
|
+
client.post(path, query: query, body: body, headers: headers)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include MailchimpAPI::Audience::Utils
|
19
|
+
extend MailchimpAPI::Audience::Utils
|
20
|
+
include APIs
|
21
|
+
extend APIs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
module Audience
|
5
|
+
class Members < Resource
|
6
|
+
module APIs
|
7
|
+
def list(audience_id, query: nil, body: nil, headers: nil)
|
8
|
+
path = "/lists/#{audience_id}/members"
|
9
|
+
client.get(path, query: query, body: body, headers: headers)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(audience_id, query: nil, body: nil, headers: nil)
|
13
|
+
path = "/lists/#{audience_id}/members"
|
14
|
+
client.post(path, query: query, body: body, headers: headers)
|
15
|
+
end
|
16
|
+
|
17
|
+
def show(audience_id, email, query: nil, body: nil, headers: nil)
|
18
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}"
|
19
|
+
client.get(path, query: query, body: body, headers: headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def archive(audience_id, email, query: nil, body: nil, headers: nil)
|
23
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}"
|
24
|
+
client.delete(path, query: query, body: body, headers: headers)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(audience_id, email, query: nil, body: nil, headers: nil)
|
28
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}"
|
29
|
+
client.patch(path, query: query, body: body, headers: headers)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_or_update(audience_id, email, query: nil, body: nil, headers: nil)
|
33
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}"
|
34
|
+
client.put(path, query: query, body: body, headers: headers)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete_permanent(audience_id, email, query: nil, body: nil, headers: nil)
|
38
|
+
path = "/lists/#{audience_id}/members/#{subscriber_hash(email)}/actions/delete-permanent"
|
39
|
+
client.post(path, query: query, body: body, headers: headers)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
include MailchimpAPI::Audience::Utils
|
44
|
+
extend MailchimpAPI::Audience::Utils
|
45
|
+
include APIs
|
46
|
+
extend APIs
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
|
5
|
+
module MailchimpAPI
|
6
|
+
module Audience
|
7
|
+
module Utils
|
8
|
+
class SubscriberHash
|
9
|
+
def self.call(email)
|
10
|
+
Digest::MD5.hexdigest(email.downcase)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def subscriber_hash(email)
|
17
|
+
SubscriberHash.call(email)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module MailchimpAPI
|
6
|
+
#
|
7
|
+
# MailchimpAPI::Response object
|
8
|
+
#
|
9
|
+
class Response
|
10
|
+
# List of Net::HTTP responses that can be retried
|
11
|
+
RETRYABLE_RESPONSES = [
|
12
|
+
Net::HTTPServerError, # 5xx
|
13
|
+
Net::HTTPTooManyRequests # 429
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
# @return [Net::HTTP::Response] Original Net::HTTP::Response object
|
17
|
+
attr_reader :http_response
|
18
|
+
|
19
|
+
# @return [Request] Request object
|
20
|
+
attr_reader :request
|
21
|
+
|
22
|
+
#
|
23
|
+
# Initializes Response object
|
24
|
+
#
|
25
|
+
# @param http_response [Net::HTTP::Response] original response
|
26
|
+
# @param request [Request] Request that generates this response
|
27
|
+
#
|
28
|
+
# @return [Response] Initialized Response object
|
29
|
+
#
|
30
|
+
def initialize(http_response, request:)
|
31
|
+
@request = request
|
32
|
+
@http_response = http_response
|
33
|
+
@http_status = nil
|
34
|
+
@http_headers = nil
|
35
|
+
@http_body = nil
|
36
|
+
@body = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parses JSON body if response body contains JSON or returns original
|
40
|
+
# http body string
|
41
|
+
#
|
42
|
+
# @return [Hash, String] Parsed response body (with symbolized keys)
|
43
|
+
def body
|
44
|
+
@body ||= json_response? ? parse_json(http_body) : http_body
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Integer] HTTP status as Integer
|
48
|
+
def http_status
|
49
|
+
@http_status ||= http_response.code.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Hash] HTTP headers as Hash
|
53
|
+
def http_headers
|
54
|
+
@http_headers ||= http_response.each_header.to_h
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [String] Original http body
|
58
|
+
def http_body
|
59
|
+
@http_body ||= http_response.body
|
60
|
+
end
|
61
|
+
|
62
|
+
# Takes specific key from body, returns nil if key not present in parsed body
|
63
|
+
def [](key)
|
64
|
+
body[key.to_sym] if body.is_a?(Hash)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Fetches specific key from body, raises error if key not exists
|
68
|
+
def fetch(key)
|
69
|
+
data = body.is_a?(Hash) ? body : {}
|
70
|
+
data.fetch(key.to_sym)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks http status code is 2xx
|
74
|
+
#
|
75
|
+
# @return [Boolean] Returns true if response has success status code (2xx)
|
76
|
+
def success?
|
77
|
+
http_response.is_a?(Net::HTTPSuccess)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Checks http status code is not 2xx
|
81
|
+
#
|
82
|
+
# @return [Boolean] Returns true if response has not success status code
|
83
|
+
def failed?
|
84
|
+
!success?
|
85
|
+
end
|
86
|
+
|
87
|
+
# Checks if response status code is retriable (5xx, 409, 429)
|
88
|
+
# @api private
|
89
|
+
#
|
90
|
+
# @return [Boolean] Returns true if status code is retriable (5xx, 409, 429)
|
91
|
+
def retryable?
|
92
|
+
failed? && RETRYABLE_RESPONSES.any? { |retryable_class| http_response.is_a?(retryable_class) }
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Instance representation string. Default was overwritten to hide secrets
|
97
|
+
#
|
98
|
+
def inspect
|
99
|
+
"#<#{self.class.name} (#{http_response.code})>"
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def json_response?
|
105
|
+
content_type = http_response["content-type"]
|
106
|
+
!content_type.nil? && content_type.include?("json")
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_json(json)
|
110
|
+
JSON.parse(json, symbolize_names: true)
|
111
|
+
rescue JSON::ParserError, TypeError
|
112
|
+
json
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MailchimpAPI
|
4
|
+
class URIBuilder
|
5
|
+
class << self
|
6
|
+
def call(url:, path:, query:)
|
7
|
+
uri =
|
8
|
+
if path.start_with?("/")
|
9
|
+
call(url: url, path: path[1, path.size - 1], query: nil)
|
10
|
+
elsif path.start_with?("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
|
11
|
+
URI.join(url, "/" + path)
|
12
|
+
else
|
13
|
+
URI.join(url, path)
|
14
|
+
end
|
15
|
+
|
16
|
+
add_query_params(uri, query)
|
17
|
+
uri
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def add_query_params(uri, query)
|
23
|
+
return if !query || query.empty?
|
24
|
+
|
25
|
+
# We should merge query params with uri query params to not remove them
|
26
|
+
uri_query_string = uri.query
|
27
|
+
|
28
|
+
if uri_query_string && !uri_query_string.empty?
|
29
|
+
old_query = URI.decode_www_form(uri_query_string).to_h
|
30
|
+
query = old_query.transform_keys!(&:to_sym).merge!(query.transform_keys(&:to_sym))
|
31
|
+
end
|
32
|
+
|
33
|
+
uri.query = URI.encode_www_form(query)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module MailchimpAPI
|
6
|
+
API_VERSION = "3.0"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
attr_writer :client
|
12
|
+
|
13
|
+
# HTTP methods
|
14
|
+
def_delegators :@client,
|
15
|
+
:post,
|
16
|
+
:get,
|
17
|
+
:patch,
|
18
|
+
:put,
|
19
|
+
:delete
|
20
|
+
|
21
|
+
# Batch methods
|
22
|
+
def_delegators :@client,
|
23
|
+
:batch,
|
24
|
+
:batch_get_request,
|
25
|
+
:batch_post_request,
|
26
|
+
:batch_put_request,
|
27
|
+
:batch_patch_request,
|
28
|
+
:batch_delete_request
|
29
|
+
|
30
|
+
# Pagination methods
|
31
|
+
def_delegators :@client,
|
32
|
+
:each_page,
|
33
|
+
:each_page_item,
|
34
|
+
:next_page
|
35
|
+
|
36
|
+
# Resources
|
37
|
+
def_delegators :@client,
|
38
|
+
:audience_members,
|
39
|
+
:audience_member_tags
|
40
|
+
|
41
|
+
def client
|
42
|
+
raise "#{name}.client must be set" unless @client
|
43
|
+
|
44
|
+
@client
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
require_relative "mailchimp-api/resource"
|
50
|
+
require_relative "mailchimp-api/resources/audience/utils/subscriber_hash"
|
51
|
+
require_relative "mailchimp-api/resources/audience/member_tags"
|
52
|
+
require_relative "mailchimp-api/resources/audience/members"
|
53
|
+
require_relative "mailchimp-api/batch_request"
|
54
|
+
require_relative "mailchimp-api/client/api_methods"
|
55
|
+
require_relative "mailchimp-api/client/batch_methods"
|
56
|
+
require_relative "mailchimp-api/client/pagination_methods"
|
57
|
+
require_relative "mailchimp-api/client"
|
58
|
+
require_relative "mailchimp-api/config"
|
59
|
+
require_relative "mailchimp-api/error"
|
60
|
+
require_relative "mailchimp-api/failed_request_error_builder"
|
61
|
+
require_relative "mailchimp-api/network_error_builder"
|
62
|
+
require_relative "mailchimp-api/request"
|
63
|
+
require_relative "mailchimp-api/request_executor"
|
64
|
+
require_relative "mailchimp-api/response"
|
65
|
+
require_relative "mailchimp-api/uri_builder"
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mailchimp-rest-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrey Glushkov
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-04-28 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
12
|
+
description: Marketing Mailchimp REST API with no dependencies.
|
13
|
+
email:
|
14
|
+
- aglushkov@shakuro.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- VERSION
|
21
|
+
- lib/mailchimp-api.rb
|
22
|
+
- lib/mailchimp-api/batch_request.rb
|
23
|
+
- lib/mailchimp-api/client.rb
|
24
|
+
- lib/mailchimp-api/client/api_methods.rb
|
25
|
+
- lib/mailchimp-api/client/batch_methods.rb
|
26
|
+
- lib/mailchimp-api/client/pagination_methods.rb
|
27
|
+
- lib/mailchimp-api/config.rb
|
28
|
+
- lib/mailchimp-api/error.rb
|
29
|
+
- lib/mailchimp-api/failed_request_error_builder.rb
|
30
|
+
- lib/mailchimp-api/network_error_builder.rb
|
31
|
+
- lib/mailchimp-api/request.rb
|
32
|
+
- lib/mailchimp-api/request_executor.rb
|
33
|
+
- lib/mailchimp-api/resource.rb
|
34
|
+
- lib/mailchimp-api/resources/audience/member_tags.rb
|
35
|
+
- lib/mailchimp-api/resources/audience/members.rb
|
36
|
+
- lib/mailchimp-api/resources/audience/utils/subscriber_hash.rb
|
37
|
+
- lib/mailchimp-api/response.rb
|
38
|
+
- lib/mailchimp-api/uri_builder.rb
|
39
|
+
- lib/mailchimp-api/version.rb
|
40
|
+
- lib/mailchimp-rest-api.rb
|
41
|
+
homepage: https://github.com/aglushkov/mailchimp-rest-api
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata:
|
45
|
+
source_code_uri: https://github.com/aglushkov/mailchimp-rest-api
|
46
|
+
documentation_uri: https://www.rubydoc.info/gems/mailchimp-rest-api
|
47
|
+
changelog_uri: https://github.com/aglushkov/mailchimp-rest-api/blob/master/CHANGELOG.md
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 2.6.0
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.6.2
|
63
|
+
specification_version: 4
|
64
|
+
summary: Mailchimp REST API (Marketing)
|
65
|
+
test_files: []
|