Skip to content

Commit

Permalink
Added settings manager (#1645)
Browse files Browse the repository at this point in the history
* Added settings manager

* Clarity improvements & test fixes for settings manager

* Updated cli-defs, features router, removed global test object

* Fixed cli definitions syntax error

* Fixed formatting issues

* Removed ENABLE_CONFIG_CHANGES env var & improved doc

* Disable config changes for most tests

* test fixes

* Correct handling of verbose
  • Loading branch information
mamaso authored and drew-gross committed May 13, 2016
1 parent 4462145 commit 993ff20
Show file tree
Hide file tree
Showing 14 changed files with 512 additions and 163 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"body-parser": "^1.14.2",
"colors": "^1.1.2",
"commander": "^2.9.0",
"deep-equal": "^1.0.1",
"deepcopy": "^0.6.1",
"express": "^4.13.4",
"intersect": "^1.0.1",
Expand Down
11 changes: 6 additions & 5 deletions spec/FileLoggerAdapter.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter;
var Parse = require('parse/node').Parse;
var configureLogger = require('../src/logger').configureLogger;

describe('info logs', () => {
describe('FileLoggerAdapter', () => {
beforeEach(() => {
configureLogger({ logsFolder: './test_logs/file_logger/'});
});

it("Verify INFO logs", (done) => {
var fileLoggerAdapter = new FileLoggerAdapter();
Expand All @@ -20,9 +24,6 @@ describe('info logs', () => {
});
});
});
});

describe('error logs', () => {

it("Verify ERROR logs", (done) => {
var fileLoggerAdapter = new FileLoggerAdapter();
Expand All @@ -42,4 +43,4 @@ describe('error logs', () => {
});
});
});
});
});
215 changes: 215 additions & 0 deletions spec/PersistentSettings.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
var request = require('request');
var deepcopy = require('deepcopy');
var Config = require('../src/Config');
var logger = require('../src/logger').default;

var settingsCollectionName = '_ServerSettings';
var configuration;
var settingsCollection;
var parseServerObject;

describe('Persistent Settings', () => {
beforeEach((done) => {
configuration = deepcopy(defaultConfiguration);
configuration.enableConfigChanges = true;
newServer().then(done);
});

describe('Upon Initialization', () => {
it('should persist settings', (done) => {
configuration.clientKey = 'local';

newServer()
.then(getPersisted)
.then(persisted => {
expect(persisted.clientKey).toEqual('local');
})
.then(done)
.catch(done.fail);
});

it('should only load mutable settings from database', (done) => {
configuration.clientKey = 'local'; // defined

updatePersisted({ logLevel: 'info', clientKey: 'persisted' })
.then(newServer)
.then(_ => {
var config = parseServerObject.config;
expect(config.logLevel).toEqual('info'); // not locked or defined, so updated
expect(config.clientKey).toEqual('local'); // configuration defined, therefore not updated
})
.then(done)
.catch(done.fail);
});

it('overwrites defined settings if lockDefinedSettings is false', (done) => {
configuration.clientKey = 'local';
configuration.lockDefinedSettings = false;

updatePersisted({ clientKey: 'persisted' })
.then(newServer)
.then(_ => {
var config = parseServerObject.config;
expect(config.clientKey).toEqual('persisted'); // defined setting was updated
})
.then(done)
.catch(done.fail);
});
});

describe('Settings Router', () => {
it('should provide error on post if config changes disabled', (done) => {
configuration.enableConfigChanges = false;
newServer()
.then(endpoint.get)
.then(res => expect(res.res.statusCode).toBe(200))
.then(_ => endpoint.post({ clientKey: 'causesError' }))
.then(res => {
expect(res.res.statusCode).toBe(400);
expect(res.body.code).toBe(119); //Parse.Error.OPERATION_FORBIDDEN
expect(res.body.error).toBe('Server config changes are disabled');
})
.then(done)
.catch(done.fail);
});

it('should run setting callbacks such as configureLogger', (done) => {
endpoint.post({ logLevel: 'debug' })
.then(res => {
expect(res.res.statusCode).toBe(200);
expect(res.body.logLevel).toBe('debug');
expect(logger.transports['parse-server'].level).toBe('debug');
})
.then(endpoint.get)
.then(res => {
expect(res.res.statusCode).toBe(200);
expect(res.body.logLevel).toBe('debug');
})
.then(done)
.catch(done.fail);
});

it('should not set defined setting', (done) => {
endpoint.post({ clientKey: 'alreadyDefined' })
.then(res => {
expect(res.res.statusCode).toBe(200);
expect(res.body.clientKey).toBeUndefined();
})
.then(endpoint.get)
.then(res => {
expect(res.res.statusCode).toBe(200);
expect(res.body.clientKey).toBe(configuration.clientKey);
})
.then(done)
.catch(done.fail);
});

it('should not allow access without masterKey', (done) => {
var invalidHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'invalid'
};

endpoint.post({ logLevel: 'silly' }, invalidHeaders)
.then(res => {
expect(res.res.statusCode).toBe(403);
expect(res.body.error).toBe('unauthorized');
})
.then(_ => endpoint.get(invalidHeaders))
.then(res => {
expect(res.res.statusCode).toBe(403);
expect(res.body.error).toBe('unauthorized');
})
.then(done)
.catch(done.fail);
});

it('should expose non-existant settings as null', (done) => {
delete configuration.clientKey;

settingsCollection.drop()
.then(newServer)
.then(endpoint.get)
.then(res => expect(res.body.clientKey).toBe(null))
.then(done)
.catch(done.fail);
});

it('should fetch database values', (done) => {
delete configuration.clientKey;

settingsCollection.drop()
.then(newServer)
.then(endpoint.get)
.then(res => expect(res.body.clientKey).toBe(null))
.then(_ => updatePersisted({ clientKey: 'persisted' }))
.then(endpoint.get)
.then(res => expect(res.body.clientKey).toBe('persisted'))
.then(done)
.catch(done.fail);
});

it('should only return modified values', (done) => {
// info is default log level
var currentLogLevel;
endpoint.get()
.then(res => currentLogLevel = res.body.logLevel)
.then(_ => endpoint.post({ logLevel: currentLogLevel }))
.then(res => expect(res.body.logLevel).toBeUndefined)
.then(done)
.catch(done.fail);
});
});
});

