gibbon 3.4.4 → 4.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a078fe9c806ec8909a1f808a6d6983de6f1d1414c361a9b54156b66f8a796bb3
4
- data.tar.gz: d0ded539be8f09eae933da1d20cace7ee804529668add90f8c892e672c61c362
3
+ metadata.gz: 1cb1352737a9335535ba7d79420ec208817700b6e8b4fcf07a449499837b721f
4
+ data.tar.gz: 5fd15b6c055607e95a2e81f8b4fda95b606dc6be3ac7aa147057acd13c946a1a
5
5
  SHA512:
6
- metadata.gz: 9963ae0173d0189cfe674e1547313ad6570f3dc6f502198e77118e67f067bb842478b36dc46ed72bd675f692b42067f1bda0db59c47e43f8f215364818ac1d33
7
- data.tar.gz: 6f855c6ad7a73d447ed9c3219ba6383cd624dfa33abf3effbb655207b888e4eb2006297413aabb62f481f0adfde56dc67c863c47eb287a599c9f907db5c29aa9
6
+ metadata.gz: 20bd382a94c31402452f63037c0f266cdb291493b72b93b1aab2c15021fb108fbf9a45b4ced1f3070c2c2a18aaee3bd117ef98dc4b8846b3a07f44ac042c864f
7
+ data.tar.gz: a030570ce92ecd7744ee1ed8dead15f0a176e3b178fd0b410e4c11a870f90ebbc0ba5909a82ae59b55959cf1ae3c06f0fd612826b5fdf93a26954907aba96729
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: [amro]
data/.travis.yml CHANGED
@@ -1,12 +1,23 @@
1
1
  language: ruby
2
- sudo: false
3
2
  cache: bundler
4
- before_install:
5
- - gem install bundler # -v 1.7.14 if a specific version is needed
6
3
  rvm:
7
- - 2.4.5
8
- - 2.5.7
9
- - 2.6.5
10
- - 2.7
11
- - 3.0.0
12
-
4
+ - 3.1
5
+ - 3.2
6
+ - 3.3
7
+ - 3.4
8
+ jobs:
9
+ include:
10
+ # rvm's stable release does not know Ruby 4.0 yet, so boot on 3.4,
11
+ # then upgrade rvm to master and install 4.0 from there
12
+ - name: "4.0 (via rvm master)"
13
+ rvm: 3.4
14
+ before_install:
15
+ - rvm get master
16
+ - rvm install ruby-4.0
17
+ - rvm use ruby-4.0
18
+ - ruby -v
19
+ # Bundler 4 (shipped with Ruby 4.0) removed the --path flag that
20
+ # Travis's default install step still passes
21
+ install:
22
+ - bundle config set --local path vendor/bundle
23
+ - bundle install --jobs=3 --retry=3
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
- ## [Unreleased][unreleased]
1
+ ## [4.0.0] - 2026-07-23
2
+ - Require Ruby 3.1+ (drops support for EOL Rubies 2.4 through 3.0)
3
+ - Require TLS 1.2 as a minimum instead of pinning it exactly, allowing TLS 1.3
4
+ - Modernize development setup: loosen rspec pin, remove Rubinius leftovers, update CI matrix to Ruby 3.1 through 4.0
5
+
6
+ ## [3.5.0] - 2023-06-07
7
+ - Remove support for retired Export API
2
8
 
3
9
  ## [3.4.4] - 2022-02-24
4
10
  - Remove non-alpha characters when parsing datacenter from API keys to prevent potential attackers from injecting a domain via the API key. This would only be possible if one were using user-provided API keys (e.g. from a form, etc.).
data/Gemfile CHANGED
@@ -1,11 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- platforms :rbx do
4
- gem 'rubinius-developer_tools'
5
- end
6
-
7
- group :development, :test do
8
- gem 'webmock', '~> 3.8'
9
- end
10
-
11
3
  gemspec
data/README.markdown CHANGED
@@ -1,12 +1,12 @@
1
1
  # gibbon
2
2
 
3
- Gibbon is an API wrapper for MailChimp's [API](http://kb.mailchimp.com/api/).
3
+ Gibbon is an API wrapper for MailChimp's [API](https://mailchimp.com/developer/marketing/api/).
4
4
 
