Skip to content

Commit 3baeaf1

Browse files
authored
Move remote command parsing and validation to RemoteDataService (#1934)
1 parent cbcdfed commit 3baeaf1

File tree

5 files changed

+30
-232
lines changed

5 files changed

+30
-232
lines changed

Loop.xcodeproj/project.pbxproj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@
383383
A9DF02CB24F72B9E00B7C988 /* CriticalEventLogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF02CA24F72B9E00B7C988 /* CriticalEventLogTests.swift */; };
384384
A9DFAFB324F0415E00950D1E /* CarbBackfillRequestUserInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFAFB224F0415E00950D1E /* CarbBackfillRequestUserInfoTests.swift */; };
385385
A9DFAFB524F048A000950D1E /* WatchHistoricalCarbsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFAFB424F048A000950D1E /* WatchHistoricalCarbsTests.swift */; };
386-
A9E8A80528A7CAC000C0A8A4 /* RemoteCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E8A80428A7CAC000C0A8A4 /* RemoteCommandTests.swift */; };
387386
A9F5F1F5251050EC00E7C8A4 /* ZipArchiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F5F1F4251050EC00E7C8A4 /* ZipArchiveTests.swift */; };
388387
A9F66FC3247F451500096EA7 /* UIDevice+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F66FC2247F451500096EA7 /* UIDevice+Loop.swift */; };
389388
A9F703732489BC8500C98AD8 /* CarbStore+SimulatedCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F703722489BC8500C98AD8 /* CarbStore+SimulatedCoreData.swift */; };
@@ -457,7 +456,6 @@
457456
C16575752539FD60004AE16E /* LoopCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16575742539FD60004AE16E /* LoopCoreConstants.swift */; };
458457
C16575762539FEF3004AE16E /* LoopCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16575742539FD60004AE16E /* LoopCoreConstants.swift */; };
459458
C1657578253A0021004AE16E /* ChartConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1657577253A0021004AE16E /* ChartConstants.swift */; };
460-
C165B8CE23302C5D0004112E /* RemoteCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C165B8CD23302C5D0004112E /* RemoteCommand.swift */; };
461459
C16B983E26B4893300256B05 /* DoseEnactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16B983D26B4893300256B05 /* DoseEnactor.swift */; };
462460
C16B984026B4898800256B05 /* DoseEnactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16B983F26B4898800256B05 /* DoseEnactorTests.swift */; };
463461
C16DA84222E8E112008624C2 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16DA84122E8E112008624C2 /* PluginManager.swift */; };
@@ -1369,7 +1367,6 @@
13691367
A9DF02CA24F72B9E00B7C988 /* CriticalEventLogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CriticalEventLogTests.swift; sourceTree = "<group>"; };
13701368
A9DFAFB224F0415E00950D1E /* CarbBackfillRequestUserInfoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbBackfillRequestUserInfoTests.swift; sourceTree = "<group>"; };
13711369
A9DFAFB424F048A000950D1E /* WatchHistoricalCarbsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchHistoricalCarbsTests.swift; sourceTree = "<group>"; };
1372-
A9E8A80428A7CAC000C0A8A4 /* RemoteCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteCommandTests.swift; sourceTree = "<group>"; };
13731370
A9F5F1F4251050EC00E7C8A4 /* ZipArchiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipArchiveTests.swift; sourceTree = "<group>"; };
13741371
A9F66FC2247F451500096EA7 /* UIDevice+Loop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Loop.swift"; sourceTree = "<group>"; };
13751372
A9F703722489BC8500C98AD8 /* CarbStore+SimulatedCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbStore+SimulatedCoreData.swift"; sourceTree = "<group>"; };
@@ -1525,7 +1522,6 @@
15251522
C16575722538AFF6004AE16E /* CGMStalenessMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMStalenessMonitorTests.swift; sourceTree = "<group>"; };
15261523
C16575742539FD60004AE16E /* LoopCoreConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopCoreConstants.swift; sourceTree = "<group>"; };
15271524
C1657577253A0021004AE16E /* ChartConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartConstants.swift; sourceTree = "<group>"; };
1528-
C165B8CD23302C5D0004112E /* RemoteCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteCommand.swift; sourceTree = "<group>"; };
15291525
C16B983D26B4893300256B05 /* DoseEnactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoseEnactor.swift; sourceTree = "<group>"; };
15301526
C16B983F26B4898800256B05 /* DoseEnactorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoseEnactorTests.swift; sourceTree = "<group>"; };
15311527
C16DA84122E8E112008624C2 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = "<group>"; };
@@ -2048,7 +2044,6 @@
20482044
A9B996EF27235191002DC09C /* LoopWarning.swift */,
20492045
4F526D601DF8D9A900A04910 /* NetBasal.swift */,
20502046
438D42F81D7C88BC003244B0 /* PredictionInputEffect.swift */,
2051-
C165B8CD23302C5D0004112E /* RemoteCommand.swift */,
20522047
4328E0311CFC068900E199AA /* WatchContext+LoopKit.swift */,
20532048
E9C00EF424C623EF00628F35 /* LoopSettings+Loop.swift */,
20542049
A987CD4824A58A0100439ADC /* ZipArchive.swift */,
@@ -2818,7 +2813,6 @@
28182813
A99A114A29A58789007919CE /* Remote */ = {
28192814
isa = PBXGroup;
28202815
children = (
2821-
A9E8A80428A7CAC000C0A8A4 /* RemoteCommandTests.swift */,
28222816
A99A114B29A5879C007919CE /* BolusActionTests.swift */,
28232817
A99A114C29A5879C007919CE /* CarbActionTests.swift */,
28242818
A99A114D29A5879C007919CE /* OverrideActionTests.swift */,
@@ -3842,7 +3836,6 @@
38423836
439BED2A1E76093C00B0AED5 /* CGMManager.swift in Sources */,
38433837
C16B983E26B4893300256B05 /* DoseEnactor.swift in Sources */,
38443838
E98A55EF24EDD6E60008715D /* DosingDecisionStoreProtocol.swift in Sources */,
3845-
C165B8CE23302C5D0004112E /* RemoteCommand.swift in Sources */,
38463839
B4001CEE28CBBC82002FB414 /* AlertManagementView.swift in Sources */,
38473840
E9C00EF524C623EF00628F35 /* LoopSettings+Loop.swift in Sources */,
38483841
4389916B1E91B689000EEF90 /* ChartSettings+Loop.swift in Sources */,
@@ -4134,7 +4127,6 @@
41344127
A96DAC2A2838EF8A00D94E38 /* DiagnosticLogTests.swift in Sources */,
41354128
A9DAE7D02332D77F006AE942 /* LoopTests.swift in Sources */,
41364129
E93E86B024DDE1BD00FF40C8 /* MockGlucoseStore.swift in Sources */,
4137-
A9E8A80528A7CAC000C0A8A4 /* RemoteCommandTests.swift in Sources */,
41384130
1DFE9E172447B6270082C280 /* UserNotificationAlertSchedulerTests.swift in Sources */,
41394131
E9B3552F2935968E0076AB04 /* HKHealthStoreMock.swift in Sources */,
41404132
B4BC56382518DEA900373647 /* CGMStatusHUDViewModelTests.swift in Sources */,

Loop/Managers/DeviceDataManager.swift

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,81 +1377,56 @@ extension DeviceDataManager {
13771377
log.error("Remote Notification: Remote Commands not enabled.")
13781378
return
13791379
}
1380-
1381-
if let expirationStr = notification["expiration"] as? String {
1382-
let formatter = ISO8601DateFormatter()
1383-
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
1384-
if let expiration = formatter.date(from: expirationStr) {
1385-
let nowDate = Date()
1386-
guard nowDate < expiration else {
1387-
let expiredInterval = nowDate.timeIntervalSince(expiration)
1388-
await NotificationManager.sendRemoteCommandExpiredNotification(timeExpired: expiredInterval)
1389-
log.error("Remote Notification: Expired: %{public}@", String(describing: notification))
1390-
return
1391-
}
1392-
} else {
1393-
log.error("Remote Notification: Invalid expiration: %{public}@", expirationStr)
1394-
return
1395-
}
1396-
}
1397-
1398-
let action: Action
13991380

1381+
let command: RemoteCommand
14001382
do {
1401-
action = try RemoteCommand.createRemoteAction(notification: notification).get()
1383+
command = try await remoteDataServicesManager.commandFromPushNotification(notification)
14021384
} catch {
14031385
log.error("Remote Notification: Parse Error: %{public}@", String(describing: error))
14041386
return
14051387
}
14061388

1407-
log.default("Remote Notification: Handling action %{public}@", String(describing: action))
1389+
await handleRemoteCommand(command)
1390+
}
1391+
1392+
func handleRemoteCommand(_ command: RemoteCommand) async {
1393+
1394+
log.default("Remote Notification: Handling command %{public}@", String(describing: command))
14081395

1409-
switch action {
1396+
switch command.action {
14101397
case .temporaryScheduleOverride(let overrideAction):
14111398
do {
1399+
try command.validate()
14121400
try await handleOverrideAction(overrideAction)
14131401
} catch {
14141402
log.error("Remote Notification: Override Action Error: %{public}@", String(describing: error))
14151403
}
14161404
case .cancelTemporaryOverride(let overrideCancelAction):
14171405
do {
1406+
try command.validate()
14181407
try await handleOverrideCancelAction(overrideCancelAction)
14191408
} catch {
14201409
log.error("Remote Notification: Override Action Cancel Error: %{public}@", String(describing: error))
14211410
}
14221411
case .bolusEntry(let bolusAction):
14231412
do {
1424-
try validatePushNotificationSource(notification: notification)
1413+
try command.validate()
14251414
try await handleBolusAction(bolusAction)
14261415
} catch {
14271416
await NotificationManager.sendRemoteBolusFailureNotification(for: error, amount: bolusAction.amountInUnits)
1428-
log.error("Remote Notification: Bolus Action Error: %{public}@", String(describing: notification))
1417+
log.error("Remote Notification: Bolus Action Error: %{public}@", String(describing: error))
14291418
}
14301419
case .carbsEntry(let carbAction):
14311420
do {
1432-
try validatePushNotificationSource(notification: notification)
1421+
try command.validate()
14331422
try await handleCarbAction(carbAction)
14341423
} catch {
14351424
await NotificationManager.sendRemoteCarbEntryFailureNotification(for: error, amountInGrams: carbAction.amountInGrams)
1436-
log.error("Remote Notification: Carb Action Error: %{public}@", String(describing: notification))
1425+
log.error("Remote Notification: Carb Action Error: %{public}@", String(describing: error))
14371426
}
14381427
}
14391428
}
14401429

1441-
func validatePushNotificationSource(notification: [String: AnyObject]) throws {
1442-
1443-
let defaultServiceIdentifier = "NightscoutService"
1444-
let serviceIdentifer = notification["serviceIdentifier"] as? String ?? defaultServiceIdentifier
1445-
1446-
let validationResult = remoteDataServicesManager.validatePushNotificationSource(notification, serviceIdentifier: serviceIdentifer)
1447-
switch validationResult {
1448-
case .success():
1449-
log.info("Remote Notification: Validation successful")
1450-
case .failure(let error):
1451-
throw error
1452-
}
1453-
}
1454-
14551430
//Remote Overrides
14561431

14571432
func handleOverrideAction(_ action: OverrideAction) async throws {

Loop/Managers/RemoteDataServicesManager.swift

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -597,13 +597,24 @@ extension RemoteDataServicesManager {
597597

598598
extension RemoteDataServicesManager {
599599

600-
func validatePushNotificationSource(_ notification: [String: AnyObject], serviceIdentifier: String) -> Result<Void, Error> {
600+
func serviceForPushNotification(_ notification: [String: AnyObject]) -> RemoteDataService? {
601601

602-
guard let matchedService = remoteDataServices.first(where: {$0.serviceIdentifier == serviceIdentifier}) else {
603-
return .failure(PushNotificationValidationError.unsupportedService(serviceIdentifier: serviceIdentifier))
602+
let defaultServiceIdentifier = "NightscoutService"
603+
let serviceIdentifier = notification["serviceIdentifier"] as? String ?? defaultServiceIdentifier
604+
return remoteDataServices.first(where: {$0.serviceIdentifier == serviceIdentifier})
605+
}
606+
607+
func commandFromPushNotification(_ notification: [String : AnyObject]) async throws -> RemoteCommand {
608+
609+
enum RemoteDataServicesManagerCommandError: LocalizedError {
610+
case missingNotificationService
604611
}
605612

606-
return matchedService.validatePushNotificationSource(notification)
613+
guard let service = serviceForPushNotification(notification) else {
614+
throw RemoteDataServicesManagerCommandError.missingNotificationService
615+
}
616+
617+
return try await service.commandFromPushNotification(notification)
607618
}
608619

609620
public func temporaryScheduleOverrideHistoryDidUpdate() {
@@ -644,21 +655,8 @@ extension RemoteDataServicesManager {
644655
UserDefaults.appGroup?.deleteQueryAnchor(for: remoteDataService, withRemoteDataType: .overrides)
645656
}
646657
}
647-
648-
private enum PushNotificationValidationError: LocalizedError {
649-
case unsupportedService(serviceIdentifier: String)
650-
651-
var errorDescription: String? {
652-
switch self {
653-
case .unsupportedService(let serviceIdentifier):
654-
return "Unsupported Loop service: \(serviceIdentifier)"
655-
}
656-
}
657-
}
658-
659658
}
660659

661-
662660
protocol RemoteDataServicesManagerDelegate: AnyObject {
663661
var shouldSyncToRemoteService: Bool {get}
664662
}

Loop/Models/RemoteCommand.swift

Lines changed: 0 additions & 59 deletions
This file was deleted.

LoopTests/Models/Remote/RemoteCommandTests.swift

Lines changed: 0 additions & 108 deletions
This file was deleted.

0 commit comments

Comments
 (0)