magicbell 0.2.0 → 1.0.3

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: 68c41d47dfcb956fd6ce283573619aafe27c5dc8ecae3db01807ae442afce912
4
+ data.tar.gz: 6dc6769e62c3b105a675854ccb0ea683f01b425a5d3fc1836fd962f1e370b9fc
5
5
  SHA512:
6
- metadata.gz: a6139d8789a33f4f5cb45216106d86d7f78c9b1fb9743af3115d4ff9f193035a6c3565ebca3195a130529f81d7110ee8fecbbf5790004642b13c4c56887ba53c
7
- data.tar.gz: 5f2f592ea6a3f13500a084a468118e2bc6ad68a7d0e48a5a087e728b052fa20bd1b6e17a09faa37fe5ddaf5b55f5b4975a231f330a98421de5fdb0f806937367
6
+ metadata.gz: 24b81b3179c672118ac6550a80c8e28bfdb73c293b9f96a1c8ca88e7836c36157eed6ab8fa5d7850199674cdc81770ab237dc5218c28d637f281c787e04b093f
7
+ data.tar.gz: edb2e6dcdc5e5827b55d4a75067c386a6278dddf52d70ea614657a47a7abcbcccef47e3255ef0918a8af4bbd053f888861a420989db248b5e37ee3d94083f784
data/README.md CHANGED
@@ -1,137 +1,213 @@
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 & mobile 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
+ Please familiarlize yourself with the [core concepts of MagicBell](https://developer.magicbell.io/docs/core-concepts) before using this gem.
6
+
7
+ This gem
8
+
9
+ 1. Makes it easy to interact with [MagicBell's REST API](https://developer.magicbell.io/reference) from Ruby.
10
+
11
+ You can use it to create a notification in your MagicBell project etc.
12
+
13
+ 2. Can BCC your ActionMailer email notifications to MagicBell if you rather not use MagicBell's API in your Rails application.
14
+
15
+ MagicBell will create an in-app notification from any email notification that's blind copied to it.
16
+
17
+ 3. Helps you calculate the HMAC for a user's email or external_id when you turn on [HMAC Authentication](https://developer.magicbell.io/docs/turn-on-hmac-authentication) for your MagicBell project
18
+
19
+ <img width="415" alt="MagicBell Notification Inbox" src="https://files.readme.io/c09b21a-image1.png">
6
20
 
7
21
  ## Installation
8
22
 
9
- Add the magicbell gem to your app's Gemfile
23
+ Sign up at magicbell.io and obtain your MagicBell project's API Key and API Secret from the "Settings" section in your MagicBell dashboard.
24
+
25
+ Add the gem to your app's Gemfile
10
26
 
11
27
  ```ruby
12
28
  gem "magicbell"
13
29
  ```
14
30
 
15
- Run
31
+ and install the gem
16
32
 
17
33
  ```
18
34
  bundle install
19
35
  ```
20
36
 
21
- Create the initializer file `config/initializers/magicbell.rb` and add your MagicBell credentials there.
37
+ 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
38
 
23
39
  ```
24
40
  vim config/initializers/magicbell.rb
25
41
  ```
26
42
 
27
43
  ```ruby
44
+ # config/initializers/magicbell.rb
45
+
28
46
  MagicBell.configure do |config|
29
47
  config.api_key = "your_magicbell_api_key"
30
48
  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
49
  end
34
50
  ```
35
51
 
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>
52
+ ## API Wrapper
53
+
54
+ This gem makes it easy to interact with MagicBell's REST API https://developer.magicbell.io/reference from Ruby
55
+
56
+ ### Create a notification
57
+
58
+ Send a notification to one or many users by identifying them with their email address
59
+
60
+ ```ruby
61
+ magicbell = MagicBell::Client.new
62
+ magicbell.create_notification(
63
+ "title" => "Rob assigned a task to you",
64
+ "recipients" => [
65
+ {
66
+ email: "joe@example.com"
67
+ },
68
+ ]
69
+ )
70
70
  ```
71
71
 
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
72
+ You can also identify users with their `external_id`, which is their ID in your database. That way, if their email address changes, they'd still have access to their notifications. You'll need to make sure you identify them with their `externalID` [in your frontend](https://developer.magicbell.io/docs/browser-js#identifying-users).
73
73
 
74
- ```erb
75
- <html>
76
- <body>
77
- <p>This is your app's layout</p>
78
- </body>
74
+ ```ruby
75
+ magicbell.create_notification(
76
+ title: "Welcome to Muziboo",
77
+ recipients: [{
78
+ external_id: "id_in_your_database"
79
+ }]
80
+ )
81
+ ```
82
+
83
+ 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
79
84
 
80
- <%= render layouts/magicbell %>
81
- </html>
85
+ ```ruby
86
+ magicbell = MagicBell::Client.new
87
+ magicbell.create_notification(
88
+ "title" => "Rob assigned to a task to you",
89
+ "recipients" => [
90
+ {
91
+ email: "joe@example.com"
92
+ },
93
+ ],
94
+ "content": "Hey Joe, can give this customer a demo of our app?",
95
+ "action_url" => "https://yourwebsite.com/task_path"
96
+ )
82
97
  ```
83
98
 
84
- Now, call the `ring_the_magicbell` method in your notification mailers. Here's an example
99
+ ### Fetch a user's notifications
100
+
101
+ Fetch a user's notifications
85
102
 
86
103
  ```ruby
87
- class NotificationMailer < ActionMailer::Base
88
- # This method will bcc your email notifications to your magicbell magic address
89
- #
90
- # Upon receiving the bcc'ed email notifications, magicbell.io will automatically
91
- # create in-app notications for users
92
- ring_the_magicbell
104
+ magicbell = MagicBell::Client.new
105
+ user = magicbell.user_with_email("joe@example.com")
106
+ user_notifications = user.notifications
107
+ user_notifications.each { |user_notification| puts user_notification.attribute("title") }
108
+ ```
93
109
 
94
- # This is an email notification in your app
95
- def new_comment
96
- # ...
97
- end
98
-
99
- # This is another email notification in your app
100
- def mentioned
101
- # ...
110
+ 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
111
+
112
+ ```ruby
113
+ magicbell = MagicBell::Client.new
114
+ user = magicbell.user_with_email("joe@example.com")
115
+ user_notifications = user.notifications
116
+ user_notifications.each_page do |page|
117
+ page.each do |user_notification|
118
+ puts user_notification.attribute("title")
102
119
  end
103
120
  end
104
121
  ```
105
122
 
106
- Deploy your app.
123
+ ### Mark a user notification as read/unread
124
+
125
+ ```ruby
126
+ magicbell = MagicBell::Client.new
127
+ user = magicbell.user_with_email("joe@example.com")
128
+ user_notification = user.notifications.first
129
+ user_notification.mark_as_read
130
+ user_notification.mark_as_unread
131
+ ```
132
+
133
+ ### Mark all notifications of a user as read/seen
134
+
135
+ ```ruby
136
+ magicbell = MagicBell::Client.new
137
+ user = magicbell.user_with_email("joe@example.com")
138
+ user.mark_all_notifications_as_read
139
+ user.mark_all_notifications_as_seen
140
+ ```
107
141
 
108
- That's it! All your users now benefit from having in-app notifications.
142
+ ### Error handling
109
143
 
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).
144
+ Please note that the gem raises a `MagicBell::Client::HTTPError` if an API returns a non 2xx response
111
145
 
112
- ## Advanced Features
146
+ ```ruby
147
+ begin
148
+ magicbell = MagicBell::Client.new
149
+ magicbell.create_notification(
150
+ "title" => "Rob assigned to a task to you"
151
+ )
152
+ rescue MagicBell::Client::HTTPError => e
153
+ # Report the error to your error tracker
154
+ error_context = {
155
+ response_status: e.response_status,
156
+ response_headers: e.response_headers,
157
+ response_body: e.response_body
158
+ }
159
+ ErrorReporter.report(e, context: error_context)
160
+ end
161
+ ```
113
162
 
114
- #### ActionUrl
163
+ ## Rails integration
115
164
 
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`.
165
+ 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.
117
166
 
118
- If you wish to redirect users to a different URL instead, set a custom `ActionUrl` in your mailers
167
+ Call the `ring_the_magicbell` method in your action mailers like in the example below
119
168
 
120
169
  ```ruby
121
170
  class NotificationMailer < ActionMailer::Base
171
+ # The ring_the_magicbell method will bcc your email notifications to your MagicBell project's BCC email address
172
+ #
173
+ # Upon receiving a blind copied email notification, magicbell.io will automatically create an in-app notification for the user
122
174
  ring_the_magicbell
123
175
 
124
- def new_comment(comment)
176
+ # This is an email notification in your app
177
+ def new_comment
125
178
  # ...
126
- magicbell_notification_action_url("https://myapp.com/comments/#{comment.id}")
179
+ end
180
+
181
+ # This is another email notification in your app
182
+ def mentioned
127
183
  # ...
128
184
  end
129
185
  end
130
186
  ```
131
187
 
132
- #### Title
188
+ 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
133
189
 
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
190
+ ```
191
+ vim config/initializers/magicbell.rb
192
+ ```
193
+
194
+ ```ruby
195
+ # config/initializers/magicbell.rb
196
+
197
+ MagicBell.configure do |config|
198
+ config.api_key = "your_magicbell_api_key"
199
+ config.api_secret = "your_magicbell_api_secret"
200
+ config.bcc_email = "your_magicbell_bcc_email@ring.magicbell.io"
201
+ end
202
+ ```
203
+
204
+ The BCC email address is available in the "Settings" section in your MagicBell dashboard.
205
+
206
+ ### Customize Action URL
207
+
208
+ 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`.
209
+
210
+ If you wish to redirect users to a different URL instead, provide an `action_url` in your mailers
135
211
 
136
212
  ```ruby
137
213
  class NotificationMailer < ActionMailer::Base
@@ -139,15 +215,15 @@ class NotificationMailer < ActionMailer::Base
139
215
 
140
216
  def new_comment(comment)
141
217
  # ...
142
- magicbell_notification_title("Richard commented on your post Drive to Lake Tahoe")
218
+ magicbell_notification_action_url("https://myapp.com/comments/#{comment.id}")
143
219
  # ...
144
220
  end
145
221
  end
146
222
  ```
147
223
 
148
- #### Metadata
224
+ ### Customize Notification Title
149
225
 
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
226
+ 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
227
 
152
228
  ```ruby
153
229
  class NotificationMailer < ActionMailer::Base
@@ -155,63 +231,31 @@ class NotificationMailer < ActionMailer::Base
155
231
 
156
232
  def new_comment(comment)
157
233
  # ...
158
- magicbell_notification_metadata(comment_id: comment.id)
234
+ magicbell_notification_title("Richard posted a new comment")
159
235
  # ...
160
236
  end
161
237
  end
162
238
  ```
163
239
 
164
- You can later use this metadata to customize the behaviour of MagicBell's widget.
240
+ ## HMAC Authentication
165
241
 
166
- #### Customize widget behaviour
242
+ ### Calculate HMAC
167
243
 
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.
169
-
170
- When initializing the widget, pass a `onNotificationClick` callback to customize the widget's behaviour
171
-
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
- }
244
+ ```
245
+ user_email = "joe@example.com"
246
+ hmac = MagicBell.hmac(user_email)
187
247
  ```
188
248
 
249
+ See https://developer.magicbell.io/docs/turn-on-hmac-authentication for more information on turning on HMAC Authentication for your MagicBell Project
189
250
 
190
- ## Managing notifications
251
+ ## API docs
191
252
 
192
- ### Creating a notification
253
+ Please visit our website https://magicbell.io and our API docs https://developer.magicbell.io for more information MagicBell's embeddable notification inbox and MagicBell's REST API
193
254
 
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.
255
+ ## Contact Us
196
256
 
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
- }
257
+ Have a query or hit upon a problem? Create a post in our Developer Community https://community.magicbell.io or contact us at hello@magicbell.io
204
258
 
205
- client = MagicBell::Client.new()
206
- client.create_notification(params)
207
259
  ```
208
260
 
209
- The response will be a Faraday response object, and methods like `status` and
210
- `body` will be available for further inspection.
211
-
212
-
213
- If you'd like us to add more callbacks to the widget, reach out to us at hana@magicbell.io
214
-
215
- ## Documentation
216
-
217
- Visit our [Docs Site](https://docs.magicbell.io) for more information on MagicBell, MagicBell's widget and Advanced Features.
261
+ ```
data/Rakefile CHANGED
@@ -25,3 +25,17 @@ Rake::TestTask.new(:test) do |t|
25
25
  end
26
26
 
27
27
  task default: :test
28
+
29
+ # @example
30
+ # GEM_HOST_API_KEY="our_rubygems_api_key" bundle exec rake publish_to_rubygems
31
+ task :publish_to_rubygems do
32
+ `gem build magicbell.gemspec`
33
+ require_relative "lib/magicbell"
34
+ `gem push magicbell-#{MagicBell::VERSION}.gem`
35
+ end
36
+
37
+ task :console do
38
+ require_relative "lib/magicbell"
39
+ require "pry"
40
+ binding.pry
41
+ end
data/lib/magicbell.rb CHANGED
@@ -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,45 @@
1
+ module MagicBell
2
+ module ApiOperations
3
+ def get(url, options = {})
4
+ defaults = { headers: default_headers }
5
+ response = HTTParty.get(url, options.merge(defaults))
6
+ raise_http_error_unless_2xx_response(response)
7
+
8
+ response
9
+ end
10
+
11
+ def post(url, options = {})
12
+ defaults = { headers: default_headers }
13
+ response = HTTParty.post(url, options.merge(defaults))
14
+ raise_http_error_unless_2xx_response(response)
15
+
16
+ response
17
+ end
18
+
19
+ def put(url, options = {})
20
+ defaults = { headers: default_headers }
21
+ response = HTTParty.put(url, options.merge(defaults))
22
+ raise_http_error_unless_2xx_response(response)
23
+
24
+ response
25
+ end
26
+
27
+ protected
28
+
29
+ def default_headers
30
+ authentication_headers.merge({ "Content-Type" => "application/json", "Accept"=> "application/json" })
31
+ end
32
+
33
+ private
34
+
35
+ def raise_http_error_unless_2xx_response(response)
36
+ return if response.success?
37
+
38
+ e = MagicBell::Client::HTTPError.new
39
+ e.response_status = response.code
40
+ e.response_headers = response.headers.to_h
41
+ e.response_body = response.body
42
+ raise e
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,126 @@
1
+ require "active_support/inflector"
2
+ require "active_support/core_ext/object/blank"
3
+ require "json"
4
+
5
+ module MagicBell
6
+ class ApiResource
7
+ class << self
8
+ def create(client, attributes = {})
9
+ new(client, attributes).create
10
+ end
11
+
12
+ def find(client, id)
13
+ api_resource = new(client, "id" => id)
14
+ api_resource.retrieve
15
+ api_resource
16
+ end
17
+
18
+ def name
19
+ to_s.demodulize.underscore
20
+ end
21
+
22
+ def create_path
23
+ "/#{name}s"
24
+ end
25
+
26
+ def create_url
27
+ MagicBell.api_host + create_path
28
+ end
29
+ end
30
+
31
+ attr_reader :id
32
+
33
+ def initialize(client, attributes = {})
34
+ @client = client
35
+ @attributes = attributes
36
+
37
+ @id = @attributes["id"]
38
+ @retrieved = true if @id
39
+ end
40
+
41
+ def attributes
42
+ retrieve_unless_retrieved
43
+ @attributes
44
+ end
45
+ alias_method :to_h, :attributes
46
+
47
+ def attribute(attribute_name)
48
+ attributes[attribute_name]
49
+ end
50
+
51
+ def retrieve
52
+ response = @client.get(url)
53
+ parse_response(response)
54
+
55
+ self
56
+ end
57
+
58
+ def name
59
+ self.class.name
60
+ end
61
+
62
+ def create_url
63
+ MagicBell.api_host + create_path
64
+ end
65
+
66
+ def create_path
67
+ "/#{name}s"
68
+ end
69
+
70
+ def url
71
+ MagicBell.api_host + path
72
+ end
73
+
74
+ def path
75
+ "/#{name}s/#{id}"
76
+ end
77
+
78
+ def create
79
+ response = @client.post(
80
+ create_url,
81
+ body: { name => attributes }.to_json,
82
+ headers: extra_headers
83
+ )
84
+ parse_response(response)
85
+
86
+ self
87
+ end
88
+
89
+ def update(new_attributes = {})
90
+ response = @client.put(
91
+ url,
92
+ body: new_attributes.to_json,
93
+ headers: extra_headers
94
+ )
95
+ parse_response(response)
96
+
97
+ self
98
+ end
99
+
100
+ protected
101
+
102
+ def extra_headers
103
+ {}
104
+ end
105
+
106
+ private
107
+
108
+ attr_reader :response,
109
+ :response_hash
110
+
111
+ def retrieve_unless_retrieved
112
+ return unless id # Never retrieve a new unsaved resource
113
+ return if @retrieved
114
+ retrieve
115
+ end
116
+
117
+ def parse_response(response)
118
+ @response = response
119
+ unless response.body.blank?
120
+ @response_hash = JSON.parse(@response.body)
121
+ @attributes = @response_hash[name]
122
+ end
123
+ @retrieved = true
124
+ end
125
+ end
126
+ 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,50 @@
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 find_notification(notification_id)
19
+ client = self
20
+ MagicBell::UserNotification.find(client, notification_id)
21
+ end
22
+
23
+ def mark_all_notifications_as_read
24
+ client = self
25
+ MagicBell::UserNotificationsRead.new(client).create
26
+ end
27
+
28
+ def mark_all_notifications_as_seen
29
+ client = self
30
+ MagicBell::UserNotificationsSeen.new(client).create
31
+ end
32
+
33
+ def notification_preferences
34
+ client = self
35
+ MagicBell::UserNotificationPreferences.new(client)
36
+ end
37
+
38
+ def path
39
+ if id
40
+ self.class.path + "/#{id}"
41
+ elsif email
42
+ self.class.path + "/email:#{email}"
43
+ end
44
+ end
45
+
46
+ def authentication_headers
47
+ MagicBell.authentication_headers.merge("X-MAGICBELL-USER-EMAIL" => email)
48
+ end
49
+ end
50
+ 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 UserNotificationUnread < 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.3'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
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.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hana Mohan
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-24 00:00:00.000000000 Z
12
+ date: 2021-03-30 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,12 +122,22 @@ 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:
@@ -106,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
158
  - !ruby/object:Gem::Version
107
159
  version: '0'
108
160
  requirements: []
109
- rubygems_version: 3.1.2
161
+ rubygems_version: 3.1.4
110
162
  signing_key:
111
163
  specification_version: 4
112
164
  summary: Ruby wrapper for MagicBell.io
@@ -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