Skip to content

Commit 1403efa

Browse files
Metadata tags (#1070)
* 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 support for metadata and tags * added more docs * removed validation for values * added docs for beforeSaveFile and afterSaveFile * updated docs * added delete file route * add getters * clean up * added decrement function to ParseObject * udpated scope * reverted package.json * added metadata and tags to save request * fixed lint fail * remove unnecessary code * fixed tests * fixed linting errors Co-authored-by: Diamond Lewis <[email protected]>
1 parent 7a06117 commit 1403efa

File tree

2 files changed

+111
-47
lines changed

2 files changed

+111
-47
lines changed

src/ParseFile.js

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -418,39 +418,40 @@ const DefaultController = {
418418
if (source.format !== 'file') {
419419
throw new Error('saveFile can only be used with File-type sources.');
420420
}
421-
// To directly upload a File, we use a REST-style AJAX request
422-
const headers = {
423-
'X-Parse-Application-ID': CoreManager.get('APPLICATION_ID'),
424-
'Content-Type': source.type || (source.file ? source.file.type : null)
421+
const base64Data = await new Promise((res, rej) => {
422+
// eslint-disable-next-line no-undef
423+
const reader = new FileReader();
424+
reader.readAsDataURL(source.file);
425+
reader.onload = () => res(reader.result);
426+
reader.onerror = error => rej(error);
427+
});
428+
// we only want the data after the comma
429+
// For example: "data:application/pdf;base64,JVBERi0xLjQKJ..." we would only want "JVBERi0xLjQKJ..."
430+
const [first, second] = base64Data.split(',');
431+
// in the event there is no 'data:application/pdf;base64,' at the beginning of the base64 string
432+
// use the entire string instead
433+
const data = second ? second : first;
434+
const newSource = {
435+
format: 'base64',
436+
base64: data,
437+
type: source.type || (source.file ? source.file.type : null),
425438
};
426-
const jsKey = CoreManager.get('JAVASCRIPT_KEY');
427-
if (jsKey) {
428-
headers['X-Parse-JavaScript-Key'] = jsKey;
429-
}
430-
let sessionToken = options.sessionToken;
431-
const userController = CoreManager.getUserController();
432-
if (!sessionToken && userController) {
433-
const currentUser = await userController.currentUserAsync();
434-
sessionToken = currentUser ? currentUser.getSessionToken() : undefined;
435-
}
436-
if (sessionToken) {
437-
headers['X-Parse-Session-Token'] = sessionToken;
438-
}
439-
let url = CoreManager.get('SERVER_URL');
440-
if (url[url.length - 1] !== '/') {
441-
url += '/';
442-
}
443-
url += 'files/' + name;
444-
return CoreManager.getRESTController().ajax('POST', url, source.file, headers, options).then(res=>res.response)
439+
return await DefaultController.saveBase64(name, newSource, options);
445440
},
446441

447442
saveBase64: function(name: string, source: FileSource, options?: FullOptions) {
448443
if (source.format !== 'base64') {
449444
throw new Error('saveBase64 can only be used with Base64-type sources.');
450445
}
451-
const data: { base64: any; _ContentType?: any } = {
452-
base64: source.base64
446+
const data: { base64: any; _ContentType?: any, fileData: Object } = {
447+
base64: source.base64,
448+
fileData: {
449+
metadata: { ...options.metadata },
450+
tags: { ...options.tags },
451+
},
453452
};
453+
delete options.metadata;
454+
delete options.tags;
454455
if (source.type) {
455456
data._ContentType = source.type;
456457
}

src/__tests__/ParseFile-test.js

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,15 @@ describe('FileController', () => {
373373
});
374374

375375
it('saves files via ajax', () => {
376-
const file = new ParseFile('parse.txt', [61, 170, 236, 120]);
376+
// eslint-disable-next-line no-undef
377+
const blob = new Blob([61, 170, 236, 120]);
378+
const file = new ParseFile('parse.txt', blob);
377379
file._source.format = 'file';
378380

379381
return file.save().then(function(f) {
380382
expect(f).toBe(file);
381-
expect(f.name()).toBe('/api.parse.com/1/files/parse.txt');
382-
expect(f.url()).toBe('https://files.parsetfss.com/a//api.parse.com/1/files/parse.txt');
383+
expect(f.name()).toBe('parse.txt');
384+
expect(f.url()).toBe('https://files.parsetfss.com/a/parse.txt');
383385
});
384386
});
385387

@@ -647,13 +649,15 @@ describe('FileController', () => {
647649
});
648650
};
649651
CoreManager.setRESTController({ request, ajax });
650-
const file = new ParseFile('parse.txt', [61, 170, 236, 120]);
652+
// eslint-disable-next-line no-undef
653+
const blob = new Blob([61, 170, 236, 120]);
654+
const file = new ParseFile('parse.txt', blob);
651655
file._source.format = 'file';
652656

653657
return file.save({ sessionToken: 'testing_sessionToken' }).then(function(f) {
654658
expect(f).toBe(file);
655-
expect(f.name()).toBe('/api.parse.com/1/files/parse.txt');
656-
expect(f.url()).toBe('https://files.parsetfss.com/a//api.parse.com/1/files/parse.txt');
659+
expect(f.name()).toBe('parse.txt');
660+
expect(f.url()).toBe('https://files.parsetfss.com/a/parse.txt');
657661
});
658662
});
659663

@@ -685,38 +689,97 @@ describe('FileController', () => {
685689
});
686690
};
687691
CoreManager.setRESTController({ request, ajax });
688-
const file = new ParseFile('parse.txt', [61, 170, 236, 120]);
692+
// eslint-disable-next-line no-undef
693+
const blob = new Blob([61, 170, 236, 120]);
694+
const file = new ParseFile('parse.txt', blob);
689695
file._source.format = 'file';
690696

