sendgrid-api 0.0.1 → 0.0.2

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.
Files changed (45) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +6 -3
  5. data/Gemfile.lock +35 -1
  6. data/README.md +48 -4
  7. data/Rakefile +3 -1
  8. data/lib/sendgrid/api.rb +3 -2
  9. data/lib/sendgrid/api/client.rb +27 -0
  10. data/lib/sendgrid/api/entities/entity.rb +83 -0
  11. data/lib/sendgrid/api/entities/profile.rb +14 -0
  12. data/lib/sendgrid/api/entities/response.rb +21 -0
  13. data/lib/sendgrid/api/entities/stats.rb +14 -0
  14. data/lib/sendgrid/api/rest/errors/error.rb +60 -0
  15. data/lib/sendgrid/api/rest/resource.rb +56 -0
  16. data/lib/sendgrid/api/rest/response/parse_error.rb +19 -0
  17. data/lib/sendgrid/api/rest/response/parse_json.rb +18 -0
  18. data/lib/sendgrid/api/service.rb +23 -0
  19. data/lib/sendgrid/api/version.rb +2 -2
  20. data/lib/sendgrid/api/web/profile.rb +38 -0
  21. data/lib/sendgrid/api/web/stats.rb +36 -0
  22. data/sendgrid-api.gemspec +4 -1
  23. data/spec/fixtures/forbidden.json +3 -0
  24. data/spec/fixtures/profile.json +18 -0
  25. data/spec/fixtures/stats.json +50 -0
  26. data/spec/fixtures/success.json +3 -0
  27. data/spec/fixtures/unauthorized.json +6 -0
  28. data/spec/sendgrid/api/client_spec.rb +22 -0
  29. data/spec/sendgrid/api/entities/entity_spec.rb +279 -0
  30. data/spec/sendgrid/api/entities/profile_spec.rb +26 -0
  31. data/spec/sendgrid/api/entities/response_spec.rb +28 -0
  32. data/spec/sendgrid/api/entities/stats_spec.rb +25 -0
  33. data/spec/sendgrid/api/rest/errors/error_spec.rb +97 -0
  34. data/spec/sendgrid/api/rest/resource_spec.rb +143 -0
  35. data/spec/sendgrid/api/rest/response/parse_error_spec.rb +39 -0
  36. data/spec/sendgrid/api/rest/response/parse_json_spec.rb +45 -0
  37. data/spec/sendgrid/api/service_spec.rb +44 -0
  38. data/spec/sendgrid/api/version_spec.rb +2 -2
  39. data/spec/sendgrid/api/web/profile_spec.rb +126 -0
  40. data/spec/sendgrid/api/web/stats_spec.rb +100 -0
  41. data/spec/spec_helper.rb +21 -0
  42. data/spec/support/helpers.rb +15 -0
  43. data/spec/support/mock.rb +26 -0
  44. data/spec/support/shared_examples.rb +11 -0
  45. metadata +78 -9
