magicbell 0.2.0 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b556f6bac34d0deac121d7b456b3322f918e3f77b4e82f0cdcbd8cef395d32d1
4
- data.tar.gz: 6a56c2b7331c5f3296293f8d2a4bbef8b785bdb762e694b506cab3ac28f21b79
3
+ metadata.gz: 8478515db437819ee9ff7f478fecfdb8b5899647609d8f57332181af74402bfb
4
+ data.tar.gz: 75ef0e7dcb952f02b2cd0dea409df4d51ef7392877089a5352bec6940c68ec75
5
5
  SHA512:
6
- metadata.gz: a6139d8789a33f4f5cb45216106d86d7f78c9b1fb9743af3115d4ff9f193035a6c3565ebca3195a130529f81d7110ee8fecbbf5790004642b13c4c56887ba53c
7
- data.tar.gz: 5f2f592ea6a3f13500a084a468118e2bc6ad68a7d0e48a5a087e728b052fa20bd1b6e17a09faa37fe5ddaf5b55f5b4975a231f330a98421de5fdb0f806937367
6
+ metadata.gz: 6e47dd2b39572412bb3c25e9d29b588004df2abcf2e189f383b524f350bd6f14ad9a8a227543a74f9297192533fb18b37863bd37c3d4d1939d57251de38c9b4b
7
+ data.tar.gz: ae556dac4b1adcf1310bc4821b06007d8080b21d7212da62b011b82dd94792994eeb201e991850c353366a6df44427b9fcd722dd3671b722f56cc310142fe691
data/README.md CHANGED
@@ -1,94 +1,161 @@
1
- # magicbell-rails
1
+ # magicbell-ruby
2
2
 
