Skip to content

Commit bccbcbb

Browse files
committed
Merge pull request #73 from ParsePlatform/wangmengyan.t8145279_create_ParseFileController_save_and_fetch_file_version
Update ParseFileController to allow save file not only byte array
2 parents 0929626 + 6929721 commit bccbcbb

File tree

3 files changed

+137
-7
lines changed

3 files changed

+137
-7
lines changed

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,54 @@ public ParseFile.State then(Task<JSONObject> task) throws Exception {
114114

115115
return newState;
116116
}
117-
}, Task.BACKGROUND_EXECUTOR);
117+
}, ParseExecutors.io());
118+
}
119+
120+
public Task<ParseFile.State> saveAsync(
121+
final ParseFile.State state,
122+
final File file,
123+
String sessionToken,
124+
ProgressCallback uploadProgressCallback,
125+
Task<Void> cancellationToken) {
126+
if (state.url() != null) { // !isDirty
127+
return Task.forResult(state);
128+
}
129+
if (cancellationToken != null && cancellationToken.isCancelled()) {
130+
return Task.cancelled();
131+
}
132+
133+
final ParseRESTCommand command = new ParseRESTFileCommand.Builder()
134+
.fileName(state.name())
135+
.file(file)
136+
.contentType(state.mimeType())
137+
.sessionToken(sessionToken)
138+
.build();
139+
command.enableRetrying();
140+
141+
return command.executeAsync(
142+
restClient,
143+
uploadProgressCallback,
144+
null,
145+
cancellationToken
146+
).onSuccess(new Continuation<JSONObject, ParseFile.State>() {
147+
@Override
148+
public ParseFile.State then(Task<JSONObject> task) throws Exception {
149+
JSONObject result = task.getResult();
150+
ParseFile.State newState = new ParseFile.State.Builder(state)
151+
.name(result.getString("name"))
152+
.url(result.getString("url"))
153+
.build();
154+
155+
// Write data to cache
156+
try {
157+
ParseFileUtils.copyFile(file, getCacheFile(newState));
158+
} catch (IOException e) {
159+
// do nothing
160+
}
161+
162+
return newState;
163+
}
164+
}, ParseExecutors.io());
118165
}
119166

