Skip to content

Commit fada61e

Browse files
stevestencildplewis
andcommitted
Added support to delete Parse.File (parse-community#1067)
* added console.log * added _batchCount and _batchIndex properties * test batch index and count * added hint * added hint to ParseQuery * fixed failed tests * removed _batchIndex and _batchCount * added documentation and support for chaining * added documentation and tests * added delete file route * fix implementation * error tests Co-authored-by: Diamond Lewis <[email protected]>
1 parent c5b3ea2 commit fada61e

File tree

5 files changed

+144
-29
lines changed

5 files changed

+144
-29
lines changed

integration/test/ParseFileTest.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const Parse = require('../../node');
66

77
describe('Parse.File', () => {
88
beforeEach((done) => {
9-
Parse.initialize('integration');
9+
Parse.initialize('integration', null, 'notsosecret');
1010
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
1111
Parse.Storage._clear();
1212
clear().then(done).catch(done.fail);
@@ -91,4 +91,28 @@ describe('Parse.File', () => {
9191
data = await file.getData();
9292
assert.equal(data, 'ParseA==');
9393
});
94+
95+
it('can delete file', async () => {
96+
const parseLogo = 'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
97+
const file = new Parse.File('parse-server-logo', { uri: parseLogo });
98+
await file.save();
99+
const data = await file.getData();
100+
101+
const deletedFile = await file.destroy();
102+
const deletedData = await file.getData();
103+
assert.equal(file, deletedFile);
104+
assert.notEqual(data, deletedData);
105+
});
106+
107+
it('can handle delete file error', async () => {
108+
const parseLogo = 'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
109+
const file = new Parse.File('parse-server-logo', { uri: parseLogo });
110+
try {
111+
await file.destroy();
112+
assert.equal(false, true);
113+
} catch (e) {
114+
assert.equal(e.code, Parse.Error.FILE_DELETE_ERROR);
115+
assert.equal(e.message, 'Could not delete file.');
116+
}
117+
});
94118
});

src/ParseFile.js

+39-1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,24 @@ class ParseFile {
271271
this._requestTask = null;
272272
}
273273

274+
/**
275+
* Deletes the file from the Parse cloud.
276+
* In Cloud Code and Node only with Master Key
277+
*
278+
* @return {Promise} Promise that is resolved when the delete finishes.
279+
*/
280+
destroy() {
281+
if (!this._name) {
282+
throw new Error('Cannot delete an unsaved ParseFile.');
283+
}
284+
const controller = CoreManager.getFileController();
285+
return controller.deleteFile(this._name).then(() => {
286+
this._data = null;
287+
this._requestTask = null;
288+
return this;
289+
});
290+
}
291+
274292
toJSON(): { name: ?string, url: ?string } {
275293
return {
276294
__type: 'File',
@@ -423,9 +441,29 @@ const DefaultController = {
423441
});
424442
},
425443

444+
deleteFile: function(name) {
445+
const headers = {
446+
'X-Parse-Application-ID': CoreManager.get('APPLICATION_ID'),
447+
'X-Parse-Master-Key': CoreManager.get('MASTER_KEY'),
448+
};
449+
let url = CoreManager.get('SERVER_URL');
450+
if (url[url.length - 1] !== '/') {
451+
url += '/';
452+
}
453+
url += 'files/' + name;
454+
return CoreManager.getRESTController().ajax('DELETE', url, '', headers).catch(response => {
455+
// TODO: return JSON object in server
456+
if (!response || response === 'SyntaxError: Unexpected end of JSON input') {
457+
return Promise.resolve();
458+
} else {
459+
return CoreManager.getRESTController().handleError(response);
460+
}
461+
});
462+
},
463+
426464
_setXHR(xhr: any) {
427465
XHR = xhr;
428-
}
466+
},
429467
};
430468

431469
CoreManager.setFileController(DefaultController);

src/ParseObject.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -2116,17 +2116,17 @@ const DefaultController = {
21162116
return Promise.resolve(results);
21172117
});
21182118
} else {
2119-
const RESTController = CoreManager.getRESTController();
2120-
const params = {};
2121-
if (options && options.include) {
2122-
params.include = options.include.join();
2123-
}
21242119
if (!target.id) {
21252120
return Promise.reject(new ParseError(
21262121
ParseError.MISSING_OBJECT_ID,
21272122
'Object does not have an ID'
21282123
));
21292124
}
2125+
const RESTController = CoreManager.getRESTController();
2126+
const params = {};
2127+
if (options && options.include) {
2128+
params.include = options.include.join();
2129+
}
21302130
return RESTController.request(
21312131
'GET',
21322132
'classes/' + target.className + '/' + target._getId(),

src/RESTController.js

+23-22
Original file line numberDiff line numberDiff line change
@@ -270,31 +270,32 @@ const RESTController = {
270270
return response;
271271
}
272272
});
273-
}).catch(function(response: { responseText: string }) {
274-
// Transform the error into an instance of ParseError by trying to parse
275-
// the error string as JSON
276-
let error;
277-
if (response && response.responseText) {
278-
try {
279-
const errorJSON = JSON.parse(response.responseText);
280-
error = new ParseError(errorJSON.code, errorJSON.error);
281-
} catch (e) {
282-
// If we fail to parse the error text, that's okay.
283-
error = new ParseError(
284-
ParseError.INVALID_JSON,
285-
'Received an error with invalid JSON from Parse: ' +
286-
response.responseText
287-
);
288-
}
289-
} else {
273+
}).catch(RESTController.handleError);
274+
},
275+
276+
handleError(response) {
277+
// Transform the error into an instance of ParseError by trying to parse
278+
// the error string as JSON
279+
let error;
280+
if (response && response.responseText) {
281+
try {
282+
const errorJSON = JSON.parse(response.responseText);
283+
error = new ParseError(errorJSON.code, errorJSON.error);
284+
} catch (e) {
285+
// If we fail to parse the error text, that's okay.
290286
error = new ParseError(
291-
ParseError.CONNECTION_FAILED,
292-
'XMLHttpRequest failed: ' + JSON.stringify(response)
287+
ParseError.INVALID_JSON,
288+
'Received an error with invalid JSON from Parse: ' +
289+
response.responseText
293290
);
294291
}
295-
296-
return Promise.reject(error);
297-
});
292+
} else {
293+
error = new ParseError(
294+
ParseError.CONNECTION_FAILED,
295+
'XMLHttpRequest failed: ' + JSON.stringify(response)
296+
);
297+
}
298+
return Promise.reject(error);
298299
},
299300

