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 +4 -4
- data/README.md +171 -103
- data/Rakefile +14 -0
- data/lib/magicbell.rb +45 -39
- data/lib/magicbell/action_mailer_extension.rb +1 -1
- data/lib/magicbell/api_operations.rb +44 -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 +28 -0
- data/lib/magicbell/config.rb +20 -5
- data/lib/magicbell/singleton_api_resource.rb +7 -0
- data/lib/magicbell/version.rb +1 -1
- metadata +65 -12
- data/lib/magicbell/hmac.rb +0 -14
- data/lib/magicbell/init/rails.rb +0 -27
- data/lib/magicbell/user.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69633fc70e1039d8b54eb3d6a0fbb2639975f881434195d1afae767d478cc0b2
|
4
|
+
data.tar.gz: c6f10dc4a264bb5bf25ece9381a008618ff3e52a7f52cc7b2b7a0f6b9921f39a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93536189c55d619230780a13d51f835bfb17cee46b7e5b1e8e62b06b88a73e74017fb0febb6e3e727dc1ddbc08b72f0d0e9f4cc1f4dbedf384769234eb381323
|
7
|
+
data.tar.gz: 773c2e66fc79342fc91a1954453e4c89ff5f7500515c73565acb00ae6c9cd9f1796aa6e854c8f65e4b9f4c9653198c91c104333bb44f0dc02b6c5eed6aef3a36
|
data/README.md
CHANGED
@@ -1,101 +1,183 @@
|
|
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
|
-
gem "magicbell
|
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
|
-
vim config/initializers/magicbell
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
-
```
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
81
|
-
|
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
|
-
|
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
|
-
#
|
171
|
+
# The ring_the_magicbell method will bcc your email notifications to your MagicBell project's BCC email address
|
89
172
|
#
|
90
|
-
# Upon receiving
|
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
|
-
|
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
|
-
|
190
|
+
```
|
191
|
+
vim config/initializers/magicbell.rb
|
192
|
+
```
|
109
193
|
|
110
|
-
|
194
|
+
```ruby
|
195
|
+
# config/initializers/magicbell.rb
|
111
196
|
|
112
|
-
|
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
|
-
|
206
|
+
### Customize Action URL
|
115
207
|
|
116
|
-
When a user clicks on a notification in MagicBell's
|
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,
|
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
|
-
|
224
|
+
### Customize Notification Title
|
133
225
|
|
134
|
-
|
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
|
234
|
+
magicbell_notification_title("Richard posted a new comment")
|
143
235
|
# ...
|
144
236
|
end
|
145
237
|
end
|
146
238
|
```
|
147
239
|
|
148
|
-
|
240
|
+
## HMAC Authentication
|
149
241
|
|
150
|
-
|
242
|
+
### Calculate HMAC
|
151
243
|
|
152
|
-
```
|
153
|
-
|
154
|
-
|
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
|
-
|
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
|
-
|
251
|
+
## API docs
|
169
252
|
|
170
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
259
|
+
```
|
192
260
|
|
193
|
-
|
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
|
-
|
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 =
|
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
|
36
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
57
|
+
# Calculate HMAC for user's email
|
58
|
+
def hmac(message)
|
59
|
+
digest = sha256_digest
|
60
|
+
secret = api_secret
|
42
61
|
|
43
|
-
|
44
|
-
config.project_id
|
62
|
+
Base64.encode64(OpenSSL::HMAC.digest(digest, secret, message)).strip
|
45
63
|
end
|
46
64
|
|
47
|
-
|
48
|
-
config.magic_address
|
49
|
-
end
|
65
|
+
private
|
50
66
|
|
51
|
-
def
|
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"
|
@@ -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,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
|
@@ -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
|
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,18 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magicbell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
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:
|
12
|
+
date: 2021-02-18 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,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/
|
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.
|
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: []
|
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/init/rails.rb
DELETED
@@ -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
|
data/lib/magicbell/user.rb
DELETED
@@ -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
|