expo_notifier 0.1.0
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 +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +172 -0
- data/Rakefile +17 -0
- data/lib/expo_notifier/mapper/base.rb +30 -0
- data/lib/expo_notifier/mapper/error.rb +24 -0
- data/lib/expo_notifier/mapper/push_message.rb +132 -0
- data/lib/expo_notifier/mapper/push_messages.rb +15 -0
- data/lib/expo_notifier/mapper/push_receipt.rb +23 -0
- data/lib/expo_notifier/mapper/push_receipt_error_details.rb +23 -0
- data/lib/expo_notifier/mapper/push_receipt_ids.rb +16 -0
- data/lib/expo_notifier/mapper/push_receipts.rb +38 -0
- data/lib/expo_notifier/mapper/push_ticket.rb +30 -0
- data/lib/expo_notifier/mapper/push_ticket_error_details.rb +19 -0
- data/lib/expo_notifier/mapper/push_tickets.rb +26 -0
- data/lib/expo_notifier/mapper/rich_content.rb +17 -0
- data/lib/expo_notifier/request/base.rb +162 -0
- data/lib/expo_notifier/request/get_push_notification_receipts.rb +18 -0
- data/lib/expo_notifier/request/send_push_notifications.rb +26 -0
- data/lib/expo_notifier/response.rb +93 -0
- data/lib/expo_notifier/sorbet_shim.rb +18 -0
- data/lib/expo_notifier/version.rb +6 -0
- data/lib/expo_notifier.rb +19 -0
- data/sorbet/config +6 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/activesupport.rbi +495 -0
- data/sorbet/rbi/annotations/faraday.rbi +17 -0
- data/sorbet/rbi/annotations/minitest.rbi +119 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/annotations/webmock.rbi +9 -0
- data/sorbet/rbi/dsl/.gitattributes +1 -0
- data/sorbet/rbi/dsl/active_support/callbacks.rbi +21 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/base.rbi +12 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/error.rbi +42 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_message.rbi +263 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_messages.rbi +29 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_receipt.rbi +45 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_receipt_error_details.rbi +38 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_receipt_ids.rbi +20 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_receipts.rbi +25 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_ticket.rbi +59 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_ticket_error_details.rbi +32 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/push_tickets.rbi +54 -0
- data/sorbet/rbi/dsl/expo_notifier/mapper/rich_content.rbi +26 -0
- data/sorbet/rbi/dsl/time.rbi +13 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/activesupport@8.1.1.rbi +22456 -0
- data/sorbet/rbi/gems/addressable@2.8.8.rbi +2005 -0
- data/sorbet/rbi/gems/ast@2.4.3.rbi +586 -0
- data/sorbet/rbi/gems/base64@0.3.0.rbi +545 -0
- data/sorbet/rbi/gems/benchmark@0.5.0.rbi +637 -0
- data/sorbet/rbi/gems/bigdecimal@4.0.1.rbi +409 -0
- data/sorbet/rbi/gems/booleans@0.1.3.rbi +31 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.3.6.rbi +11729 -0
- data/sorbet/rbi/gems/connection_pool@3.0.2.rbi +9 -0
- data/sorbet/rbi/gems/crack@1.0.1.rbi +145 -0
- data/sorbet/rbi/gems/date@3.5.1.rbi +403 -0
- data/sorbet/rbi/gems/drb@2.2.3.rbi +1661 -0
- data/sorbet/rbi/gems/erb@6.0.1.rbi +815 -0
- data/sorbet/rbi/gems/erubi@1.13.1.rbi +157 -0
- data/sorbet/rbi/gems/faraday-net_http@3.4.2.rbi +72 -0
- data/sorbet/rbi/gems/faraday@2.14.0.rbi +3301 -0
- data/sorbet/rbi/gems/hashdiff@1.2.1.rbi +355 -0
- data/sorbet/rbi/gems/i18n@1.14.8.rbi +2383 -0
- data/sorbet/rbi/gems/io-console@0.8.2.rbi +9 -0
- data/sorbet/rbi/gems/json@2.18.0.rbi +2278 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +323 -0
- data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
- data/sorbet/rbi/gems/minitest@6.0.1.rbi +1524 -0
- data/sorbet/rbi/gems/net-http@0.9.1.rbi +4304 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +177 -0
- data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
- data/sorbet/rbi/gems/parser@3.3.10.0.rbi +5537 -0
- data/sorbet/rbi/gems/pp@0.6.3.rbi +390 -0
- data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
- data/sorbet/rbi/gems/prism@1.6.0.rbi +42126 -0
- data/sorbet/rbi/gems/psych@5.3.1.rbi +2556 -0
- data/sorbet/rbi/gems/public_suffix@7.0.2.rbi +957 -0
- data/sorbet/rbi/gems/racc@1.8.1.rbi +168 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +404 -0
- data/sorbet/rbi/gems/rake@13.3.1.rbi +3038 -0
- data/sorbet/rbi/gems/rbi@0.3.8.rbi +5238 -0
- data/sorbet/rbi/gems/rbs@4.0.0.dev.4.rbi +7805 -0
- data/sorbet/rbi/gems/regexp_parser@2.11.3.rbi +3849 -0
- data/sorbet/rbi/gems/reline@0.6.3.rbi +2995 -0
- data/sorbet/rbi/gems/require-hooks@0.2.2.rbi +110 -0
- data/sorbet/rbi/gems/rexml@3.4.4.rbi +5258 -0
- data/sorbet/rbi/gems/rubocop-espago@1.2.0.rbi +9 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
- data/sorbet/rbi/gems/securerandom@0.4.1.rbi +75 -0
- data/sorbet/rbi/gems/shale-builder@0.8.5.rbi +267 -0
- data/sorbet/rbi/gems/shale@1.2.2.rbi +2323 -0
- data/sorbet/rbi/gems/shoulda-context@2.0.0.rbi +563 -0
- data/sorbet/rbi/gems/spoom@1.7.11.rbi +5878 -0
- data/sorbet/rbi/gems/stringio@3.2.0.rbi +9 -0
- data/sorbet/rbi/gems/tapioca@0.17.4.rbi +3507 -0
- data/sorbet/rbi/gems/thor@1.4.0.rbi +4399 -0
- data/sorbet/rbi/gems/tsort@0.2.0.rbi +393 -0
- data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5919 -0
- data/sorbet/rbi/gems/unicode-display_width@3.2.0.rbi +132 -0
- data/sorbet/rbi/gems/unicode-emoji@4.2.0.rbi +254 -0
- data/sorbet/rbi/gems/uri@1.1.1.rbi +2407 -0
- data/sorbet/rbi/gems/vcr@6.4.0.rbi +3055 -0
- data/sorbet/rbi/gems/webmock@3.26.1.rbi +1816 -0
- data/sorbet/rbi/shims/gems/set.rbi +2 -0
- data/sorbet/rbi/shims/gems/shale.rbi +27 -0
- data/sorbet/rbi/shims/gems/shoulda-context.rbi +20 -0
- data/sorbet/tapioca/config.yml +21 -0
- data/sorbet/tapioca/extensions/load_gem.rb +1 -0
- data/sorbet/tapioca/require.rb +15 -0
- metadata +228 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'faraday'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'stringio'
|
|
7
|
+
require 'zlib'
|
|
8
|
+
|
|
9
|
+
require_relative '../mapper/base'
|
|
10
|
+
require_relative '../response'
|
|
11
|
+
|
|
12
|
+
module ExpoNotifier
|
|
13
|
+
module Request
|
|
14
|
+
# An abstract class for building and sending requests.
|
|
15
|
+
# @abstract
|
|
16
|
+
class Base
|
|
17
|
+
extend T::Generic
|
|
18
|
+
|
|
19
|
+
RequestBody = type_member(:out) { { upper: Mapper::Base } }
|
|
20
|
+
ResponseBody = type_member(:out) { { upper: Mapper::Base } }
|
|
21
|
+
|
|
22
|
+
Generic = T.type_alias { Base[Mapper::Base, Mapper::Base] }
|
|
23
|
+
|
|
24
|
+
########## ---------- Default config ---------- ##########
|
|
25
|
+
BASE_URL = 'https://exp.host'
|
|
26
|
+
WRITE_TIMEOUT = 5
|
|
27
|
+
OPEN_TIMEOUT = 5
|
|
28
|
+
READ_TIMEOUT = 30
|
|
29
|
+
GZIP_MIN_SIZE = 1024
|
|
30
|
+
BASE_HEADERS = {
|
|
31
|
+
'Content-Type' => 'application/json',
|
|
32
|
+
'Accept' => 'application/json',
|
|
33
|
+
'Accept-Encoding' => 'gzip, deflate',
|
|
34
|
+
}.freeze #: Hash[String, String]
|
|
35
|
+
########## ------------------------------------ ##########
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
#: String
|
|
39
|
+
attr_accessor :path
|
|
40
|
+
|
|
41
|
+
#: singleton(Mapper::Base)
|
|
42
|
+
attr_accessor :response_class
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#: String
|
|
46
|
+
attr_reader :url
|
|
47
|
+
|
|
48
|
+
#: RequestBody
|
|
49
|
+
attr_reader :body
|
|
50
|
+
|
|
51
|
+
#: Response[ResponseBody]?
|
|
52
|
+
attr_reader :response
|
|
53
|
+
|
|
54
|
+
#: (
|
|
55
|
+
#| RequestBody,
|
|
56
|
+
#| ?access_token: String?,
|
|
57
|
+
#| ?faraday_adapter: Symbol,
|
|
58
|
+
#| ?base_url: String,
|
|
59
|
+
#| ?write_timeout: Integer,
|
|
60
|
+
#| ?open_timeout: Integer,
|
|
61
|
+
#| ?read_timeout: Integer,
|
|
62
|
+
#| ?gzip: bool,
|
|
63
|
+
#| ?gzip_min_size: Integer,
|
|
64
|
+
#| ?additional_headers: Hash[String, String]?,
|
|
65
|
+
#| ) -> void
|
|
66
|
+
def initialize(
|
|
67
|
+
body,
|
|
68
|
+
access_token: nil,
|
|
69
|
+
faraday_adapter: Faraday.default_adapter,
|
|
70
|
+
base_url: BASE_URL,
|
|
71
|
+
write_timeout: WRITE_TIMEOUT,
|
|
72
|
+
open_timeout: OPEN_TIMEOUT,
|
|
73
|
+
read_timeout: READ_TIMEOUT,
|
|
74
|
+
gzip: true,
|
|
75
|
+
gzip_min_size: GZIP_MIN_SIZE,
|
|
76
|
+
additional_headers: nil
|
|
77
|
+
)
|
|
78
|
+
@body = body
|
|
79
|
+
@access_token = access_token
|
|
80
|
+
@faraday_adapter = faraday_adapter
|
|
81
|
+
@url = URI.join(base_url, self.class.path).to_s #: String
|
|
82
|
+
@write_timeout = write_timeout
|
|
83
|
+
@open_timeout = open_timeout
|
|
84
|
+
@read_timeout = read_timeout
|
|
85
|
+
@gzip = gzip
|
|
86
|
+
@gzip_min_size = gzip_min_size
|
|
87
|
+
@additional_headers = additional_headers
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#: -> Response[ResponseBody]
|
|
91
|
+
def execute
|
|
92
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
93
|
+
|
|
94
|
+
faraday_response = begin
|
|
95
|
+
faraday_connection.post do |req|
|
|
96
|
+
req.body = body_to_send
|
|
97
|
+
end
|
|
98
|
+
rescue Faraday::Error => e
|
|
99
|
+
e
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
103
|
+
@response = Response[ResponseBody].new(self.class.response_class, duration.to_f, faraday_response)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
#: -> T::Hash[String, String]
|
|
107
|
+
def headers
|
|
108
|
+
h = BASE_HEADERS
|
|
109
|
+
h = h.merge({ 'Content-Encoding' => 'gzip' }) if compress_body?
|
|
110
|
+
h = h.merge(@additional_headers) if @additional_headers
|
|
111
|
+
h
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# String in the JSON format
|
|
115
|
+
#: -> String
|
|
116
|
+
def raw_body
|
|
117
|
+
@raw_body ||= body.to_json
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# String in the JSON format, compressed with gzip if needed
|
|
121
|
+
#: -> String
|
|
122
|
+
def body_to_send
|
|
123
|
+
return raw_body unless compress_body?
|
|
124
|
+
|
|
125
|
+
io = StringIO.new
|
|
126
|
+
Zlib::GzipWriter.wrap(io) { |gz| gz.write(raw_body) }
|
|
127
|
+
io.string
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
#: -> bool
|
|
131
|
+
def compress_body?
|
|
132
|
+
return false unless @gzip
|
|
133
|
+
|
|
134
|
+
raw_body.bytesize >= @gzip_min_size
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
alias body_compressed? compress_body?
|
|
138
|
+
|
|
139
|
+
#: -> String?
|
|
140
|
+
def name = self.class.name&.split('::')&.last
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
#: -> Faraday::Connection
|
|
145
|
+
def faraday_connection
|
|
146
|
+
Faraday.new(
|
|
147
|
+
@url,
|
|
148
|
+
headers: headers,
|
|
149
|
+
request: {
|
|
150
|
+
write_timeout: @write_timeout,
|
|
151
|
+
open_timeout: @open_timeout,
|
|
152
|
+
read_timeout: @read_timeout,
|
|
153
|
+
},
|
|
154
|
+
) do |conn|
|
|
155
|
+
conn.request :authorization, 'Bearer', @access_token if @access_token
|
|
156
|
+
conn.adapter @faraday_adapter
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'base'
|
|
5
|
+
require_relative '../mapper/push_receipt_ids'
|
|
6
|
+
require_relative '../mapper/push_receipts'
|
|
7
|
+
|
|
8
|
+
module ExpoNotifier
|
|
9
|
+
module Request
|
|
10
|
+
class GetPushNotificationReceipts < Base
|
|
11
|
+
RequestBody = type_member(:out) { { fixed: Mapper::PushReceiptIds } }
|
|
12
|
+
ResponseBody = type_member(:out) { { fixed: Mapper::PushReceipts } }
|
|
13
|
+
|
|
14
|
+
self.path = '/--/api/v2/push/getReceipts'
|
|
15
|
+
self.response_class = Mapper::PushReceipts
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'base'
|
|
5
|
+
require_relative '../mapper/push_messages'
|
|
6
|
+
require_relative '../mapper/push_tickets'
|
|
7
|
+
|
|
8
|
+
module ExpoNotifier
|
|
9
|
+
module Request
|
|
10
|
+
class SendPushNotifications < Base
|
|
11
|
+
RequestBody = type_member(:out) { { fixed: Mapper::PushMessages } }
|
|
12
|
+
ResponseBody = type_member(:out) { { fixed: Mapper::PushTickets } }
|
|
13
|
+
|
|
14
|
+
self.path = '/--/api/v2/push/send'
|
|
15
|
+
self.response_class = Mapper::PushTickets
|
|
16
|
+
|
|
17
|
+
# String in the JSON format
|
|
18
|
+
# We override the raw body to send an array as a main object.
|
|
19
|
+
#: -> String
|
|
20
|
+
def raw_body
|
|
21
|
+
@raw_body ||= ExpoNotifier::Mapper::PushMessage.to_json(body.push_message) #: String?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module ExpoNotifier
|
|
5
|
+
class Response
|
|
6
|
+
extend T::Generic
|
|
7
|
+
|
|
8
|
+
Body = type_member(:out) { { upper: Mapper::Base } }
|
|
9
|
+
|
|
10
|
+
#: Integer?
|
|
11
|
+
attr_reader :status_code
|
|
12
|
+
|
|
13
|
+
#: Hash[(String | Symbol), String]?
|
|
14
|
+
attr_reader :headers
|
|
15
|
+
|
|
16
|
+
#: String?
|
|
17
|
+
attr_reader :raw_body
|
|
18
|
+
|
|
19
|
+
#: Float
|
|
20
|
+
attr_reader :duration
|
|
21
|
+
|
|
22
|
+
#: Faraday::Error?
|
|
23
|
+
attr_reader :communication_error
|
|
24
|
+
|
|
25
|
+
#: (singleton(Mapper::Base), Float, (Faraday::Response | Faraday::Error)) -> void
|
|
26
|
+
def initialize(body_class, duration, faraday_response)
|
|
27
|
+
if faraday_response.is_a?(Faraday::Response)
|
|
28
|
+
initialize_with_faraday_response(faraday_response)
|
|
29
|
+
elsif faraday_response.is_a?(Faraday::Error)
|
|
30
|
+
@communication_error = faraday_response #: Faraday::Error?
|
|
31
|
+
end
|
|
32
|
+
@body_class = body_class
|
|
33
|
+
@duration = duration
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Response body mapped to Ruby objects.
|
|
37
|
+
#: -> Body?
|
|
38
|
+
def body
|
|
39
|
+
@body ||= parse_response_body #: Body?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#: -> String?
|
|
43
|
+
def communication_error_message
|
|
44
|
+
return unless communication_error
|
|
45
|
+
|
|
46
|
+
"[#{communication_error.class}] => #{T.must(communication_error).message}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#: -> bool
|
|
50
|
+
def success?
|
|
51
|
+
@status_code == 200
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#: -> bool
|
|
55
|
+
def too_many_requests_error?
|
|
56
|
+
@status_code == 429
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#: -> bool
|
|
60
|
+
def malformed_request_error?
|
|
61
|
+
@status_code.to_s.start_with?('4') && !too_many_requests_error?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#: -> bool
|
|
65
|
+
def server_error?
|
|
66
|
+
@status_code.to_s.start_with?('5')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: -> bool
|
|
70
|
+
def communication_error?
|
|
71
|
+
!!communication_error
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
#: (Faraday::Response) -> void
|
|
77
|
+
def initialize_with_faraday_response(faraday_response)
|
|
78
|
+
@status_code = faraday_response.status #: Integer?
|
|
79
|
+
@headers = faraday_response.headers #: Hash[(String | Symbol), String]?
|
|
80
|
+
@raw_body = faraday_response.body #: String?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#: -> Body?
|
|
84
|
+
def parse_response_body
|
|
85
|
+
return unless @raw_body
|
|
86
|
+
|
|
87
|
+
@body_class.from_json(@raw_body)
|
|
88
|
+
rescue Shale::ParseError, JSON::ParserError
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'sorbet-runtime'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
T::Configuration.default_checked_level = :never
|
|
8
|
+
rescue StandardError
|
|
9
|
+
puts 'WARN: Sorbet default checked level could not be set!'
|
|
10
|
+
end
|
|
11
|
+
error_handler = ->(error, *_) do
|
|
12
|
+
end
|
|
13
|
+
# Suppresses errors caused by incorrect parameter and return types
|
|
14
|
+
T::Configuration.call_validation_error_handler = error_handler
|
|
15
|
+
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
|
16
|
+
T::Configuration.inline_type_error_handler = error_handler
|
|
17
|
+
# Suppresses errors caused by incorrect parameter ordering
|
|
18
|
+
T::Configuration.sig_validation_error_handler = error_handler
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'expo_notifier/sorbet_shim'
|
|
5
|
+
require_relative 'expo_notifier/version'
|
|
6
|
+
require_relative 'expo_notifier/request/send_push_notifications'
|
|
7
|
+
require_relative 'expo_notifier/request/get_push_notification_receipts'
|
|
8
|
+
|
|
9
|
+
module ExpoNotifier
|
|
10
|
+
class << self
|
|
11
|
+
#: (String) -> bool
|
|
12
|
+
def expo_push_token?(token)
|
|
13
|
+
return false unless token.is_a?(String)
|
|
14
|
+
|
|
15
|
+
((token.start_with?('ExponentPushToken[') || token.start_with?('ExpoPushToken[')) && token.end_with?(']')) ||
|
|
16
|
+
token.match?(/\A[a-z\d]{8}-[a-z\d]{4}-[a-z\d]{4}-[a-z\d]{4}-[a-z\d]{12}\z/i)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/sorbet/config
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
**/*.rbi linguist-vendored=true
|