react-native-spike-sdk 2.4.4 → 2.4.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.
@@ -18,6 +18,8 @@ import kotlinx.coroutines.CoroutineScope
18
18
  import kotlinx.coroutines.Dispatchers
19
19
  import kotlinx.coroutines.SupervisorJob
20
20
  import kotlinx.coroutines.launch
21
+ import kotlinx.coroutines.sync.Mutex
22
+ import kotlinx.coroutines.sync.withLock
21
23
  import java.time.OffsetDateTime
22
24
  import java.util.UUID
23
25
 
@@ -32,7 +34,8 @@ fun <I, O> ComponentActivity.registerActivityResultLauncher(
32
34
  class SpikeSdkModule(reactContext: ReactApplicationContext) :
33
35
  ReactContextBaseJavaModule(reactContext), LifecycleEventListener {
34
36
 
35
- private val connections = mutableMapOf<String, SpikeConnection>()
37
+ // Do not use this directly, use `addConnection` and `getConnection` instead
38
+ private val _connections = mutableMapOf<String, SpikeConnection>()
36
39
 
37
40
  private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
38
41
 
@@ -85,7 +88,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
85
88
  } else {
86
89
  null
87
90
  }
88
- connections[uuid] = SpikeConnection.createConnection(
91
+ addConnection(connection = SpikeConnection.createConnection(
89
92
  context = reactApplicationContext,
90
93
  authToken = authToken,
91
94
  customerEndUserId = customerEndUserId,
@@ -93,7 +96,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
93
96
  callbackUrl = callbackUrl,
94
97
  env = SpikeEnvironment.PROD,
95
98
  logger = logger,
96
- )
99
+ ), uuid = uuid)
97
100
  promise.resolve(uuid)
98
101
  } catch (e: SpikeExceptions) {
99
102
  promise.reject(e.mapException(), e.message)
@@ -103,38 +106,43 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
103
106
 
104
107
  @ReactMethod
105
108
  fun getAppId(uuid: String, promise: Promise) {
106
- try {
107
- val connection = connections[uuid] ?: return promise.reject(
108
- SpikeExceptions.SpikeException(
109
- "Connection not found"
110
- ).mapException(), "Connection not found"
111
- )
112
- val appId = connection.getAppId()
113
- promise.resolve(appId)
114
- } catch (e: SpikeExceptions) {
115
- promise.reject(e.mapException(), e.message)
109
+ scope.launch {
110
+ try {
111
+ val connection = getConnection(uuid) ?: return@launch promise.reject(
112
+ SpikeExceptions.SpikeException(
113
+ "Connection not found"
114
+ ).mapException(), "Connection not found"
115
+ )
116
+ val appId = connection.getAppId()
117
+ promise.resolve(appId)
118
+ } catch (e: SpikeExceptions) {
119
+ promise.reject(e.mapException(), e.message)
120
+ }
116
121
  }
117
122
  }
118
123
 
119
124
  @ReactMethod
120
125
  fun getSpikeEndUserId(uuid: String, promise: Promise) {
121
- try {
122
- val connection = connections[uuid] ?: return promise.reject(
123
- SpikeExceptions.SpikeException(
124
- "Connection not found"
125
- ).mapException(), "Connection not found"
126
- )
127
- val spikeEndUserId = connection.getSpikeUserId()
128
- promise.resolve(spikeEndUserId)
129
- } catch (e: SpikeExceptions) {
130
- promise.reject(e.mapException(), e.message)
126
+ scope.launch {
127
+ try {
128
+ val connection = getConnection(uuid) ?: return@launch promise.reject(
129
+ SpikeExceptions.SpikeException(
130
+ "Connection not found"
131
+ ).mapException(), "Connection not found"
132
+ )
133
+ val spikeEndUserId = connection.getSpikeUserId()
134
+ promise.resolve(spikeEndUserId)
135
+ } catch (e: SpikeExceptions) {
136
+ promise.reject(e.mapException(), e.message)
137
+ }
131
138
  }
132
139
  }
133
140
 
134
141
  @ReactMethod
135
142
  fun getCustomerEndUserId(uuid: String, promise: Promise) {
143
+ scope.launch {
136
144
  try {
137
- val connection = connections[uuid] ?: return promise.reject(
145
+ val connection = getConnection(uuid) ?: return@launch promise.reject(
138
146
  SpikeExceptions.SpikeException(
139
147
  "Connection not found"
140
148
  ).mapException(), "Connection not found"
@@ -144,21 +152,23 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
144
152
  } catch (e: SpikeExceptions) {
145
153
  promise.reject(e.mapException(), e.message)
146
154
  }
155
+ }
147
156
  }
148
157
 
149
158
  @ReactMethod
150
159
  fun getCallbackUrl(uuid: String, promise: Promise) {
151
- try {
152
- val connection =
153
- connections[uuid] ?: return promise.reject(
160
+ scope.launch {
161
+ try {
162
+ val connection = getConnection(uuid) ?: return@launch promise.reject(
154
163
  SpikeExceptions.SpikeException(
155
164
  "Connection not found"
156
165
  ).mapException(), "Connection not found"
157
166
  )
158
- val callbackUrl = connection.getPostbackUrl()
159
- promise.resolve(callbackUrl)
160
- } catch (e: SpikeExceptions) {
161
- promise.reject(e.mapException(), e.message)
167
+ val callbackUrl = connection.getPostbackUrl()
168
+ promise.resolve(callbackUrl)
169
+ } catch (e: SpikeExceptions) {
170
+ promise.reject(e.mapException(), e.message)
171
+ }
162
172
  }
163
173
  }
164
174
 
@@ -167,7 +177,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
167
177
  fun close(uuid: String, promise: Promise) {
168
178
  scope.launch {
169
179
  try {
170
- val connection = connections[uuid] ?: return@launch promise.reject(
180
+ val connection = getConnection(uuid) ?: return@launch promise.reject(
171
181
  SpikeExceptions.SpikeException(
172
182
  "Connection not found"
173
183
  ).mapException(), "Connection not found"
@@ -190,7 +200,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
190
200
  ) {
191
201
  scope.launch {
192
202
  try {
193
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
203
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
194
204
  SpikeExceptions.SpikeException(
195
205
  "Connection not found"
196
206
  ).mapException(), "Connection not found"
@@ -217,7 +227,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
217
227
  ) {
218
228
  scope.launch {
219
229
  try {
220
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
230
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
221
231
  SpikeExceptions.SpikeException(
222
232
  "Connection not found"
223
233
  ).mapException(), "Connection not found"
@@ -242,7 +252,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
242
252
  ) {
243
253
  scope.launch {
244
254
  try {
245
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
255
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
246
256
  SpikeExceptions.SpikeException(
247
257
  "Connection not found"
248
258
  ).mapException(), "Connection not found"
@@ -265,7 +275,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
265
275
  ) {
266
276
  scope.launch {
267
277
  try {
268
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
278
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
269
279
  SpikeExceptions.SpikeException(
270
280
  "Connection not found"
271
281
  ).mapException(), "Connection not found"
@@ -282,16 +292,18 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
282
292
 
283
293
  @ReactMethod
284
294
  fun manageHealthConnect(connectionUUID: String, promise: Promise) {
285
- try {
286
- val connection = connections[connectionUUID] ?: return promise.reject(
287
- SpikeExceptions.SpikeException(
288
- "Connection not found"
289
- ).mapException(), "Connection not found"
290
- )
291
- connection.manageHealthConnect()
292
- promise.resolve(true)
293
- } catch (e: SpikeExceptions) {
294
- promise.reject(e.mapException(), e.message)
295
+ scope.launch {
296
+ try {
297
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
298
+ SpikeExceptions.SpikeException(
299
+ "Connection not found"
300
+ ).mapException(), "Connection not found"
301
+ )
302
+ connection.manageHealthConnect()
303
+ promise.resolve(true)
304
+ } catch (e: SpikeExceptions) {
305
+ promise.reject(e.mapException(), e.message)
306
+ }
295
307
  }
296
308
  }
297
309
 
@@ -299,7 +311,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
299
311
  fun checkPermissionsGranted(connectionUUID: String, dataType: String, promise: Promise) {
300
312
  scope.launch {
301
313
  try {
302
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
314
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
303
315
  SpikeExceptions.SpikeException(
304
316
  "Connection not found"
305
317
  ).mapException(), "Connection not found"
@@ -320,7 +332,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
320
332
  fun getHealthConnectAvailability(connectionUUID: String, promise: Promise) {
321
333
  scope.launch {
322
334
  try {
323
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
335
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
324
336
  SpikeExceptions.SpikeException(
325
337
  "Connection not found"
326
338
  ).mapException(), "Connection not found"
@@ -337,7 +349,7 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
337
349
  fun revokeAllPermissions(connectionUUID: String, promise: Promise) {
338
350
  scope.launch {
339
351
  try {
340
- val connection = connections[connectionUUID] ?: return@launch promise.reject(
352
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
341
353
  SpikeExceptions.SpikeException(
342
354
  "Connection not found"
343
355
  ).mapException(), "Connection not found"
@@ -352,98 +364,117 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
352
364
 
353
365
  @ReactMethod
354
366
  fun requestHealthPermissions(connectionUUID: String, dataType: String, promise: Promise) {
355
- val connection = connections[connectionUUID] ?: return promise.reject(
356
- SpikeExceptions.SpikeException(
357
- "Connection not found"
358
- ).mapException(), "Connection not found"
359
- )
360
-
361
- val permissions =
362
- connection.getRequiredHealthPermissionsMetadata(dataType.toSpikeDataType())
363
-
364
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
365
- val intent = SpikeConnection.requestReadAuthorization().createIntent(
366
- reactApplicationContext, permissions
367
- )
368
-
369
- val availability = connection.getHealthConnectAvailability()
370
- if (availability == HealthConnectAvailability.INSTALLED) {
371
- reactApplicationContext.currentActivity?.startActivityForResult(
372
- intent,
373
- REQUEST_CODE
367
+ scope.launch {
368
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
369
+ SpikeExceptions.SpikeException(
370
+ "Connection not found"
371
+ ).mapException(), "Connection not found"
372
+ )
373
+
374
+ val permissions =
375
+ connection.getRequiredHealthPermissionsMetadata(dataType.toSpikeDataType())
376
+
377
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
378
+ val intent = SpikeConnection.requestReadAuthorization().createIntent(
379
+ reactApplicationContext, permissions
374
380
  )
375
- promise.resolve(true)
376
- } else {
377
- promise.reject(SpikeExceptions.SpikeException().mapException(), SpikeExceptions.SpikeException().message)
378
- }
379
- } else {
380
- val activity = reactApplicationContext.currentActivity
381
- if (activity is ComponentActivity) {
382
- checkPermissionsFor = Triple(connectionUUID, setOf(dataType), promise)
383
- val launcher =
384
- activity.registerActivityResultLauncher(
385
- SpikeConnection.requestReadAuthorization()
386
- ) {
387
381
 
382
+ val availability = connection.getHealthConnectAvailability()
383
+ if (availability == HealthConnectAvailability.INSTALLED) {
384
+ reactApplicationContext.currentActivity?.startActivityForResult(
385
+ intent,
386
+ REQUEST_CODE
387
+ )
388
+ promise.resolve(true)
389
+ } else {
390
+ promise.reject(
391
+ SpikeExceptions.SpikeException().mapException(),
392
+ SpikeExceptions.SpikeException().message
393
+ )
394
+ }
395
+ } else {
396
+ val activity = reactApplicationContext.currentActivity
397
+ if (activity is ComponentActivity) {
398
+ checkPermissionsFor = Triple(connectionUUID, setOf(dataType), promise)
399
+ val launcher =
400
+ activity.registerActivityResultLauncher(
401
+ SpikeConnection.requestReadAuthorization()
402
+ ) {
403
+
404
+ }
405
+
406
+ if (permissions.isNotEmpty()) {
407
+ launcher.launch(permissions)
408
+ } else {
409
+ promise.resolve(false)
388
410
  }
389
-
390
- if (permissions.isNotEmpty()) {
391
- launcher.launch(permissions)
392
411
  } else {
393
412
  promise.resolve(false)
394
413
  }
395
- } else {
396
- promise.resolve(false)
397
414
  }
398
415
  }
399
416
  }
400
417
 
401
418
  @ReactMethod
402
419
  fun requestMultipleHealthPermissions(connectionUUID: String, dataTypes: ReadableArray, promise: Promise) {
403
- val connection = connections[connectionUUID] ?: return promise.reject(
404
- SpikeExceptions.SpikeException(
405
- "Connection not found"
406
- ).mapException(), "Connection not found"
407
- )
408
-
409
- val permissions = mutableSetOf<String>()
410
- dataTypes.toArrayList().forEach {
411
- connection.getRequiredHealthPermissionsMetadata((it as String).toSpikeDataType()).forEach {
412
- permissions.add(it)
413
- }
414
- }
415
-
416
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
417
- val availability = connection.getHealthConnectAvailability()
418
- if (permissions.isNotEmpty() && availability == HealthConnectAvailability.INSTALLED) {
419
- val intent = SpikeConnection.requestReadAuthorization().createIntent(
420
- reactApplicationContext, permissions
421
- )
420
+ scope.launch {
421
+ val connection = getConnection(connectionUUID) ?: return@launch promise.reject(
422
+ SpikeExceptions.SpikeException(
423
+ "Connection not found"
424
+ ).mapException(), "Connection not found"
425
+ )
422
426
 
423
- reactApplicationContext.currentActivity?.startActivityForResult(
424
- intent,
425
- REQUEST_CODE
426
- )
427
- promise.resolve(true)
428
- } else {
429
- promise.reject(SpikeExceptions.SpikeException().mapException(), SpikeExceptions.SpikeException().message)
427
+ val permissions = mutableSetOf<String>()
428
+ dataTypes.toArrayList().forEach {
429
+ connection.getRequiredHealthPermissionsMetadata((it as String).toSpikeDataType())
430
+ .forEach {
431
+ permissions.add(it)
432
+ }
430
433
  }
431
- } else {
432
- val activity = reactApplicationContext.currentActivity
433
- if (activity is ComponentActivity) {
434
- checkPermissionsFor = Triple(connectionUUID, dataTypes.toArrayList().toSet() as Set<String>, promise)
435
- val launcher =
436
- activity.registerActivityResultLauncher(
437
- SpikeConnection.requestReadAuthorization()
438
- ) {
439
434
 
440
- }
441
- if (permissions.isNotEmpty()) {
442
- launcher.launch(permissions)
435
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
436
+ val availability = connection.getHealthConnectAvailability()
437
+ if (permissions.isNotEmpty() && availability == HealthConnectAvailability.INSTALLED) {
438
+ val intent = SpikeConnection.requestReadAuthorization().createIntent(
439
+ reactApplicationContext, permissions
440
+ )
441
+
442
+ reactApplicationContext.currentActivity?.startActivityForResult(
443
+ intent,
444
+ REQUEST_CODE
445
+ )
446
+ promise.resolve(true)
443
447
  } else {
444
- promise.reject(SpikeExceptions.SpikeException().mapException(), SpikeExceptions.SpikeException().message) }
448
+ promise.reject(
449
+ SpikeExceptions.SpikeException().mapException(),
450
+ SpikeExceptions.SpikeException().message
451
+ )
452
+ }
445
453
  } else {
446
- promise.resolve(false)
454
+ val activity = reactApplicationContext.currentActivity
455
+ if (activity is ComponentActivity) {
456
+ checkPermissionsFor = Triple(
457
+ connectionUUID,
458
+ dataTypes.toArrayList().toSet() as Set<String>,
459
+ promise
460
+ )
461
+ val launcher =
462
+ activity.registerActivityResultLauncher(
463
+ SpikeConnection.requestReadAuthorization()
464
+ ) {
465
+
466
+ }
467
+ if (permissions.isNotEmpty()) {
468
+ launcher.launch(permissions)
469
+ } else {
470
+ promise.reject(
471
+ SpikeExceptions.SpikeException().mapException(),
472
+ SpikeExceptions.SpikeException().message
473
+ )
474
+ }
475
+ } else {
476
+ promise.resolve(false)
477
+ }
447
478
  }
448
479
  }
449
480
  }
@@ -504,4 +535,23 @@ class SpikeSdkModule(reactContext: ReactApplicationContext) :
504
535
  const val NAME = "SpikeSdk"
505
536
  const val REQUEST_CODE = 4200
506
537
  }
538
+
539
+ // Connections
540
+
541
+ private val mutex = Mutex()
542
+
543
+ // Add new connection to the dictionary in a thread-safe manner
544
+ private suspend fun addConnection(connection: SpikeConnection, uuid: String) {
545
+ mutex.withLock {
546
+ _connections[uuid] = connection
547
+ }
548
+ }
549
+
550
+ // Synchronised read for safe multithreaded usage
551
+ private suspend fun getConnection(uuid: String): SpikeConnection? {
552
+ mutex.withLock {
553
+ return _connections[uuid]
554
+ }
555
+ }
556
+
507
557
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-spike-sdk",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "iosVersion": "2.3.2",
5
5
  "description": "Spike API for health and productivity data from wearables and IoT devices",
6
6
  "main": "lib/commonjs/index",