magicbell 0.1.0 → 1.0.2

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: 489e38783982ec38f0044781f6bfdb156d7feb782a4bf51d624659fb35accd25
4
- data.tar.gz: 3283ccf989b48e59448386d6471e31bd0e34fb5ef3a33e2ed9df9ec78e11545b
3
+ metadata.gz: 69633fc70e1039d8b54eb3d6a0fbb2639975f881434195d1afae767d478cc0b2
4
+ data.tar.gz: c6f10dc4a264bb5bf25ece9381a008618ff3e52a7f52cc7b2b7a0f6b9921f39a
5
5
  SHA512:
6
- metadata.gz: 1c463bee03db65cbde34bc722a5ee368965e56342e0207426daf61337eb1f4fc56b3a394ef4133407beb641b36434085936a81669b9a4f48d78c162453aebee8
7
- data.tar.gz: 26906f1128c18a296bd0478682e79c0151bccbf9b21e809ac07a22840e0cf9e79d52c1f1b97c54078c84386c14a9d789793494856d1d65f1630e95c66693a253
6
+ metadata.gz: 93536189c55d619230780a13d51f835bfb17cee46b7e5b1e8e62b06b88a73e74017fb0febb6e3e727dc1ddbc08b72f0d0e9f4cc1f4dbedf384769234eb381323
7
+ data.tar.gz: 773c2e66fc79342fc91a1954453e4c89ff5f7500515c73565acb00ae6c9cd9f1796aa6e854c8f65e4b9f4c9653198c91c104333bb44f0dc02b6c5eed6aef3a36
data/README.md CHANGED
@@ -1,101 +1,183 @@
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-rails 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
- gem "magicbell-rails"
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-rails.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
- vim config/initializers/magicbell-rails.rb
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
+ ```
71
+
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
+
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
84
+
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
+ )
97
+ ```
98
+
99
+ ### Fetch a user's notifications
100
+
101
+ Fetch a user's notifications
102
+
103
+ ```ruby
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") }
70
108
  ```
71
109
 
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
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
73
111
 
74
- ```erb
75
- <html>
76
- <body>
77
- <p>This is your app's layout</p>
78
- </body>
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")
119
+ end
120
+ end
121
+ ```
79
122
 
80
- <%= render layouts/magicbell %>
81
- </html>
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
82
131
  ```
83
132
 
84
- Now, call the `ring_the_magicbell` method in your notification mailers. Here's an example
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
+ ```
141
+
142
+ ### Error handling
143
+
144
+ Please note that the gem raises a `MagicBell::Client::HTTPError` if an API returns a non 2xx response
145
+
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
+ ```
162
+
163
+ ## Rails integration
164
+
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.
166
+
167
+ Call the `ring_the_magicbell` method in your action mailers like in the example below
85
168
 
86
169
  ```ruby
87
170
  class NotificationMailer < ActionMailer::Base
88
- # This method will bcc your email notifications to your magicbell magic address
171
+ # The ring_the_magicbell method will bcc your email notifications to your MagicBell project's BCC email address
89
172
  #
90
- # Upon receiving the bcc'ed email notifications, magicbell.io will automatically
91
- # create in-app notications for users
173
+ # Upon receiving a blind copied email notification, magicbell.io will automatically create an in-app notification for the user
92
174
  ring_the_magicbell
93
175
 
94
176
  # This is an email notification in your app
95
177
  def new_comment
96
178
  # ...
97
179
  end
98
-
180
+
99
181
  # This is another email notification in your app
100
182
  def mentioned
101
183
  # ...
@@ -103,19 +185,29 @@ class NotificationMailer < ActionMailer::Base
103
185
  end
104
186
  ```
105
187
 
106
- Deploy your app.
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
107
189
 
108
- That's it! All your users now benefit from having in-app notifications.
190
+ ```
191
+ vim config/initializers/magicbell.rb
192
+ ```
109
193
 
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).
194
+ ```ruby
195
+ # config/initializers/magicbell.rb
111
196
 
112
- ## Advanced Features
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.
113
205
 
114
- #### ActionUrl
206
+ ### Customize Action URL
115
207
 
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`.
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`.
117
209
 
118
- If you wish to redirect users to a different URL instead, set a custom `ActionUrl` in your mailers
210
+ If you wish to redirect users to a different URL instead, provide an `action_url` in your mailers
119
211
 
