exponent-server-sdk 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +8 -5
- data/lib/exponent-server-sdk.rb +92 -52
- data/lib/exponent-server-sdk/version.rb +1 -1
- data/test/exponent-server-sdk-test.rb +86 -20
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61012c4117f2e3c9357a2b99e92ad363fb956844
|
4
|
+
data.tar.gz: 7c565d390d7e8d2b737c6c6a684a14c1f988ff97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a21006404a04267901a8c607dd9dfd7c154e57f93c07da8d0299c4c26a3292a13393d6bbe60a0471f66e53efa21529f7b1919a9b55b1d463203f88d2cfe594b
|
7
|
+
data.tar.gz: 30730a58b90f42f5cb20382abeb4affd59275d6ec78be59e511e06249ffb5324b5554d39a5e21c850ba2bc8be5cf747c2bb7e9d271eb32b247ebcae6e7d60ebc
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
|
4
4
|
Use to send push notifications to Exponent Experiences from a Ruby server.
|
5
5
|
|
6
|
-
If you have problems with the code in this repository, please file issues & bug reports at https://github.com/expo/expo. Thanks!
|
7
|
-
|
8
6
|
## Installation
|
9
7
|
|
10
8
|
Add this line to your application's Gemfile:
|
@@ -32,7 +30,7 @@ $ gem install exponent-server-sdk
|
|
32
30
|
The push client is the preferred way. This hits the latest version of the api.
|
33
31
|
|
34
32
|
```ruby
|
35
|
-
|
33
|
+
client = Exponent::Push::Client.new
|
36
34
|
|
37
35
|
messages = [{
|
38
36
|
to: "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
|
@@ -44,7 +42,12 @@ messages = [{
|
|
44
42
|
body: "You've got mail"
|
45
43
|
}]
|
46
44
|
|
47
|
-
|
45
|
+
client.publish messages
|
48
46
|
```
|
49
47
|
|
50
|
-
The complete format of the messages can be found [here.](https://docs.expo.io/versions/
|
48
|
+
The complete format of the messages can be found [here.](https://docs.expo.io/versions/latest/index.html#http2-api)
|
49
|
+
|
50
|
+
## Contributing
|
51
|
+
|
52
|
+
If you have problems with the code in this repository, please file issues & bug reports. We encourage you
|
53
|
+
to submit a solution or a failing test to reproduce your issue. Thanks!
|
data/lib/exponent-server-sdk.rb
CHANGED
@@ -8,104 +8,144 @@ module Exponent
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module Push
|
11
|
-
Error = Class.new(StandardError)
|
12
11
|
|
13
12
|
class Client
|
14
|
-
|
15
|
-
|
16
|
-
@http_client
|
13
|
+
def initialize(http_client = Typhoeus,
|
14
|
+
response_handler = ResponseHandler.new)
|
15
|
+
@http_client = http_client
|
16
|
+
@response_handler = response_handler
|
17
17
|
end
|
18
18
|
|
19
19
|
def publish(messages)
|
20
|
-
|
20
|
+
response_handler.handle(push_notifications(messages))
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
-
attr_reader :http_client
|
25
|
+
attr_reader :http_client, :response_handler
|
26
|
+
|
27
|
+
def push_notifications(messages)
|
28
|
+
http_client.post(
|
29
|
+
push_url,
|
30
|
+
body: messages.to_json,
|
31
|
+
headers: headers
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def push_url
|
36
|
+
'https://exp.host/--/api/v2/push/send'
|
37
|
+
end
|
38
|
+
|
39
|
+
def headers
|
40
|
+
{
|
41
|
+
'Content-Type' => 'application/json',
|
42
|
+
'Accept' => 'application/json'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ResponseHandler
|
48
|
+
def initialize(error_builder = ErrorBuilder.new)
|
49
|
+
@error_builder = error_builder
|
50
|
+
end
|
26
51
|
|
27
|
-
def
|
52
|
+
def handle(response)
|
28
53
|
case response.code.to_s
|
29
54
|
when /(^4|^5)/
|
30
|
-
raise
|
55
|
+
raise build_error_from_failure(parse_json(response))
|
31
56
|
else
|
32
57
|
handle_success(parse_json(response))
|
33
58
|
end
|
34
59
|
end
|
35
60
|
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_reader :error_builder
|
64
|
+
|
36
65
|
def parse_json(response)
|
37
66
|
JSON.parse(response.body)
|
38
67
|
end
|
39
68
|
|
40
69
|
def build_error_from_failure(response)
|
41
|
-
|
42
|
-
extract_error_from_response(response)
|
43
|
-
end
|
70
|
+
error_builder.build_from_erroneous(response)
|
44
71
|
end
|
45
72
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
73
|
+
def handle_success(response)
|
74
|
+
extract_data(response).tap do |data|
|
75
|
+
validate_status(data.fetch('status'), response)
|
76
|
+
end
|
49
77
|
end
|
50
78
|
|
51
|
-
def
|
52
|
-
|
53
|
-
rescue KeyError
|
54
|
-
unknown_error_format(response)
|
79
|
+
def extract_data(response)
|
80
|
+
response.fetch('data').first
|
55
81
|
end
|
56
82
|
|
57
|
-
def
|
58
|
-
|
59
|
-
push_url,
|
60
|
-
body: messages.to_json,
|
61
|
-
headers: headers
|
62
|
-
)
|
83
|
+
def validate_status(status, response)
|
84
|
+
raise build_error_from_success(response) unless status == 'ok'
|
63
85
|
end
|
64
86
|
|
65
|
-
def
|
66
|
-
|
87
|
+
def build_error_from_success(response)
|
88
|
+
error_builder.build_from_successful(response)
|
67
89
|
end
|
90
|
+
end
|
68
91
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
92
|
+
class ErrorBuilder
|
93
|
+
%i[erroneous successful].each do |selector|
|
94
|
+
define_method(:"build_from_#{selector}") do |response|
|
95
|
+
with_error_handling(response) do
|
96
|
+
send "from_#{selector}_response", response
|
97
|
+
end
|
98
|
+
end
|
74
99
|
end
|
75
100
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
101
|
+
private
|
102
|
+
|
103
|
+
def with_error_handling(response)
|
104
|
+
yield(response)
|
105
|
+
rescue KeyError
|
106
|
+
unknown_error_format(response)
|
83
107
|
end
|
84
108
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
109
|
+
def from_erroneous_response(response)
|
110
|
+
error = response.fetch('errors').first
|
111
|
+
error_name = error.fetch('code')
|
112
|
+
message = error.fetch('message')
|
113
|
+
|
114
|
+
get_error_class(error_name).new(message)
|
89
115
|
end
|
90
116
|
|
91
|
-
def
|
92
|
-
data
|
117
|
+
def from_successful_response(response)
|
118
|
+
data = response.fetch('data').first
|
93
119
|
message = data.fetch('message')
|
94
120
|
|
95
|
-
|
96
|
-
"#{data.fetch('details').fetch('error')} -> #{message}"
|
97
|
-
else
|
98
|
-
"#{data.fetch('status')} -> #{message}"
|
99
|
-
end
|
121
|
+
get_error_class(data.fetch('details').fetch('error')).new(message)
|
100
122
|
end
|
101
123
|
|
102
|
-
def
|
103
|
-
|
124
|
+
def validate_error_name(condition)
|
125
|
+
condition ? yield : Exponent::Push::UnknownError
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_error_class(error_name)
|
129
|
+
validate_error_name(Exponent::Push.error_names.include?(error_name)) do
|
130
|
+
Exponent::Push.const_get("#{error_name}Error")
|
131
|
+
end
|
104
132
|
end
|
105
133
|
|
106
134
|
def unknown_error_format(response)
|
107
|
-
"Unknown error format: #{response}"
|
135
|
+
Exponent::Push::UnknownError.new("Unknown error format: #{response}")
|
108
136
|
end
|
109
137
|
end
|
138
|
+
|
139
|
+
Error = Class.new(StandardError)
|
140
|
+
|
141
|
+
def self.error_names
|
142
|
+
%w[DeviceNotRegistered MessageTooBig
|
143
|
+
MessageRateExceeded InvalidCredentials
|
144
|
+
Unknown]
|
145
|
+
end
|
146
|
+
|
147
|
+
error_names.each do |error_name|
|
148
|
+
const_set "#{error_name}Error", Class.new(Error)
|
149
|
+
end
|
110
150
|
end
|
111
151
|
end
|
@@ -19,14 +19,14 @@ class ExponentServerSdkTest < Minitest::Test
|
|
19
19
|
@mock.verify
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def test_publish_with_unknown_error
|
23
23
|
@response_mock.expect(:code, 400)
|
24
24
|
@response_mock.expect(:body, error_body.to_json)
|
25
|
-
message = '
|
25
|
+
message = 'An unknown error occurred.'
|
26
26
|
|
27
27
|
@mock.expect(:post, @response_mock, client_args)
|
28
28
|
|
29
|
-
exception = assert_raises Exponent::Push::
|
29
|
+
exception = assert_raises Exponent::Push::UnknownError do
|
30
30
|
@exponent.publish(messages)
|
31
31
|
end
|
32
32
|
|
@@ -35,14 +35,14 @@ class ExponentServerSdkTest < Minitest::Test
|
|
35
35
|
@mock.verify
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
38
|
+
def test_publish_with_device_not_registered_error
|
39
39
|
@response_mock.expect(:code, 200)
|
40
|
-
@response_mock.expect(:body,
|
41
|
-
message = '
|
40
|
+
@response_mock.expect(:body, not_registered_device_error_body.to_json)
|
41
|
+
message = '"ExponentPushToken[42]" is not a registered push notification recipient'
|
42
42
|
|
43
43
|
@mock.expect(:post, @response_mock, client_args)
|
44
44
|
|
45
|
-
exception = assert_raises Exponent::Push::
|
45
|
+
exception = assert_raises Exponent::Push::DeviceNotRegisteredError do
|
46
46
|
@exponent.publish(messages)
|
47
47
|
end
|
48
48
|
|
@@ -51,14 +51,14 @@ class ExponentServerSdkTest < Minitest::Test
|
|
51
51
|
@mock.verify
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
54
|
+
def test_publish_with_message_too_big_error
|
55
55
|
@response_mock.expect(:code, 200)
|
56
|
-
@response_mock.expect(:body,
|
57
|
-
message = '
|
56
|
+
@response_mock.expect(:body, message_too_big_error_body.to_json)
|
57
|
+
message = 'Message too big'
|
58
58
|
|
59
59
|
@mock.expect(:post, @response_mock, client_args)
|
60
60
|
|
61
|
-
exception = assert_raises Exponent::Push::
|
61
|
+
exception = assert_raises Exponent::Push::MessageTooBigError do
|
62
62
|
@exponent.publish(messages)
|
63
63
|
end
|
64
64
|
|
@@ -67,6 +67,53 @@ class ExponentServerSdkTest < Minitest::Test
|
|
67
67
|
@mock.verify
|
68
68
|
end
|
69
69
|
|
70
|
+
def test_publish_with_message_rate_exceeded_error
|
71
|
+
@response_mock.expect(:code, 200)
|
72
|
+
@response_mock.expect(:body, message_rate_exceeded_error_body.to_json)
|
73
|
+
message = 'Message rate exceeded'
|
74
|
+
|
75
|
+
@mock.expect(:post, @response_mock, client_args)
|
76
|
+
|
77
|
+
exception = assert_raises Exponent::Push::MessageRateExceededError do
|
78
|
+
@exponent.publish(messages)
|
79
|
+
end
|
80
|
+
|
81
|
+
assert_equal(message, exception.message)
|
82
|
+
|
83
|
+
@mock.verify
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_publish_with_invalid_credentials_error
|
87
|
+
@response_mock.expect(:code, 200)
|
88
|
+
@response_mock.expect(:body, invalid_credentials_error_body.to_json)
|
89
|
+
message = 'Invalid credentials'
|
90
|
+
|
91
|
+
@mock.expect(:post, @response_mock, client_args)
|
92
|
+
|
93
|
+
exception = assert_raises Exponent::Push::InvalidCredentialsError do
|
94
|
+
@exponent.publish(messages)
|
95
|
+
end
|
96
|
+
|
97
|
+
assert_equal(message, exception.message)
|
98
|
+
|
99
|
+
@mock.verify
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_publish_with_apn_error
|
103
|
+
@response_mock.expect(:code, 200)
|
104
|
+
@response_mock.expect(:body, apn_error_body.to_json)
|
105
|
+
|
106
|
+
@mock.expect(:post, @response_mock, client_args)
|
107
|
+
|
108
|
+
exception = assert_raises Exponent::Push::UnknownError do
|
109
|
+
@exponent.publish(messages)
|
110
|
+
end
|
111
|
+
|
112
|
+
assert_match(/Unknown error format/, exception.message)
|
113
|
+
|
114
|
+
@mock.verify
|
115
|
+
end
|
116
|
+
|
70
117
|
private
|
71
118
|
|
72
119
|
def success_body
|
@@ -82,17 +129,26 @@ class ExponentServerSdkTest < Minitest::Test
|
|
82
129
|
}
|
83
130
|
end
|
84
131
|
|
85
|
-
def
|
86
|
-
|
87
|
-
'data' => [{
|
88
|
-
'status' => 'error',
|
89
|
-
'message' => '"ExponentPushToken[42]" is not a registered push notification recipient',
|
90
|
-
'details' => { 'error' => 'DeviceNotRegistered' }
|
91
|
-
}]
|
92
|
-
}
|
132
|
+
def message_too_big_error_body
|
133
|
+
build_error_body('MessageTooBig', 'Message too big')
|
93
134
|
end
|
94
135
|
|
95
|
-
def
|
136
|
+
def not_registered_device_error_body
|
137
|
+
build_error_body(
|
138
|
+
'DeviceNotRegistered',
|
139
|
+
'"ExponentPushToken[42]" is not a registered push notification recipient'
|
140
|
+
)
|
141
|
+
end
|
142
|
+
|
143
|
+
def message_rate_exceeded_error_body
|
144
|
+
build_error_body('MessageRateExceeded', 'Message rate exceeded')
|
145
|
+
end
|
146
|
+
|
147
|
+
def invalid_credentials_error_body
|
148
|
+
build_error_body('InvalidCredentials', 'Invalid credentials')
|
149
|
+
end
|
150
|
+
|
151
|
+
def apn_error_body
|
96
152
|
{
|
97
153
|
'data' => [{
|
98
154
|
'status' => 'error',
|
@@ -126,4 +182,14 @@ class ExponentServerSdkTest < Minitest::Test
|
|
126
182
|
body: "You've got mail"
|
127
183
|
}]
|
128
184
|
end
|
185
|
+
|
186
|
+
def build_error_body(error_code, message)
|
187
|
+
{
|
188
|
+
'data' => [{
|
189
|
+
'status' => 'error',
|
190
|
+
'message' => message,
|
191
|
+
'details' => { 'error' => error_code }
|
192
|
+
}]
|
193
|
+
}
|
194
|
+
end
|
129
195
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exponent-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jesse Ruder
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: typhoeus
|