5
- [![Build Status](https://travis-ci.com/amro/gibbon.svg?branch=master)](https://app.travis-ci.com/github/amro/gibbon)
5
+ [![Build Status](https://app.travis-ci.com/amro/gibbon.svg?branch=master)](https://app.travis-ci.com/github/amro/gibbon)
6
6
 
7
7
  ## Important Notes
8
8
 
9
- Please read MailChimp's [Getting Started Guide](http://kb.mailchimp.com/api/article/api-3-overview).
9
+ Please read MailChimp's [Getting Started Guide](https://mailchimp.com/developer/marketing/guides/quick-start/).
10
10
 
11
11
  Gibbon 3.0.0+ returns a `Gibbon::Response` instead of the response body directly. `Gibbon::Response` exposes the parsed response `body` and `headers`.
12
12
 
@@ -16,7 +16,7 @@ Gibbon 3.0.0+ returns a `Gibbon::Response` instead of the response body directly
16
16
 
17
17
  ## Requirements
18
18
 
19
- A MailChimp account and API key. You can see your API keys [here](http://admin.mailchimp.com/account/api).
19
+ A MailChimp account and API key. You can see your API keys [here](https://admin.mailchimp.com/account/api).
20
20
 
21
21
  ## Usage
22
22
 
@@ -35,9 +35,9 @@ gibbon.timeout = 30
35
35
  gibbon.open_timeout = 30
36
36
  ```
37
37
 
38
- You can read about `timeout` and `open_timeout` in the [Net::HTTP](https://ruby-doc.org/stdlib-2.3.3/libdoc/net/http/rdoc/Net/HTTP.html) doc.
38
+ You can read about `timeout` and `open_timeout` in the [Net::HTTP](https://docs.ruby-lang.org/en/master/Net/HTTP.html) doc.
39
39
 
40
- Now you can make requests using the resources defined in [MailChimp's docs](http://kb.mailchimp.com/api/resources). Resource IDs
40
+ Now you can make requests using the resources defined in [MailChimp's docs](https://mailchimp.com/developer/marketing/api/). Resource IDs
41
41
  are specified inline and a `CRUD` (`create`, `retrieve` (or `get`), `update`, `upsert`, or `delete`) verb initiates the request. `upsert` lets you update a record, if it exists, or insert it otherwise where supported by MailChimp's API.
42
42
 
43
43
  ***Note*** `upsert` requires Gibbon version 2.1.0 or newer!
@@ -84,7 +84,7 @@ Pass `symbolize_keys: true` to use symbols (instead of strings) as hash keys in
84
84
  gibbon = Gibbon::Request.new(api_key: "your_api_key", symbolize_keys: true)
85
85
  ```
86
86
 
87
- MailChimp's [resource documentation](http://kb.mailchimp.com/api/resources) is a list of available resources.
87
+ MailChimp's [resource documentation](https://mailchimp.com/developer/marketing/api/) is a list of available resources.
88
88
 
89
89
  ## Debug Logging
90
90
 
@@ -180,6 +180,12 @@ Get a specific member's information (open/click rates etc.) from MailChimp:
180
180
  gibbon.lists(list_id).members(lower_case_md5_hashed_email_address).retrieve
181
181
  ```
182
182
 
183
+ Permanently delete a specific member from a list:
184
+
185
+ ```ruby
186
+ gibbon.lists(list_id).members(lower_case_md5_hashed_email_address).actions.delete_permanent.create
187
+ ```
188
+
183
189
  ### Tags
184
190
 
185
191
  [Tags](https://mailchimp.com/help/getting-started-tags/) are a flexible way to organize (slice and dice) your list: for example, you can send a campaign directly to one or more tags.
@@ -235,7 +241,7 @@ gibbon.batches(batch_id).retrieve
235
241
  ```
236
242
 
237
243
  ***Note*** This response truncated for brevity. Reference the MailChimp
238
- [API documentation for Batch Operations](http://developer.mailchimp.com/documentation/mailchimp/reference/batches/) for more details.
244
+ [API documentation for Batch Operations](https://mailchimp.com/developer/marketing/api/batches/) for more details.
239
245
 
240
246
  ### Fields
241
247
 
@@ -357,7 +363,7 @@ The response body (i.e. `response.body`) looks someting like this (unrelated thi
357
363
  {"id"=>"...", "email_address"=>"...", ..., "interests"=>{"3def637141"=>true, "f7cc4ee841"=>false, "fcdc951b9f"=>false, "3daf3cf27d"=>true, "293a3703ed"=>false, "72370e0d1f"=>false, "d434d21a1c"=>false, "bdb1ff199f"=>false, "a54e78f203"=>false, "c4527fd018"=>false} ...}
358
364
  ```
359
365
 
360
- The API returns a map of interest ID to boolean value. Now we to get interest details so we know what these interest IDs map to. Looking at [this doc page](http://kb.mailchimp.com/api/resources/lists/interest-categories/interests/lists-interests-collection), we need to do this:
366
+ The API returns a map of interest ID to boolean value. Now we to get interest details so we know what these interest IDs map to. Looking at [this doc page](https://mailchimp.com/developer/marketing/api/interests/), we need to do this:
361
367
 
362
368
  ```ruby
363
369
  gibbon.lists(list_id).interest_categories.retrieve
@@ -394,7 +400,7 @@ end
394
400
 
395
401
  ### Other
396
402
 
397
- Overriding Gibbon's API endpoint (i.e. if using an access token from OAuth and have the `api_endpoint` from the [metadata](http://apidocs.mailchimp.com/oauth2/)):
403
+ Overriding Gibbon's API endpoint (i.e. if using an access token from OAuth and have the `api_endpoint` from the [metadata](https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/)):
398
404
 
399
405
  ```ruby
400
406
  Gibbon::Request.api_endpoint = "https://us1.api.mailchimp.com"
@@ -474,33 +480,6 @@ Gibbon 2.x+:
474
480
  ```ruby
475
481
  gibbon.lists(list_id).members.create(body: {email_address: "foo@bar.com", status: "subscribed", merge_fields: {FNAME: "Bob", LNAME: "Smith"}})
476
482
  ```
477
- ## Export API 1.0
478
-
479
- 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
480
- 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`:
481
-
482
- ```ruby
483
- export = Gibbon::Export.new(api_key: "your_api_key")
484
- ```
485
-
486
- Next, call the method corresponding to the API endpoint you'd like to query:
487
-
488
- ```ruby
489
- export.list(id: list_id)
490
- ```
491
-
492
- This fetches and returns all of the results at once. Pass a block if you'd like to handle the response as individual lines:
493
-
494
- ```ruby
495
- export = Gibbon::Export.new(api_key: "your_api_key", timeout: 1200)
496
- export.list(id: list_id) do |row|
497
- puts row
498
- end
499
- ```
500
-
501
- 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.
502
-
503
- 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.
504
483
 
505
484
  ## Thanks
506
485
 
data/gibbon.gemspec CHANGED
@@ -7,23 +7,22 @@ Gem::Specification.new do |s|
7
7
  s.version = Gibbon::VERSION
8
8
  s.authors = ["Amro Mousa"]
9
9
  s.email = ["amromousa@gmail.com"]
10
- s.homepage = "http://github.com/amro/gibbon"
10
+ s.homepage = "https://github.com/amro/gibbon"
11
11
 
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}
12
+ s.summary = %q{A wrapper for MailChimp API 3.0}
13
+ s.description = %q{A wrapper for MailChimp API 3.0}
14
14
  s.license = "MIT"
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
18
  s.require_paths = ["lib"]
20
- s.required_ruby_version = '>= 2.4.0'
19
+ s.required_ruby_version = '>= 3.1.0'
21
20
 
22
21
  s.add_dependency('faraday', '>= 1.0')
23
22
  s.add_dependency('multi_json', '>= 1.11.0')
24
23
 
25
24
  s.add_development_dependency 'rake'
26
- s.add_development_dependency "rspec", "3.5.0"
25
+ s.add_development_dependency "rspec", "~> 3.13"
27
26
  s.add_development_dependency 'webmock', '~> 3.8'
28
27
 
29
28
  end
@@ -106,6 +106,10 @@ module Gibbon
106
106
  # Helpers
107
107
 
108
108
  def handle_error(error)
109
+ # Errors we raised ourselves (e.g. unparseable response bodies) already
110
+ # carry their details; re-wrapping them here would strip those attributes
111
+ raise error if error.is_a?(MailChimpError)
112
+
109
113
  error_params = {}
110
114
 
111
115
  begin
@@ -146,7 +150,7 @@ module Gibbon
146
150
  end
147
151
 
148
152
  def rest_client
149
- client = Faraday.new(self.api_url, proxy: self.proxy, ssl: { version: "TLSv1_2" }) do |faraday|
153
+ client = Faraday.new(self.api_url, proxy: self.proxy, ssl: { min_version: :TLS1_2 }) do |faraday|
150
154
  faraday.response :raise_error
151
155
  faraday.adapter adapter
152
156
  if @request_builder.debug
@@ -1,3 +1,3 @@
1
1
  module Gibbon
2
- VERSION = "3.4.4"
2
+ VERSION = "4.0.0"
3
3
  end
data/lib/gibbon.rb CHANGED
@@ -9,7 +9,6 @@ require 'gibbon/mailchimp_error'
9
9
  require 'gibbon/request'
10
10
  require 'gibbon/api_request'
11
11
  require 'gibbon/response'
12
- require 'gibbon/export'
13
12
 
14
13
  module Gibbon
15
14
  end
@@ -66,4 +66,133 @@ describe Gibbon::APIRequest do
66
66
 
67
67
  include_examples 'client error handling'
68
68
  end
69
+
70
+ context 'Faraday::ServerError' do
71
+ let(:error_class) { Faraday::ServerError }
72
+
73
+ include_examples 'client error handling'
74
+ end
75
+
76
+ context "error responses over HTTP" do
77
+ it "raises a MailChimpError with details parsed from a 4xx JSON response" do
78
+ error_body = '{"title": "Invalid Resource", "detail": "The resource submitted could not be validated.", "status": 422}'
79
+ stub_request(:get, "#{@api_root}/lists")
80
+ .with(basic_auth: @basic_auth_credentials)
81
+ .to_return(status: 422, body: error_body, headers: { 'Content-Type' => 'application/json' })
82
+
83
+ begin
84
+ @gibbon.lists.retrieve
85
+ fail "expected a Gibbon::MailChimpError"
86
+ rescue Gibbon::MailChimpError => e
87
+ expect(e.status_code).to eq 422
88
+ expect(e.title).to eq "Invalid Resource"
89
+ expect(e.detail).to eq "The resource submitted could not be validated."
90
+ expect(e.raw_body).to eq error_body
91
+ expect(e.body).to eq MultiJson.load(error_body)
92
+ end
93
+ end
94
+
95
+ it "raises a MailChimpError on a 5xx response" do
96
+ stub_request(:get, "#{@api_root}/lists")
97
+ .with(basic_auth: @basic_auth_credentials)
98
+ .to_return(status: 503, body: '{"title": "Service Unavailable"}')
99
+
100
+ begin
101
+ @gibbon.lists.retrieve
102
+ fail "expected a Gibbon::MailChimpError"
103
+ rescue Gibbon::MailChimpError => e
104
+ expect(e.status_code).to eq 503
105
+ expect(e.title).to eq "Service Unavailable"
106
+ end
107
+ end
108
+ end
109
+
110
+ context "response parsing" do
111
+ it "returns a Gibbon::Response with the parsed body and headers" do
112
+ stub_request(:get, "#{@api_root}/lists")
113
+ .with(basic_auth: @basic_auth_credentials)
114
+ .to_return(status: 200, body: '{"lists": [{"id": "abc123"}]}', headers: { 'Content-Type' => 'application/json' })
115
+
116
+ response = @gibbon.lists.retrieve
117
+ expect(response).to be_a Gibbon::Response
118
+ expect(response.body).to eq({ "lists" => [{ "id" => "abc123" }] })
119
+ expect(response.headers["content-type"]).to eq "application/json"
120
+ end
121
+
122
+ it "symbolizes response keys when symbolize_keys is true" do
123
+ stub_request(:get, "#{@api_root}/lists")
124
+ .with(basic_auth: @basic_auth_credentials)
125
+ .to_return(status: 200, body: '{"lists": [{"id": "abc123"}]}')
126
+
127
+ response = Gibbon::Request.new(api_key: api_key, symbolize_keys: true).lists.retrieve
128
+ expect(response.body).to eq({ lists: [{ id: "abc123" }] })
129
+ end
130
+
131
+ it "returns nil for an empty response body" do
132
+ stub_request(:get, "#{@api_root}/lists")
133
+ .with(basic_auth: @basic_auth_credentials)
134
+ .to_return(status: 200, body: "")
135
+
136
+ expect(@gibbon.lists.retrieve).to be_nil
137
+ end
138
+
139
+ it "raises a MailChimpError with details intact for an unparseable success response" do
140
+ stub_request(:get, "#{@api_root}/lists")
141
+ .with(basic_auth: @basic_auth_credentials)
142
+ .to_return(status: 200, body: "not json")
143
+
144
+ begin
145
+ @gibbon.lists.retrieve
146
+ fail "expected a Gibbon::MailChimpError"
147
+ rescue Gibbon::MailChimpError => e
148
+ expect(e.title).to eq "UNPARSEABLE_RESPONSE"
149
+ expect(e.status_code).to eq 500
150
+ end
151
+ end
152
+ end
153
+
154
+ context "configure_request" do
155
+ it "sets params, headers, body, and timeouts on the request" do
156
+ gibbon = Gibbon::Request.new(api_key: api_key, timeout: 45, open_timeout: 15)
157
+ api_request = Gibbon::APIRequest.new(builder: gibbon.lists)
158
+ request = Struct.new(:params, :headers, :body, :options).new({}, {}, nil, Faraday::RequestOptions.new)
159
+
160
+ api_request.send(:configure_request, request: request, params: { "count" => "10" }, headers: { "X-Custom" => "value" }, body: '{"a":1}')
161
+
162
+ expect(request.params).to eq({ "count" => "10" })
163
+ expect(request.headers).to eq({ "Content-Type" => "application/json", "X-Custom" => "value" })
164
+ expect(request.body).to eq '{"a":1}'
165
+ expect(request.options.timeout).to eq 45
166
+ expect(request.options.open_timeout).to eq 15
167
+ end
168
+ end
169
+
170
+ context "rest_client configuration" do
171
+ it "includes the logger middleware when debug is enabled" do
172
+ gibbon = Gibbon::Request.new(api_key: api_key, debug: true)
173
+ client = Gibbon::APIRequest.new(builder: gibbon.lists).send(:rest_client)
174
+ expect(client.builder.handlers).to include(Faraday::Response::Logger)
175
+ end
176
+
177
+ it "omits the logger middleware by default" do
178
+ client = Gibbon::APIRequest.new(builder: @gibbon.lists).send(:rest_client)
179
+ expect(client.builder.handlers).not_to include(Faraday::Response::Logger)
180
+ end
181
+
182
+ it "configures the proxy" do
183
+ gibbon = Gibbon::Request.new(api_key: api_key, proxy: "http://proxy.example.com:8080")
184
+ client = Gibbon::APIRequest.new(builder: gibbon.lists).send(:rest_client)
185
+ expect(client.proxy.host).to eq "proxy.example.com"
186
+ expect(client.proxy.port).to eq 8080
187
+ end
188
+ end
189
+
190
+ context "rest_client ssl configuration" do
191
+ it "requires TLS 1.2 as a minimum without capping the TLS version" do
192
+ api_request = Gibbon::APIRequest.new(builder: @gibbon.lists)
193
+ client = api_request.send(:rest_client)
194
+ expect(client.ssl.min_version).to eq :TLS1_2
195
+ expect(client.ssl.max_version).to be_nil
196
+ end
197
+ end
69
198
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gibbon::Helpers do
4
+ let(:helper) { Class.new { include Gibbon::Helpers }.new }
5
+
6
+ describe "#get_data_center_from_api_key" do
7
+ it "returns an empty string for a nil api key" do
8
+ expect(helper.get_data_center_from_api_key(nil)).to eq("")
9
+ end
10
+
11
+ it "returns an empty string for an api key without a data center" do
12
+ expect(helper.get_data_center_from_api_key("1234")).to eq("")
13
+ end
14
+
15
+ it "returns the data center with a trailing dot" do
16
+ expect(helper.get_data_center_from_api_key("1234-us1")).to eq("us1.")
17
+ end
18
+
19
+ it "uses the last segment of a key containing multiple hyphens" do
20
+ expect(helper.get_data_center_from_api_key("12-34-us10")).to eq("us10.")
21
+ end
22
+
23
+ it "strips non-alphanumeric characters to prevent domain injection" do
24
+ expect(helper.get_data_center_from_api_key("1234-attacker.net/path?")).to eq("attackernetpath.")
25
+ end
26
+ end
27
+ end
@@ -46,4 +46,22 @@ describe Gibbon::MailChimpError do
46
46
  it 'sets the status_code attribute' do
47
47
  expect(@gibbon.status_code).to eq(params[:status_code])
48
48
  end
49
+
50
+ context "without params" do
51
+ before do
52
+ @gibbon = Gibbon::MailChimpError.new(message)
53
+ end
54
+
55
+ it "leaves the attributes nil" do
56
+ expect(@gibbon.title).to be_nil
57
+ expect(@gibbon.detail).to be_nil
58
+ expect(@gibbon.body).to be_nil
59
+ expect(@gibbon.raw_body).to be_nil
60
+ expect(@gibbon.status_code).to be_nil
61
+ end
62
+
63
+ it "still includes the attributes in the message" do
64
+ expect(@gibbon.message).to eq "Foo @title=nil, @detail=nil, @body=nil, @raw_body=nil, @status_code=nil"
65
+ end
66
+ end
49
67
  end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+ require 'webmock/rspec'
3
+
4
+ describe Gibbon::Request do
5
+ let(:api_key) { "1234-us1" }
6
+ let(:api_root) { "https://us1.api.mailchimp.com/3.0" }
7
+ let(:basic_auth_credentials) { ['apikey', api_key] }
8
+
9
+ before do
10
+ @gibbon = Gibbon::Request.new(api_key: api_key)
11
+ end
12
+
13
+ describe "path building" do
14
+ it "builds a path from chained calls" do
15
+ @gibbon.lists("list123").members
16
+ expect(@gibbon.path).to eq("lists/list123/members")
17
+ end
18
+
19
+ it "replaces underscores with hyphens" do
20
+ @gibbon.lists("list123").interest_categories
21
+ expect(@gibbon.path).to eq("lists/list123/interest-categories")
22
+ end
23
+
24
+ it "downcases path parts" do
25
+ @gibbon.LISTS
26
+ expect(@gibbon.path).to eq("lists")
27
+ end
28
+
29
+ it "responds to any method" do
30
+ expect(@gibbon.respond_to?(:anything_at_all)).to be true
31
+ end
32
+
33
+ it "appends 'send' to the path when called without arguments" do
34
+ @gibbon.campaigns("abc").actions.send
35
+ expect(@gibbon.path).to eq("campaigns/abc/actions/send")
36
+ end
37
+
38
+ it "dispatches send with arguments as a regular method call" do
39
+ expect(@gibbon.send(:api_key)).to eq(api_key)
40
+ end
41
+
42
+ it "resets the path after a request" do
43
+ stub_request(:get, "#{api_root}/lists").with(basic_auth: basic_auth_credentials).to_return(status: 200)
44
+ @gibbon.lists.retrieve
45
+ expect(@gibbon.path).to eq("")
46
+ end
47
+
48
+ it "resets the path when a request raises" do
49
+ stub_request(:get, "#{api_root}/lists").with(basic_auth: basic_auth_credentials).to_return(status: 500, body: "{}")
50
+ expect { @gibbon.lists.retrieve }.to raise_error(Gibbon::MailChimpError)
51
+ expect(@gibbon.path).to eq("")
52
+ end
53
+ end
54
+
55
+ describe "HTTP verb mapping" do
56
+ let(:body) { { name: "Foo" } }
57
+
58
+ it "maps create to POST" do
59
+ stub = stub_request(:post, "#{api_root}/lists")
60
+ .with(body: MultiJson.dump(body), basic_auth: basic_auth_credentials, headers: { 'Content-Type' => 'application/json' })
61
+ .to_return(status: 200)
62
+ @gibbon.lists.create(body: body)
63
+ expect(stub).to have_been_requested
64
+ end
65
+
66
+ it "maps update to PATCH" do
67
+ stub = stub_request(:patch, "#{api_root}/lists/list123")
68
+ .with(body: MultiJson.dump(body), basic_auth: basic_auth_credentials, headers: { 'Content-Type' => 'application/json' })
69
+ .to_return(status: 200)
70
+ @gibbon.lists("list123").update(body: body)
71
+ expect(stub).to have_been_requested
72
+ end
73
+
74
+ it "maps upsert to PUT" do
75
+ stub = stub_request(:put, "#{api_root}/lists/list123")
76
+ .with(body: MultiJson.dump(body), basic_auth: basic_auth_credentials, headers: { 'Content-Type' => 'application/json' })
77
+ .to_return(status: 200)
78
+ @gibbon.lists("list123").upsert(body: body)
79
+ expect(stub).to have_been_requested
80
+ end
81
+
82
+ it "maps retrieve to GET" do
83
+ stub = stub_request(:get, "#{api_root}/lists").with(basic_auth: basic_auth_credentials).to_return(status: 200)
84
+ @gibbon.lists.retrieve
85
+ expect(stub).to have_been_requested
86
+ end
87
+
88
+ it "maps get to GET" do
89
+ stub = stub_request(:get, "#{api_root}/lists").with(basic_auth: basic_auth_credentials).to_return(status: 200)
90
+ @gibbon.lists.get
91
+ expect(stub).to have_been_requested
92
+ end
93
+
94
+ it "maps delete to DELETE" do
95
+ stub = stub_request(:delete, "#{api_root}/lists/list123").with(basic_auth: basic_auth_credentials).to_return(status: 204)
96
+ @gibbon.lists("list123").delete
97
+ expect(stub).to have_been_requested
98
+ end
99
+ end
100
+
101
+ describe "request options" do
102
+ it "passes query params" do
103
+ stub = stub_request(:get, "#{api_root}/lists")
104
+ .with(query: { "count" => "10", "offset" => "20" }, basic_auth: basic_auth_credentials)
105
+ .to_return(status: 200)
106
+ @gibbon.lists.retrieve(params: { "count" => "10", "offset" => "20" })
107
+ expect(stub).to have_been_requested
108
+ end
109
+
110
+ it "passes custom headers" do
111
+ stub = stub_request(:get, "#{api_root}/lists")
112
+ .with(headers: { 'X-Custom' => 'custom-value' }, basic_auth: basic_auth_credentials)
113
+ .to_return(status: 200)
114
+ @gibbon.lists.retrieve(headers: { 'X-Custom' => 'custom-value' })
115
+ expect(stub).to have_been_requested
116
+ end
117
+ end
118
+
119
+ describe "api key handling" do
120
+ it "strips whitespace from the api key" do
121
+ gibbon = Gibbon::Request.new(api_key: " #{api_key} ")
122
+ expect(gibbon.api_key).to eq(api_key)
123
+ end
124
+ end
125
+
126
+ describe "class-level requests" do
127
+ before do
128
+ Gibbon::Request.api_key = api_key
129
+ end
130
+
131
+ after do
132
+ Gibbon::Request.api_key = nil
133
+ end
134
+
135
+ it "makes requests directly on the class" do
136
+ stub = stub_request(:get, "#{api_root}/lists").with(basic_auth: basic_auth_credentials).to_return(status: 200)
137
+ Gibbon::Request.lists.retrieve
138
+ expect(stub).to have_been_requested
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gibbon::Response do
4
+ it "defaults body and headers to empty hashes" do
5
+ response = Gibbon::Response.new
6
+ expect(response.body).to eq({})
7
+ expect(response.headers).to eq({})
8
+ end
9
+
10
+ it "exposes the body and headers it is given" do
11
+ body = { "id" => "abc" }
12
+ headers = { "content-type" => "application/json" }
13
+ response = Gibbon::Response.new(body: body, headers: headers)
14
+ expect(response.body).to eq(body)
15
+ expect(response.headers).to eq(headers)
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gibbon
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.4
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amro Mousa
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-02-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -56,16 +55,16 @@ dependencies:
56
55
  name: rspec
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - '='
58
+ - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: 3.5.0
60
+ version: '3.13'
62
61
  type: :development
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
- - - '='
65
+ - - "~>"
67
66
  - !ruby/object:Gem::Version
68
- version: 3.5.0
67
+ version: '3.13'
69
68
  - !ruby/object:Gem::Dependency
70
69
  name: webmock
71
70
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +79,7 @@ dependencies:
80
79
  - - "~>"
81
80
  - !ruby/object:Gem::Version
82
81
  version: '3.8'
83
- description: A wrapper for MailChimp API 3.0 and Export API
82
+ description: A wrapper for MailChimp API 3.0
84
83
  email:
85
84
  - amromousa@gmail.com
86
85
  executables: []
@@ -88,6 +87,7 @@ extensions: []
88
87
  extra_rdoc_files: []
89
88
  files:
90
89
  - ".document"
90
+ - ".github/FUNDING.yml"
91
91
  - ".gitignore"
92
92
  - ".travis.yml"
93
93
  - CHANGELOG.md
@@ -99,7 +99,6 @@ files:
99
99
  - gibbon.gemspec
100
100
  - lib/gibbon.rb
101
101
  - lib/gibbon/api_request.rb
102
- - lib/gibbon/export.rb
103
102
  - lib/gibbon/gibbon_error.rb
104
103
  - lib/gibbon/gibbon_helpers.rb
105
104
  - lib/gibbon/mailchimp_error.rb
@@ -107,16 +106,17 @@ files:
107
106
  - lib/gibbon/response.rb
108
107
  - lib/gibbon/version.rb
109
108
  - spec/gibbon/api_request_spec.rb
110
- - spec/gibbon/export_spec.rb
109
+ - spec/gibbon/gibbon_helpers_spec.rb
111
110
  - spec/gibbon/gibbon_spec.rb
112
111
  - spec/gibbon/mailchimp_error_spec.rb
112
+ - spec/gibbon/request_spec.rb
113
+ - spec/gibbon/response_spec.rb
113
114
  - spec/gibbon/upsert_spec.rb
114
115
  - spec/spec_helper.rb
115
- homepage: http://github.com/amro/gibbon
116
+ homepage: https://github.com/amro/gibbon
116
117
  licenses:
117
118
  - MIT
118
119
  metadata: {}
119
- post_install_message:
120
120
  rdoc_options: []
121
121
  require_paths:
122
122
  - lib
@@ -124,21 +124,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
124
  requirements:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
- version: 2.4.0
127
+ version: 3.1.0
128
128
  required_rubygems_version: !ruby/object:Gem::Requirement
129
129
  requirements:
130
130
  - - ">="
131
131
  - !ruby/object:Gem::Version
132
132
  version: '0'
133
133
  requirements: []
134
- rubygems_version: 3.2.27
135
- signing_key:
134
+ rubygems_version: 4.0.16
136
135
  specification_version: 4
137
- summary: A wrapper for MailChimp API 3.0 and Export API
138
- test_files:
139
- - spec/gibbon/api_request_spec.rb
140
- - spec/gibbon/export_spec.rb
141
- - spec/gibbon/gibbon_spec.rb
142
- - spec/gibbon/mailchimp_error_spec.rb
143
- - spec/gibbon/upsert_spec.rb
144
- - spec/spec_helper.rb
136
+ summary: A wrapper for MailChimp API 3.0
137
+ test_files: []
data/lib/gibbon/export.rb DELETED
@@ -1,93 +0,0 @@
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 || ENV['MAILCHIMP_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
@@ -1,39 +0,0 @@
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 an API key from the 'MAILCHIMP_API_KEY' ENV variable" do
21
- ENV['MAILCHIMP_API_KEY'] = 'TESTKEY-us1'
22
- @export = Gibbon::Export.new
23
- expect(@export.api_key).to eq('TESTKEY-us1')
24
- ENV.delete('MAILCHIMP_API_KEY')
25
- end
26
-
27
- it "sets correct endpoint from api key" do
28
- @api_key = "TESTKEY-us1"
29
- @export.api_key = @api_key
30
- expect(@export.export_api_url).to eq("https://us1.api.mailchimp.com/export/1.0/")
31
- end
32
-
33
- it "sets correct timeout" do
34
- @api_key = "TESTKEY-us1"
35
- @export.api_key = @api_key
36
- @export.timeout = 9
37
- expect(@export.timeout).to eq(9)
38
- end
39
- end