120
212
  ```ruby
121
213
  class NotificationMailer < ActionMailer::Base
@@ -129,9 +221,9 @@ class NotificationMailer < ActionMailer::Base
129
221
  end
130
222
  ```
131
223
 
132
- #### Title
224
+ ### Customize Notification Title
133
225
 
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
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
135
227
 
136
228
  ```ruby
137
229
  class NotificationMailer < ActionMailer::Base
@@ -139,55 +231,31 @@ class NotificationMailer < ActionMailer::Base
139
231
 
140
232
  def new_comment(comment)
141
233
  # ...
142
- magicbell_notification_title("Richard commented on your post Drive to Lake Tahoe")
234
+ magicbell_notification_title("Richard posted a new comment")
143
235
  # ...
144
236
  end
145
237
  end
146
238
  ```
147
239
 
148
- #### Metadata
240
+ ## HMAC Authentication
149
241
 
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
242
+ ### Calculate HMAC
151
243
 
152
- ```ruby
153
- class NotificationMailer < ActionMailer::Base
154
- ring_the_magicbell
155
-
156
- def new_comment(comment)
157
- # ...
158
- magicbell_notification_metadata(comment_id: comment.id)
159
- # ...
160
- end
161
- end
244
+ ```
245
+ user_email = "joe@example.com"
246
+ hmac = MagicBell.hmac(user_email)
162
247
  ```
163
248
 
164
- You can later use this metadata to customize the behaviour of MagicBell's widget.
165
-
166
- #### Customize widget behaviour
249
+ See https://developer.magicbell.io/docs/turn-on-hmac-authentication for more information on turning on HMAC Authentication for your MagicBell Project
167
250
 
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.
251
+ ## API docs
169
252
 
170
- When initializing the widget, pass a `onNotificationClick` callback to customize the widget's behaviour
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
171
254
 
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
- ```
255
+ ## Contact Us
188
256
 
189
- If you'd like us to add more callbacks to the widget, reach out to us at hana@supportbee.com
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
190
258
 