@@ -0,0 +1,19 @@
1
+ require 'faraday'
2
+ require 'sendgrid/api/rest/errors/error'
3
+
4
+ module Sendgrid
5
+ module API
6
+ module REST
7
+ module Response
8
+ class ParseError < Faraday::Response::Middleware
9
+
10
+ def on_complete(env)
11
+ error = REST::Errors::Error.from_response(env)
12
+ raise error unless error.nil?
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Sendgrid
5
+ module API
6
+ module REST
7
+ module Response
8
+ class ParseJson < Faraday::Response::Middleware
9
+
10
+ def parse(body)
11
+ JSON.parse(body, :symbolize_names => true) if body
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ module Sendgrid
2
+ module API
3
+ class Service
4
+
5
+ attr_reader :resource
6
+
7
+ def initialize(resource)
8
+ @resource = resource
9
+ end
10
+
11
+ def perform_request(entity, url, params = {})
12
+ entity.from_response(request(url, params))
13
+ end
14
+
15
+ private
16
+
17
+ def request(url, params = {})
18
+ resource.post(url, params)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Sendgrid
2
- module Api
3
- VERSION = "0.0.1"
2
+ module API
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -0,0 +1,38 @@
1
+ require 'sendgrid/api/service'
2
+ require 'sendgrid/api/entities/profile'
3
+ require 'sendgrid/api/entities/response'
4
+
5
+ module Sendgrid
6
+ module API
7
+ module Web
8
+ module Profile
9
+
10
+ def profile
11
+ Services.new(resource)
12
+ end
13
+
14
+ class Services < Sendgrid::API::Service
15
+
16
+ # View your SendGrid profile
17
+ #
18
+ # @see http://sendgrid.com/docs/API_Reference/Web_API/profile.html
19
+ # @return profile [Profile] An Entities::Profile object.
20
+ def get
21
+ perform_request(Entities::Profile, 'profile.get.json').first
22
+ end
23
+
24
+ # Update your SendGrid profile
25
+ #
26
+ # @see http://sendgrid.com/docs/API_Reference/Web_API/profile.html#-set
27
+ # @param profile [Profile] An Entities::Profile object.
28
+ # @return response [Response] An Entities::Response object.
29
+ def set(profile)
30
+ perform_request(Entities::Response, 'profile.set.json', profile.as_json)
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ require 'sendgrid/api/service'
2
+ require 'sendgrid/api/entities/stats'
3
+
4
+ module Sendgrid
5
+ module API
6
+ module Web
7
+ module Stats
8
+
9
+ def stats
10
+ Services.new(resource)
11
+ end
12
+
13
+ class Services < Sendgrid::API::Service
14
+
15
+ # Get Advanced Statistics
16
+ #
17
+ # @see http://sendgrid.com/docs/API_Reference/Web_API/Statistics/statistics_advanced.html
18
+ # @param options [Hash] A customizable set of options.
19
+ # @option options [String] :data_type One of the following: browsers, clients, devices, geo, global, isps. Required.
20
+ # @option options [Date] :start_date Date format is based on aggregated_by value (default is yyyy-mm-dd). Required.
21
+ # @option options [Date] :end_date Date format is based on aggregated_by value (default is yyyy-mm-dd).
22
+ # @option options [String] :metric One of the following (default is all): open, click, unique_open, unique_click, processed, delivered, drop, bounce, deferred, spamreport, blocked, all.
23
+ # @option options [String] :category Return stats for the given category.
24
+ # @option options [String] :aggregated_by Aggregate the data by the given period (default is day): day, week or month.
25
+ # @option options [String] :country Get stats for each region/state for the given country. Only US (United States) and CA (Canada) is supported at this time.
26
+ # @return stats [Stats] An array of Entities::Stats object.
27
+ def advanced(params = {})
28
+ perform_request(Entities::Stats, 'stats.getAdvanced.json', params)
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -5,7 +5,7 @@ require 'sendgrid/api/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "sendgrid-api"
8
- spec.version = Sendgrid::Api::VERSION
8
+ spec.version = Sendgrid::API::VERSION
9
9
  spec.authors = ["Renato Neves"]
10
10
  spec.email = ["renatosnrg@gmail.com"]
11
11
  spec.description = %q{A Ruby interface to the SendGrid API}
@@ -17,4 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'faraday', '~> 0.8.8'
22
+ spec.add_dependency 'json', '~> 1.8.0'
20
23
  end
