Skip to content

Commit 37f9911

Browse files
hermanliangflovilmart
authored andcommitted
Allowing to re-save installation if LDS is enabled (#1125)
* Allowing to re-save installation if LDS is enabled * fix target compatibility * minor fixes & add test * minor optimization * add call count assertion * update pod spec homepage * verify offline store operations * add synchronized block to map operation
1 parent 90de166 commit 37f9911

File tree

6 files changed

+110
-7
lines changed

6 files changed

+110
-7
lines changed

Parse.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,6 +2566,13 @@
25662566
97DE045A16321492007154E8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97DE045916321492007154E8 /* Security.framework */; };
25672567
97DE045C163214C0007154E8 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97DE045B163214C0007154E8 /* SystemConfiguration.framework */; };
25682568
97EB055516F7CCE400E09147 /* PFAnalytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 9739513916B9D28E0010B884 /* PFAnalytics.m */; };
2569+
A6E2958C1E96173D009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2570+
A6E2958D1E96173F009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2571+
A6E2958E1E961741009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2572+
A6E2958F1E961742009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2573+
A6E295901E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2574+
A6E295911E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
2575+
A6E295921E961744009917BF /* ParseManagerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E295801E961727009917BF /* ParseManagerPrivate.h */; };
25692576
B141169E1E5BC24B00F70D7A /* PFFileUploadController.h in Headers */ = {isa = PBXBuildFile; fileRef = B141169D1E5BC24B00F70D7A /* PFFileUploadController.h */; settings = {ATTRIBUTES = (Public, ); }; };
25702577
B14116F41E5D075C00F70D7A /* PFFileUploadController.h in Headers */ = {isa = PBXBuildFile; fileRef = B141169D1E5BC24B00F70D7A /* PFFileUploadController.h */; settings = {ATTRIBUTES = (Public, ); }; };
25712578
B14116F51E5D075E00F70D7A /* PFFileUploadController.h in Headers */ = {isa = PBXBuildFile; fileRef = B141169D1E5BC24B00F70D7A /* PFFileUploadController.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -3491,6 +3498,7 @@
34913498
97DE045B163214C0007154E8 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
34923499
97E18AE41623835600B17A67 /* PFLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFLocationManager.h; sourceTree = "<group>"; };
34933500
97E18AE51623835600B17A67 /* PFLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFLocationManager.m; sourceTree = "<group>"; };
3501+
A6E295801E961727009917BF /* ParseManagerPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParseManagerPrivate.h; sourceTree = "<group>"; };
34943502
B141169D1E5BC24B00F70D7A /* PFFileUploadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFFileUploadController.h; sourceTree = "<group>"; };
34953503
B14116FB1E5D078E00F70D7A /* PFFileUploadResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFFileUploadResult.m; sourceTree = "<group>"; };
34963504
B141170A1E5D081500F70D7A /* PFFileUploadResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFFileUploadResult.h; sourceTree = "<group>"; };
@@ -3759,6 +3767,7 @@
37593767
09EEA1351435143500E3A3FA /* ParseInternal.h */,
37603768
81A245F11B1FB188006A6953 /* PFDataProvider.h */,
37613769
812714861AE6F1270076AE8D /* ParseManager.h */,
3770+
A6E295801E961727009917BF /* ParseManagerPrivate.h */,
37623771
812714871AE6F1270076AE8D /* ParseManager.m */,
37633772
8124C8811B27542A00758E00 /* PFCoreDataProvider.h */,
37643773
8196D58B1B0BD23B000465A1 /* PFCoreManager.h */,
@@ -5418,6 +5427,7 @@
54185427
810156481BB3832700D7C7BD /* PFURLSessionDataTaskDelegate.h in Headers */,
54195428
81CA29D21C28DF8F00C4F34A /* PFAnonymousUtils+Deprecated.h in Headers */,
54205429
810156491BB3832700D7C7BD /* PFDateFormatter.h in Headers */,
5430+
A6E295911E961744009917BF /* ParseManagerPrivate.h in Headers */,
54215431
8101564A1BB3832700D7C7BD /* PFCloudCodeController.h in Headers */,
54225432
8101564B1BB3832700D7C7BD /* PFMultiProcessFileLockController.h in Headers */,
54235433
8101564C1BB3832700D7C7BD /* PFCurrentUserController.h in Headers */,
@@ -5580,6 +5590,7 @@
55805590
815F23C51BD04D150054659F /* PFURLSessionCommandRunner.h in Headers */,
55815591
815F23C61BD04D150054659F /* PFRESTObjectCommand.h in Headers */,
55825592
815F23C71BD04D150054659F /* PFCommandRunning.h in Headers */,
5593+
A6E2958F1E961742009917BF /* ParseManagerPrivate.h in Headers */,
55835594
815F23C81BD04D150054659F /* PFRESTCloudCommand.h in Headers */,
55845595
815F23C91BD04D150054659F /* PFProduct.h in Headers */,
55855596
815F23CA1BD04D150054659F /* PFQuery.h in Headers */,
@@ -5685,6 +5696,7 @@
56855696
818AAA7619D36B1C00FC1B3C /* PFFile.h in Headers */,
56865697
816AC9BA1A3F48250031D94C /* PFApplication.h in Headers */,
56875698
F5B0B2EC1B449F1D00F3EBC4 /* BFTask+Private.h in Headers */,
5699+
A6E2958C1E96173D009917BF /* ParseManagerPrivate.h in Headers */,
56885700
F5B0B2ED1B449F1D00F3EBC4 /* PFCategoryLoader.h in Headers */,
56895701
F5B0B2EE1B449F1D00F3EBC4 /* PFThreadsafety.h in Headers */,
56905702
F5B0B2EF1B449F1D00F3EBC4 /* PFRelationState_Private.h in Headers */,
@@ -6031,6 +6043,7 @@
60316043
81C584121C3B0A98000063C6 /* PFFilePersistenceGroup.h in Headers */,
60326044
81C584131C3B0A98000063C6 /* PFRESTQueryCommand.h in Headers */,
60336045
81C584141C3B0A98000063C6 /* PFACLState_Private.h in Headers */,
6046+
A6E2958D1E96173F009917BF /* ParseManagerPrivate.h in Headers */,
60346047
81C584151C3B0A98000063C6 /* PFConfig+Synchronous.h in Headers */,
60356048
81C584161C3B0A98000063C6 /* PFObject.h in Headers */,
60366049
81C584171C3B0A98000063C6 /* PFPushUtilities.h in Headers */,
@@ -6238,6 +6251,7 @@
62386251
81C585701C3B0AA1000063C6 /* PFURLSessionCommandRunner.h in Headers */,
62396252
81C585711C3B0AA1000063C6 /* PFRESTObjectCommand.h in Headers */,
62406253
81C585721C3B0AA1000063C6 /* PFCommandRunning.h in Headers */,
6254+
A6E295901E961744009917BF /* ParseManagerPrivate.h in Headers */,
62416255
81C585731C3B0AA1000063C6 /* PFRESTCloudCommand.h in Headers */,
62426256
81C585741C3B0AA1000063C6 /* PFProduct.h in Headers */,
62436257
81C585751C3B0AA1000063C6 /* PFQuery.h in Headers */,
@@ -6483,6 +6497,7 @@
64836497
81C586EF1C3B0AA9000063C6 /* PFURLSessionDataTaskDelegate.h in Headers */,
64846498
81C586F01C3B0AA9000063C6 /* PFAnonymousUtils+Deprecated.h in Headers */,
64856499
81C586F11C3B0AA9000063C6 /* PFDateFormatter.h in Headers */,
6500+
A6E295921E961744009917BF /* ParseManagerPrivate.h in Headers */,
64866501
81C586F21C3B0AA9000063C6 /* PFCloudCodeController.h in Headers */,
64876502
81C586F31C3B0AA9000063C6 /* PFMultiProcessFileLockController.h in Headers */,
64886503
81C586F41C3B0AA9000063C6 /* PFCurrentUserController.h in Headers */,
@@ -6558,6 +6573,7 @@
65586573
F5B0B3261B44A33100F3EBC4 /* PFAssert.h in Headers */,
65596574
F5B0B3271B44A33100F3EBC4 /* PFAsyncTaskQueue.h in Headers */,
65606575
F5B0B3281B44A33100F3EBC4 /* PFBaseState.h in Headers */,
6576+
A6E2958E1E961741009917BF /* ParseManagerPrivate.h in Headers */,
65616577
F51535091B57240900C49F56 /* PFMutableACLState.h in Headers */,
65626578
F5B0B32B1B44A33100F3EBC4 /* PFCoreDataProvider.h in Headers */,
65636579
814881611B795CD4008763BF /* PFMultiProcessFileLock.h in Headers */,

Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,16 @@ - (void)updateObjectIdForObject:(PFObject *)object
10001000
oldObjectId:(NSString *)oldObjectId
10011001
newObjectId:(NSString *)newObjectId {
10021002
if (oldObjectId != nil) {
1003+
#if TARGET_OS_IOS
1004+
if ([object isKindOfClass:[PFInstallation class]]
1005+
&& newObjectId == nil) {
1006+
NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:oldObjectId];
1007+
@synchronized(self.lock) {
1008+
[self.classNameAndObjectIdToObjectMap removeObjectForKey:key];
1009+
}
1010+
return;
1011+
}
1012+
#endif
10031013
PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode.");
10041014
return;
10051015
}

Parse/Internal/ParseManager.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#import "PFUser.h"
2828
#import "PFURLSessionCommandRunner.h"
2929
#import "PFPersistenceController.h"
30+
#import "ParseManagerPrivate.h"
3031

3132
#if !TARGET_OS_WATCH && !TARGET_OS_TV
3233
#import "PFPushManager.h"
@@ -304,6 +305,11 @@ - (PFInstallationIdentifierStore *)installationIdentifierStore {
304305

305306
#pragma mark CommandRunner
306307

308+
// Set Command Runner. Used for testing.
309+
- (void)setCommandRunner:(id<PFCommandRunning>)commandRunner {
310+
_commandRunner = commandRunner;
311+
}
312+
307313
- (id<PFCommandRunning>)commandRunner {
308314
__block id<PFCommandRunning> runner = nil;
309315
dispatch_sync(_commandRunnerAccessQueue, ^{

Parse/Internal/ParseManagerPrivate.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) 2015-present, Parse, LLC.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "ParseManager.h"
11+
12+
@interface ParseManager ()
13+
14+
- (void)setCommandRunner:(id<PFCommandRunning>)commandRunner;
15+
16+
@end

Parse/PFInstallation.m

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#import "PFQueryPrivate.h"
2929
#import "Parse_Private.h"
3030
#import "PFErrorUtilities.h"
31+
#import "PFObjectState_Private.h"
32+
#import "PFObjectConstants.h"
3133

3234
@implementation PFInstallation (Private)
3335

@@ -36,7 +38,8 @@ @implementation PFInstallation (Private)
3638
+ (void)initialize {
3739
static dispatch_once_t onceToken;
3840
dispatch_once(&onceToken, ^{
39-
protectedKeys = PF_SET(PFInstallationKeyDeviceType,
41+
protectedKeys = PF_SET(PFObjectObjectIdRESTKey,
42+
PFInstallationKeyDeviceType,
4043
PFInstallationKeyInstallationId,
4144
PFInstallationKeyTimeZone,
4245
PFInstallationKeyLocaleIdentifier,
@@ -83,6 +86,14 @@ - (NSString *)displayClassName {
8386
return NSStringFromClass([PFInstallation class]);
8487
}
8588

89+
///--------------------------------------
90+
#pragma mark - Properties
91+
///--------------------------------------
92+
93+
- (void) setObjectId:(NSString *)objectId {
94+
PFParameterAssertionFailure(@"Installation's objectIds cannot be changed");
95+
}
96+
8697
///--------------------------------------
8798
#pragma mark - Command Handlers
8899
///--------------------------------------
@@ -223,16 +234,13 @@ - (void)setChannels:(NSArray<NSString *> *)channels {
223234

224235
- (BFTask *)saveAsync:(BFTask *)toAwait {
225236
return [[super saveAsync:toAwait] continueWithBlock:^id(BFTask *task) {
226-
// Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed.
227-
if ([Parse _currentManager].offlineStoreLoaded) {
228-
return task;
229-
}
230-
231237
if (task.error.code == kPFErrorObjectNotFound) {
232238
@synchronized (self.lock) {
233239
// Retry the fetch as a save operation because this Installation was deleted on the server.
234240
// We always want [currentInstallation save] to succeed.
235-
self.objectId = nil;
241+
PFObjectState *state = [PFObjectState stateWithState:self._state];
242+
state.objectId = nil;
243+
self._state = state;
236244
[self _markAllFieldsDirty];
237245
return [super saveAsync:nil];
238246
}

Tests/Unit/InstallationUnitTests.m

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,64 @@
66
* LICENSE file in the root directory of this source tree. An additional grant
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*/
9+
#import <OCMock/OCMock.h>
910

1011
#import "PFInstallation.h"
1112
#import "PFUnitTestCase.h"
1213
#import "Parse.h"
14+
#import "Parse_Private.h"
15+
#import "PFCommandRunning.h"
16+
#import "ParseManagerPrivate.h"
17+
#import "PFObjectState.h"
18+
#import "PFObjectPrivate.h"
1319

1420
@interface InstallationUnitTests : PFUnitTestCase
1521

1622
@end
1723

1824
@implementation InstallationUnitTests
1925

26+
- (void)testInstallationObjectIdCannotBeChanged {
27+
PFInstallation *installation = [PFInstallation currentInstallation];
28+
PFAssertThrowsInvalidArgumentException(installation.objectId = nil);
29+
PFAssertThrowsInvalidArgumentException(installation[@"objectId"] = @"abc");
30+
}
31+
32+
- (void)testObjectNotFoundWhenSave {
33+
#if TARGET_OS_IOS
34+
// enable LDS
35+
[[Parse _currentManager]loadOfflineStoreWithOptions:0];
36+
PFOfflineStore *offlineStoreSpy = PFPartialMock([Parse _currentManager].offlineStore);
37+
[Parse _currentManager].offlineStore = offlineStoreSpy;
38+
39+
// create and save installation
40+
PFInstallation *installation = [PFInstallation currentInstallation];
41+
PFObjectState *state = [PFObjectState stateWithParseClassName:[PFInstallation parseClassName] objectId:@"abc" isComplete:YES];
42+
installation._state = state;
43+
[installation save];
44+
45+
// mocking installation was deleted on the server
46+
id commandRunner = PFStrictProtocolMock(@protocol(PFCommandRunning));
47+
[Parse _currentManager].commandRunner = commandRunner;
48+
49+
BFTask *mockedTask = [BFTask taskWithError:[NSError errorWithDomain:@"Object Not Found" code:kPFErrorObjectNotFound userInfo:nil]];
50+
51+
__block int callCount = 0;
52+
OCMStub([commandRunner runCommandAsync:[OCMArg any] withOptions:PFCommandRunningOptionRetryIfFailed])
53+
.andReturn(mockedTask)
54+
.andDo(^(NSInvocation *invocation) {
55+
callCount++;
56+
});
57+
58+
installation.deviceToken = @"11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306";
59+
[installation save];
60+
OCMVerifyAll(commandRunner);
61+
XCTAssertEqual(2, callCount);
62+
OCMVerify([offlineStoreSpy updateObjectIdForObject:installation oldObjectId:nil newObjectId:@"abc"]);
63+
OCMVerify([offlineStoreSpy updateObjectIdForObject:installation oldObjectId:@"abc" newObjectId:nil]);
64+
#endif
65+
}
66+
2067
- (void)testInstallationImmutableFieldsCannotBeChanged {
2168
PFInstallation *installation = [PFInstallation currentInstallation];
2269
installation.deviceToken = @"11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306";

0 commit comments

Comments
 (0)