191
- ## Documentation
259
+ ```
192
260
 
193
- Visit our [Docs Site](https://magicbell.supportbee.com/149-magicbell-s-help-docs) 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,12 +1,40 @@
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
7
 
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"
23
+
24
+ require "magicbell/railtie" if defined?(Rails)
5
25
 
6
26
  module MagicBell
7
- CLOUDFRONT_DOMAIN = "dxd8ma9fvw6e2.cloudfront.net"
27
+ WIDGET_JAVASCRIPT_URL = "https://assets.magicbell.io/widget.magicbell.js"
28
+ EXTRAS_CSS_URL = "https://assets.magicbell.io/extras.magicbell.css"
8
29
 
9
30
  class << self
31
+ extend Forwardable
32
+
33
+ def_delegators :config, :api_key,
34
+ :api_secret,
35
+ :bcc_email,
36
+ :api_host
37
+
10
38
  def configure
11
39
  yield(config)
12
40
  end
@@ -16,50 +44,28 @@ module MagicBell
16
44
  end
17
45
 
18
46
  def reset_config
19
- @config = nil
20
- end
21
-
22
- def extras_css_url
23
- "//#{CLOUDFRONT_DOMAIN}/extras.magicbell.css"
24
- end
25
- alias_method :host_page_css_url, :extras_css_url # Backward compatibility
26
-
27
- def api_host
28
- config.api_host
29
- end
30
-
31
- def widget_javascript_url
32
- "//#{CLOUDFRONT_DOMAIN}/widget.magicbell.js"
47
+ @config = Config.new
33
48
  end
34
49
 
35
- def api_key
36
- config.api_key
50
+ def authentication_headers
51
+ {
52
+ "X-MAGICBELL-API-KEY" => api_key,
53
+ "X-MAGICBELL-API-SECRET" => api_secret
54
+ }
37
55
  end
38
56
 
39
- def api_secret
40
- config.api_secret
41
- end
57
+ # Calculate HMAC for user's email
58
+ def hmac(message)
59
+ digest = sha256_digest
60
+ secret = api_secret
42
61
 
43
- def project_id
44
- config.project_id
62
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, message)).strip
45
63
  end
46
64
 
47
- def magic_address
48
- config.magic_address
49
- end
65
+ private
50
66
 
51
- def project_specific_headers
52
- {
53
- 'X-MAGICBELL-API-KEY' => config.api_key,
54
- 'X-MAGICBELL-API-SECRET' => config.api_secret
55
- }
56
- end
57
-
58
- # Calculate HMAC for user's email
59
- def user_key(user_email)
60
- MagicBell::HMAC.calculate(user_email, MagicBell.api_secret)
67
+ def sha256_digest
68
+ OpenSSL::Digest::Digest.new('sha256')
61
69
  end
62
70
  end
63
71
  end
64
-
65
- require "magicbell/railtie"
@@ -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,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: authentication_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: authentication_headers
94
+ )
95
+ parse_response(response)
96
+
97
+ self
98
+ end
99
+
100
+ protected
101
+
102
+ def authentication_headers
103
+ MagicBell.authentication_headers
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
@@ -0,0 +1,28 @@
1
+ module MagicBell
2
+ class Client
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
23
+
24
+ def authentication_headers
25
+ MagicBell.authentication_headers
26
+ end
27
+ end
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.1.0'
2
+ VERSION = '1.0.2'
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.1.0
4
+ version: 1.0.2
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-22 00:00:00.000000000 Z
12
+ date: 2021-02-18 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,17 +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
137
+ - lib/magicbell/client.rb
83
138
  - lib/magicbell/config.rb
84
- - lib/magicbell/hmac.rb
85
- - lib/magicbell/init/rails.rb
86
139
  - lib/magicbell/railtie.rb
87
- - lib/magicbell/user.rb
140
+ - lib/magicbell/singleton_api_resource.rb
88
141
  - lib/magicbell/version.rb
89
142
  homepage: https://magicbell.io
90
143
  licenses:
91
144
  - MIT
92
145
  metadata: {}
93
- post_install_message:
146
+ post_install_message:
94
147
  rdoc_options: []
95
148
  require_paths:
96
149
  - lib
@@ -105,8 +158,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
158
  - !ruby/object:Gem::Version
106
159
  version: '0'
107
160
  requirements: []
108
- rubygems_version: 3.1.2
109
- signing_key:
161
+ rubygems_version: 3.1.4
162
+ signing_key:
110
163
  specification_version: 4
111
164
  summary: Ruby wrapper for MagicBell.io
112
165
  test_files: []
@@ -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,27 +0,0 @@
1
- require 'rails'
2
- require 'yaml'
3
-
4
- module MagicBell
5
- module Init
6
- module Rails
7
- class Railtie < ::Rails::Railtie
8
- #rake_tasks do
9
- # load 'magicbell/tasks.rb'
10
- #end
11
-
12
- config.after_initialize do
13
- MagicBell.init!({
14
- :root => ::Rails.root.to_s,
15
- :env => ::Rails.env,
16
- :'config.path' => ::Rails.root.join('config', 'magicbell.yml'),
17
- :logger => Logging::FormattedLogger.new(::Rails.logger),
18
- :framework => :rails
19
- })
20
- MagicBell.load_plugins!
21
- end
22
- end
23
- end
24
- end
25
- end
26
-
27
- MagicBell.install_at_exit_callback
@@ -1,40 +0,0 @@
1
- require 'faraday'
2
-
3
- module MagicBell
4
- class User
5
-
6
- attr_accessor :email
7
- attr_accessor :preferences
8
-
9
- def initialize(attributes)
10
- @attributes = attributes
11
- @email = @attributes[:email]
12
- @conn = Faraday.new({
13
- url: MagicBell.api_host,
14
- headers: MagicBell.project_specific_headers.merge(
15
- 'X-MAGICBELL-USER-EMAIL' => @email
16
- )
17
- })
18
- end
19
-
20
- def notifications
21
- response = @conn.get "/notifications.json"
22
- JSON.parse(response.body)["notifications"]
23
- end
24
-
25
- def hmac_signature
26
- MagicBell::HMAC.calculate @email
27
- end
28
-
29
- def notification_preferences
30
- response = @conn.get "/notification_preferences.json"
31
- JSON.parse(response.body)["notification_preferences"]
32
- end
33
-
34
- def notification_preferences=(preferences)
35
- @conn.put "/notification_preferences.json",
36
- {notification_preferences: preferences}
37
- end
38
-
39
- end
40
- end