120167
public Task<File> fetchAsync(

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@
1111
/**
1212
* REST network command for creating & uploading {@link ParseFile}s.
1313
*/
14+
15+
import java.io.File;
16+
1417
/** package */ class ParseRESTFileCommand extends ParseRESTCommand {
1518

1619
public static class Builder extends Init<Builder> {
1720

1821
private byte[] data = null;
1922
private String contentType = null;
23+
private File file;
2024

2125
public Builder() {
2226
// We only ever use ParseRESTFileCommand for file uploads, so default to POST.
@@ -37,6 +41,11 @@ public Builder contentType(String contentType) {
3741
return this;
3842
}
3943

44+
public Builder file(File file) {
45+
this.file = file;
46+
return this;
47+
}
48+
4049
@Override
4150
/* package */ Builder self() {
4251
return this;
@@ -49,18 +58,28 @@ public ParseRESTFileCommand build() {
4958

5059
private final byte[] data;
5160
private final String contentType;
61+
private final File file;
5262

5363
public ParseRESTFileCommand(Builder builder) {
5464
super(builder);
65+
if (builder.file != null && builder.data != null) {
66+
throw new IllegalArgumentException("File and data can not be set at the same time");
67+
}
5568
this.data = builder.data;
5669
this.contentType = builder.contentType;
70+
this.file = builder.file;
5771
}
5872

5973
@Override
6074
protected ParseHttpBody newBody(final ProgressCallback progressCallback) {
75+
// TODO(mengyan): Delete ParseByteArrayHttpBody when we change input byte array to staged file
76+
// in ParseFileController
6177
if (progressCallback == null) {
62-
return new ParseByteArrayHttpBody(data, contentType);
78+
return data != null ?
79+
new ParseByteArrayHttpBody(data, contentType) : new ParseFileHttpBody(file, contentType);
6380
}
64-
return new ParseCountingByteArrayHttpBody(data, contentType, progressCallback);
81+
return data != null ?
82+
new ParseCountingByteArrayHttpBody(data, contentType, progressCallback) :
83+
new ParseCountingFileHttpBody(file, contentType, progressCallback);
6584
}
6685
}

Parse/src/test/java/com/parse/ParseFileControllerTest.java

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void testSaveAsyncNotDirty() throws Exception {
9797
ParseFile.State state = new ParseFile.State.Builder()
9898
.url("http://example.com")
9999
.build();
100-
Task<ParseFile.State> task = controller.saveAsync(state, null, null, null, null);
100+
Task<ParseFile.State> task = controller.saveAsync(state, (byte[])null, null, null, null);
101101
task.waitForCompletion();
102102

103103
verify(restClient, times(0)).execute(any(ParseHttpRequest.class));
@@ -113,15 +113,15 @@ public void testSaveAsyncAlreadyCancelled() throws Exception {
113113

114114
ParseFile.State state = new ParseFile.State.Builder().build();
115115
Task<Void> cancellationToken = Task.cancelled();
116-
Task<ParseFile.State> task = controller.saveAsync(state, null, null, null, cancellationToken);
116+
Task<ParseFile.State> task = controller.saveAsync(state, (byte[])null, null, null, cancellationToken);
117117
task.waitForCompletion();
118118

119119
verify(restClient, times(0)).execute(any(ParseHttpRequest.class));
120120
assertTrue(task.isCancelled());
121121
}
122122

123123
@Test
124-
public void testSaveAsyncSuccess() throws Exception {
124+
public void testSaveAsyncSuccessWithByteArray() throws Exception {
125125
JSONObject json = new JSONObject();
126126
json.put("name", "new_file_name");
127127
json.put("url", "http://example.com");
@@ -155,7 +155,43 @@ public void testSaveAsyncSuccess() throws Exception {
155155
}
156156

157157
@Test
158-
public void testSaveAsyncFailure() throws Exception {
158+
public void testSaveAsyncSuccessWithFile() throws Exception {
159+
JSONObject json = new JSONObject();
160+
json.put("name", "new_file_name");
161+
json.put("url", "http://example.com");
162+
String content = json.toString();
163+
164+
ParseHttpResponse response = mock(ParseHttpResponse.class);
165+
when(response.getStatusCode()).thenReturn(200);
166+
when(response.getContent()).thenReturn(new ByteArrayInputStream(content.getBytes()));
167+
when(response.getTotalSize()).thenReturn((long) content.length());
168+
169+
ParseHttpClient restClient = mock(ParseHttpClient.class);
170+
when(restClient.execute(any(ParseHttpRequest.class))).thenReturn(response);
171+
172+
File root = temporaryFolder.getRoot();
173+
ParseFileController controller = new ParseFileController(restClient, root);
174+
175+
File file = new File(root, "test");
176+
ParseFileUtils.writeStringToFile(file, "content", "UTF-8");
177+
ParseFile.State state = new ParseFile.State.Builder()
178+
.name("file_name")
179+
.mimeType("mime_type")
180+
.build();
181+
Task<ParseFile.State> task = controller.saveAsync(state, file, null, null, null);
182+
ParseFile.State result = ParseTaskUtils.wait(task);
183+
184+
verify(restClient, times(1)).execute(any(ParseHttpRequest.class));
185+
assertEquals("new_file_name", result.name());
186+
assertEquals("http://example.com", result.url());
187+
File cachedFile = new File(root, "new_file_name");
188+
assertTrue(cachedFile.exists());
189+
assertTrue(file.exists());
190+
assertEquals("content", ParseFileUtils.readFileToString(cachedFile, "UTF-8"));
191+
}
192+
193+
@Test
194+
public void testSaveAsyncFailureWithByteArray() throws Exception {
159195
// TODO(grantland): Remove once we no longer rely on retry logic.
160196
ParseRequest.setDefaultInitialRetryDelay(1L);
161197

@@ -180,6 +216,34 @@ public void testSaveAsyncFailure() throws Exception {
180216
assertEquals(0, root.listFiles().length);
181217
}
182218

219+
@Test
220+
public void testSaveAsyncFailureWithFile() throws Exception {
221+
// TODO(grantland): Remove once we no longer rely on retry logic.
222+
ParseRequest.setDefaultInitialRetryDelay(1L);
223+
224+
ParseHttpClient restClient = mock(ParseHttpClient.class);
225+
when(restClient.execute(any(ParseHttpRequest.class))).thenThrow(new IOException());
226+
227+
File root = temporaryFolder.getRoot();
228+
ParseFileController controller = new ParseFileController(restClient, root);
229+
230+
File file = temporaryFolder.newFile("test");
231+
ParseFile.State state = new ParseFile.State.Builder()
232+
.build();
233+
Task<ParseFile.State> task = controller.saveAsync(state, file, null, null, null);
234+
task.waitForCompletion();
235+
236+
// TODO(grantland): Abstract out command runner so we don't have to account for retries.
237+
verify(restClient, times(5)).execute(any(ParseHttpRequest.class));
238+
assertTrue(task.isFaulted());
239+
Exception error = task.getError();
240+
assertThat(error, instanceOf(ParseException.class));
241+
assertEquals(ParseException.CONNECTION_FAILED, ((ParseException) error).getCode());
242+
// Make sure the original file is not deleted and there is no cache file in the folder
243+
assertEquals(1, root.listFiles().length);
244+
assertTrue(file.exists());
245+
}
246+
183247
//endregion
184248

185249
//region testFetchAsync

0 commit comments

Comments
 (0)