Skip to content

Commit a5b3fea

Browse files
committed
Merging dev into master
2 parents 41199d6 + 9bc0819 commit a5b3fea

File tree

71 files changed

+3429
-596
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+3429
-596
lines changed

.circleci/config.yml

+6-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ aliases:
4747
sudo npm install -g typescript
4848
cordova telemetry off
4949
./install.sh
50+
sudo chmod -R a+w /var/lib/gems/ && sudo chmod -R a+w /usr/local/bin
5051
gem install bundler
5152
gem install danger
5253
gem install danger-junit
@@ -75,9 +76,8 @@ executors:
7576
linux:
7677
working_directory: ~/SalesforceMobileSDK-Android
7778
docker:
78-
- image: circleci/android:api-29-node
79+
- image: cimg/android:2022.03.1-node
7980
environment:
80-
- TERM: "dumb"
8181
- GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
8282

8383
jobs:
@@ -92,11 +92,13 @@ jobs:
9292
name: Setup Environment
9393
command: |
9494
./install.sh
95+
sudo chmod -R a+w /var/lib/gems/ && sudo chmod -R a+w /usr/local/bin
9596
gem install bundler
9697
gem install danger
9798
gem install danger-junit
9899
gem install danger-android_lint
99100
gem install danger-jacoco
101+
echo $TEST_CREDENTIALS > ./shared/test/test_credentials.json
100102
- run:
101103
name: Run Android Lint
102104
command: ./gradlew lint
@@ -190,8 +192,8 @@ jobs:
190192
--directories-to-pull=/sdcard \
191193
--results-dir=<< parameters.lib >>-${CIRCLE_BUILD_NUM} \
192194
--results-history-name=<< parameters.lib >> \
193-
--timeout=15m --no-auto-google-login --no-record-video --no-performance-metrics
194-
no_output_timeout: 900
195+
--timeout=20m --no-auto-google-login --no-record-video --no-performance-metrics
196+
no_output_timeout: 1200
195197
- run:
196198
name: Copy test results data
197199
command: |

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ This pulls submodule dependencies from github.
2323
Introduction
2424
==
2525