@@ -0,0 +1,3 @@
1
+ {
2
+ "error":"Bad username / password"
3
+ }
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "username":"sendgrid",
4
+ "email":"contact@sendgrid.com",
5
+ "active":"true",
6
+ "first_name":"Jim",
7
+ "last_name":"Franklin",
8
+ "address":"1065 N Pacificenter Drive, Suite 425",
9
+ "address2":"",
10
+ "city":"Anaheim",
11
+ "state":"CA",
12
+ "zip":"92806",
13
+ "country":"US",
14
+ "phone":"123456789",
15
+ "website":"http:\/\/www.sendgrid.com",
16
+ "website_access":"true"
17
+ }
18
+ ]
@@ -0,0 +1,50 @@
1
+ [
2
+ {
3
+ "delivered":4792,
4
+ "unique_open":308,
5
+ "spamreport":3,
6
+ "unique_click":11,
7
+ "drop":57,
8
+ "request":5359,
9
+ "bounce":622,
10
+ "deferred":1975,
11
+ "processed":5302,
12
+ "date":"2013-06-18",
13
+ "open":481,
14
+ "click":11,
15
+ "blocked":29
16
+ },
17
+ {
18
+ "delivered":3,
19
+ "unique_open":17,
20
+ "unique_click":1,
21
+ "deferred":405,
22
+ "date":"2013-06-19",
23
+ "open":37,
24
+ "click":1
25
+ },
26
+ {
27
+ "date":"2013-06-20",
28
+ "deferred":381,
29
+ "unique_open":4,
30
+ "open":7,
31
+ "bounce":2
32
+ },
33
+ {
34
+ "unique_open":2,
35
+ "bounce":1,
36
+ "deferred":113,
37
+ "date":"2013-06-21",
38
+ "open":8,
39
+ "blocked":31
40
+ },
41
+ {
42
+ "date":"2013-06-23",
43
+ "unique_open":2,
44
+ "open":2
45
+ },
46
+ {
47
+ "date":"2013-06-24",
48
+ "open":1
49
+ }
50
+ ]
@@ -0,0 +1,3 @@
1
+ {
2
+ "message":"success"
3
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "error":{
3
+ "code":401,
4
+ "message":"Permission denied, wrong credentials"
5
+ }
6
+ }
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ module Sendgrid
4
+ module API
5
+ describe Client do
6
+
7
+ subject { described_class.new(user, key) }
8
+ let(:user) { 'some user' }
9
+ let(:key) { 'some key' }
10
+
11
+ its(:user) { should == user }
12
+ its(:key) { should == key }
13
+
14
+ it { should respond_to(:profile) }
15
+ it { should respond_to(:stats) }
16
+
17
+ its(:profile) { should_not be_nil }
18
+ its(:stats) { should_not be_nil }
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,279 @@
1
+ require 'spec_helper'
2
+
3
+ module Sendgrid
4
+ module API
5
+ module Entities
6
+ describe Entity do
7
+
8
+ subject { described_class }
9
+
10
+ after do
11
+ described_class.clear_attributes
12
+ end
13
+
14
+ describe '.clear_attributes' do
15
+ before do
16
+ subject.attribute :attr1
17
+ end
18
+
19
+ it 'should clear the attributes' do
20
+ subject.clear_attributes
21
+ subject.attributes.should be_empty
22
+ end
23
+ end
24
+
25
+ describe '.attribute' do
26
+ context 'when the attributes are empty' do
27
+ before do
28
+ subject.clear_attributes
29
+ end
30
+
31
+ it 'should set the entity attributes' do
32
+ subject.attribute :attr1, :attr2
33
+ subject.attributes.should have(2).items
34
+ end
35
+ end
36
+
37
+ context 'when the attributes are not empty' do
38
+ before do
39
+ subject.clear_attributes
40
+ subject.attribute :attr1
41
+ end
42
+
43
+ it 'should add attributes to the entity' do
44
+ subject.attribute :attr2
45
+ subject.attributes.should have(2).items
46
+ end
47
+ end
48
+
49
+ context 'when the attributes are repeated' do
50
+ before do
51
+ subject.clear_attributes
52
+ subject.attribute :attr1
53
+ end
54
+
55
+ it 'should add distinct attributes to the entity' do
56
+ subject.attribute :attr1, :attr2
57
+ subject.attributes.should have(2).items
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#attributes' do
63
+ before do
64
+ subject.clear_attributes
65
+ end
66
+
67
+ context 'without attributes' do
68
+ its(:attributes) { should be_empty }
69
+ end
70
+
71
+ context 'with attributes' do
72
+ before do
73
+ subject.attribute :attr1, :attr2
74
+ end
75
+
76
+ its(:attributes) { should have(2).items }
77
+ end
78
+ end
79
+
80
+ describe '.from_response' do
81
+ context 'when response is an array' do
82
+ before do
83
+ response.should_receive(:body).and_return([item1, item2])
84
+ end
85
+ let(:response) { double('response') }
86
+ let(:item1) { double('item').as_null_object }
87
+ let(:item2) { double('item').as_null_object }
88
+
89
+ subject { described_class.from_response(response) }
90
+
91
+ it { should have(2).items }
92
+ its([0]) { should be_instance_of(described_class) }
93
+ its([1]) { should be_instance_of(described_class) }
94
+ end
95
+
96
+ context 'when response is a hash' do
97
+ before do
98
+ response.should_receive(:body).and_return(item)
99
+ end
100
+ let(:response) { double('response') }
101
+ let(:item) { Hash.new }
102
+
103
+ subject { described_class.from_response(response) }
104
+
105
+ it { should be_instance_of(described_class) }
106
+ end
107
+
108
+ context 'when response is a string' do
109
+ before do
110
+ response.should_receive(:body).and_return(item)
111
+ end
112
+ let(:response) { double('response') }
113
+ let(:item) { 'some string' }
114
+
115
+ subject { described_class.from_response(response) }
116
+
117
+ it { should be_nil }
118
+ end
119
+
120
+ context 'when response is a number' do
121
+ before do
122
+ response.should_receive(:body).and_return(item)
123
+ end
124
+ let(:response) { double('response') }
125
+ let(:item) { 45 }
126
+
127
+ subject { described_class.from_response(response) }
128
+
129
+ it { should be_nil }
130
+ end
131
+
132
+ context 'when response is a nil object' do
133
+ before do
134
+ response.should_receive(:body).and_return(item)
135
+ end
136
+ let(:response) { double('response') }
137
+ let(:item) { nil }
138
+
139
+ subject { described_class.from_response(response) }
140
+
141
+ it { should be_nil }
142
+ end
143
+ end
144
+
145
+ describe '.new' do
146
+ before do
147
+ described_class.attribute :attr1, :attr2
148
+ end
149
+
150
+ context 'creating entity with no attributes' do
151
+ subject { entity }
152
+ let(:entity) { described_class.new }
153
+
154
+ it { should respond_to(:attr1) }
155
+ it { should respond_to(:attr2) }
156
+
157
+ its(:attr1) { should be_nil }
158
+ its(:attr2) { should be_nil }
159
+
160
+ it 'should raise error for invalid attributes' do
161
+ expect { subject.attr3 }.to raise_error(NameError)
162
+ end
163
+
164
+ it 'should set attr1' do
165
+ subject.attr1 = 'attr1 value'
166
+ subject.attr1.should == 'attr1 value'
167
+ end
168
+
169
+ it 'should set attr2' do
170
+ subject.attr1 = 'attr2 value'
171
+ subject.attr1.should == 'attr2 value'
172
+ end
173
+
174
+ describe '#attributes' do
175
+ subject { entity.attributes }
176
+
177
+ it { should be_empty }
178
+ end
179
+
180
+ describe '#as_json' do
181
+ subject { entity.as_json }
182
+
183
+ it { should be_empty }
184
+ end
185
+
186
+ describe '#to_json' do
187
+ subject { JSON.parse(entity.to_json) }
188
+
189
+ it { should be_empty }
190
+ end
191
+ end
192
+
193
+ context 'creating entity with valid attributes' do
194
+ subject { entity }
195
+ let(:entity) { described_class.new(:attr1 => attr1, :attr2 => attr2) }
196
+ let(:attr1) { 'attr1 value' }
197
+ let(:attr2) { 'attr2 value' }
198
+
199
+ it { should respond_to(:attr1) }
200
+ it { should respond_to(:attr2) }
201
+
202
+ its(:attr1) { should == attr1 }
203
+ its(:attr2) { should == attr2 }
204
+
205
+ describe '#attributes' do
206
+ subject { entity.attributes }
207
+
208
+ it { should have(2).items }
209
+ it { should include(:attr1) }
210
+ it { should include(:attr2) }
211
+ it { should_not include(:attr3) }
212
+ end
213
+
214
+ describe '#as_json' do
215
+ subject { entity.as_json }
216
+
217
+ it { should have(2).items }
218
+ it { should include(:attr1) }
219
+ it { should include(:attr2) }
220
+ it { should_not include(:attr3) }
221
+ end
222
+
223
+ describe '#to_json' do
224
+ subject { JSON.parse(entity.to_json) }
225
+
226
+ it { should have(2).items }
227
+ it { should include('attr1') }
228
+ it { should include('attr2') }
229
+ it { should_not include('attr3') }
230
+ end
231
+ end
232
+
233
+ context 'creating entity with invalid attributes' do
234
+ subject { entity }
235
+ let(:entity) { described_class.new(:attr1 => attr1, :attr2 => attr2, :attr3 => attr3) }
236
+ let(:attr1) { 'attr1 value' }
237
+ let(:attr2) { 'attr2 value' }
238
+ let(:attr3) { 'attr3 value' }
239
+
240
+ its(:attr1) { should == attr1 }
241
+ its(:attr2) { should == attr2 }
242
+
243
+ it 'should raise error for invalid attributes' do
244
+ expect { subject.attr3 }.to raise_error(NameError)
245
+ end
246
+
247
+ describe '#attributes' do
248
+ subject { entity.attributes }
249
+
250
+ it { should have(2).items }
251
+ it { should include(:attr1) }
252
+ it { should include(:attr2) }
253
+ it { should_not include(:attr3) }
254
+ end
255
+
256
+ describe '#as_json' do
257
+ subject { entity.as_json }
258
+
259
+ it { should have(2).items }
260
+ it { should include(:attr1) }
261
+ it { should include(:attr2) }
262
+ it { should_not include(:attr3) }
263
+ end
264
+
265
+ describe '#to_json' do
266
+ subject { JSON.parse(entity.to_json) }
267
+
268
+ it { should have(2).items }
269
+ it { should include('attr1') }
270
+ it { should include('attr2') }
271
+ it { should_not include('attr3') }
272
+ end
273
+ end
274
+ end
275
+
276
+ end
277
+ end
278
+ end
279
+ end