Database version in features (#5627)

* adding database.version in the serverInfo (only MongoDB, it gives undefined when using Postgres)

* . correction of old 'features' tests
. adding engine and database in the StorageAdapter interface and implementations

* . version retrieval done in performInitialization
. PostgreSQL version

* performInitialization now returns a Promise
This commit is contained in:
Olivier Allouch
2019-06-03 23:58:21 +02:00
committed by Diamond Lewis
parent 266d6328a3
commit 7fc0d45b89
5 changed files with 61 additions and 12 deletions

View File

@@ -3,20 +3,41 @@
const request = require('../lib/request'); const request = require('../lib/request');
describe('features', () => { describe('features', () => {
it('requires the master key to get features', done => { it('should return the serverInfo', async () => {
request({ const response = await request({
url: 'http://localhost:8378/1/serverInfo', url: 'http://localhost:8378/1/serverInfo',
json: true, json: true,
headers: { headers: {
'X-Parse-Application-Id': 'test', 'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest', 'X-Parse-REST-API-Key': 'rest',
'X-Parse-Master-Key': 'test',
}, },
}).then(fail, response => {
expect(response.status).toEqual(403);
expect(response.data.error).toEqual(
'unauthorized: master key is required'
);
done();
}); });
const data = response.data;
expect(data).toBeDefined();
expect(data.features).toBeDefined();
expect(data.parseServerVersion).toBeDefined();
expect(data.database).toBeDefined();
expect(['MongoDB', 'PostgreSQL']).toContain(data.database.engine);
});
it('requires the master key to get features', async done => {
try {
await request({
url: 'http://localhost:8378/1/serverInfo',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
});
done.fail(
'The serverInfo request should be rejected without the master key'
);
} catch (error) {
expect(error.status).toEqual(403);
expect(error.data.error).toEqual('unauthorized: master key is required');
done();
}
}); });
}); });

View File

@@ -126,6 +126,8 @@ export class MongoStorageAdapter implements StorageAdapter {
client: MongoClient; client: MongoClient;
_maxTimeMS: ?number; _maxTimeMS: ?number;
canSortOnJoinTables: boolean; canSortOnJoinTables: boolean;
databaseVersion: string;
engine: string;
constructor({ constructor({
uri = defaults.DefaultMongoURI, uri = defaults.DefaultMongoURI,
@@ -136,6 +138,7 @@ export class MongoStorageAdapter implements StorageAdapter {
this._collectionPrefix = collectionPrefix; this._collectionPrefix = collectionPrefix;
this._mongoOptions = mongoOptions; this._mongoOptions = mongoOptions;
this._mongoOptions.useNewUrlParser = true; this._mongoOptions.useNewUrlParser = true;
this.engine = 'MongoDB';
// MaxTimeMS is not a global MongoDB client option, it is applied per operation. // MaxTimeMS is not a global MongoDB client option, it is applied per operation.
this._maxTimeMS = mongoOptions.maxTimeMS; this._maxTimeMS = mongoOptions.maxTimeMS;
@@ -959,7 +962,15 @@ export class MongoStorageAdapter implements StorageAdapter {
} }
performInitialization(): Promise<void> { performInitialization(): Promise<void> {
return Promise.resolve(); // databaseVersion
return this.connect()
.then(() => {
const adminDb = this.database.admin();
return adminDb.serverStatus();
})
.then(status => {
this.databaseVersion = status.version;
});
} }
createIndex(className: string, index: any) { createIndex(className: string, index: any) {

View File

@@ -778,6 +778,8 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => {
export class PostgresStorageAdapter implements StorageAdapter { export class PostgresStorageAdapter implements StorageAdapter {
canSortOnJoinTables: boolean; canSortOnJoinTables: boolean;
databaseVersion: string;
engine: string;
// Private // Private
_collectionPrefix: string; _collectionPrefix: string;
@@ -790,6 +792,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
this._client = client; this._client = client;
this._pgp = pgp; this._pgp = pgp;
this.canSortOnJoinTables = false; this.canSortOnJoinTables = false;
this.engine = 'PostgreSQL';
} }
handleShutdown() { handleShutdown() {
@@ -2276,6 +2279,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
}) })
.then(data => { .then(data => {
debug(`initializationDone in ${data.duration}`); debug(`initializationDone in ${data.duration}`);
// databaseVersion
return this._client.query('SHOW server_version');
})
.then(versionData => {
// versionData is like [ { server_version: '11.3' } ]
this.databaseVersion = versionData[0].server_version;
}) })
.catch(error => { .catch(error => {
/* eslint-disable no-console */ /* eslint-disable no-console */

View File

@@ -25,6 +25,8 @@ export type FullQueryOptions = QueryOptions & UpdateQueryOptions;
export interface StorageAdapter { export interface StorageAdapter {
canSortOnJoinTables: boolean; canSortOnJoinTables: boolean;
databaseVersion: string;
engine: string;
classExists(className: string): Promise<boolean>; classExists(className: string): Promise<boolean>;
setClassLevelPermissions(className: string, clps: any): Promise<void>; setClassLevelPermissions(className: string, clps: any): Promise<void>;

View File

@@ -9,6 +9,7 @@ export class FeaturesRouter extends PromiseRouter {
'/serverInfo', '/serverInfo',
middleware.promiseEnforceMasterKeyAccess, middleware.promiseEnforceMasterKeyAccess,
req => { req => {
const { config } = req;
const features = { const features = {
globalConfig: { globalConfig: {
create: true, create: true,
@@ -33,9 +34,9 @@ export class FeaturesRouter extends PromiseRouter {
from: true, from: true,
}, },
push: { push: {
immediatePush: req.config.hasPushSupport, immediatePush: config.hasPushSupport,
scheduledPush: req.config.hasPushScheduledSupport, scheduledPush: config.hasPushScheduledSupport,
storedPushData: req.config.hasPushSupport, storedPushData: config.hasPushSupport,
pushAudiences: true, pushAudiences: true,
localization: true, localization: true,
}, },
@@ -51,10 +52,15 @@ export class FeaturesRouter extends PromiseRouter {
}, },
}; };
const dbAdapter = config.database.adapter;
return { return {
response: { response: {
features: features, features: features,
parseServerVersion: version, parseServerVersion: version,
database: {
engine: dbAdapter.engine,
version: dbAdapter.databaseVersion,
},
}, },
}; };
} }