300301
_setXHR(xhr: any) {

src/__tests__/ParseFile-test.js

+52
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jest.autoMockOff();
1111
jest.mock('http');
1212
jest.mock('https');
1313

14+
const ParseError = require('../ParseError').default;
1415
const ParseFile = require('../ParseFile').default;
1516
const CoreManager = require('../CoreManager');
1617
const EventEmitter = require('../EventEmitter');
@@ -613,4 +614,55 @@ describe('FileController', () => {
613614
expect(f.url()).toBe('https://files.parsetfss.com/a//api.parse.com/1/files/parse.txt');
614615
});
615616
});
617+
618+
it('should throw error if file deleted without name', async (done) => {
619+
const file = new ParseFile('', [1, 2, 3]);
620+
try {
621+
await file.destroy();
622+
} catch (e) {
623+
expect(e.message).toBe('Cannot delete an unsaved ParseFile.');
624+
done();
625+
}
626+
});
627+
628+
it('should delete file', async () => {
629+
const file = new ParseFile('filename', [1, 2, 3]);
630+
const ajax = jest.fn().mockResolvedValueOnce({ foo: 'bar' });
631+
CoreManager.setRESTController({ ajax, request: () => {} });
632+
const result = await file.destroy();
633+
expect(result).toEqual(file);
634+
expect(ajax).toHaveBeenCalledWith('DELETE', 'https://api.parse.com/1/files/filename', '', {
635+
"X-Parse-Application-ID": null,
636+
"X-Parse-Master-Key": null,
637+
});
638+
});
639+
640+
it('should handle delete file error', async () => {
641+
const file = new ParseFile('filename', [1, 2, 3]);
642+
const ajax = jest.fn().mockResolvedValueOnce(Promise.reject(new ParseError(403, 'Cannot delete file.')));
643+
const handleError = jest.fn();
644+
CoreManager.setRESTController({ ajax, request: () => {}, handleError });
645+
const result = await file.destroy();
646+
expect(result).toEqual(file);
647+
expect(ajax).toHaveBeenCalledWith('DELETE', 'https://api.parse.com/1/files/filename', '', {
648+
"X-Parse-Application-ID": null,
649+
"X-Parse-Master-Key": null,
650+
});
651+
expect(handleError).toHaveBeenCalled();
652+
});
653+
654+
it('should handle delete file error invalid server response', async () => {
655+
const file = new ParseFile('filename', [1, 2, 3]);
656+
const response = null;
657+
const ajax = jest.fn().mockResolvedValueOnce(Promise.reject(response));
658+
const handleError = jest.fn();
659+
CoreManager.setRESTController({ ajax, request: () => {}, handleError });
660+
const result = await file.destroy();
661+
expect(result).toEqual(file);
662+
expect(ajax).toHaveBeenCalledWith('DELETE', 'https://api.parse.com/1/files/filename', '', {
663+
"X-Parse-Application-ID": null,
664+
"X-Parse-Master-Key": null,
665+
});
666+
expect(handleError).not.toHaveBeenCalled();
667+
});
616668
});

0 commit comments

Comments
 (0)