Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added settings manager #1645

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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