691697
return file.save().then(function(f) {
692698
expect(f).toBe(file);
693-
expect(f.name()).toBe('/api.parse.com/1/files/parse.txt');
694-
expect(f.url()).toBe('https://files.parsetfss.com/a//api.parse.com/1/files/parse.txt');
699+
expect(f.name()).toBe('parse.txt');
700+
expect(f.url()).toBe('https://files.parsetfss.com/a/parse.txt');
695701
});
696702
});
697703

698-
it('saves files via object saveAll options', async () => {
699-
const ajax = jest.fn().mockResolvedValueOnce({
700-
response: {
701-
name: 'parse.txt',
702-
url: 'http://files.parsetfss.com/a/parse.txt'
704+
it('should save file using saveFile with metadata and tags', async () => {
705+
CoreManager.set('UserController', {
706+
currentUserAsync() {
707+
return Promise.resolve({
708+
getSessionToken() {
709+
return 'currentUserToken';
710+
}
711+
});
703712
}
704713
});
705-
CoreManager.setRESTController({ ajax, request: () => {
714+
const request = jest.fn((method, path) => {
715+
const name = path.substr(path.indexOf('/') + 1);
716+
return Promise.resolve({
717+
name: name,
718+
url: 'https://files.parsetfss.com/a/' + name
719+
});
720+
});
721+
const ajax = function(method, path, data, headers, options) {
722+
expect(options.sessionToken).toBe('currentUserToken')
723+
const name = path.substr(path.indexOf('/') + 1);
724+
return Promise.resolve({
725+
response: {
726+
name: name,
727+
url: 'https://files.parsetfss.com/a/' + name
728+
}
729+
});
730+
};
731+
CoreManager.setRESTController({ request, ajax });
732+
// eslint-disable-next-line no-undef
733+
const blob = new Blob([61, 170, 236, 120]);
734+
const file = new ParseFile('parse.txt', blob);
735+
file._source.format = 'file';
736+
file.addMetadata('foo', 'bar');
737+
file.addTag('bar', 'foo');
738+
const f = await file.save();
739+
expect(f).toBe(file);
740+
expect(f.name()).toBe('parse.txt');
741+
expect(f.url()).toBe('https://files.parsetfss.com/a/parse.txt');
742+
expect(request).toHaveBeenCalledWith(
743+
'POST',
744+
'files/parse.txt',
745+
{
746+
base64: 'NjExNzAyMzYxMjA=',
747+
fileData: {
748+
metadata: {
749+
foo: 'bar',
750+
},
751+
tags: {
752+
bar: 'foo',
753+
},
754+
},
755+
},
756+
{ requestTask: expect.any(Function) },
757+
);
758+
});
759+
760+
it('saves files via object saveAll options', async () => {
761+
const ajax = async () => {};
762+
const request = jest.fn(async (method, path, data, options) => {
763+
if (path.indexOf('files/') === 0) {
764+
expect(options.sessionToken).toBe('testToken');
765+
return {
766+
name: 'parse.txt',
767+
url: 'http://files.parsetfss.com/a/parse.txt'
768+
};
769+
}
706770
return [ { success: { objectId: 'child' } } ];
707-
} });
771+
});
772+
CoreManager.setRESTController({ ajax, request });
708773
CoreManager.setLocalDatastore(mockLocalDatastore);
709774

710-
const file = new ParseFile('parse.txt', [61, 170, 236, 120]);
775+
// eslint-disable-next-line no-undef
776+
const blob = new Blob([61, 170, 236, 120]);
777+
const file = new ParseFile('parse.txt', blob);
711778
file._source.format = 'file';
712779
const object = ParseObject.fromJSON({ className: 'TestObject' });
713780
object.set('file', file);
714781
await ParseObject.saveAll([object], { sessionToken: 'testToken' });
715-
716-
const request = ajax.mock.calls[0];
717-
expect(request[1]).toBe('https://api.parse.com/1/files/parse.txt')
718-
expect(request[3]['X-Parse-Session-Token']).toBe('testToken');
719-
expect(request[4].sessionToken).toBe('testToken');
782+
expect(request).toHaveBeenCalled();
720783
});
721784

722785
it('should throw error if file deleted without name', async (done) => {

0 commit comments

Comments
 (0)