function newServer() {
parseServerObject = setServerConfiguration(deepcopy(configuration));
return parseServerObject.config.settingsInitialized
.then(_ => {
var config = new Config(configuration.appId);
return config.database.adapter.adaptiveCollection(settingsCollectionName);
})
.then(coll => { settingsCollection = coll; });
}

function updatePersisted(settings) {
settings.applicationId = configuration.appId;
return parseServerObject.config.settingsInitialized
.then(_ => settingsCollection.upsertOne({ applicationId: configuration.appId }, { $set: settings }))
.then(_ => undefined);
}

function getPersisted() {
return parseServerObject.config.settingsInitialized
.then(_ => settingsCollection.find({ applicationId: configuration.appId }))
.then(results => results && results.length && results[0]);
}

var settingsUrl = 'http://localhost:8378/1/settings';
var defaultHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test'
};

var req = (method, headers, body) => new Promise((resolve, reject) => {
request[method]({
url: settingsUrl,
json: body,
headers: headers || defaultHeaders
}, (err, res, body) => {
if (err) {
reject(err);
} else {
if (typeof body === 'string') body = JSON.parse(body);
resolve({
res: res,
body: body
});
}
});
});

var endpoint = {
get: headers => req('get', headers),
post: (body, headers) => req('post', headers, body)
}
3 changes: 2 additions & 1 deletion spec/PublicAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ describe("public API", () => {
masterKey: 'test',
collectionPrefix: 'test_',
fileKey: 'test',
publicServerURL: 'http://localhost:8378/1'
publicServerURL: 'http://localhost:8378/1',
enableConfigChanges: false
});
done();
})
Expand Down
57 changes: 0 additions & 57 deletions spec/SettingsRouter.spec.js

This file was deleted.

21 changes: 14 additions & 7 deletions spec/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var cache = require('../src/cache').default;
var DatabaseAdapter = require('../src/DatabaseAdapter');
var express = require('express');
var facebook = require('../src/authDataManager/facebook');
var ParseServer = require('../src/index').ParseServer;
var ParseServer = require('../src/index').default;
var path = require('path');

var databaseURI = process.env.DATABASE_URI;
Expand Down Expand Up @@ -39,13 +39,14 @@ var defaultConfiguration = {
myoauth: {
module: path.resolve(__dirname, "myoauth") // relative path as it's run from src
}
}
},
enableConfigChanges: false
};

// Set up a default API server for testing with default configuration.
var api = new ParseServer(defaultConfiguration);
var parseServer = new ParseServer(defaultConfiguration);
var app = express();
app.use('/1', api);
app.use('/1', parseServer.app);
var server = app.listen(port);

// Prevent reinitializing the server from clobbering Cloud Code
Expand All @@ -63,9 +64,10 @@ var setServerConfiguration = configuration => {
server.close();
cache.clearCache();
app = express();
api = new ParseServer(configuration);
app.use('/1', api);
parseServer = new ParseServer(configuration);
app.use('/1', parseServer.app);
server = app.listen(port);
return parseServer;
};

var restoreServerConfiguration = () => setServerConfiguration(defaultConfiguration);
Expand Down Expand Up @@ -140,15 +142,19 @@ function notWorking() {}
function ok(bool, message) {
expect(bool).toBeTruthy(message);
}

function equal(a, b, message) {
expect(a).toEqual(b, message);
}

function strictEqual(a, b, message) {
expect(a).toBe(b, message);
}

function notEqual(a, b, message) {
expect(a).not.toEqual(b, message);
}

function expectSuccess(params) {
return {
success: params.success,
Expand All @@ -158,6 +164,7 @@ function expectSuccess(params) {
},
}
}

function expectError(errorCode, callback) {
return {
success: function(result) {
Expand Down Expand Up @@ -277,4 +284,4 @@ jasmine.restoreLibrary = function(library, name) {
throw 'Can not find library ' + library + ' ' + name;
}
require(library)[name] = libraryCache[library][name];
}
}
Loading

0 comments on commit 993ff20

Please sign in to comment.