Skip to content

Commit 8cea8da

Browse files
Added documentation, tests.
1 parent f3e78cc commit 8cea8da

File tree

2 files changed

+185
-46
lines changed

2 files changed

+185
-46
lines changed

Parse/src/main/java/com/parse/Parse.java

+125-46
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.UnsupportedEncodingException;
2525
import java.util.ArrayList;
2626
import java.util.Collection;
27+
import java.util.Collections;
2728
import java.util.HashSet;
2829
import java.util.List;
2930
import java.util.Set;
@@ -36,28 +37,103 @@
3637
* library.
3738
*/
3839
public class Parse {
40+
/**
41+
* Represents an opaque configuration for the {@code Parse} SDK configuration.
42+
*/
3943
public static final class Configuration {
44+
/**
45+
* Allows for simple constructing of a {@code Configuration} object.
46+
*/
4047
public static final class Builder {
4148
private Context context;
4249
private String applicationId;
4350
private String clientKey;
4451
private boolean localDataStoreEnabled;
4552
private List<ParseNetworkInterceptor> interceptors;
4653

54+
/**
55+
* Initialize a bulider with a given context.
56+
*
57+
* This context will then be passed through to the rest of the Parse SDK for use during
58+
* initialization.
59+
*
60+
* <p/>
61+
* You may define {@code com.parse.APPLICATION_ID} and {@code com.parse.CLIENT_KEY}
62+
* {@code meta-data} in your {@code AndroidManifest.xml}:
63+
* <pre>
64+
* &lt;manifest ...&gt;
65+
*
66+
* ...
67+
*
68+
* &lt;application ...&gt;
69+
* &lt;meta-data
70+
* android:name="com.parse.APPLICATION_ID"
71+
* android:value="@string/parse_app_id" /&gt;
72+
* &lt;meta-data
73+
* android:name="com.parse.CLIENT_KEY"
74+
* android:value="@string/parse_client_key" /&gt;
75+
*
76+
* ...
77+
*
78+
* &lt;/application&gt;
79+
* &lt;/manifest&gt;
80+
* </pre>
81+
* <p/>
82+
*
83+
* This will cause the values for {@code applicationId} and {@code clientKey} to be set to
84+
* those defined in your manifest.
85+
*
86+
* @param context The active {@link Context} for your application. Cannot be null.
87+
*/
4788
public Builder(Context context) {
4889
this.context = context;
90+
91+
// Yes, our public API states we cannot be null. But for unit tests, it's easier just to
92+
// support null here.
93+
if (context != null) {
94+
Context applicationContext = context.getApplicationContext();
95+
Bundle metaData = ManifestInfo.getApplicationMetadata(applicationContext);
96+
if (metaData != null) {
97+
applicationId = metaData.getString(PARSE_APPLICATION_ID);
98+
clientKey = metaData.getString(PARSE_CLIENT_KEY);
99+
}
100+
}
49101
}
50102

103+
/**
104+
* Set the application id to be used by Parse.
105+
*
106+
* This method is only required if you intend to use a different {@code applicationId} than
107+
* is defined by {@code com.parse.APPLICATION_ID} in your {@code AndroidManifest.xml}.
108+
*
109+
* @param applicationId The application id to set.
110+
* @return The same builder, for easy chaining.
111+
*/
51112
public Builder applicationId(String applicationId) {
52113
this.applicationId = applicationId;
53114
return this;
54115
}
55116

117+
/**
118+
* Set the client key to be used by Parse.
119+
*
120+
* This method is only required if you intend to use a different {@code clientKey} than
121+
* is defined by {@code com.parse.CLIENT_KEY} in your {@code AndroidManifest.xml}.
122+
*
123+
* @param clientKey The client key to set.
124+
* @return The same builder, for easy chaining.
125+
*/
56126
public Builder clientKey(String clientKey) {
57127
this.clientKey = clientKey;
58128
return this;
59129
}
60130

131+
/**
132+
* Add a {@link ParseNetworkInterceptor}.
133+
*
134+
* @param interceptor The interceptor to add.
135+
* @return The same builder, for easy chaining.
136+
*/
61137
public Builder addNetworkInterceptor(ParseNetworkInterceptor interceptor) {
62138
if (interceptors == null) {
63139
interceptors = new ArrayList<>();
@@ -66,36 +142,52 @@ public Builder addNetworkInterceptor(ParseNetworkInterceptor interceptor) {
66142
return this;
67143
}
68144

145+
/**
146+
* Enable pinning in your application. This must be called before your application can use
147+
* pinning.
148+
* @return The same builder, for easy chaining.
149+
*/
69150
public Builder enableLocalDataStore() {
70151
localDataStoreEnabled = true;
71152
return this;
72153
}
73154

74155
private Builder setNetworkInterceptors(Collection<ParseNetworkInterceptor> interceptors) {
75156
if (interceptors != null) {
76-
interceptors.clear();
77-
interceptors.addAll(interceptors);
157+
this.interceptors.clear();
158+
this.interceptors.addAll(interceptors);
78159
}
79160
return this;
80161
}
81162

163+
private Builder setLocalDatastoreEnabled(boolean enabled) {
164+
localDataStoreEnabled = enabled;
165+
return this;
166+
}
167+
168+
/**
169+
* Construct this builder into a concrete {@code Configuration} instance.
170+
* @return A constructed {@code Configuration} object.
171+
*/
82172
public Configuration build() {
83173
return new Configuration(this);
84174
}
85175
}
86176

87-
private final Context context;
88-
private final String applicationId;
89-
private final String clientKey;
90-
private final boolean localDataStoreEnabled;
91-
private final List<ParseNetworkInterceptor> interceptors;
177+
/* package for tests */ final Context context;
178+
/* package for tests */ final String applicationId;
179+
/* package for tests */ final String clientKey;
180+
/* package for tests */ final boolean localDataStoreEnabled;
181+
/* package for tests */ final List<ParseNetworkInterceptor> interceptors;
92182

93183
private Configuration(Builder builder) {
94184
this.context = builder.context;
95185
this.applicationId = builder.applicationId;
96186
this.clientKey = builder.clientKey;
97187
this.localDataStoreEnabled = builder.localDataStoreEnabled;
98-
this.interceptors = builder.interceptors;
188+
this.interceptors = builder.interceptors != null ?
189+
Collections.unmodifiableList(new ArrayList<>(builder.interceptors)) :
190+
null;
99191
}
100192
}
101193

@@ -110,8 +202,6 @@ private Configuration(Builder builder) {
110202
private static boolean isLocalDatastoreEnabled;
111203
private static OfflineStore offlineStore;
112204

113-
private static Configuration configuration;
114-
115205
/**
116206
* Enable pinning in your application. This must be called before your application can use
117207
* pinning. You must invoke {@code enableLocalDatastore(Context)} before
@@ -199,36 +289,23 @@ public static void enableLocalDatastore(Context context) {
199289
* The active {@link Context} for your application.
200290
*/
201291
public static void initialize(Context context) {
202-
Context applicationContext = context.getApplicationContext();
203-
String applicationId;
204-
String clientKey;
205-
Bundle metaData = ManifestInfo.getApplicationMetadata(applicationContext);
206-
if (metaData != null) {
207-
applicationId = metaData.getString(PARSE_APPLICATION_ID);
208-
clientKey = metaData.getString(PARSE_CLIENT_KEY);
209-
210-
if (applicationId == null) {
211-
throw new RuntimeException("ApplicationId not defined. " +
212-
"You must provide ApplicationId in AndroidManifest.xml.\n" +
213-
"<meta-data\n" +
214-
" android:name=\"com.parse.APPLICATION_ID\"\n" +
215-
" android:value=\"<Your Application Id>\" />");
216-
}
217-
if (clientKey == null) {
218-
throw new RuntimeException("ClientKey not defined. " +
219-
"You must provide ClientKey in AndroidManifest.xml.\n" +
220-
"<meta-data\n" +
221-
" android:name=\"com.parse.CLIENT_KEY\"\n" +
222-
" android:value=\"<Your Client Key>\" />");
223-
}
224-
} else {
225-
throw new RuntimeException("Can't get Application Metadata");
226-
}
227-
initialize(new Configuration.Builder(context)
228-
.applicationId(applicationId)
229-
.clientKey(clientKey)
230-
.setNetworkInterceptors(interceptors)
231-
.build()
292+
Configuration.Builder builder = new Configuration.Builder(context);
293+
if (builder.applicationId == null) {
294+
throw new RuntimeException("ApplicationId not defined. " +
295+
"You must provide ApplicationId in AndroidManifest.xml.\n" +
296+
"<meta-data\n" +
297+
" android:name=\"com.parse.APPLICATION_ID\"\n" +
298+
" android:value=\"<Your Application Id>\" />");
299+
} if (builder.clientKey == null) {
300+
throw new RuntimeException("ClientKey not defined. " +
301+
"You must provide ClientKey in AndroidManifest.xml.\n" +
302+
"<meta-data\n" +
303+
" android:name=\"com.parse.CLIENT_KEY\"\n" +
304+
" android:value=\"<Your Client Key>\" />");
305+
}
306+
initialize(builder.setNetworkInterceptors(interceptors)
307+
.setLocalDatastoreEnabled(isLocalDatastoreEnabled)
308+
.build()
232309
);
233310
}
234311

@@ -263,26 +340,29 @@ public static void initialize(Context context, String applicationId, String clie
263340
.applicationId(applicationId)
264341
.clientKey(clientKey)
265342
.setNetworkInterceptors(interceptors)
343+
.setLocalDatastoreEnabled(isLocalDatastoreEnabled)
266344
.build()
267345
);
268346
}
269347

270348
public static void initialize(Configuration configuration) {
271-
Parse.configuration = configuration;
349+
// NOTE (richardross): We will need this here, as ParsePlugins uses the return value of
350+
// isLocalDataStoreEnabled() to perform additional behavior.
351+
isLocalDatastoreEnabled = configuration.localDataStoreEnabled;
272352

273353
ParsePlugins.Android.initialize(configuration.context, configuration.applicationId, configuration.clientKey);
274354
Context applicationContext = configuration.context.getApplicationContext();
275355

276356
ParseHttpClient.setKeepAlive(true);
277357
ParseHttpClient.setMaxConnections(20);
278358
// If we have interceptors in list, we have to initialize all http clients and add interceptors
279-
if (interceptors != null) {
280-
initializeParseHttpClientsWithParseNetworkInterceptors();
359+
if (configuration.interceptors != null) {
360+
initializeParseHttpClientsWithParseNetworkInterceptors(configuration.interceptors);
281361
}
282362

283363
ParseObject.registerParseSubclasses();
284364

285-
if (isLocalDatastoreEnabled()) {
365+
if (configuration.localDataStoreEnabled) {
286366
offlineStore = new OfflineStore(configuration.context);
287367
} else {
288368
ParseKeyValueCache.initialize(configuration.context);
@@ -665,9 +745,8 @@ private Parse() {
665745
private static List<ParseNetworkInterceptor> interceptors;
666746

667747
// Initialize all necessary http clients and add interceptors to these http clients
668-
private static void initializeParseHttpClientsWithParseNetworkInterceptors() {
748+
private static void initializeParseHttpClientsWithParseNetworkInterceptors(List<ParseNetworkInterceptor> interceptors) {
669749
// This means developers have not called addInterceptor method so we should do nothing.
670-
List<ParseNetworkInterceptor> interceptors = configuration.interceptors;
671750
if (interceptors == null) {
672751
return;
673752
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
package com.parse;
10+
11+
import com.parse.http.ParseNetworkInterceptor;
12+
13+
import org.junit.Test;
14+
15+
import static org.junit.Assert.assertEquals;
16+
import static org.junit.Assert.assertFalse;
17+
import static org.junit.Assert.assertNull;
18+
import static org.junit.Assert.assertTrue;
19+
import static org.junit.Assert.fail;
20+
import static org.mockito.Mockito.mock;
21+
22+
public class ParseClientConfigurationTest {
23+
24+
@Test
25+
public void testBuilder() {
26+
Parse.Configuration.Builder builder = new Parse.Configuration.Builder(null);
27+
builder.applicationId("foo");
28+
builder.clientKey("bar");
29+
builder.enableLocalDataStore();
30+
Parse.Configuration configuration = builder.build();
31+
32+
assertNull(configuration.context);
33+
assertEquals(configuration.applicationId, "foo");
34+
assertEquals(configuration.clientKey, "bar");
35+
assertEquals(configuration.localDataStoreEnabled, true);
36+
}
37+
38+
@Test
39+
public void testNetworkInterceptors() {
40+
ParseNetworkInterceptor interceptorA = mock(ParseNetworkInterceptor.class);
41+
ParseNetworkInterceptor interceptorB = mock(ParseNetworkInterceptor.class);
42+
43+
Parse.Configuration.Builder builder = new Parse.Configuration.Builder(null);
44+
45+
builder.addNetworkInterceptor(interceptorA);
46+
Parse.Configuration configurationA = builder.build();
47+
builder.addNetworkInterceptor(interceptorB);
48+
Parse.Configuration configurationB = builder.build();
49+
50+
assertFalse(configurationA.interceptors.contains(interceptorB));
51+
assertTrue(configurationB.interceptors.contains(interceptorB));
52+
53+
try {
54+
configurationA.interceptors.add(interceptorB);
55+
fail();
56+
} catch (UnsupportedOperationException ex) {
57+
// Expected
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)