From 1dfc7e42407f05f2764cac462dfc618799cd9935 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sun, 28 Feb 2016 23:49:33 +0200 Subject: [PATCH 01/12] Add FileSystemAdapter --- .gitignore | 3 ++ src/Adapters/Files/FileSystemAdapter.js | 72 +++++++++++++++++++++++++ src/cli/parse-server.js | 1 + src/index.js | 4 +- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/Adapters/Files/FileSystemAdapter.js diff --git a/.gitignore b/.gitignore index 8ea322c438..33a78f6cb4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ lib/ # Mac DS_Store files .DS_Store + +# Folder created by FileSystemAdapter +/files diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js new file mode 100644 index 0000000000..7912438087 --- /dev/null +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -0,0 +1,72 @@ +// FileSystemAdapter +// +// Stores files in local file system +// Requires write access to the server's file system. + +import { FilesAdapter } from './FilesAdapter'; +var fs = require('fs'); + +export class FileSystemAdapter extends FilesAdapter { + // For a given config object, filename, and data, store a file + // Returns a promise + createFile(config, filename, data) { + return new Promise((resolve, reject) => { + let filepath = this._getLocalFilePath(config, filename); + fs.writeFile(filepath, data, (err) => { + if(err !== null) { + return reject(err); + } + resolve(data); + }); + }); + } + + deleteFile(config, filename) { + return new Promise((resolve, reject) => { + let filepath = this._getLocalFilePath(config, filename); + fs.readFile( filepath , function (err, data) { + if(err !== null) { + return reject(err); + } + fs.unlink(filepath, (unlinkErr) => { + if(err !== null) { + return reject(unlinkErr); + } + resolve(data); + }); + }); + + }); + } + + getFileData(config, filename) { + return new Promise((resolve, reject) => { + let filepath = this._getLocalFilePath(config, filename); + fs.readFile( filepath , function (err, data) { + if(err !== null) { + return reject(err); + } + resolve(data); + }); + }); + } + + getFileLocation(config, filename) { + return (config.mount + '/' + this._getLocalFilePath(config, filename)); + } + + _getLocalFilePath(config, filename) { + let filesDir = 'files'; + if (!fs.existsSync(filesDir)) { + fs.mkdirSync(filesDir); + } + + let applicationDir = filesDir + '/' + config.applicationId; + if (!fs.existsSync(applicationDir)) { + fs.mkdirSync(applicationDir); + } + return (applicationDir + '/' + encodeURIComponent(filename)); + } +} + +export default FileSystemAdapter; \ No newline at end of file diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index b4cbbb1263..407db677dd 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -4,6 +4,7 @@ import { ParseServer } from '../index'; import definitions from './cli-definitions'; import program from './utils/commander'; import colors from 'colors'; +import { FileSystemAdapter } from '../Adapters/Files/FileSystemAdapter'; program.loadDefinitions(definitions); diff --git a/src/index.js b/src/index.js index 87ab0331eb..8b6c7fba4e 100644 --- a/src/index.js +++ b/src/index.js @@ -46,6 +46,7 @@ import { SessionsRouter } from './Routers/SessionsRouter'; import { setFeature } from './features'; import { UserController } from './Controllers/UserController'; import { UsersRouter } from './Routers/UsersRouter'; +import { FilesController } from './Controllers/FilesController'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -265,5 +266,6 @@ function addParseCloud() { module.exports = { ParseServer: ParseServer, S3Adapter: S3Adapter, - GCSAdapter: GCSAdapter + GCSAdapter: GCSAdapter, + FileSystemAdapter: FileSystemAdapter }; From ae8fe88b0d5424e86c2797bf1ce714c76d1364f8 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Mon, 29 Feb 2016 01:33:54 +0200 Subject: [PATCH 02/12] Add options to the FileSystemAdapter constructor --- src/Adapters/Files/FileSystemAdapter.js | 42 +++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js index 7912438087..98803e5bdb 100644 --- a/src/Adapters/Files/FileSystemAdapter.js +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -4,9 +4,19 @@ // Requires write access to the server's file system. import { FilesAdapter } from './FilesAdapter'; +import colors from 'colors'; var fs = require('fs'); +var path = require('path'); export class FileSystemAdapter extends FilesAdapter { + + constructor({filesSubDirectory = ''} = {}) { + super(); + + this._filesDir = filesSubDirectory; + this._mkdir(filesSubDirectory); + } + // For a given config object, filename, and data, store a file // Returns a promise createFile(config, filename, data) { @@ -55,18 +65,38 @@ export class FileSystemAdapter extends FilesAdapter { return (config.mount + '/' + this._getLocalFilePath(config, filename)); } + /* + Helpers + --------------- */ + _getLocalFilePath(config, filename) { let filesDir = 'files'; - if (!fs.existsSync(filesDir)) { - fs.mkdirSync(filesDir); - } - - let applicationDir = filesDir + '/' + config.applicationId; + let applicationDir = filesDir + '/' + this._filesDir; if (!fs.existsSync(applicationDir)) { - fs.mkdirSync(applicationDir); + this._mkdir(applicationDir); } return (applicationDir + '/' + encodeURIComponent(filename)); } + + _mkdir(path, root) { + // snippet found on -> http://stackoverflow.com/a/10600228 + var dirs = path.split('/'), dir = dirs.shift(), root = (root || '') + dir + '/'; + + try { + fs.mkdirSync(root); + } + catch (e) { + if ( e.code == 'EACCES' ) { + console.error(""); + console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); + console.error(""); + process.exit(1); + } + //dir wasn't made, something went wrong + if(!fs.statSync(root).isDirectory()) throw new Error(e); + } + return !dirs.length || this._mkdir(dirs.join('/'), root); + } } export default FileSystemAdapter; \ No newline at end of file From 5866e611dd1ef84763fdfdab8abf64066393f2ba Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Mon, 29 Feb 2016 01:34:42 +0200 Subject: [PATCH 03/12] Remove file system permissions validation from CLI --- src/cli/parse-server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index 407db677dd..b4cbbb1263 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -4,7 +4,6 @@ import { ParseServer } from '../index'; import definitions from './cli-definitions'; import program from './utils/commander'; import colors from 'colors'; -import { FileSystemAdapter } from '../Adapters/Files/FileSystemAdapter'; program.loadDefinitions(definitions); From b8c08a3bc5be437237ebbf5f8dc5c2cea123d786 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Mon, 29 Feb 2016 01:41:19 +0200 Subject: [PATCH 04/12] Remove config parameter from _getLocalFilePath() --- src/Adapters/Files/FileSystemAdapter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js index 98803e5bdb..5e2519020d 100644 --- a/src/Adapters/Files/FileSystemAdapter.js +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -21,7 +21,7 @@ export class FileSystemAdapter extends FilesAdapter { // Returns a promise createFile(config, filename, data) { return new Promise((resolve, reject) => { - let filepath = this._getLocalFilePath(config, filename); + let filepath = this._getLocalFilePath(filename); fs.writeFile(filepath, data, (err) => { if(err !== null) { return reject(err); @@ -33,7 +33,7 @@ export class FileSystemAdapter extends FilesAdapter { deleteFile(config, filename) { return new Promise((resolve, reject) => { - let filepath = this._getLocalFilePath(config, filename); + let filepath = this._getLocalFilePath(filename); fs.readFile( filepath , function (err, data) { if(err !== null) { return reject(err); @@ -51,7 +51,7 @@ export class FileSystemAdapter extends FilesAdapter { getFileData(config, filename) { return new Promise((resolve, reject) => { - let filepath = this._getLocalFilePath(config, filename); + let filepath = this._getLocalFilePath(filename); fs.readFile( filepath , function (err, data) { if(err !== null) { return reject(err); @@ -62,14 +62,14 @@ export class FileSystemAdapter extends FilesAdapter { } getFileLocation(config, filename) { - return (config.mount + '/' + this._getLocalFilePath(config, filename)); + return (config.mount + '/' + this._getLocalFilePath(filename)); } /* Helpers --------------- */ - _getLocalFilePath(config, filename) { + _getLocalFilePath(filename) { let filesDir = 'files'; let applicationDir = filesDir + '/' + this._filesDir; if (!fs.existsSync(applicationDir)) { From 13bc54042e73233e480a9116cea1570c3fefbd9d Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sun, 28 Feb 2016 23:49:33 +0200 Subject: [PATCH 05/12] Add FileSystemAdapter --- src/cli/parse-server.js | 39 +++++++++++++++++++++++---------------- src/index.js | 1 + 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index b4cbbb1263..e8493a8ad8 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -4,6 +4,7 @@ import { ParseServer } from '../index'; import definitions from './cli-definitions'; import program from './utils/commander'; import colors from 'colors'; +import { FileSystemAdapter } from '../Adapters/Files/FileSystemAdapter'; program.loadDefinitions(definitions); @@ -42,6 +43,14 @@ if (program.args.length > 0 ) { console.log(`Configuation loaded from ${jsonPath}`) } +if (!program.appId || !program.masterKey || !program.serverURL) { + program.outputHelp(); + console.error(""); + console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); + console.error(""); + process.exit(1); +} + options = Object.keys(definitions).reduce(function (options, key) { if (program[key]) { options[key] = program[key]; @@ -53,19 +62,26 @@ if (!options.serverURL) { options.serverURL = `http://localhost:${options.port}${options.mountPath}`; } -if (!options.appId || !options.masterKey || !options.serverURL) { - program.outputHelp(); - console.error(""); - console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); - console.error(""); - process.exit(1); +if (options.filesAdapter instanceof FileSystemAdapter) { + var fs = require('fs'); + + try { + fs.mkdirSync('files'); + } catch(e) { + if ( e.code == 'EACCES' ) { + console.error(""); + console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); + console.error(""); + process.exit(1); + } + } } const app = express(); const api = new ParseServer(options); app.use(options.mountPath, api); -var server = app.listen(options.port, function() { +app.listen(options.port, function() { for (let key in options) { let value = options[key]; @@ -77,12 +93,3 @@ var server = app.listen(options.port, function() { console.log(''); console.log('parse-server running on '+options.serverURL); }); - -var handleShutdown = function() { - console.log('Termination signal received. Shutting down.'); - server.close(function () { - process.exit(0); - }); -}; -process.on('SIGTERM', handleShutdown); -process.on('SIGINT', handleShutdown); diff --git a/src/index.js b/src/index.js index 8b6c7fba4e..fb0684f2c7 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,7 @@ import { setFeature } from './features'; import { UserController } from './Controllers/UserController'; import { UsersRouter } from './Routers/UsersRouter'; import { FilesController } from './Controllers/FilesController'; +import { FileSystemAdapter } from './Adapters/Files/FileSystemAdapter'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); From 273ee52db590d731be4d6035f356acd766419bd0 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Mon, 29 Feb 2016 01:34:42 +0200 Subject: [PATCH 06/12] Remove file system permissions validation from CLI --- src/cli/parse-server.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index e8493a8ad8..627964f981 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -4,7 +4,6 @@ import { ParseServer } from '../index'; import definitions from './cli-definitions'; import program from './utils/commander'; import colors from 'colors'; -import { FileSystemAdapter } from '../Adapters/Files/FileSystemAdapter'; program.loadDefinitions(definitions); @@ -62,21 +61,6 @@ if (!options.serverURL) { options.serverURL = `http://localhost:${options.port}${options.mountPath}`; } -if (options.filesAdapter instanceof FileSystemAdapter) { - var fs = require('fs'); - - try { - fs.mkdirSync('files'); - } catch(e) { - if ( e.code == 'EACCES' ) { - console.error(""); - console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); - console.error(""); - process.exit(1); - } - } -} - const app = express(); const api = new ParseServer(options); app.use(options.mountPath, api); From ac4275433f6cf1d0f99ffd84fff7838a0238d8a6 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 10:39:51 +0200 Subject: [PATCH 07/12] Add helper methods to the FileSystemAdapter --- src/Adapters/Files/FileSystemAdapter.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js index 5e2519020d..5de6b1bbc2 100644 --- a/src/Adapters/Files/FileSystemAdapter.js +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -14,7 +14,7 @@ export class FileSystemAdapter extends FilesAdapter { super(); this._filesDir = filesSubDirectory; - this._mkdir(filesSubDirectory); + this._mkdir(this._getApplicationDir()); } // For a given config object, filename, and data, store a file @@ -68,10 +68,20 @@ export class FileSystemAdapter extends FilesAdapter { /* Helpers --------------- */ + _getApplicationDir() { + if (this._filesDir) { + return 'files/' + this._filesDir; + } else { + return 'files'; + } + } + + _applicationDirExist() { + return fs.existsSync(this._getApplicationDir()); + } _getLocalFilePath(filename) { - let filesDir = 'files'; - let applicationDir = filesDir + '/' + this._filesDir; + let applicationDir = this._getApplicationDir(); if (!fs.existsSync(applicationDir)) { this._mkdir(applicationDir); } From 89ae73a3a2d8267bae0cde4ad29106c10be05912 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 10:41:11 +0200 Subject: [PATCH 08/12] Throw on FileSystemAdapter constructor instead of 'process.exit(1)' --- src/Adapters/Files/FileSystemAdapter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js index 5de6b1bbc2..09c2214c32 100644 --- a/src/Adapters/Files/FileSystemAdapter.js +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -15,6 +15,9 @@ export class FileSystemAdapter extends FilesAdapter { this._filesDir = filesSubDirectory; this._mkdir(this._getApplicationDir()); + if (!this._applicationDirExist()) { + throw "Files directory doesn't exist."; + } } // For a given config object, filename, and data, store a file @@ -100,7 +103,6 @@ export class FileSystemAdapter extends FilesAdapter { console.error(""); console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); console.error(""); - process.exit(1); } //dir wasn't made, something went wrong if(!fs.statSync(root).isDirectory()) throw new Error(e); From a9b2f46ae4ad99b153c6e647c660a368fb716f07 Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 10:42:10 +0200 Subject: [PATCH 09/12] Add test for the FileSystemAdapter --- spec/FilesController.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 3b2108e71e..c3a281dceb 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -2,6 +2,7 @@ var FilesController = require('../src/Controllers/FilesController').FilesControl var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter; var S3Adapter = require("../src/Adapters/Files/S3Adapter").S3Adapter; var GCSAdapter = require("../src/Adapters/Files/GCSAdapter").GCSAdapter; +var FileSystemAdapter = require("../src/Adapters/Files/FileSystemAdapter").FileSystemAdapter; var Config = require("../src/Config"); var FCTestFactory = require("./FilesControllerTestFactory"); @@ -49,4 +50,15 @@ describe("FilesController",()=>{ } else if (!process.env.TRAVIS) { console.log("set GCP_PROJECT_ID, GCP_KEYFILE_PATH, and GCS_BUCKET to test GCSAdapter") } + + try { + // Test the file system adapter + var fsAdapter = new FileSystemAdapter({ + filesSubDirectory: 'sub1/sub2' + }); + + FCTestFactory.testAdapter("FileSystemAdapter", fsAdapter); + } catch (e) { + console.log("Give write access to the file system to test the FileSystemAdapter. Error: " + e); + } }); From 8b551280b822e31f95ad7ebd66cf2c0c40cae88f Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 21:50:44 +0200 Subject: [PATCH 10/12] Fix importing FilesController two times --- src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.js b/src/index.js index fb0684f2c7..e35f7448e0 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,6 @@ import { AnalyticsRouter } from './Routers/AnalyticsRouter'; import { ClassesRouter } from './Routers/ClassesRouter'; import { FeaturesRouter } from './Routers/FeaturesRouter'; import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; -import { FilesController } from './Controllers/FilesController'; import { FilesRouter } from './Routers/FilesRouter'; import { FunctionsRouter } from './Routers/FunctionsRouter'; import { GCSAdapter } from './Adapters/Files/GCSAdapter'; From 31f56ee8d474b509fcc2203125ebd41e3d5a1adb Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 22:23:34 +0200 Subject: [PATCH 11/12] Fix conflict problems --- src/cli/parse-server.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index 627964f981..50c98202f8 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -40,14 +40,6 @@ if (program.args.length > 0 ) { jsonPath = path.resolve(jsonPath); options = require(jsonPath); console.log(`Configuation loaded from ${jsonPath}`) -} - -if (!program.appId || !program.masterKey || !program.serverURL) { - program.outputHelp(); - console.error(""); - console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); - console.error(""); - process.exit(1); } options = Object.keys(definitions).reduce(function (options, key) { @@ -61,6 +53,14 @@ if (!options.serverURL) { options.serverURL = `http://localhost:${options.port}${options.mountPath}`; } +if (!program.appId || !program.masterKey || !program.serverURL) { + program.outputHelp(); + console.error(""); + console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); + console.error(""); + process.exit(1); +} + const app = express(); const api = new ParseServer(options); app.use(options.mountPath, api); @@ -77,3 +77,12 @@ app.listen(options.port, function() { console.log(''); console.log('parse-server running on '+options.serverURL); }); + +var handleShutdown = function() { + console.log('Termination signal received. Shutting down.'); + server.close(function () { + process.exit(0); + }); +}; +process.on('SIGTERM', handleShutdown); +process.on('SIGINT', handleShutdown); From 2f08a576839d78868372ab12643201595825700c Mon Sep 17 00:00:00 2001 From: Tsolis Dimitris Sotiris Date: Sat, 12 Mar 2016 23:33:49 +0200 Subject: [PATCH 12/12] Add path support for windows --- src/Adapters/Files/FileSystemAdapter.js | 40 +++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Adapters/Files/FileSystemAdapter.js b/src/Adapters/Files/FileSystemAdapter.js index 09c2214c32..cd5a6cb857 100644 --- a/src/Adapters/Files/FileSystemAdapter.js +++ b/src/Adapters/Files/FileSystemAdapter.js @@ -7,6 +7,7 @@ import { FilesAdapter } from './FilesAdapter'; import colors from 'colors'; var fs = require('fs'); var path = require('path'); +var pathSep = require('path').sep; export class FileSystemAdapter extends FilesAdapter { @@ -73,7 +74,7 @@ export class FileSystemAdapter extends FilesAdapter { --------------- */ _getApplicationDir() { if (this._filesDir) { - return 'files/' + this._filesDir; + return path.join('files', this._filesDir); } else { return 'files'; } @@ -88,26 +89,33 @@ export class FileSystemAdapter extends FilesAdapter { if (!fs.existsSync(applicationDir)) { this._mkdir(applicationDir); } - return (applicationDir + '/' + encodeURIComponent(filename)); + return path.join(applicationDir, encodeURIComponent(filename)); } - _mkdir(path, root) { - // snippet found on -> http://stackoverflow.com/a/10600228 - var dirs = path.split('/'), dir = dirs.shift(), root = (root || '') + dir + '/'; + _mkdir(path) { + // snippet found on -> https://gist.github.com/danherbert-epam/3960169 + var dirs = path.split(pathSep); + var root = ""; - try { - fs.mkdirSync(root); - } - catch (e) { - if ( e.code == 'EACCES' ) { - console.error(""); - console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); - console.error(""); + while (dirs.length > 0) { + var dir = dirs.shift(); + if (dir === "") { // If directory starts with a /, the first path will be an empty string. + root = pathSep; + } + if (!fs.existsSync(root + dir)) { + try { + fs.mkdirSync(root + dir); + } + catch (e) { + if ( e.code == 'EACCES' ) { + console.error(""); + console.error(colors.red("ERROR: In order to use the FileSystemAdapter, write access to the server's file system is required")); + console.error(""); + } + } } - //dir wasn't made, something went wrong - if(!fs.statSync(root).isDirectory()) throw new Error(e); + root += dir + pathSep; } - return !dirs.length || this._mkdir(dirs.join('/'), root); } }