From 8ca25cbabe1de7665b7d1cc3f1be2cde19ef2b21 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Tue, 9 Feb 2016 19:31:23 -0800 Subject: [PATCH 1/2] Moved getting the url for every file from RestQuery into FilesController. --- src/Config.js | 5 +- src/Controllers/FilesController.js | 37 +++++++++++++-- src/FilesAdapter.js | 2 +- src/GridStoreAdapter.js | 8 +--- src/RestQuery.js | 75 ++++++++++-------------------- src/index.js | 6 ++- 6 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/Config.js b/src/Config.js index df44f8b170..06d7af9497 100644 --- a/src/Config.js +++ b/src/Config.js @@ -13,7 +13,6 @@ function Config(applicationId, mount) { this.applicationId = applicationId; this.collectionPrefix = cacheInfo.collectionPrefix || ''; - this.database = DatabaseAdapter.getDatabaseConnection(applicationId); this.masterKey = cacheInfo.masterKey; this.clientKey = cacheInfo.clientKey; this.javascriptKey = cacheInfo.javascriptKey; @@ -21,6 +20,10 @@ function Config(applicationId, mount) { this.restAPIKey = cacheInfo.restAPIKey; this.fileKey = cacheInfo.fileKey; this.facebookAppIds = cacheInfo.facebookAppIds; + + this.database = DatabaseAdapter.getDatabaseConnection(applicationId); + this.filesController = cacheInfo.filesController; + this.mount = mount; } diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js index 4a929a6e1e..a22e257208 100644 --- a/src/Controllers/FilesController.js +++ b/src/Controllers/FilesController.js @@ -18,9 +18,10 @@ export class FilesController { getHandler() { return (req, res) => { let config = new Config(req.params.appId); - this._filesAdapter.getFileDataAsync(config, req.params.filename).then((data) => { + let filename = req.params.filename; + this._filesAdapter.getFileDataAsync(config, filename).then((data) => { res.status(200); - var contentType = mime.lookup(req.params.filename); + var contentType = mime.lookup(filename); res.set('Content-type', contentType); res.end(data); }).catch((error) => { @@ -63,17 +64,45 @@ export class FilesController { let filename = rack() + '_' + req.params.filename + extension; this._filesAdapter.createFileAsync(req.config, filename, req.body).then(() => { res.status(201); - var location = this._filesAdapter.getFileLocation(req.config, req, filename); + var location = this._filesAdapter.getFileLocation(req.config, filename); res.set('Location', location); res.json({ url: location, name: filename }); }).catch((error) => { - console.log(error); next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Could not store file.')); }); }; } + /** + * Find file references in REST-format object and adds the url key + * with the current mount point and app id. + * Object may be a single object or list of REST-format objects. + */ + expandFilesInObject(config, object) { + if (object instanceof Array) { + object.map((obj) => this.expandFilesInObject(config, obj)); + return; + } + if (typeof object !== 'object') { + return; + } + for (let key in object) { + let fileObject = object[key]; + if (fileObject && fileObject['__type'] === 'File') { + if (fileObject['url']) { + continue; + } + let filename = fileObject['name']; + if (filename.indexOf('tfss-') === 0) { + fileObject['url'] = 'http://files.parsetfss.com/' + config.fileKey + '/' + encodeURIComponent(filename); + } else { + fileObject['url'] = this._filesAdapter.getFileLocation(config, filename); + } + } + } + } + getExpressRouter() { let router = express.Router(); router.get('/files/:appId/:filename', this.getHandler()); diff --git a/src/FilesAdapter.js b/src/FilesAdapter.js index 62fe07018e..dbfc923c75 100644 --- a/src/FilesAdapter.js +++ b/src/FilesAdapter.js @@ -16,7 +16,7 @@ export class FilesAdapter { getFileDataAsync(config, filename) { } - getFileLocation(config, request, filename) { } + getFileLocation(config, filename) { } } export default FilesAdapter; diff --git a/src/GridStoreAdapter.js b/src/GridStoreAdapter.js index 161515c66c..d221b0213b 100644 --- a/src/GridStoreAdapter.js +++ b/src/GridStoreAdapter.js @@ -4,8 +4,6 @@ // Requires the database adapter to be based on mongoclient import { GridStore } from 'mongodb'; - -import * as Path from 'path'; import { FilesAdapter } from './FilesAdapter'; class GridStoreAdapter extends FilesAdapter { @@ -33,10 +31,8 @@ class GridStoreAdapter extends FilesAdapter { }); } - getFileLocation(config, request, filename) { - return (request.protocol + '://' + request.get('host') + - Path.dirname(request.originalUrl) + '/' + config.applicationId + - '/' + encodeURIComponent(filename)); + getFileLocation(config, filename) { + return (config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename)); } } diff --git a/src/RestQuery.js b/src/RestQuery.js index d677e2ddc0..91ebe536b7 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -3,6 +3,8 @@ var Parse = require('parse/node').Parse; +import { default as FilesController } from './Controllers/FilesController'; + // restOptions can include: // skip // limit @@ -316,35 +318,35 @@ RestQuery.prototype.replaceDontSelect = function() { RestQuery.prototype.runFind = function() { return this.config.database.find( this.className, this.restWhere, this.findOptions).then((results) => { - if (this.className == '_User') { - for (var result of results) { - delete result.password; - } + if (this.className == '_User') { + for (var result of results) { + delete result.password; } + } - updateParseFiles(this.config, results); + this.config.filesController.expandFilesInObject(this.config, results); - if (this.keys) { - var keySet = this.keys; - results = results.map((object) => { - var newObject = {}; - for (var key in object) { - if (keySet.has(key)) { - newObject[key] = object[key]; - } + if (this.keys) { + var keySet = this.keys; + results = results.map((object) => { + var newObject = {}; + for (var key in object) { + if (keySet.has(key)) { + newObject[key] = object[key]; } - return newObject; - }); - } - - if (this.redirectClassName) { - for (var r of results) { - r.className = this.redirectClassName; } + return newObject; + }); + } + + if (this.redirectClassName) { + for (var r of results) { + r.className = this.redirectClassName; } + } - this.response = {results: results}; - }); + this.response = {results: results}; + }); }; // Returns a promise for whether it was successful. @@ -497,35 +499,6 @@ function replacePointers(object, path, replace) { return answer; } -// Find file references in REST-format object and adds the url key -// with the current mount point and app id -// Object may be a single object or list of REST-format objects -function updateParseFiles(config, object) { - if (object instanceof Array) { - object.map((obj) => updateParseFiles(config, obj)); - return; - } - if (typeof object !== 'object') { - return; - } - for (var key in object) { - if (object[key] && object[key]['__type'] && - object[key]['__type'] == 'File') { - var filename = object[key]['name']; - var encoded = encodeURIComponent(filename); - encoded = encoded.replace('%40', '@'); - if (filename.indexOf('tfss-') === 0) { - object[key]['url'] = 'http://files.parsetfss.com/' + - config.fileKey + '/' + encoded; - } else { - object[key]['url'] = config.mount + '/files/' + - config.applicationId + '/' + - encoded; - } - } - } -} - // Finds a subobject that has the given key, if there is one. // Returns undefined otherwise. function findObjectWithKey(root, key) { diff --git a/src/index.js b/src/index.js index 93d363f76a..d685e79fcc 100644 --- a/src/index.js +++ b/src/index.js @@ -66,6 +66,8 @@ function ParseServer(args) { } + let filesController = new FilesController(filesAdapter); + cache.apps[args.appId] = { masterKey: args.masterKey, collectionPrefix: args.collectionPrefix || '', @@ -74,7 +76,8 @@ function ParseServer(args) { dotNetKey: args.dotNetKey || '', restAPIKey: args.restAPIKey || '', fileKey: args.fileKey || 'invalid-file-key', - facebookAppIds: args.facebookAppIds || [] + facebookAppIds: args.facebookAppIds || [], + filesController: filesController }; // To maintain compatibility. TODO: Remove in v2.1 @@ -93,7 +96,6 @@ function ParseServer(args) { var api = express(); // File handling needs to be before default middlewares are applied - let filesController = new FilesController(filesAdapter); api.use('/', filesController.getExpressRouter()); // TODO: separate this from the regular ParseServer object From 053ac990e69406f5291e8b2328e3205327380cd4 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Tue, 9 Feb 2016 19:31:50 -0800 Subject: [PATCH 2/2] Fixed missing url for files on user login. --- spec/ParseUser.spec.js | 16 ++++++++++++++++ src/users.js | 2 ++ 2 files changed, 18 insertions(+) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index c9f25bd84d..6c7ec26b58 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -64,6 +64,22 @@ describe('Parse.User testing', () => { }); }); + it("user login with files", (done) => { + "use strict"; + + let file = new Parse.File("yolo.txt", [1,2,3], "text/plain"); + file.save().then((file) => { + return Parse.User.signUp("asdf", "zxcv", { "file" : file }); + }).then(() => { + return Parse.User.logIn("asdf", "zxcv"); + }).then((user) => { + let fileAgain = user.get('file'); + ok(fileAgain.name()); + ok(fileAgain.url()); + done(); + }); + }); + it("become", (done) => { var user = null; var sessionToken = null; diff --git a/src/users.js b/src/users.js index d769b9c5d0..5f0e01e736 100644 --- a/src/users.js +++ b/src/users.js @@ -58,6 +58,8 @@ function handleLogIn(req) { user.sessionToken = token; delete user.password; + req.config.filesController.expandFilesInObject(req.config, user); + var expiresAt = new Date(); expiresAt.setFullYear(expiresAt.getFullYear() + 1);