exponent-server-sdk 0.0.4 → 0.0.5

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
  SHA1:
3
- metadata.gz: e290026a253948ce4a49f205bb465c4fdff55ca9
4
- data.tar.gz: 685948552e549b3e73a661a6926566995a9f8242
3
+ metadata.gz: 61012c4117f2e3c9357a2b99e92ad363fb956844
4
+ data.tar.gz: 7c565d390d7e8d2b737c6c6a684a14c1f988ff97
5
5
  SHA512:
6
- metadata.gz: abdab3e84de434c00a58110af875108e3f04cfe0df00e32d52c1d71b96673843de012c422849f108fa515730e253ad75d088d89c9895e9f8909dec6acdf7b409
7
- data.tar.gz: b8140c4d97ab32205fbd9b77a51b632d92264d05d5f1e642d0fecc644f85413c12c36fe4b11f394e3a85581c9de2ed314516cedbf489b8cdcd4d2b36f088fd46
6
+ metadata.gz: 0a21006404a04267901a8c607dd9dfd7c154e57f93c07da8d0299c4c26a3292a13393d6bbe60a0471f66e53efa21529f7b1919a9b55b1d463203f88d2cfe594b
7
+ data.tar.gz: 30730a58b90f42f5cb20382abeb4affd59275d6ec78be59e511e06249ffb5324b5554d39a5e21c850ba2bc8be5cf747c2bb7e9d271eb32b247ebcae6e7d60ebc
data/.gitignore CHANGED
@@ -17,3 +17,4 @@ test/version_tmp
17
17
  tmp
18
18
  *.log
19
19
  .ruby-version
20
+ /.idea
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
- exponent = Exponent::Push::Client.new
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
- exponent.publish messages
45
+ client.publish messages
48
46
  ```
49
47
 
50
- The complete format of the messages can be found [here.](https://docs.expo.io/versions/v16.0.0/guides/push-notifications.html#http2-api)
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!
@@ -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
- def initialize(new_http_client = nil)
16
- @http_client = new_http_client || Typhoeus
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
- handle_response(push_notifications(messages))
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 handle_response(response)
52
+ def handle(response)
28
53
  case response.code.to_s
29
54
  when /(^4|^5)/
30
- raise Error, build_error_from_failure(parse_json(response))
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
- build_error_with_handling(response) do
42
- extract_error_from_response(response)
43
- end
70
+ error_builder.build_from_erroneous(response)
44
71
  end
45
72
 
46
- def extract_error_from_response(response)
47
- error = response.fetch('errors').first { unknown_error_format(response) }
48
- "#{error.fetch('code')} -> #{error.fetch('message')}"
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 build_error_with_handling(response)
52
- yield(response)
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 push_notifications(messages)
58
- http_client.post(
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 push_url
66
- 'https://exp.host/--/api/v2/push/send'
87
+ def build_error_from_success(response)
88
+ error_builder.build_from_successful(response)
67
89
  end
90
+ end
68
91
 
69
- def headers
70
- {
71
- 'Content-Type' => 'application/json',
72
- 'Accept' => 'application/json'
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
- def handle_success(response)
77
- data = extract_data(response)
78
- if data.fetch('status') == 'ok'
79
- data
80
- else
81
- raise Error, build_error_from_success(response)
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 build_error_from_success(response)
86
- build_error_with_handling(response) do
87
- extract_error_from_success(response)
88
- end
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 extract_error_from_success(response)
92
- data = extract_data(response)
117
+ def from_successful_response(response)
118
+ data = response.fetch('data').first
93
119
  message = data.fetch('message')
94
120
 
95
- if data['details']
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 extract_data(response)
103
- response.fetch('data').first
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
@@ -1,3 +1,3 @@
1
1
  module Exponent
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'.freeze
3
3
  end
@@ -19,14 +19,14 @@ class ExponentServerSdkTest < Minitest::Test
19
19
  @mock.verify
20
20
  end
21
21
 
22
- def test_publish_with_error
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 = 'INTERNAL_SERVER_ERROR -> An unknown error occurred.'
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::Error do
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 test_publish_with_success_and_errors
38
+ def test_publish_with_device_not_registered_error
39
39
  @response_mock.expect(:code, 200)
40
- @response_mock.expect(:body, success_with_error_body.to_json)
41
- message = 'DeviceNotRegistered -> "ExponentPushToken[42]" is not a registered push notification recipient'
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::Error do
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 test_publish_with_success_and_apn_error
54
+ def test_publish_with_message_too_big_error
55
55
  @response_mock.expect(:code, 200)
56
- @response_mock.expect(:body, success_with_apn_error_body.to_json)
57
- message = 'error -> Could not find APNs credentials for you (your_app). Check whether you are trying to send a notification to a detached app.'
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::Error do
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 success_with_error_body
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 success_with_apn_error_body
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
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-02-26 00:00:00.000000000 Z
12
+ date: 2018-03-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: typhoeus