26-
### What's New in 10.0.0
27-
See [release notes](https://github.com/forcedotcom/SalesforceMobileSDK-Android/releases/tag/v10.0.0).
26+
### What's New in 10.1.0
27+
See [release notes](https://github.com/forcedotcom/SalesforceMobileSDK-Android/releases).
2828

2929
### Native Applications
3030
The Salesforce Mobile SDK provides essential libraries for quickly building native mobile apps that seamlessly integrate with the Salesforce cloud architecture. Out of the box, we provide an implementation of OAuth2, abstracting away the complexity of securely storing refresh tokens or fetching a new session ID when a session expires. The SDK also provides Java wrappers for the Salesforce REST API, making it easy to retrieve, store, and manipulate data.

build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ buildscript {
1010
}
1111

1212
dependencies {
13-
classpath 'com.android.tools.build:gradle:7.1.0'
13+
classpath 'com.android.tools.build:gradle:7.2.1'
1414
classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
1515
}
1616
}
1717

1818
allprojects {
1919
group = 'com.salesforce.mobilesdk'
20-
version = '10.0.0'
20+
version = '10.1.0'
2121
repositories {
2222
mavenLocal()
2323
maven {

external/shared

Submodule shared updated 38 files
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

libs/MobileSync/AndroidManifest.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
44
package="com.salesforce.androidsdk.mobilesync"
5-
android:versionCode="76"
6-
android:versionName="10.0.0">
5+
android:versionCode="77"
6+
android:versionName="10.1.0.dev">
77

88
<application />
99

libs/MobileSync/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ android {
6969

7070
ext {
7171
PUBLISH_GROUP_ID = 'com.salesforce.mobilesdk'
72-
PUBLISH_VERSION = '10.0.0'
72+
PUBLISH_VERSION = '10.1.0'
7373
PUBLISH_ARTIFACT_ID = 'MobileSync'
7474
}
7575

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/app/Features.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public class Features {
3434
public static final String FEATURE_LAYOUT_SYNC = "LY";
3535
public static final String FEATURE_METADATA_SYNC = "MD";
3636
public static final String FEATURE_RELATED_RECORDS = "RR";
37+
public static final String FEATURE_BRIEFCASE = "BC";
3738
public static final String FEATURE_MOBILE_SYNC = "SY";
38-
3939
}

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/manager/AdvancedSyncUpTask.java

+32-8
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
import com.salesforce.androidsdk.mobilesync.target.SyncUpTarget;
3232
import com.salesforce.androidsdk.mobilesync.util.SyncOptions;
3333
import com.salesforce.androidsdk.mobilesync.util.SyncState;
34-
35-
import org.json.JSONException;
36-
import org.json.JSONObject;
37-
34+
import com.salesforce.androidsdk.mobilesync.util.SyncState.MergeMode;
35+
import com.salesforce.androidsdk.smartstore.store.SmartStore;
3836
import java.io.IOException;
3937
import java.util.ArrayList;
38+
import java.util.HashMap;
4039
import java.util.List;
40+
import java.util.Map;
41+
import org.json.JSONException;
42+
import org.json.JSONObject;
4143

4244
/**
4345
* Runnable class responsible for running a sync up that uses and AdvancedSyncUpTarget
@@ -58,18 +60,24 @@ protected void syncUp(SyncState sync, SyncManager.SyncUpdateCallback callback, L
5860
List<JSONObject> batch = new ArrayList<>();
5961

6062
updateSync(sync, SyncState.Status.RUNNING, 0, callback);
63+
List<JSONObject> dirtyRecords = target.getFromLocalStore(syncManager, soupName, dirtyRecordIds);
64+
65+
// Figuring out what records need to be synced up based on merge mode and last mod date on server
66+
Map<String, Boolean> recordIdToShouldSyncUp = shouldSyncUpRecords(syncManager, target, dirtyRecords, options);
67+
68+
// Syncing up records
6169
for (int i=0; i<totalSize; i++) {
6270
checkIfStopRequested();
6371

64-
JSONObject record = target.getFromLocalStore(syncManager, soupName, dirtyRecordIds.get(i));
72+
JSONObject record = dirtyRecords.get(i);
73+
String recordId = record.getString(SmartStore.SOUP_ENTRY_ID);
6574

66-
if (shouldSyncUpRecord(target, record, options)) {
75+
if (Boolean.TRUE.equals(recordIdToShouldSyncUp.get(recordId))) {
6776
batch.add(record);
6877
}
6978

7079
// Process batch if max batch size reached or at the end of dirtyRecordIds
7180
if (batch.size() == maxBatchSize || i == totalSize - 1) {
72-
7381
((AdvancedSyncUpTarget) target).syncUpRecords(syncManager, batch, options.getFieldlist(), options.getMergeMode(), sync.getSoupName());
7482
batch.clear();
7583
}
@@ -80,4 +88,20 @@ protected void syncUp(SyncState sync, SyncManager.SyncUpdateCallback callback, L
8088
updateSync(sync, SyncState.Status.RUNNING, progress, callback);
8189
}
8290
}
83-
}}
91+
}
92+
93+
protected Map<String, Boolean> shouldSyncUpRecords(SyncManager syncManager, SyncUpTarget target, List<JSONObject> records, SyncOptions options) throws IOException, JSONException {
94+
Map<String, Boolean> recordIdToShouldSyncUp = new HashMap<>();
95+
96+
if (options.getMergeMode() == MergeMode.OVERWRITE) {
97+
for (JSONObject record : records) {
98+
String recordId = record.getString(SmartStore.SOUP_ENTRY_ID);
99+
recordIdToShouldSyncUp.put(recordId, true);
100+
}
101+
} else {
102+
recordIdToShouldSyncUp = target.areNewerThanServer(syncManager, records);
103+
}
104+
105+
return recordIdToShouldSyncUp;
106+
}
107+
}

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/manager/CleanSyncGhostsTask.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ protected void runSync() throws Exception {
7676
}
7777
}
7878

79+
syncManager.removeFromActiveSyncs(this);
7980
if (cleanSyncCallback != null) {
8081
cleanSyncCallback.onSuccess(localIdSize);
8182
}
8283

8384
} catch (Exception e) {
84-
8585
MobileSyncLogger.e(TAG, "Exception thrown cleaning resync ghosts", e);
86-
86+
syncManager.removeFromActiveSyncs(this);
8787
if (cleanSyncCallback != null) {
8888
cleanSyncCallback.onError(e);
8989
}

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/manager/SyncTask.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ public void run() {
9393
sync.setError(e.getMessage());
9494
// Update status to failed
9595
updateSync(sync, SyncState.Status.FAILED, UNCHANGED, callback);
96-
} finally {
97-
syncManager.removeFromActiveSyncs(this);
9896
}
9997
}
10098

@@ -141,6 +139,11 @@ protected void updateSync(SyncState sync, SyncState.Status status, int progress,
141139
} catch (SmartStore.SmartStoreException e) {
142140
MobileSyncLogger.e(TAG, "Unexpected smart store error for sync: " + sync.getId(), e);
143141
} finally {
142+
// Removing from active syncs before calling callback
143+
if (!sync.isRunning()) {
144+
syncManager.removeFromActiveSyncs(this);
145+
}
146+
144147
if (callback != null) {
145148
callback.onUpdate(sync);
146149
}

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/target/BatchSyncUpTarget.java

+33-30
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,14 @@
2727
package com.salesforce.androidsdk.mobilesync.target;
2828

2929
import com.salesforce.androidsdk.mobilesync.manager.SyncManager;
30+
import com.salesforce.androidsdk.mobilesync.target.CompositeRequestHelper.RecordRequest;
31+
import com.salesforce.androidsdk.mobilesync.target.CompositeRequestHelper.RecordResponse;
3032
import com.salesforce.androidsdk.mobilesync.util.Constants;
3133
import com.salesforce.androidsdk.mobilesync.util.SyncState;
32-
import com.salesforce.androidsdk.rest.CompositeResponse.CompositeSubResponse;
33-
import com.salesforce.androidsdk.rest.RestRequest;
34-
import com.salesforce.androidsdk.rest.RestResponse;
3534
import com.salesforce.androidsdk.smartstore.store.SmartStore;
3635
import com.salesforce.androidsdk.util.JSONObjectHelper;
3736
import java.io.IOException;
38-
import java.net.HttpURLConnection;
39-
import java.util.LinkedHashMap;
37+
import java.util.LinkedList;
4038
import java.util.List;
4139
import java.util.Map;
4240
import org.json.JSONException;
@@ -131,7 +129,7 @@ private void syncUpRecords(SyncManager syncManager, List<JSONObject> records, Li
131129
return;
132130
}
133131

134-
LinkedHashMap<String, RestRequest> refIdToRequests = new LinkedHashMap<>();
132+
List<RecordRequest> recordRequests = new LinkedList<>();
135133
for (int i = 0; i < records.size(); i++) {
136134
JSONObject record = records.get(i);
137135
String id = JSONObjectHelper.optString(record, getIdFieldName());
@@ -142,15 +140,18 @@ private void syncUpRecords(SyncManager syncManager, List<JSONObject> records, Li
142140
record.put(getIdFieldName(), id);
143141
}
144142

145-
RestRequest request = buildRequestForRecord(syncManager.apiVersion, record, fieldlist);
146-
if (request != null) refIdToRequests.put(id, request);
143+
RecordRequest request = buildRequestForRecord(record, fieldlist);
144+
if (request != null) {
145+
request.referenceId = id;
146+
recordRequests.add(request);
147+
}
147148
}
148149

149-
// Sending composite request
150-
Map<String, CompositeSubResponse> refIdToResponses = CompositeRequestHelper.sendCompositeRequest(syncManager, false, refIdToRequests);
150+
// Sending requests
151+
Map<String, RecordResponse> refIdToRecordResponses = sendRecordRequests(syncManager, recordRequests);
151152

152-
// Build refId to server id / status code / time stamp maps
153-
Map<String, String> refIdToServerId = CompositeRequestHelper.parseIdsFromResponses(refIdToResponses.values());
153+
// Build refId to server id map
154+
Map<String, String> refIdToServerId = CompositeRequestHelper.parseIdsFromResponses(refIdToRecordResponses);
154155

155156
// Will a re-run be required?
156157
boolean needReRun = false;
@@ -161,7 +162,7 @@ private void syncUpRecords(SyncManager syncManager, List<JSONObject> records, Li
161162
String id = record.getString(getIdFieldName());
162163

163164
if (isDirty(record)) {
164-
needReRun = needReRun || updateRecordInLocalStore(syncManager, syncSoupName, record, mergeMode, refIdToServerId, refIdToResponses.get(id), isReRun);
165+
needReRun = needReRun || updateRecordInLocalStore(syncManager, syncSoupName, record, mergeMode, refIdToServerId, refIdToRecordResponses.get(id), isReRun);
165166
}
166167
}
167168

@@ -172,9 +173,13 @@ private void syncUpRecords(SyncManager syncManager, List<JSONObject> records, Li
172173

173174
}
174175

175-
protected RestRequest buildRequestForRecord(String apiVersion,
176-
JSONObject record,
177-
List<String> fieldlist) throws JSONException {
176+
protected Map<String, RecordResponse> sendRecordRequests(SyncManager syncManager, List<RecordRequest> recordRequests)
177+
throws JSONException, IOException {
178+
return CompositeRequestHelper.sendAsCompositeBatchRequest(syncManager, false, recordRequests);
179+
}
180+
181+
protected RecordRequest buildRequestForRecord(JSONObject record,
182+
List<String> fieldlist) throws JSONException {
178183

179184
if (!isDirty(record)) {
180185
return null; // nothing to do
@@ -191,7 +196,7 @@ protected RestRequest buildRequestForRecord(String apiVersion,
191196
return null; // no need to go to server
192197
}
193198
else {
194-
return RestRequest.getRequestForDelete(apiVersion, objectType, id);
199+
return RecordRequest.requestForDelete(objectType, id);
195200
}
196201
}
197202
// Create/update cases
@@ -208,53 +213,52 @@ protected RestRequest buildRequestForRecord(String apiVersion,
208213
// where the the external id field is the id field
209214
// and the field is populated by a local id
210215
&& !isLocalId(externalId)) {
211-
return RestRequest.getRequestForUpsert(apiVersion, objectType, getExternalIdFieldName(), externalId, fields);
216+
return RecordRequest.requestForUpsert(objectType, getExternalIdFieldName(), externalId, fields);
212217
}
213218
// Do a create otherwise
214219
else {
215-
return RestRequest.getRequestForCreate(apiVersion, objectType, fields);
220+
return RecordRequest.requestForCreate(objectType, fields);
216221
}
217222
}
218223
else {
219224
fieldlist = this.updateFieldlist != null ? this.updateFieldlist : fieldlist;
220225
fields = buildFieldsMap(record, fieldlist, getIdFieldName(), getModificationDateFieldName());
221-
return RestRequest.getRequestForUpdate(apiVersion, objectType, id, fields);
226+
return RecordRequest.requestForUpdate(objectType, id, fields);
222227
}
223228
}
224229
}
225230

226231

227-
protected boolean updateRecordInLocalStore(SyncManager syncManager, String soupName, JSONObject record, SyncState.MergeMode mergeMode, Map<String, String> refIdToServerId, CompositeSubResponse response, boolean isReRun) throws JSONException, IOException {
232+
protected boolean updateRecordInLocalStore(SyncManager syncManager, String soupName, JSONObject record, SyncState.MergeMode mergeMode, Map<String, String> refIdToServerId, RecordResponse response, boolean isReRun) throws JSONException, IOException {
228233

229234
boolean needReRun = false;
230-
final Integer statusCode = response != null ? response.httpStatusCode : -1;
235+
String lastError = (response != null && response.errorJson != null) ? response.errorJson.toString() : null;
231236

232237
// Delete case
233238
if (isLocallyDeleted(record)) {
234239
if (isLocallyCreated(record) // we didn't go to the sever
235-
|| RestResponse.isSuccess(statusCode) // or we successfully deleted on the server
236-
|| statusCode == HttpURLConnection.HTTP_NOT_FOUND) // or the record was already deleted on the server
237-
240+
|| response.success // or we successfully deleted on the server
241+
|| response.recordDoesNotExist) // or the record was already deleted on the server
238242
{
239243
deleteFromLocalStore(syncManager, soupName, record);
240244
}
241245
// Failure
242246
else {
243-
saveRecordToLocalStoreWithError(syncManager, soupName, record, response != null ? response.toString() : null);
247+
saveRecordToLocalStoreWithError(syncManager, soupName, record, lastError);
244248
}
245249
}
246250
// Create / update case
247251
else {
248252
// Success case
249-
if (RestResponse.isSuccess(statusCode)) {
253+
if (response.success) {
250254
// Plugging server id in id field
251255
CompositeRequestHelper.updateReferences(record, getIdFieldName(), refIdToServerId);
252256

253257
// Clean and save
254258
cleanAndSaveInLocalStore(syncManager, soupName, record);
255259
}
256260
// Handling remotely deleted records
257-
else if (statusCode == HttpURLConnection.HTTP_NOT_FOUND
261+
else if (response.recordDoesNotExist
258262
&& mergeMode == SyncState.MergeMode.OVERWRITE // Record needs to be recreated
259263
&& !isReRun) {
260264
record.put(LOCAL, true);
@@ -263,10 +267,9 @@ else if (statusCode == HttpURLConnection.HTTP_NOT_FOUND
263267
}
264268
// Failure
265269
else {
266-
saveRecordToLocalStoreWithError(syncManager, soupName, record, response != null ? response.toString() : null);
270+
saveRecordToLocalStoreWithError(syncManager, soupName, record, lastError);
267271
}
268272
}
269273
return needReRun;
270274
}
271-
272275
}

0 commit comments

Comments
 (0)