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 +4 -4
- data/README.md +167 -123
- data/Rakefile +14 -0
- data/lib/magicbell.rb +40 -12
- data/lib/magicbell/action_mailer_extension.rb +1 -1
- data/lib/magicbell/api_operations.rb +45 -0
- data/lib/magicbell/api_resource.rb +126 -0
- data/lib/magicbell/api_resource_collection.rb +89 -0
- data/lib/magicbell/api_resources/notification.rb +4 -0
- data/lib/magicbell/api_resources/user.rb +50 -0
- data/lib/magicbell/api_resources/user_notification.rb +21 -0
- data/lib/magicbell/api_resources/user_notification_preferences.rb +11 -0
- data/lib/magicbell/api_resources/user_notification_read.rb +14 -0
- data/lib/magicbell/api_resources/user_notification_unread.rb +14 -0
- data/lib/magicbell/api_resources/user_notifications.rb +15 -0
- data/lib/magicbell/api_resources/user_notifications_read.rb +7 -0
- data/lib/magicbell/api_resources/user_notifications_seen.rb +7 -0
- data/lib/magicbell/client.rb +22 -7
- data/lib/magicbell/config.rb +20 -5
- data/lib/magicbell/singleton_api_resource.rb +7 -0
- data/lib/magicbell/version.rb +1 -1
- metadata +61 -9
- data/lib/magicbell/client/notifications.rb +0 -20
- data/lib/magicbell/hmac.rb +0 -14
- data/lib/magicbell/user.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68c41d47dfcb956fd6ce283573619aafe27c5dc8ecae3db01807ae442afce912
|
4
|
+
data.tar.gz: 6dc6769e62c3b105a675854ccb0ea683f01b425a5d3fc1836fd962f1e370b9fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24b81b3179c672118ac6550a80c8e28bfdb73c293b9f96a1c8ca88e7836c36157eed6ab8fa5d7850199674cdc81770ab237dc5218c28d637f281c787e04b093f
|
7
|
+
data.tar.gz: edb2e6dcdc5e5827b55d4a75067c386a6278dddf52d70ea614657a47a7abcbcccef47e3255ef0918a8af4bbd053f888861a420989db248b5e37ee3d94083f784
|
data/README.md
CHANGED
@@ -1,137 +1,213 @@
|
|
1
|
-
# magicbell-
|
1
|
+
# magicbell-ruby
|
2
2
|
|
3
|
-
|
3
|
+
[MagicBell](https://magicbell.io) is an embeddable Notification Inbox for web & mobile applications.
|
4
4
|
|
5
|
-
|
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
|
-
|
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
|
-
|
31
|
+
and install the gem
|
16
32
|
|
17
33
|
```
|
18
34
|
bundle install
|
19
35
|
```
|
20
36
|
|
21
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
```
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
-
```
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
81
|
-
|
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
|
-
|
99
|
+
### Fetch a user's notifications
|
100
|
+
|
101
|
+
Fetch a user's notifications
|
85
102
|
|
86
103
|
```ruby
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
-
|
142
|
+
### Error handling
|
109
143
|
|
110
|
-
|
144
|
+
Please note that the gem raises a `MagicBell::Client::HTTPError` if an API returns a non 2xx response
|
111
145
|
|
112
|
-
|
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
|
-
|
163
|
+
## Rails integration
|
115
164
|
|
116
|
-
|
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
|
-
|
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
|
-
|
176
|
+
# This is an email notification in your app
|
177
|
+
def new_comment
|
125
178
|
# ...
|
126
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
218
|
+
magicbell_notification_action_url("https://myapp.com/comments/#{comment.id}")
|
143
219
|
# ...
|
144
220
|
end
|
145
221
|
end
|
146
222
|
```
|
147
223
|
|
148
|
-
|
224
|
+
### Customize Notification Title
|
149
225
|
|
150
|
-
|
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
|
-
|
234
|
+
magicbell_notification_title("Richard posted a new comment")
|
159
235
|
# ...
|
160
236
|
end
|
161
237
|
end
|
162
238
|
```
|
163
239
|
|
164
|
-
|
240
|
+
## HMAC Authentication
|
165
241
|
|
166
|
-
|
242
|
+
### Calculate HMAC
|
167
243
|
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
##
|
251
|
+
## API docs
|
191
252
|
|
192
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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 =
|
47
|
+
@config = Config.new
|
29
48
|
end
|
30
49
|
|
31
|
-
def
|
50
|
+
def authentication_headers
|
32
51
|
{
|
33
|
-
|
34
|
-
|
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
|
40
|
-
|
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
|
@@ -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,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,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
|
data/lib/magicbell/client.rb
CHANGED
@@ -1,13 +1,28 @@
|
|
1
|
-
require "magicbell/client/notifications"
|
2
|
-
|
3
1
|
module MagicBell
|
4
2
|
class Client
|
5
|
-
|
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
|
-
|
8
|
-
|
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
|
data/lib/magicbell/config.rb
CHANGED
@@ -1,13 +1,28 @@
|
|
1
1
|
module MagicBell
|
2
2
|
class Config
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
data/lib/magicbell/version.rb
CHANGED
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.
|
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:
|
12
|
+
date: 2021-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
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:
|
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:
|
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/
|
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.
|
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
|
data/lib/magicbell/hmac.rb
DELETED
@@ -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
|
data/lib/magicbell/user.rb
DELETED
@@ -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
|