Class: Expo::Push::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/push/client.rb,
lib/expo/server/sdk/version.rb

Overview

This is the Push Client for Expo’s Push Service. It is responsible for sending the notifications themselves as well as retrieving the receipts.

It will attempt to keep a persistent connection once the first request is made, and allow at most #concurrency concurrent requests.

Constant Summary collapse

VERSION =
Expo::Server::SDK::VERSION

Instance Method Summary collapse

Constructor Details

#initialize(access_token: nil, concurrency: DEFAULT_CONCURRENT_REQUEST_LIMIT, logger: false, instrumentation: false) ⇒ Client

Returns a new instance of Client.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/push/client.rb', line 164

def initialize(
  access_token: nil,
  concurrency: DEFAULT_CONCURRENT_REQUEST_LIMIT,
  logger: false,
  instrumentation: false
)
  self.access_token = access_token
  self.concurrency = concurrency
  self.logger = logger
  self.instrumentation = if instrumentation == true
                           { instrumentation: ActiveSupport::Notifications.instrumenter }
                         else
                           instrumentation
                         end
end

Instance Method Details

#connectObject

rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/AbcSize



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/push/client.rb', line 246

def connect # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  shutdown

  self.pool = ConnectionPool.new(size: concurrency, timeout: 5) do
    http = HTTP.headers(
      # All request should return JSON (in this client)
      Accept: 'application/json',
      # All responses are allowed to be gzip-encoded
      'Accept-Encoding': 'gzip',
      # Set user-agent so that expo can track usage
      'User-Agent': format('expo-server-sdk-ruby/%<version>s', version: VERSION)
    )

    http = http.auth("Bearer #{access_token}") if access_token

    # All requests are allowed to automatically gzip
    http = http.use(:auto_inflate)
    # Turn on logging if there is a logger
    http = http.use(logging: { logger: logger }) if logger
    # Turn on instrumentation
    http = http.use(instrumentation: instrumentation) if instrumentation

    http.persistent(BASE_URL)
  end
end

#notificationObject



280
281
282
# File 'lib/push/client.rb', line 280

def notification
  Expo::Push::Notification.new
end

#receipts(receipt_ids) ⇒ Object

rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/AbcSize



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/push/client.rb', line 219

def receipts(receipt_ids)
  connect unless pool?

  pool.with do |http|
    response = http.post(RECEIPTS_API_URL, json: { ids: Array(receipt_ids) })
    parsed_response = response.parse

    if !parsed_response || parsed_response.is_a?(Array) || !parsed_response.is_a?(Hash)
      raise ServerError, 'Expected hash with receipt id => receipt, but got some other data structure'
    end

    errors = parsed_response['errors']
    data = parsed_response['data']

    if errors&.length&.positive?
      ReceiptsWithErrors.new(data: parsed_response, errors: errors)
    else
      results = data.map do |receipt_id, receipt_value|
        Receipt.new(data: receipt_value, receipt_id: receipt_id)
      end

      Receipts.new(results: results, requested_ids: receipt_ids)
    end
  end
end

#send(notifications) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/push/client.rb', line 181

def send(notifications)
  connect unless pool?

  threads = Chunk.for(notifications).map do |chunk|
    expected_count = chunk.count

    Thread.new do
      pool.with do |http|
        response = http.post(PUSH_API_URL, json: chunk.as_json)
        parsed_response = response.parse

        data = parsed_response['data']
        errors = parsed_response['errors']

        if errors&.length&.positive?
          TicketsWithErrors.new(data: data, errors: errors)
        elsif !data.is_a?(Array) || data.length != expected_count
          TicketsExpectationFailed.new(expected_count: expected_count, data: data)
        else
          data.map { |ticket| Ticket.new(ticket) }
        end
      end
    end
  end

  Tickets.new(threads.map(&:value))
end

#send!(notifications) ⇒ Object

rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize



210
211
212
213
214
215
216
# File 'lib/push/client.rb', line 210

def send!(notifications)
  send(notifications).tap do |result|
    result.each_error do |error|
      raise error if error.is_a?(Error)
    end
  end
end

#shutdownObject



272
273
274
275
276
277
278
# File 'lib/push/client.rb', line 272

def shutdown
  return unless pool?

  pool.shutdown do |conn|
    conn&.close
  end
end