3
- Convert your email notifications to an in-app notification center. This gem makes it easy to add [MagicBell's](https://magicbell.io/) notification center widget to your rails app.
3
+ [MagicBell](https://magicbell.io) is an embeddable Notification Inbox for web applications.
4
4
 
5
- <img width="415" alt="magicbell notification center widget" src="https://user-images.githubusercontent.com/1789832/28327736-f3503f44-6c01-11e7-9a72-c15023db18c6.png">
5
+ This gem
6
+
7
+ 1. Makes it easy to interact with [MagicBell's REST API](https://developer.magicbell.io/reference) from Ruby.
8
+
9
+ You can use it to create a notification in your MagicBell project etc.
10
+ 3. Can BCC your ActionMailer email notifications to MagicBell if you rather not use MagicBell's API in your Rails application.
11
+
12
+ MagicBell will create an in-app notification from any email notification that's blind copied to it.
13
+ 3. Helps you calculate the HMAC for a user's email when you turn on HMAC Authentication for your MagicBell project
14
+
15
+ <img width="415" alt="MagicBell Notification Inbox" src="https://user-images.githubusercontent.com/1789832/28327736-f3503f44-6c01-11e7-9a72-c15023db18c6.png">
6
16
 
7
17
  ## Installation
8
18
 
9
- Add the magicbell gem to your app's Gemfile
19
+ Sign up at magicbell.io and obtain your MagicBell project's API Key and API Secret from the "Settings" section in your MagicBell dashboard.
20
+
21
+ Add the gem to your app's Gemfile
10
22
 
11
23
  ```ruby
12
24
  gem "magicbell"
13
25
  ```
14
26
 
15
- Run
27
+ and install the gem
16
28
 
17
29
  ```
18
30
  bundle install
19
31
  ```
20
32
 
21
- Create the initializer file `config/initializers/magicbell.rb` and add your MagicBell credentials there.
33
+ The gem will automatically pick your MagicBell project's API Key and API Secret from the `MAGICBELL_API_KEY` and `MAGICBELL_API_SECRET` environment variables. Alternatively, provide the API Key and API Secret in an initializer
22
34
 
23
35
  ```
24
36
  vim config/initializers/magicbell.rb
25
37
  ```
26
38
 
27
39
  ```ruby
40
+ # config/initializers/magicbell.rb
41
+
28
42
  MagicBell.configure do |config|
29
43
  config.api_key = "your_magicbell_api_key"
30
44
  config.api_secret = "your_magicbell_api_secret"
31
- config.project_id = 1 # your magicbell project id
32
- config.magic_address = "your_magicell_magic_address@ring.magicbell.io"
33
45
  end
34
46
  ```
35
47
 
36
- If you haven't signed up for MagicBell yet and don't have credentials, visit https://magicbell.io/, sign in with your google account and create a Project.
37
-
38
- Add MagicBell's icon to your app's interface. Our customers usually add MagicBell's icon to their app's navigation bar.
39
-
40
- ```html
41
- <div id="magicbell_notifications" style="display:inline-block;">
42
- <i class="icon-magicbell"></i>
43
- </div>
44
- ```
45
-
46
- Create the partial file `config/layouts/_magicbell.html.erb` and copy paste the code below
47
-
48
- ```erb
49
- <!-- MagicBell notification center widget -->
50
- <script>
51
- $('<link/>', {
52
- rel: 'stylesheet',
53
- type: 'text/css',
54
- href: "<%= MagicBell::EXTRAS_CSS_URL %>"
55
- }).appendTo('head');
56
- $(document).ready(function () {
57
- // Initialize the widget after fetching its javascript
58
- $.getScript("<%= MagicBell::WIDGET_JAVASCRIPT_URL %>", initializeMagicBell);
59
- });
60
- function initializeMagicBell() {
61
- MagicBell.initialize({
62
- target: document.getElementById('magicbell_notifications'),
63
- projectId: "<%= MagicBell.project_id %>",
64
- apiKey: "<%= MagicBell.api_key %>",
65
- userEmail: "<%= current_user.email %>",
66
- userKey: "<%= MagicBell.user_key(current_user.email) %>"
67
- });
68
- }
69
- </script>
48
+ ## API Wrapper
49
+
50
+ This gem makes it easy to interact with MagicBell's REST API https://developer.magicbell.io/reference from Ruby
51
+
52
+ ### Create a notification
53
+
54
+ Send a notification to one or many users
55
+
56
+ ```ruby
57
+ magicbell = MagicBell::Client.new
58
+ magicbell.create_notification(
59
+ "title" => "Rob assigned a task to you",
60
+ "recipients" => [
61
+ {
62
+ email: "joe@example.com"
63
+ },
64
+ ]
65
+ )
66
+ ```
67
+
68
+ You can even provide content for the notification and a URL to redirect the user to when they click on the notification the MagicBell Notification Inbox
69
+
70
+ ```ruby
71
+ magicbell = MagicBell::Client.new
72
+ magicbell.create_notification(
73
+ "title" => "Rob assigned to a task to you",
74
+ "recipients" => [
75
+ {
76
+ email: "joe@example.com"
77
+ },
78
+ ],
79
+ "content": "Hey Joe, can give this customer a demo of our app?",
80
+ "action_url" => "https://yourwebsite.com/task_path"
81
+ )
82
+ ```
83
+
84
+ ### Fetch a user's notifications
85
+
86
+ Fetch a user's notifications
87
+
88
+ ```ruby
89
+ magicbell = MagicBell::Client.new
90
+ user = magicbell.user_with_email("joe@example.com")
91
+ user_notifications = user.notifications
92
+ user_notifications.each { |user_notification| puts user_notification.attribute("title") }
93
+ ```
94
+
95
+ Please note that the example above fetches the user's 15 most recent notification (the default number of notifications per page) If you'd like to fetch subsequent pages, use
96
+
97
+ ```ruby
98
+ magicbell = MagicBell::Client.new
99
+ user = magicbell.user_with_email("joe@example.com")
100
+ user_notifications = user.notifications
101
+ user_notifications.each_page do |page|
102
+ page.each do |user_notification|
103
+ puts user_notification.attribute("title")
104
+ end
105
+ end
70
106
  ```
71
107
 
72
- Render the `_magicbell.html.erb` partial in your app's layout. Say, your app's layout file is `config/layouts/app.html.erb`, render the partial at the bottom. Here's an example
108
+ ### Mark a user notification as read/unread
73
109
 
74
- ```erb
75
- <html>
76
- <body>
77
- <p>This is your app's layout</p>
78
- </body>
110
+ ```ruby
111
+ magicbell = MagicBell::Client.new
112
+ user = magicbell.user_with_email("joe@example.com")
113
+ user_notification = user.notifications.first
114
+ user_notification.mark_as_read
115
+ user_notification.mark_as_unread
116
+ ```
117
+
118
+ ### Mark all notifications of a user as read/seen
79
119
 
80
- <%= render layouts/magicbell %>
81
- </html>
120
+ ```ruby
121
+ magicbell = MagicBell::Client.new
122
+ user = magicbell.user_with_email("joe@example.com")
123
+ user.mark_all_notifications_as_read
124
+ user.mark_all_notifications_as_seen
82
125
  ```
83
126
 
84
- Now, call the `ring_the_magicbell` method in your notification mailers. Here's an example
127
+ ### Error handling
128
+
129
+ Please note that the gem raises a `MagicBell::Client::HTTPError` if an API returns a non 2xx response
130
+
131
+ ```ruby
132
+ begin
133
+ magicbell = MagicBell::Client.new
134
+ magicbell.create_notification(
135
+ "title" => "Rob assigned to a task to you"
136
+ )
137
+ rescue MagicBell::Client::HTTPError => e
138
+ # Report the error to your error tracker
139
+ error_context = {
140
+ response_status: e.response_status,
141
+ response_headers: e.response_headers,
142
+ response_body: e.response_body
143
+ }
144
+ ErrorReporter.report(e, context: error_context)
145
+ end
146
+ ```
147
+
148
+ ## Rails integration
149
+
150
+ If you've existing ActionMailer email notifications in your Rails app and prefer to not spend time migrating to MagicBell's API, you can blind copy your ActionMailer email notifications to MagicBell. MagicBell will create in-app notifications from email notifications blind copied to it.
151
+
152
+ Call the `ring_the_magicbell` method in your action mailers like in the example below
85
153
 
86
154
  ```ruby
87
155
  class NotificationMailer < ActionMailer::Base
88
- # This method will bcc your email notifications to your magicbell magic address
156
+ # The ring_the_magicbell method will bcc your email notifications to your MagicBell project's BCC email address
89
157
  #
90
- # Upon receiving the bcc'ed email notifications, magicbell.io will automatically
91
- # create in-app notications for users
158
+ # Upon receiving a blind copied email notification, magicbell.io will automatically create an in-app notification for the user
92
159
  ring_the_magicbell
93
160
 
94
161
  # This is an email notification in your app
@@ -103,35 +170,29 @@ class NotificationMailer < ActionMailer::Base
103
170
  end
104
171
  ```
105
172
 
106
- Deploy your app.
107
-
108
- That's it! All your users now benefit from having in-app notifications.
109
-
110
- If you've trouble adding MagicBell to your app or find yourself stuck, please don't hestitate to reach out to us at hana@magicbell.io We usually respond within 24 hours (often much lesser).
111
-
112
- ## Advanced Features
113
-
114
- #### ActionUrl
115
-
116
- When a user clicks on a notification in MagicBell's widget, the widget redirects the user to the first URL the body of the email notification. We call this URL the `ActionUrl`.
173
+ The gem will automatically pick your MagicBell project's BCC email from the `MAGICBELL_BCC_EMAIL` environment variable. Alternatively, provide your MagicBell project's BCC email address in an initializer
117
174
 
118
- If you wish to redirect users to a different URL instead, set a custom `ActionUrl` in your mailers
175
+ ```
176
+ vim config/initializers/magicbell.rb
177
+ ```
119
178
 
120
179
  ```ruby
121
- class NotificationMailer < ActionMailer::Base
122
- ring_the_magicbell
180
+ # config/initializers/magicbell.rb
123
181
 
124
- def new_comment(comment)
125
- # ...
126
- magicbell_notification_action_url("https://myapp.com/comments/#{comment.id}")
127
- # ...
128
- end
182
+ MagicBell.configure do |config|
183
+ config.api_key = "your_magicbell_api_key"
184
+ config.api_secret = "your_magicbell_api_secret"
185
+ config.bcc_email = "your_magicbell_bcc_email@ring.magicbell.io"
129
186
  end
130
187
  ```
131
188
 
132
- #### Title
189
+ The BCC email address is available in the "Settings" section in your MagicBell dashboard.
190
+
191
+ ### Customize Action URL
133
192
 
134
- We use the subject of the email notification as a notification's title. If this behaviour isn't sutiable for your app, you can set a custom title in your mailers
193
+ When a user clicks on a notification in MagicBell's Notification Inbox, the Notification Inbox redirects the user to the first URL the body of the email notification. This URL is called the `action_url`.
194
+
195
+ If you wish to redirect users to a different URL instead, provide an `action_url` in your mailers
135
196
 
136
197
  ```ruby
137
198
  class NotificationMailer < ActionMailer::Base
@@ -139,15 +200,15 @@ class NotificationMailer < ActionMailer::Base
139
200
 
140
201
  def new_comment(comment)
141
202
  # ...
142
- magicbell_notification_title("Richard commented on your post Drive to Lake Tahoe")
203
+ magicbell_notification_action_url("https://myapp.com/comments/#{comment.id}")
143
204
  # ...
144
205
  end
145
206
  end
146
207
  ```
147
208
 
148
- #### Metadata
209
+ ### Customize Notification Title
149
210
 
150
- Its possible to attach custom metadata to every notification. Say you wish to attach a comment's id to a new comment notification, here's how it can be done
211
+ The Notification inbox will use the subject of the email notification as a notification's title. If this behaviour isn't sutiable for your app, provide a title in your mailers
151
212
 
152
213
  ```ruby
153
214
  class NotificationMailer < ActionMailer::Base
@@ -155,63 +216,24 @@ class NotificationMailer < ActionMailer::Base
155
216
 
156
217
  def new_comment(comment)
157
218
  # ...
158
- magicbell_notification_metadata(comment_id: comment.id)
219
+ magicbell_notification_title("Richard posted a new comment")
159
220
  # ...
160
221
  end
161
222
  end
162
223
  ```
163
224
 
164
- You can later use this metadata to customize the behaviour of MagicBell's widget.
165
-
166
- #### Customize widget behaviour
167
-
168
- When a user clicks on a notification in MagicBell's widget, the widget redirects the user to the notification's `ActionUrl`. If this behaviour isn't suitable for your app (if your app is a Single Page Application for example), you can customize it.
225
+ ## HMAC Authentication
169
226
 
170
- When initializing the widget, pass a `onNotificationClick` callback to customize the widget's behaviour
227
+ ### Calculate HMAC
171
228
 
172
- ```javascript
173
- function initializeMagicBell() {
174
- MagicBell.initialize({
175
- target: document.getElementById('magicbell_notifications'),
176
- projectId: "<%= MagicBell.project_id %>",
177
- apiKey: "<%= MagicBell.api_key %>",
178
- userEmail: <%= current_user.email %>,
179
- userKey: "<%= MagicBell.user_key(current_user.email) %>",
180
- onNotificationClick: function (notification) {
181
- // openComment is a function that you've defined in your app's javascript to open
182
- // and display a specific comment to the user
183
- openComment(notification.meta_data.comment_id)
184
- }
185
- });
186
- }
187
229
  ```
188
-
189
-
190
- ## Managing notifications
191
-
192
- ### Creating a notification
193
-
194
- You can create notifications for users. Once `MagicBell` is configured, you can
195
- call `create_notification` method to create a notification for a given project.
196
-
197
- ```
198
- params = {
199
- to: "user@example.com",
200
- title: "Your download is ready",
201
- content: "Zip file to download is here",
202
- action_url: "https://example.com/notifications/1"
203
- }
204
-
205
- client = MagicBell::Client.new()
206
- client.create_notification(params)
230
+ user_email = "joe@example.com"
231
+ hmac = MagicBell.hmac(user_email)
207
232
  ```
208
233
 
209
- The response will be a Faraday response object, and methods like `status` and
210
- `body` will be available for further inspection.
211
-
234
+ See https://developer.magicbell.io/docs/turn-on-hmac-authentication for more information on turning on HMAC Authentication for your MagicBell Project
212
235
 
213
- If you'd like us to add more callbacks to the widget, reach out to us at hana@magicbell.io
214
236
 
215
- ## Documentation
237
+ ## Developer Hub
216
238
 
217
- Visit our [Docs Site](https://docs.magicbell.io) for more information on MagicBell, MagicBell's widget and Advanced Features.
239
+ Please visit our [Developer Hub](https://developer.magicbell.io) for documentation on MagicBell's API and MagicBell's embeddable Notification Inbox
data/Rakefile CHANGED
@@ -25,3 +25,15 @@ Rake::TestTask.new(:test) do |t|
25
25
  end
26
26
 
27
27
  task default: :test
28
+
29
+ task :publish_to_rubygems do
30
+ `gem build magicbell.gemspec`
31
+ require_relative "lib/magicbell"
32
+ `gem push magicbell-#{MagicBell::VERSION}.gem`
33
+ end
34
+
35
+ task :console do
36
+ require_relative "lib/magicbell"
37
+ require "pry"
38
+ binding.pry
39
+ end
@@ -1,11 +1,27 @@
1
+ require 'forwardable'
2
+
3
+ require "openssl"
4
+ require "base64"
5
+
1
6
  require "magicbell/config"
2
- require "magicbell/hmac"
3
- require "magicbell/user"
4
- require "magicbell/railtie" if defined?(Rails)
5
- require "magicbell/client"
6
7
 
7
- require 'forwardable'
8
+ require "httparty"
9
+ require "magicbell/api_operations"
10
+ require "magicbell/client"
11
+ require "magicbell/api_resource"
12
+ require "magicbell/singleton_api_resource"
13
+ require "magicbell/api_resource_collection"
14
+ require "magicbell/api_resources/notification"
15
+ require "magicbell/api_resources/user"
16
+ require "magicbell/api_resources/user_notification"
17
+ require "magicbell/api_resources/user_notifications"
18
+ require "magicbell/api_resources/user_notification_read"
19
+ require "magicbell/api_resources/user_notification_unread"
20
+ require "magicbell/api_resources/user_notifications_read"
21
+ require "magicbell/api_resources/user_notifications_seen"
22
+ require "magicbell/api_resources/user_notification_preferences"
8
23
 
24
+ require "magicbell/railtie" if defined?(Rails)
9
25
 
10
26
  module MagicBell
11
27
  WIDGET_JAVASCRIPT_URL = "https://assets.magicbell.io/widget.magicbell.js"
@@ -14,7 +30,10 @@ module MagicBell
14
30
  class << self
15
31
  extend Forwardable
16
32
 
17
- def_delegators :@config, :api_key, :api_secret, :project_id, :magic_address, :api_host
33
+ def_delegators :config, :api_key,
34
+ :api_secret,
35
+ :bcc_email,
36
+ :api_host
18
37
 
19
38
  def configure
20
39
  yield(config)
@@ -25,19 +44,28 @@ module MagicBell
25
44
  end
26
45
 
27
46
  def reset_config
28
- @config = nil
47
+ @config = Config.new
29
48
  end
30
49
 
31
- def project_specific_headers
50
+ def authentication_headers
32
51
  {
33
- 'X-MAGICBELL-API-KEY' => config.api_key,
34
- 'X-MAGICBELL-API-SECRET' => config.api_secret
52
+ "X-MAGICBELL-API-KEY" => api_key,
53
+ "X-MAGICBELL-API-SECRET" => api_secret
35
54
  }
36
55
  end
37
56
 
38
57
  # Calculate HMAC for user's email
39
- def user_key(user_email)
40
- MagicBell::HMAC.calculate(user_email, MagicBell.api_secret)
58
+ def hmac(message)
59
+ digest = sha256_digest
60
+ secret = api_secret
61
+
62
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, message)).strip
63
+ end
64
+
65
+ private
66
+
67
+ def sha256_digest
68
+ OpenSSL::Digest::Digest.new('sha256')
41
69
  end
42
70
  end
43
71
  end
@@ -8,7 +8,7 @@ module MagicBell
8
8
 
9
9
  module ClassMethods
10
10
  def ring_the_magicbell
11
- default bcc: MagicBell.magic_address
11
+ default bcc: MagicBell.bcc_email
12
12
  end
13
13
  end
14
14
 
@@ -0,0 +1,44 @@
1
+ module MagicBell
2
+ module ApiOperations
3
+ def get(url, options = {})
4
+ response = HTTParty.get(
5
+ url,
6
+ options.merge(headers: authentication_headers)
7
+ )
8
+ raise_http_error_unless_2xx_response(response)
9
+
10
+ response
11
+ end
12
+
13
+ def post(url, options = {})
14
+ response = HTTParty.post(
15
+ url,
16
+ options.merge(headers: authentication_headers)
17
+ )
18
+ raise_http_error_unless_2xx_response(response)
19
+
20
+ response
21
+ end
22
+
23
+ def put(url, options = {})
24
+ response = HTTParty.put(
25
+ url,
26
+ options.merge(headers: authentication_headers)
27
+ )
28
+ raise_http_error_unless_2xx_response(response)
29
+
30
+ response
31
+ end
32
+
33
+ private
34
+
35
+ def raise_http_error_unless_2xx_response(response)
36
+ return if response.success?
37
+ e = MagicBell::Client::HTTPError.new
38
+ e.response_status = response.code
39
+ e.response_headers = response.headers.to_h
40
+ e.response_body = response.body
41
+ raise e
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,119 @@
1
+ require "active_support/inflector"
2
+ require "json"
3
+
4
+ module MagicBell
5
+ class ApiResource
6
+ class << self
7
+ def create(client, attributes = {})
8
+ new(client, attributes).create
9
+ end
10
+
11
+ def name
12
+ to_s.demodulize.underscore
13
+ end
14
+
15
+ def create_path
16
+ "/#{name}s"
17
+ end
18
+
19
+ def create_url
20
+ MagicBell.api_host + create_path
21
+ end
22
+ end
23
+
24
+ attr_reader :id
25
+
26
+ def initialize(client, attributes = {})
27
+ @client = client
28
+ @attributes = attributes
29
+
30
+ @id = @attributes["id"]
31
+ @retrieved = true if @id
32
+ end
33
+
34
+ def attributes
35
+ retrieve_unless_retrieved
36
+ @attributes
37
+ end
38
+ alias_method :to_h, :attributes
39
+
40
+ def attribute(attribute_name)
41
+ attributes[attribute_name]
42
+ end
43
+
44
+ def retrieve
45
+ response = @client.get(url)
46
+ parse_response(response)
47
+
48
+ self
49
+ end
50
+
51
+ def name
52
+ self.class.name
53
+ end
54
+
55
+ def create_url
56
+ MagicBell.api_host + create_path
57
+ end
58
+
59
+ def create_path
60
+ "/#{name}s"
61
+ end
62
+
63
+ def url
64
+ MagicBell.api_host + path
65
+ end
66
+
67
+ def path
68
+ "/#{name}s/#{id}"
69
+ end
70
+
71
+ def create
72
+ response = @client.post(
73
+ create_url,
74
+ body: { name => attributes }.to_json,
75
+ headers: authentication_headers
76
+ )
77
+ parse_response(response)
78
+
79
+ self
80
+ end
81
+
82
+ def update(new_attributes = {})
83
+ response = @client.put(
84
+ url,
85
+ body: new_attributes.to_json,
86
+ headers: authentication_headers
87
+ )
88
+ parse_response(response)
89
+
90
+ self
91
+ end
92
+
93
+ protected
94
+
95
+ def authentication_headers
96
+ MagicBell.authentication_headers
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :response,
102
+ :response_hash
103
+
104
+ def retrieve_unless_retrieved
105
+ return unless id # Never retrieve a new unsaved resource
106
+ return if @retrieved
107
+ retrieve
108
+ end
109
+
110
+ def parse_response(response)
111
+ @response = response
112
+ unless response.body.empty?
113
+ @response_hash = JSON.parse(@response.body)
114
+ @attributes = @response_hash[name]
115
+ end
116
+ @retrieved = true
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,89 @@
1
+ module MagicBell
2
+ class ApiResourceCollection
3
+ def initialize(client, query_params = {})
4
+ @client = client
5
+ @query_params = query_params
6
+ @retrieved = false
7
+ end
8
+
9
+ # @todo Add examples
10
+ def retrieve
11
+ @response = @client.get(
12
+ url,
13
+ query: @query_params
14
+ )
15
+ @response_hash = JSON.parse(response.body)
16
+ @resources = response_hash[name].map { |resource_attributes| resource_class.new(@client, resource_attributes) }
17
+ @retrieved = true
18
+
19
+ self
20
+ end
21
+
22
+ def to_a
23
+ resources
24
+ end
25
+
26
+ def first
27
+ resources.first
28
+ end
29
+
30
+ def url
31
+ MagicBell.api_host + path
32
+ end
33
+
34
+ def authentication_headers
35
+ MagicBell.authentication_headers
36
+ end
37
+
38
+ def each(&block)
39
+ resources.each(&block)
40
+ end
41
+
42
+ def each_page
43
+ current_page = self
44
+ loop do
45
+ yield(current_page)
46
+ break if current_page.last_page?
47
+ current_page = current_page.next_page
48
+ end
49
+ end
50
+
51
+ def last_page?
52
+ current_page == total_pages
53
+ end
54
+
55
+ def next_page
56
+ self.class.new(@client, page: current_page + 1, per_page: per_page)
57
+ end
58
+
59
+ def current_page
60
+ retrieve_unless_retrieved
61
+ response_hash["current_page"]
62
+ end
63
+
64
+ def total_pages
65
+ retrieve_unless_retrieved
66
+ response_hash["total_pages"]
67
+ end
68
+
69
+ def per_page
70
+ retrieve_unless_retrieved
71
+ response_hash["per_page"]
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :response,
77
+ :response_hash
78
+
79
+ def resources
80
+ retrieve_unless_retrieved
81
+ return @resources
82
+ end
83
+
84
+ def retrieve_unless_retrieved
85
+ return if @retrieved
86
+ retrieve
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,4 @@
1
+ module MagicBell
2
+ class Notification < ApiResource
3
+ end
4
+ end
@@ -0,0 +1,45 @@
1
+ module MagicBell
2
+ class User < ApiResource
3
+ include ApiOperations
4
+
5
+ attr_reader :email
6
+
7
+ def initialize(client, attributes)
8
+ @client = client
9
+ @email = attributes["email"]
10
+ super(client, attributes)
11
+ end
12
+
13
+ def notifications(query_params = {})
14
+ client = self
15
+ MagicBell::UserNotifications.new(client, query_params)
16
+ end
17
+
18
+ def mark_all_notifications_as_read
19
+ client = self
20
+ MagicBell::UserNotificationsRead.new(client).create
21
+ end
22
+
23
+ def mark_all_notifications_as_seen
24
+ client = self
25
+ MagicBell::UserNotificationsSeen.new(client).create
26
+ end
27
+
28
+ def notification_preferences
29
+ client = self
30
+ MagicBell::UserNotificationPreferences.new(client)
31
+ end
32
+
33
+ def path
34
+ if id
35
+ self.class.path + "/#{id}"
36
+ elsif email
37
+ self.class.path + "/email:#{email}"
38
+ end
39
+ end
40
+
41
+ def authentication_headers
42
+ MagicBell.authentication_headers.merge("X-MAGICBELL-USER-EMAIL" => email)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ module MagicBell
2
+ class UserNotification < ApiResource
3
+ class << self
4
+ def path
5
+ "/notifications"
6
+ end
7
+ end
8
+
9
+ def path
10
+ "/notifications/#{id}"
11
+ end
12
+
13
+ def mark_as_read
14
+ UserNotificationRead.new(@client, "user_notification" => self).create
15
+ end
16
+
17
+ def mark_as_unread
18
+ UserNotificationUnread.new(@client, "user_notification" => self).create
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module MagicBell
2
+ class UserNotificationPreferences < ApiResource
3
+ def name
4
+ "notification_preferences"
5
+ end
6
+
7
+ def path
8
+ "/notification_preferences"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module MagicBell
2
+ class UserNotificationRead < SingletonApiResource
3
+ attr_reader :user_notification
4
+
5
+ def initialize(client, attributes)
6
+ @user_notification = attributes.delete("user_notification")
7
+ super(client, attributes)
8
+ end
9
+
10
+ def path
11
+ user_notification.path + "/read"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module MagicBell
2
+ class UserNotificationRead < SingletonApiResource
3
+ attr_reader :user_notification
4
+
5
+ def initialize(client, attributes)
6
+ @user_notification = attributes.delete("user_notification")
7
+ super(client, attributes)
8
+ end
9
+
10
+ def path
11
+ user_notification.path + "/unread"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module MagicBell
2
+ class UserNotifications < ApiResourceCollection
3
+ def name
4
+ "notifications"
5
+ end
6
+
7
+ def path
8
+ "/notifications"
9
+ end
10
+
11
+ def resource_class
12
+ MagicBell::UserNotification
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module MagicBell
2
+ class UserNotificationsRead < SingletonApiResource
3
+ def path
4
+ "/notifications/read"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module MagicBell
2
+ class UserNotificationsSeen < SingletonApiResource
3
+ def path
4
+ "/notifications/seen"
5
+ end
6
+ end
7
+ end
@@ -1,13 +1,28 @@
1
- require "magicbell/client/notifications"
2
-
3
1
  module MagicBell
4
2
  class Client
5
- include MagicBell::Client::Notifications
3
+ class HTTPError < StandardError
4
+ attr_accessor :response_status,
5
+ :response_headers,
6
+ :response_body
7
+ end
8
+
9
+ include ApiOperations
10
+
11
+ def create_notification(notification_attributes)
12
+ MagicBell::Notification.create(self, notification_attributes)
13
+ end
14
+
15
+ # def user(user_id)
16
+ # MagicBell::User.find(user_id)
17
+ # end
18
+
19
+ def user_with_email(email)
20
+ client = self
21
+ MagicBell::User.new(client, "email" => email)
22
+ end
6
23
 
7
- # Returns an authenticated connection object which can be
8
- # used to make requests
9
- def connection
10
- @connection ||= Faraday.new(url: MagicBell.api_host)
24
+ def authentication_headers
25
+ MagicBell.authentication_headers
11
26
  end
12
27
  end
13
28
  end
@@ -1,13 +1,28 @@
1
1
  module MagicBell
2
2
  class Config
3
- attr_accessor :api_key
4
- attr_accessor :api_secret
5
- attr_accessor :project_id
6
- attr_accessor :magic_address
7
- attr_accessor :api_host
3
+ attr_writer :api_key
4
+ attr_writer :api_secret
5
+ attr_writer :bcc_email
6
+ attr_writer :api_host
8
7
 
9
8
  def initialize
10
9
  @api_host = "https://api.magicbell.io"
11
10
  end
11
+
12
+ def api_key
13
+ @api_key || ENV["MAGICBELL_API_KEY"]
14
+ end
15
+
16
+ def api_secret
17
+ @api_secret || ENV["MAGICBELL_API_SECRET"]
18
+ end
19
+
20
+ def bcc_email
21
+ @bcc_email || ENV["MAGICBELL_BCC_EMAIL"]
22
+ end
23
+
24
+ def api_host
25
+ @api_host || ENV["MAGICBELL_API_HOST"] || "https://api.magicbell.io"
26
+ end
12
27
  end
13
28
  end
@@ -0,0 +1,7 @@
1
+ module MagicBell
2
+ class SingletonApiResource < ApiResource
3
+ def create_path
4
+ path
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module MagicBell
2
- VERSION = '0.2.0'
2
+ VERSION = '1.0.0.beta2'
3
3
  end
metadata CHANGED
@@ -1,18 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magicbell
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hana Mohan
8
8
  - Nisanth Chunduru
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-24 00:00:00.000000000 Z
12
+ date: 2020-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: activesupport
15
+ name: httparty
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
@@ -26,7 +26,7 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
- name: faraday
29
+ name: activesupport
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
@@ -40,7 +40,7 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
- name: rails
43
+ name: actionmailer
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
@@ -67,6 +67,48 @@ dependencies:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '3.9'
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pry
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rake
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
70
112
  description: Notifications like never before!
71
113
  email:
72
114
  - hana@magicbell.io
@@ -80,18 +122,28 @@ files:
80
122
  - Rakefile
81
123
  - lib/magicbell.rb
82
124
  - lib/magicbell/action_mailer_extension.rb
125
+ - lib/magicbell/api_operations.rb
126
+ - lib/magicbell/api_resource.rb
127
+ - lib/magicbell/api_resource_collection.rb
128
+ - lib/magicbell/api_resources/notification.rb
129
+ - lib/magicbell/api_resources/user.rb
130
+ - lib/magicbell/api_resources/user_notification.rb
131
+ - lib/magicbell/api_resources/user_notification_preferences.rb
132
+ - lib/magicbell/api_resources/user_notification_read.rb
133
+ - lib/magicbell/api_resources/user_notification_unread.rb
134
+ - lib/magicbell/api_resources/user_notifications.rb
135
+ - lib/magicbell/api_resources/user_notifications_read.rb
136
+ - lib/magicbell/api_resources/user_notifications_seen.rb
83
137
  - lib/magicbell/client.rb
84
- - lib/magicbell/client/notifications.rb
85
138
  - lib/magicbell/config.rb
86
- - lib/magicbell/hmac.rb
87
139
  - lib/magicbell/railtie.rb
88
- - lib/magicbell/user.rb
140
+ - lib/magicbell/singleton_api_resource.rb
89
141
  - lib/magicbell/version.rb
90
142
  homepage: https://magicbell.io
91
143
  licenses:
92
144
  - MIT
93
145
  metadata: {}
94
- post_install_message:
146
+ post_install_message:
95
147
  rdoc_options: []
96
148
  require_paths:
97
149
  - lib
@@ -102,12 +154,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
154
  version: '0'
103
155
  required_rubygems_version: !ruby/object:Gem::Requirement
104
156
  requirements:
105
- - - ">="
157
+ - - ">"
106
158
  - !ruby/object:Gem::Version
107
- version: '0'
159
+ version: 1.3.1
108
160
  requirements: []
109
- rubygems_version: 3.1.2
110
- signing_key:
161
+ rubygems_version: 3.1.4
162
+ signing_key:
111
163
  specification_version: 4
112
164
  summary: Ruby wrapper for MagicBell.io
113
165
  test_files: []
@@ -1,20 +0,0 @@
1
- module MagicBell
2
- class Client
3
- module Notifications
4
- # Creates a notification for a given project
5
- #
6
- # @param to [String] Email of a user for this notification
7
- # @param title [String] Title of the notification
8
- # @param content [String] Content of the notification
9
- # @param action_url [String] Url to redirect to from widget
10
- def create_notification(to, title, content, action_url)
11
- params = {to: to, title: title, content: content, action_url: action_url}
12
-
13
- connection.post("/notifications.json") do |req|
14
- req.headers = {"X-MAGICBELL-API-SECRET" => MagicBell.api_secret}
15
- req.body = {notification: params}
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,14 +0,0 @@
1
- require "openssl"
2
- require "base64"
3
-
4
- module MagicBell
5
- module HMAC
6
- class << self
7
- def calculate(message)
8
- secret = MagicBell.api_secret
9
- digest = OpenSSL::Digest::Digest.new('sha256')
10
- Base64.encode64(OpenSSL::HMAC.digest(digest, secret, message)).strip
11
- end
12
- end
13
- end
14
- end
@@ -1,41 +0,0 @@
1
- require 'faraday'
2
- require 'json'
3
-
4
- module MagicBell
5
- class User
6
-
7
- attr_accessor :email
8
- attr_accessor :preferences
9
-
10
- def initialize(attributes)
11
- @attributes = attributes
12
- @email = @attributes[:email]
13
- @conn = Faraday.new({
14
- url: MagicBell.api_host,
15
- headers: MagicBell.project_specific_headers.merge(
16
- 'X-MAGICBELL-USER-EMAIL' => @email
17
- )
18
- })
19
- end
20
-
21
- def notifications
22
- response = @conn.get "/notifications.json"
23
- JSON.parse(response.body)["notifications"]
24
- end
25
-
26
- def hmac_signature
27
- MagicBell::HMAC.calculate @email
28
- end
29
-
30
- def notification_preferences
31
- response = @conn.get "/notification_preferences.json"
32
- JSON.parse(response.body)["notification_preferences"]
33
- end
34
-
35
- def notification_preferences=(preferences)
36
- @conn.put "/notification_preferences.json",
37
- {notification_preferences: preferences}
38
- end
39
-
40
- end
41
- end