gibbon 2.2.5 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of gibbon might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +43 -3
- data/LICENSE.txt +1 -1
- data/README.markdown +48 -18
- data/gibbon.gemspec +2 -2
- data/lib/gibbon.rb +3 -0
- data/lib/gibbon/api_request.rb +22 -27
- data/lib/gibbon/export.rb +93 -0
- data/lib/gibbon/gibbon_helpers.rb +15 -0
- data/lib/gibbon/request.rb +12 -4
- data/lib/gibbon/response.rb +10 -0
- data/lib/gibbon/version.rb +1 -1
- data/spec/gibbon/api_request_spec.rb +14 -0
- data/spec/gibbon/export_spec.rb +32 -0
- data/spec/gibbon/gibbon_spec.rb +37 -2
- data/spec/gibbon/mailchimp_error_spec.rb +49 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12a6fecb32704fd28e939c71d68d04b002daa26b
|
4
|
+
data.tar.gz: b203c08f25fab5920c4714448709cf16c1297a74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 049a905ad6d6ccb68ed25d76e3192ab38e2342405c57aa89070817e79ad64a3d0d635129821b44cc69da778381134f9cada53480644defc8350c9da065851fc2
|
7
|
+
data.tar.gz: 15b2cc277b73e3e9b6af78045061d16924cdf677547e82e96ec5c04f37a966f359bbb8e1b589d12f6493e82c5d02b3884dac82328ee92560fab6487d4cf3ce26
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,33 @@
|
|
1
1
|
## [Unreleased][unreleased]
|
2
2
|
|
3
|
+
## [3.2.0] - 2017-11-08
|
4
|
+
- Force TLS version 1.2
|
5
|
+
|
6
|
+
## [3.1.1] - 2017-09-25
|
7
|
+
- Fix MailChimpError initialization
|
8
|
+
|
9
|
+
## [3.1.0] - 2017-07-27
|
10
|
+
- Add back support for Export API until MailChimp stops supporting it
|
11
|
+
- Implement `responds_to_missing`
|
12
|
+
|
13
|
+
## [3.0.2] - 2017-05-08
|
14
|
+
- Fix subtle bug in `symbolize_keys` when parsing error
|
15
|
+
|
16
|
+
## [3.0.1] - 2017-01-13
|
17
|
+
- Gibbon::Request (API 3.0) now returns a `Gibbon::Response` object that exposes `headers` and the parsed response `body`
|
18
|
+
- Remove Export API support (this is deprected after all)
|
19
|
+
- Adds `symbolize_keys`, `debug`, and `faraday_adapter` as class vars
|
20
|
+
- Bump version to 3.0.1 (not sticking to semver here so folks who downloaded 3.0.0 have a way to easily move forward)
|
21
|
+
|
22
|
+
## [3.0.0] - 2017-01-13
|
23
|
+
- Gibbon::Request (API 3.0) now returns a `Gibbon::Response` object that exposes `headers` and the parsed response `body`
|
24
|
+
- Adds Export API support
|
25
|
+
- Adds `symbolize_keys`, `debug`, and `faraday_adapter` as class vars
|
26
|
+
- Bump version to 3.0.0 due to breaking API change
|
27
|
+
|
3
28
|
## [2.2.5] - 2016-12-23
|
4
29
|
- Adds open_timeout
|
5
|
-
- Adds symbolize_keys
|
30
|
+
- Adds `symbolize_keys`
|
6
31
|
- Change default timeout from 30 to 60 seconds
|
7
32
|
|
8
33
|
## [2.2.4] - 2016-05-21
|
@@ -54,8 +79,23 @@
|
|
54
79
|
## [1.1.4] - 2012-11-04
|
55
80
|
- Fix JSON::ParserError on export calls that return blank results
|
56
81
|
|
57
|
-
[unreleased]: https://github.com/amro/gibbon/compare/
|
82
|
+
[unreleased]: https://github.com/amro/gibbon/compare/v3.1.0...HEAD
|
83
|
+
[3.1.0]: https://github.com/amro/gibbon/compare/v3.0.2...v3.1.0
|
84
|
+
[3.0.2]: https://github.com/amro/gibbon/compare/v3.0.1...v3.0.2
|
85
|
+
[3.0.1]: https://github.com/amro/gibbon/compare/v3.0.0...v3.0.1
|
86
|
+
[3.0.0]: https://github.com/amro/gibbon/compare/v2.2.5...v3.0.0
|
87
|
+
[2.2.5]: https://github.com/amro/gibbon/compare/v2.2.4...v2.2.5
|
88
|
+
[2.2.4]: https://github.com/amro/gibbon/compare/v2.2.2...v2.2.4
|
89
|
+
[2.2.3]: https://github.com/amro/gibbon/compare/v2.2.2...v2.2.3
|
90
|
+
[2.2.2]: https://github.com/amro/gibbon/compare/v2.2.1...v2.2.2
|
91
|
+
[2.2.1]: https://github.com/amro/gibbon/compare/v2.2.0...v2.2.1
|
92
|
+
[2.2.0]: https://github.com/amro/gibbon/compare/v2.1.3...v2.2.0
|
93
|
+
[2.1.3]: https://github.com/amro/gibbon/compare/v2.1.2...v2.1.3
|
94
|
+
[2.1.2]: https://github.com/amro/gibbon/compare/v2.1.1...v2.1.2
|
95
|
+
[2.1.1]: https://github.com/amro/gibbon/compare/v2.1.0...v2.1.1
|
96
|
+
[2.1.0]: https://github.com/amro/gibbon/compare/v2.0.0...v2.1.0
|
58
97
|
[2.0.0]: https://github.com/amro/gibbon/compare/v1.2.0...v2.0.0
|
59
98
|
[1.2.0]: https://github.com/amro/gibbon/compare/v1.1.5...v1.2.0
|
60
|
-
[1.1.
|
99
|
+
[1.1.6]: https://github.com/amro/gibbon/compare/v1.1.5...v1.1.6
|
100
|
+
[1.1.5]: https://github.com/amro/gibbon/compare/v1.1.4...v1.1.5
|
61
101
|
[1.1.4]: https://github.com/amro/gibbon/compare/v1.1.3...v1.1.4
|
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -4,23 +4,24 @@ Gibbon is an API wrapper for MailChimp's [API](http://kb.mailchimp.com/api/).
|
|
4
4
|
|
5
5
|
[![Build Status](https://secure.travis-ci.org/amro/gibbon.svg)](http://travis-ci.org/amro/gibbon)
|
6
6
|
[![Dependency Status](https://gemnasium.com/amro/gibbon.svg)](https://gemnasium.com/amro/gibbon)
|
7
|
-
##Important Notes
|
8
7
|
|
9
|
-
|
8
|
+
## Important Notes
|
10
9
|
|
11
10
|
Please read MailChimp's [Getting Started Guide](http://kb.mailchimp.com/api/article/api-3-overview).
|
12
11
|
|
13
|
-
|
12
|
+
Gibbon 3.0.0+ returns a `Gibbon::Response` instead of the response body directly. `Gibbon::Response` exposes the parsed response `body` and `headers`.
|
13
|
+
|
14
|
+
## Installation
|
14
15
|
|
15
16
|
$ gem install gibbon
|
16
17
|
|
17
|
-
##Requirements
|
18
|
+
## Requirements
|
18
19
|
|
19
20
|
A MailChimp account and API key. You can see your API keys [here](http://admin.mailchimp.com/account/api).
|
20
21
|
|
21
|
-
##Usage
|
22
|
+
## Usage
|
22
23
|
|
23
|
-
First, create a *one-time use instance* of Gibbon::Request
|
24
|
+
First, create a *one-time use instance* of `Gibbon::Request`:
|
24
25
|
|
25
26
|
```ruby
|
26
27
|
gibbon = Gibbon::Request.new(api_key: "your_api_key")
|
@@ -50,12 +51,14 @@ gibbon.lists.retrieve(headers: {"SomeHeader": "SomeHeaderValue"}, params: {"quer
|
|
50
51
|
|
51
52
|
Of course, `body` is only supported on `create`, `update`, and `upsert` calls. Those map to HTTP `POST`, `PATCH`, and `PUT` verbs respectively.
|
52
53
|
|
53
|
-
You can set `api_key`, `timeout`, and `
|
54
|
+
You can set `api_key`, `timeout`, `open_timeout`, `faraday_adapter`, `proxy`, `symbolize_keys`, `logger`, and `debug` globally:
|
54
55
|
|
55
56
|
```ruby
|
56
57
|
Gibbon::Request.api_key = "your_api_key"
|
57
58
|
Gibbon::Request.timeout = 15
|
58
59
|
Gibbon::Request.open_timeout = 15
|
60
|
+
Gibbon::Request.symbolize_keys = true
|
61
|
+
Gibbon::Request.debug = false
|
59
62
|
```
|
60
63
|
|
61
64
|
For example, you could set the values above in an `initializer` file in your `Rails` app (e.g. your\_app/config/initializers/gibbon.rb).
|
@@ -82,7 +85,7 @@ gibbon = Gibbon::Request.new(api_key: "your_api_key", symbolize_keys: true)
|
|
82
85
|
|
83
86
|
MailChimp's [resource documentation](http://kb.mailchimp.com/api/resources) is a list of available resources.
|
84
87
|
|
85
|
-
##Debug Logging
|
88
|
+
## Debug Logging
|
86
89
|
|
87
90
|
Pass `debug: true` to enable debug logging to STDOUT.
|
88
91
|
|
@@ -201,7 +204,7 @@ This will create a new batch job and return a Batch response. The response will
|
|
201
204
|
gibbon.batches(batch_id).retrieve
|
202
205
|
```
|
203
206
|
|
204
|
-
###### Response
|
207
|
+
###### Response Body (i.e. `response.body`)
|
205
208
|
```ruby
|
206
209
|
{
|
207
210
|
"id"=>"0ca62e43cc",
|
@@ -332,7 +335,7 @@ So how do we get the interest IDs? When you query the API for a specific list me
|
|
332
335
|
gibbon.lists(list_id).members(member_id).retrieve
|
333
336
|
```
|
334
337
|
|
335
|
-
The response looks someting like this (unrelated things removed):
|
338
|
+
The response body (i.e. `response.body`) looks someting like this (unrelated things removed):
|
336
339
|
|
337
340
|
```ruby
|
338
341
|
{"id"=>"...", "email_address"=>"...", ..., "interests"=>{"3def637141"=>true, "f7cc4ee841"=>false, "fcdc951b9f"=>false, "3daf3cf27d"=>true, "293a3703ed"=>false, "72370e0d1f"=>false, "d434d21a1c"=>false, "bdb1ff199f"=>false, "a54e78f203"=>false, "c4527fd018"=>false} ...}
|
@@ -344,7 +347,7 @@ The API returns a map of interest ID to boolean value. Now we to get interest de
|
|
344
347
|
gibbon.lists(list_id).interest_categories.retrieve
|
345
348
|
```
|
346
349
|
|
347
|
-
To get a list of interest categories. That gives us something like:
|
350
|
+
To get a list of interest categories. That gives us something like (again, this is the `response.body`):
|
348
351
|
|
349
352
|
```ruby
|
350
353
|
{"list_id"=>"...", "categories"=>[{"list_id"=>"...", "id"=>"0ace7aa498", "title"=>"Food Preferences", ...}] ...}
|
@@ -396,7 +399,7 @@ gibbon = Gibbon::Request.new(api_key: "your_api_key", faraday_adapter: :net_http
|
|
396
399
|
|
397
400
|
### Migrating from Gibbon 1.x
|
398
401
|
|
399
|
-
Gibbon 2.x
|
402
|
+
Gibbon 2.x+ exposes a different API from version 1.x. This is because Gibbon maps to MailChimp's API and because version 3 of the API is quite different from version 2. First, the name of the primary class has changed from `API` to `Request`. And the way you pass an API key during initialization is different. A few examples below.
|
400
403
|
|
401
404
|
#### Initialization
|
402
405
|
|
@@ -406,7 +409,7 @@ Gibbon 1.x:
|
|
406
409
|
gibbon = Gibbon::API.new("your_api_key")
|
407
410
|
```
|
408
411
|
|
409
|
-
Gibbon 2.x
|
412
|
+
Gibbon 2.x+:
|
410
413
|
|
411
414
|
```ruby
|
412
415
|
gibbon = Gibbon::Request.new(api_key: "your_api_key")
|
@@ -422,7 +425,7 @@ Gibbon 1.x:
|
|
422
425
|
gibbon.lists.list
|
423
426
|
```
|
424
427
|
|
425
|
-
Gibbon 2.x
|
428
|
+
Gibbon 2.x+:
|
426
429
|
|
427
430
|
```ruby
|
428
431
|
gibbon.lists.retrieve
|
@@ -436,7 +439,7 @@ Gibbon 1.x:
|
|
436
439
|
gibbon.lists.members({:id => list_id})
|
437
440
|
```
|
438
441
|
|
439
|
-
Gibbon 2.x
|
442
|
+
Gibbon 2.x+:
|
440
443
|
|
441
444
|
```ruby
|
442
445
|
gibbon.lists(list_id).members.retrieve
|
@@ -450,11 +453,38 @@ Gibbon 1.x:
|
|
450
453
|
gibbon.lists.subscribe({:id => list_id, :email => {:email => "foo@bar.com"}, :merge_vars => {:FNAME => "Bob", :LNAME => "Smith"}})
|
451
454
|
```
|
452
455
|
|
453
|
-
Gibbon 2.x
|
456
|
+
Gibbon 2.x+:
|
454
457
|
|
455
458
|
```ruby
|
456
459
|
gibbon.lists(list_id).members.create(body: {email_address: "foo@bar.com", status: "subscribed", merge_fields: {FNAME: "Bob", LNAME: "Smith"}})
|
457
460
|
```
|
461
|
+
## Export API 1.0
|
462
|
+
|
463
|
+
Gibbon 3.0.0+ supports MailChimp's [Export API 1.0](https://apidocs.mailchimp.com/export/1.0/). You can choose to handle the API response all at
|
464
|
+
once or line by line by passing a block. To access the Export API with Gibbon, you must first create an instance of `Gibbon::Export`:
|
465
|
+
|
466
|
+
```ruby
|
467
|
+
export = Gibbon::Export.new(api_key: "your_api_key")
|
468
|
+
```
|
469
|
+
|
470
|
+
Next, call the method corresponding to the API endpoint you'd like to query:
|
471
|
+
|
472
|
+
```ruby
|
473
|
+
export.list(id: list_id)
|
474
|
+
```
|
475
|
+
|
476
|
+
This fetches and returns all of the results at once. Pass a block if you'd like to handle the response as individual lines:
|
477
|
+
|
478
|
+
```ruby
|
479
|
+
export = Gibbon::Export.new(api_key: "your_api_key", timeout: 1200)
|
480
|
+
export.list(id: list_id) do |row|
|
481
|
+
puts row
|
482
|
+
end
|
483
|
+
```
|
484
|
+
|
485
|
+
This is useful when handling large sets of data. Setting the `timeout` here is optional, but a high value is recommended given the nature of the Export API. The default timeout is 600 seconds.
|
486
|
+
|
487
|
+
Gibbon supports all current Export API endpoints: `/list`, `/ecommOrders`, and `/campaignSubscriberActivity`. They're mapped onto Ruby methods with similar names: `list()`, `ecomm_orders()`, and `campaign_subscriber_activity()` respectively. Please see [MailChimp's API documentation](https://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-the-export-api/) for supported parameters.
|
458
488
|
|
459
489
|
## Thanks
|
460
490
|
|
@@ -462,5 +492,5 @@ Thanks to everyone who has [contributed](https://github.com/amro/gibbon/contribu
|
|
462
492
|
|
463
493
|
## Copyright
|
464
494
|
|
465
|
-
* Copyright (c) 2010-
|
466
|
-
* MailChimp (c) 2001-
|
495
|
+
* Copyright (c) 2010-2017 Amro Mousa. See LICENSE.txt for details.
|
496
|
+
* MailChimp (c) 2001-2017 The Rocket Science Group.
|
data/gibbon.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["amromousa@gmail.com"]
|
10
10
|
s.homepage = "http://github.com/amro/gibbon"
|
11
11
|
|
12
|
-
s.summary = %q{A wrapper for MailChimp API 3.0}
|
13
|
-
s.description = %q{A wrapper for MailChimp API 3.0}
|
12
|
+
s.summary = %q{A wrapper for MailChimp API 3.0 and Export API}
|
13
|
+
s.description = %q{A wrapper for MailChimp API 3.0 and Export API}
|
14
14
|
s.license = "MIT"
|
15
15
|
|
16
16
|
s.rubyforge_project = "gibbon"
|
data/lib/gibbon.rb
CHANGED
@@ -3,10 +3,13 @@ require 'multi_json'
|
|
3
3
|
require 'cgi'
|
4
4
|
require 'logger'
|
5
5
|
|
6
|
+
require 'gibbon/gibbon_helpers'
|
6
7
|
require 'gibbon/gibbon_error'
|
7
8
|
require 'gibbon/mailchimp_error'
|
8
9
|
require 'gibbon/request'
|
9
10
|
require 'gibbon/api_request'
|
11
|
+
require 'gibbon/response'
|
12
|
+
require 'gibbon/export'
|
10
13
|
|
11
14
|
module Gibbon
|
12
15
|
end
|
data/lib/gibbon/api_request.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Gibbon
|
2
2
|
class APIRequest
|
3
|
+
include Helpers
|
4
|
+
|
3
5
|
def initialize(builder: nil)
|
4
6
|
@request_builder = builder
|
5
7
|
end
|
@@ -11,7 +13,7 @@ module Gibbon
|
|
11
13
|
response = self.rest_client.post do |request|
|
12
14
|
configure_request(request: request, params: params, headers: headers, body: MultiJson.dump(body))
|
13
15
|
end
|
14
|
-
parse_response(response
|
16
|
+
parse_response(response)
|
15
17
|
rescue => e
|
16
18
|
handle_error(e)
|
17
19
|
end
|
@@ -24,7 +26,7 @@ module Gibbon
|
|
24
26
|
response = self.rest_client.patch do |request|
|
25
27
|
configure_request(request: request, params: params, headers: headers, body: MultiJson.dump(body))
|
26
28
|
end
|
27
|
-
parse_response(response
|
29
|
+
parse_response(response)
|
28
30
|
rescue => e
|
29
31
|
handle_error(e)
|
30
32
|
end
|
@@ -37,7 +39,7 @@ module Gibbon
|
|
37
39
|
response = self.rest_client.put do |request|
|
38
40
|
configure_request(request: request, params: params, headers: headers, body: MultiJson.dump(body))
|
39
41
|
end
|
40
|
-
parse_response(response
|
42
|
+
parse_response(response)
|
41
43
|
rescue => e
|
42
44
|
handle_error(e)
|
43
45
|
end
|
@@ -50,7 +52,7 @@ module Gibbon
|
|
50
52
|
response = self.rest_client.get do |request|
|
51
53
|
configure_request(request: request, params: params, headers: headers)
|
52
54
|
end
|
53
|
-
parse_response(response
|
55
|
+
parse_response(response)
|
54
56
|
rescue => e
|
55
57
|
handle_error(e)
|
56
58
|
end
|
@@ -63,7 +65,7 @@ module Gibbon
|
|
63
65
|
response = self.rest_client.delete do |request|
|
64
66
|
configure_request(request: request, params: params, headers: headers)
|
65
67
|
end
|
66
|
-
parse_response(response
|
68
|
+
parse_response(response)
|
67
69
|
rescue => e
|
68
70
|
handle_error(e)
|
69
71
|
end
|
@@ -115,8 +117,12 @@ module Gibbon
|
|
115
117
|
|
116
118
|
if parsed_response
|
117
119
|
error_params[:body] = parsed_response
|
118
|
-
|
119
|
-
|
120
|
+
|
121
|
+
title_key = symbolize_keys ? :title : "title"
|
122
|
+
detail_key = symbolize_keys ? :detail : "detail"
|
123
|
+
|
124
|
+
error_params[:title] = parsed_response[title_key] if parsed_response[title_key]
|
125
|
+
error_params[:detail] = parsed_response[detail_key] if parsed_response[detail_key]
|
120
126
|
end
|
121
127
|
|
122
128
|
end
|
@@ -140,7 +146,7 @@ module Gibbon
|
|
140
146
|
end
|
141
147
|
|
142
148
|
def rest_client
|
143
|
-
client = Faraday.new(self.api_url, proxy: self.proxy) do |faraday|
|
149
|
+
client = Faraday.new(self.api_url, proxy: self.proxy, ssl: { version: "TLSv1_2" }) do |faraday|
|
144
150
|
faraday.response :raise_error
|
145
151
|
faraday.adapter adapter
|
146
152
|
if @request_builder.debug
|
@@ -151,16 +157,17 @@ module Gibbon
|
|
151
157
|
client
|
152
158
|
end
|
153
159
|
|
154
|
-
def parse_response(
|
160
|
+
def parse_response(response)
|
155
161
|
parsed_response = nil
|
156
162
|
|
157
|
-
if
|
163
|
+
if response.body && !response.body.empty?
|
158
164
|
begin
|
159
|
-
|
165
|
+
headers = response.headers
|
166
|
+
body = MultiJson.load(response.body, symbolize_keys: symbolize_keys)
|
167
|
+
parsed_response = Response.new(headers: headers, body: body)
|
160
168
|
rescue MultiJson::ParseError
|
161
|
-
|
162
|
-
error
|
163
|
-
error.status_code = 500
|
169
|
+
error_params = { title: "UNPARSEABLE_RESPONSE", status_code: 500 }
|
170
|
+
error = MailChimpError.new("Unparseable response: #{response.body}", error_params)
|
164
171
|
raise error
|
165
172
|
end
|
166
173
|
end
|
@@ -180,20 +187,8 @@ module Gibbon
|
|
180
187
|
end
|
181
188
|
|
182
189
|
def base_api_url
|
183
|
-
computed_api_endpoint = "https://#{get_data_center_from_api_key}api.mailchimp.com"
|
190
|
+
computed_api_endpoint = "https://#{get_data_center_from_api_key(self.api_key)}api.mailchimp.com"
|
184
191
|
"#{self.api_endpoint || computed_api_endpoint}/3.0/"
|
185
192
|
end
|
186
|
-
|
187
|
-
def get_data_center_from_api_key
|
188
|
-
# Return an empty string for invalid API keys so Gibbon hits the main endpoint
|
189
|
-
data_center = ""
|
190
|
-
|
191
|
-
if self.api_key && self.api_key["-"]
|
192
|
-
# Add a period since the data_center is a subdomain and it keeps things dry
|
193
|
-
data_center = "#{self.api_key.split('-').last}."
|
194
|
-
end
|
195
|
-
|
196
|
-
data_center
|
197
|
-
end
|
198
193
|
end
|
199
194
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module Gibbon
|
4
|
+
class Export
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
attr_accessor :api_key, :timeout
|
8
|
+
|
9
|
+
def initialize(api_key: nil, timeout: nil)
|
10
|
+
@api_key = api_key || self.class.api_key
|
11
|
+
@timeout = timeout || self.class.timeout || 600
|
12
|
+
end
|
13
|
+
|
14
|
+
def list(params = {}, &block)
|
15
|
+
call("list", params, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ecomm_orders(params = {}, &block)
|
19
|
+
call("ecommOrders", params, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def campaign_subscriber_activity(params = {}, &block)
|
23
|
+
call("campaignSubscriberActivity", params, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def export_api_url
|
29
|
+
"https://#{get_data_center_from_api_key(@api_key)}api.mailchimp.com/export/1.0/"
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(method, params = {}, &block)
|
33
|
+
rows = []
|
34
|
+
|
35
|
+
api_url = export_api_url + method + "/"
|
36
|
+
params = params.merge({ apikey: @api_key })
|
37
|
+
block = Proc.new { |row| rows << row } unless block_given?
|
38
|
+
|
39
|
+
ensure_api_key(params)
|
40
|
+
|
41
|
+
url = URI.parse(api_url)
|
42
|
+
req = Net::HTTP::Post.new(url.path, initheader = {'Content-Type' => 'application/json'})
|
43
|
+
req.body = MultiJson.dump(params)
|
44
|
+
Net::HTTP.start(url.host, url.port, read_timeout: @timeout, use_ssl: true, ssl_version: :TLSv1_2) do |http|
|
45
|
+
# http://stackoverflow.com/questions/29598196/ruby-net-http-read-body-nethttpokread-body-called-twice-ioerror
|
46
|
+
http.request req do |response|
|
47
|
+
i = -1
|
48
|
+
last = ''
|
49
|
+
response.read_body do |chunk|
|
50
|
+
next if chunk.nil? or chunk.strip.empty?
|
51
|
+
infix = "\n" if last[-1, 1]==']'
|
52
|
+
lines, last = try_parse_line("#{last}#{infix}#{chunk}")
|
53
|
+
lines.each { |line| block.call(line, i += 1) }
|
54
|
+
end
|
55
|
+
block.call(parse_line(last), i += 1) unless last.nil? or last.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rows unless block_given?
|
59
|
+
end
|
60
|
+
|
61
|
+
def try_parse_line(res)
|
62
|
+
lines = res.split("\n")
|
63
|
+
last = lines.pop || ''
|
64
|
+
lines.map! { |line| parse_line(line) }
|
65
|
+
[lines.compact, last]
|
66
|
+
rescue MultiJson::ParseError
|
67
|
+
[[], last]
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def parse_line(line)
|
72
|
+
parsed_response = MultiJson.load(line)
|
73
|
+
rescue MultiJson::ParseError
|
74
|
+
return []
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def ensure_api_key(params)
|
80
|
+
unless params[:apikey] && (get_data_center_from_api_key(params[:apikey]) != "")
|
81
|
+
raise Gibbon::GibbonError, "You must set an api_key prior to making a call"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class << self
|
86
|
+
attr_accessor :api_key, :timeout
|
87
|
+
|
88
|
+
def method_missing(sym, *args, &block)
|
89
|
+
new(api_key: self.api_key, timeout: self.timeout).send(sym, *args, &block)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gibbon
|
2
|
+
module Helpers
|
3
|
+
def get_data_center_from_api_key(api_key)
|
4
|
+
# Return an empty string for invalid API keys so Gibbon hits the main endpoint
|
5
|
+
data_center = ""
|
6
|
+
|
7
|
+
if api_key && api_key["-"]
|
8
|
+
# Add a period since the data_center is a subdomain and it keeps things dry
|
9
|
+
data_center = "#{api_key.split('-').last}."
|
10
|
+
end
|
11
|
+
|
12
|
+
data_center
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/gibbon/request.rb
CHANGED
@@ -14,9 +14,9 @@ module Gibbon
|
|
14
14
|
@open_timeout = open_timeout || self.class.open_timeout || DEFAULT_OPEN_TIMEOUT
|
15
15
|
@proxy = proxy || self.class.proxy || ENV['MAILCHIMP_PROXY']
|
16
16
|
@faraday_adapter = faraday_adapter || Faraday.default_adapter
|
17
|
-
@symbolize_keys = symbolize_keys
|
17
|
+
@symbolize_keys = symbolize_keys || self.class.symbolize_keys || false
|
18
|
+
@debug = debug || self.class.debug || false
|
18
19
|
@logger = logger || self.class.logger || ::Logger.new(STDOUT)
|
19
|
-
@debug = debug
|
20
20
|
end
|
21
21
|
|
22
22
|
def method_missing(method, *args)
|
@@ -27,6 +27,10 @@ module Gibbon
|
|
27
27
|
self
|
28
28
|
end
|
29
29
|
|
30
|
+
def respond_to_missing?(method_name, include_private = false)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
30
34
|
def send(*args)
|
31
35
|
if args.length == 0
|
32
36
|
method_missing(:send, args)
|
@@ -76,10 +80,14 @@ module Gibbon
|
|
76
80
|
end
|
77
81
|
|
78
82
|
class << self
|
79
|
-
attr_accessor :api_key, :timeout, :open_timeout, :api_endpoint, :proxy, :logger
|
83
|
+
attr_accessor :api_key, :timeout, :open_timeout, :api_endpoint, :proxy, :faraday_adapter, :symbolize_keys, :debug, :logger
|
80
84
|
|
81
85
|
def method_missing(sym, *args, &block)
|
82
|
-
new(api_key: self.api_key, api_endpoint: self.api_endpoint, timeout: self.timeout, open_timeout: self.open_timeout, proxy: self.proxy, logger: self.logger).send(sym, *args, &block)
|
86
|
+
new(api_key: self.api_key, api_endpoint: self.api_endpoint, timeout: self.timeout, open_timeout: self.open_timeout, faraday_adapter: self.faraday_adapter, symbolize_keys: self.symbolize_keys, debug: self.debug, proxy: self.proxy, logger: self.logger).send(sym, *args, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def respond_to_missing?(method_name, include_private = false)
|
90
|
+
true
|
83
91
|
end
|
84
92
|
end
|
85
93
|
end
|
data/lib/gibbon/version.rb
CHANGED
@@ -42,5 +42,19 @@ describe Gibbon::APIRequest do
|
|
42
42
|
expect(boom.raw_body).to eq "A non JSON response"
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
context "when symbolize_keys is true" do
|
47
|
+
it "sets title and detail on the error params" do
|
48
|
+
response_values = {:status => 422, :headers => {}, :body => '{"title": "foo", "detail": "bar"}'}
|
49
|
+
exception = Faraday::Error::ClientError.new("the server responded with status 422", response_values)
|
50
|
+
api_request = Gibbon::APIRequest.new(builder: Gibbon::Request.new(symbolize_keys: true))
|
51
|
+
begin
|
52
|
+
api_request.send :handle_error, exception
|
53
|
+
rescue => boom
|
54
|
+
expect(boom.title).to eq "foo"
|
55
|
+
expect(boom.detail).to eq "bar"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
45
59
|
end
|
46
60
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
|
4
|
+
describe Gibbon::Export do
|
5
|
+
before do
|
6
|
+
Gibbon::Export.send(:public, *Gibbon::Export.protected_instance_methods)
|
7
|
+
@export = Gibbon::Export.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "doesn't allow empty api key" do
|
11
|
+
expect {@export.list(id: "123456")}.to raise_error(Gibbon::GibbonError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "doesn't allow api key without data center" do
|
15
|
+
@api_key = "123"
|
16
|
+
@export.api_key = @api_key
|
17
|
+
expect {@export.list(id: "123456")}.to raise_error(Gibbon::GibbonError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets correct endpoint from api key" do
|
21
|
+
@api_key = "TESTKEY-us1"
|
22
|
+
@export.api_key = @api_key
|
23
|
+
expect(@export.export_api_url).to eq("https://us1.api.mailchimp.com/export/1.0/")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sets correct timeout" do
|
27
|
+
@api_key = "TESTKEY-us1"
|
28
|
+
@export.api_key = @api_key
|
29
|
+
@export.timeout = 9
|
30
|
+
expect(@export.timeout).to eq(9)
|
31
|
+
end
|
32
|
+
end
|
data/spec/gibbon/gibbon_spec.rb
CHANGED
@@ -102,6 +102,10 @@ describe Gibbon do
|
|
102
102
|
expect(@gibbon.symbolize_keys).to be true
|
103
103
|
end
|
104
104
|
|
105
|
+
it "sets symbolize_keys in the constructor" do
|
106
|
+
@gibbon = Gibbon::Request.new(symbolize_keys: true)
|
107
|
+
expect(@gibbon.symbolize_keys).to be true
|
108
|
+
end
|
105
109
|
it "debug false by default" do
|
106
110
|
@gibbon = Gibbon::Request.new
|
107
111
|
expect(@gibbon.debug).to be false
|
@@ -123,7 +127,6 @@ describe Gibbon do
|
|
123
127
|
expect(@gibbon.logger).to be_a Logger
|
124
128
|
end
|
125
129
|
|
126
|
-
|
127
130
|
end
|
128
131
|
|
129
132
|
describe "build api url" do
|
@@ -131,7 +134,6 @@ describe Gibbon do
|
|
131
134
|
Gibbon::APIRequest.send(:public, *Gibbon::APIRequest.protected_instance_methods)
|
132
135
|
|
133
136
|
@gibbon = Gibbon::Request.new
|
134
|
-
@url = "https://api.mailchimp.com/3.0/lists/"
|
135
137
|
end
|
136
138
|
|
137
139
|
it "doesn't allow empty api key" do
|
@@ -170,6 +172,10 @@ describe Gibbon do
|
|
170
172
|
Gibbon::Request.timeout = 15
|
171
173
|
Gibbon::Request.api_endpoint = 'https://us6.api.mailchimp.com'
|
172
174
|
Gibbon::Request.logger = logger
|
175
|
+
Gibbon::Request.proxy = "http://1234.com"
|
176
|
+
Gibbon::Request.symbolize_keys = true
|
177
|
+
Gibbon::Request.faraday_adapter = :net_http
|
178
|
+
Gibbon::Request.debug = true
|
173
179
|
end
|
174
180
|
|
175
181
|
after do
|
@@ -177,6 +183,10 @@ describe Gibbon do
|
|
177
183
|
Gibbon::Request.timeout = nil
|
178
184
|
Gibbon::Request.api_endpoint = nil
|
179
185
|
Gibbon::Request.logger = nil
|
186
|
+
Gibbon::Request.proxy = nil
|
187
|
+
Gibbon::Request.symbolize_keys = nil
|
188
|
+
Gibbon::Request.faraday_adapter = nil
|
189
|
+
Gibbon::Request.debug = nil
|
180
190
|
end
|
181
191
|
|
182
192
|
it "set api key on new instances" do
|
@@ -192,8 +202,33 @@ describe Gibbon do
|
|
192
202
|
expect(Gibbon::Request.new.api_endpoint).to eq(Gibbon::Request.api_endpoint)
|
193
203
|
end
|
194
204
|
|
205
|
+
it "set proxy on new instances" do
|
206
|
+
expect(Gibbon::Request.new.proxy).to eq(Gibbon::Request.proxy)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "set symbolize_keys on new instances" do
|
210
|
+
expect(Gibbon::Request.new.symbolize_keys).to eq(Gibbon::Request.symbolize_keys)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "set debug on new instances" do
|
214
|
+
expect(Gibbon::Request.new.debug).to eq(Gibbon::Request.debug)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "set faraday_adapter on new instances" do
|
218
|
+
expect(Gibbon::Request.new.faraday_adapter).to eq(Gibbon::Request.faraday_adapter)
|
219
|
+
end
|
220
|
+
|
195
221
|
it "set logger on new instances" do
|
196
222
|
expect(Gibbon::Request.new.logger).to eq(logger)
|
197
223
|
end
|
198
224
|
end
|
225
|
+
|
226
|
+
describe "missing methods" do
|
227
|
+
it "respond to .method call on class" do
|
228
|
+
expect(Gibbon::Request.method(:lists)).to be_a(Method)
|
229
|
+
end
|
230
|
+
it "respond to .method call on instance" do
|
231
|
+
expect(Gibbon::Request.new.method(:lists)).to be_a(Method)
|
232
|
+
end
|
233
|
+
end
|
199
234
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gibbon::MailChimpError do
|
4
|
+
let(:message) { 'Foo' }
|
5
|
+
let(:params) do
|
6
|
+
{
|
7
|
+
title: 'error_title',
|
8
|
+
detail: 'error_detail',
|
9
|
+
body: 'error_body',
|
10
|
+
raw_body: 'error_raw_body',
|
11
|
+
status_code: 'error_status_code'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
@gibbon = Gibbon::MailChimpError.new(message, params)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "adds the error params to the error message" do
|
20
|
+
expected_message = "Foo " \
|
21
|
+
"@title=\"error_title\", " \
|
22
|
+
"@detail=\"error_detail\", " \
|
23
|
+
"@body=\"error_body\", " \
|
24
|
+
"@raw_body=\"error_raw_body\", " \
|
25
|
+
"@status_code=\"error_status_code\""
|
26
|
+
|
27
|
+
expect(@gibbon.message).to eq(expected_message)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'sets the title attribute' do
|
31
|
+
expect(@gibbon.title).to eq(params[:title])
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets the detail attribute' do
|
35
|
+
expect(@gibbon.detail).to eq(params[:detail])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sets the body attribute' do
|
39
|
+
expect(@gibbon.body).to eq(params[:body])
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'sets the raw_body attribute' do
|
43
|
+
expect(@gibbon.raw_body).to eq(params[:raw_body])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sets the status_code attribute' do
|
47
|
+
expect(@gibbon.status_code).to eq(params[:status_code])
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gibbon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amro Mousa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 1.21.0
|
83
|
-
description: A wrapper for MailChimp API 3.0
|
83
|
+
description: A wrapper for MailChimp API 3.0 and Export API
|
84
84
|
email:
|
85
85
|
- amromousa@gmail.com
|
86
86
|
executables: []
|
@@ -98,12 +98,17 @@ files:
|
|
98
98
|
- gibbon.gemspec
|
99
99
|
- lib/gibbon.rb
|
100
100
|
- lib/gibbon/api_request.rb
|
101
|
+
- lib/gibbon/export.rb
|
101
102
|
- lib/gibbon/gibbon_error.rb
|
103
|
+
- lib/gibbon/gibbon_helpers.rb
|
102
104
|
- lib/gibbon/mailchimp_error.rb
|
103
105
|
- lib/gibbon/request.rb
|
106
|
+
- lib/gibbon/response.rb
|
104
107
|
- lib/gibbon/version.rb
|
105
108
|
- spec/gibbon/api_request_spec.rb
|
109
|
+
- spec/gibbon/export_spec.rb
|
106
110
|
- spec/gibbon/gibbon_spec.rb
|
111
|
+
- spec/gibbon/mailchimp_error_spec.rb
|
107
112
|
- spec/gibbon/upsert_spec.rb
|
108
113
|
- spec/spec_helper.rb
|
109
114
|
homepage: http://github.com/amro/gibbon
|
@@ -129,9 +134,11 @@ rubyforge_project: gibbon
|
|
129
134
|
rubygems_version: 2.4.6
|
130
135
|
signing_key:
|
131
136
|
specification_version: 4
|
132
|
-
summary: A wrapper for MailChimp API 3.0
|
137
|
+
summary: A wrapper for MailChimp API 3.0 and Export API
|
133
138
|
test_files:
|
134
139
|
- spec/gibbon/api_request_spec.rb
|
140
|
+
- spec/gibbon/export_spec.rb
|
135
141
|
- spec/gibbon/gibbon_spec.rb
|
142
|
+
- spec/gibbon/mailchimp_error_spec.rb
|
136
143
|
- spec/gibbon/upsert_spec.rb
|
137
144
|
- spec/spec_helper.rb
|