diff --git a/spec/features.spec.js b/spec/features.spec.js index 3ddd7a6055..9d18adf781 100644 --- a/spec/features.spec.js +++ b/spec/features.spec.js @@ -5,7 +5,7 @@ const request = require("request"); describe('features', () => { it('set and get features', (done) => { - features.setFeature('users', { + features.setFeature('push', { testOption1: true, testOption2: false }); @@ -14,10 +14,10 @@ describe('features', () => { var expected = { testOption1: true, - testOption2: false + testOption2: false }; - expect(_features.users).toEqual(expected); + expect(_features.push).toEqual(expected); done(); }); @@ -29,7 +29,7 @@ describe('features', () => { it('requires the master key to get all schemas', done => { request.get({ - url: 'http://localhost:8378/1/features', + url: 'http://localhost:8378/1/serverInfo', json: true, headers: { 'X-Parse-Application-Id': 'test', diff --git a/spec/index.spec.js b/spec/index.spec.js index e3e2cb0bd3..56f9bb6bff 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -1,4 +1,5 @@ var request = require('request'); +var parseServerPackage = require('../package.json'); var MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions'); describe('server', () => { @@ -153,4 +154,18 @@ describe('server', () => { })).toThrow('SimpleMailgunAdapter requires an API Key and domain.'); done(); }); + + it('can report the server version', done => { + request.get({ + url: 'http://localhost:8378/1/serverInfo', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + }, + json: true, + }, (error, response, body) => { + expect(body.parseServerVersion).toEqual(parseServerPackage.version); + done(); + }) + }); }); diff --git a/src/Routers/FeaturesRouter.js b/src/Routers/FeaturesRouter.js index 05ccad5b5b..f0cdb3eab4 100644 --- a/src/Routers/FeaturesRouter.js +++ b/src/Routers/FeaturesRouter.js @@ -1,13 +1,15 @@ -import PromiseRouter from '../PromiseRouter'; +import { version } from '../../package.json'; +import PromiseRouter from '../PromiseRouter'; import * as middleware from "../middlewares"; import { getFeatures } from '../features'; export class FeaturesRouter extends PromiseRouter { mountRoutes() { - this.route('GET','/features', middleware.promiseEnforceMasterKeyAccess, () => { + this.route('GET','/serverInfo', middleware.promiseEnforceMasterKeyAccess, () => { return { response: { - results: [getFeatures()] - } }; + features: getFeatures(), + parseServerVersion: version, + } }; }); } } diff --git a/src/features.js b/src/features.js index 1048b91cab..6ff0009594 100644 --- a/src/features.js +++ b/src/features.js @@ -14,26 +14,18 @@ * Features that use Adapters should specify the feature options through * the setFeature method in your controller and feature * Reference PushController and ParsePushAdapter as an example. - * + * * NOTE: When adding new endpoints be sure to update this list both (features, featureSwitch) - * if you are planning to have a UI consume it. + * if you are planning to have a UI consume it. */ // default features let features = { - analytics: { - slowQueries: false, - performanceAnalysis: false, - retentionAnalysis: false, - }, - classes: {}, - files: {}, - functions: {}, globalConfig: { - create: true, - read: true, - update: true, - delete: true, + create: false, + read: false, + update: false, + delete: false, }, hooks: { create: false, @@ -41,15 +33,19 @@ let features = { update: false, delete: false, }, - iapValidation: {}, - installations: {}, logs: { - info: true, - error: true, + level: false, + size: false, + order: false, + until: false, + from: false, + }, + push: { + immediatePush: false, + scheduledPush: false, + storedPushData: false, + pushAudiences: false, }, - publicAPI: {}, - push: {}, - roles: {}, schemas: { addField: true, removeField: true, @@ -58,27 +54,15 @@ let features = { clearAllDataFromClass: false, exportClass: false, }, - sessions: {}, - users: {}, }; // master switch for features let featuresSwitch = { - analytics: true, - classes: true, - files: true, - functions: true, globalConfig: true, hooks: true, - iapValidation: true, - installations: true, logs: true, - publicAPI: true, push: true, - roles: true, schemas: true, - sessions: true, - users: true, }; /** @@ -94,7 +78,7 @@ function setFeature(key, value) { function getFeatures() { let result = {}; Object.keys(features).forEach((key) => { - if (featuresSwitch[key]) { + if (featuresSwitch[key] && features[key]) { result[key] = features[key]; } }); diff --git a/src/index.js b/src/index.js index b521a26f03..076035f874 100644 --- a/src/index.js +++ b/src/index.js @@ -10,12 +10,13 @@ var batch = require('./batch'), multer = require('multer'), Parse = require('parse/node').Parse; +//import passwordReset from './passwordReset'; import cache from './cache'; import Config from './Config'; - +import parseServerPackage from '../package.json'; import ParsePushAdapter from './Adapters/Push/ParsePushAdapter'; -//import passwordReset from './passwordReset'; import PromiseRouter from './PromiseRouter'; +import requiredParameter from './requiredParameter'; import { AnalyticsRouter } from './Routers/AnalyticsRouter'; import { ClassesRouter } from './Routers/ClassesRouter'; import { FeaturesRouter } from './Routers/FeaturesRouter'; @@ -23,28 +24,27 @@ import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; import { FilesController } from './Controllers/FilesController'; import { FilesRouter } from './Routers/FilesRouter'; import { FunctionsRouter } from './Routers/FunctionsRouter'; -import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter'; -import { IAPValidationRouter } from './Routers/IAPValidationRouter'; -import { LogsRouter } from './Routers/LogsRouter'; -import { HooksRouter } from './Routers/HooksRouter'; -import { PublicAPIRouter } from './Routers/PublicAPIRouter'; import { GlobalConfigRouter } from './Routers/GlobalConfigRouter'; - +import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter'; import { HooksController } from './Controllers/HooksController'; -import { UserController } from './Controllers/UserController'; +import { HooksRouter } from './Routers/HooksRouter'; +import { IAPValidationRouter } from './Routers/IAPValidationRouter'; import { InstallationsRouter } from './Routers/InstallationsRouter'; import { loadAdapter } from './Adapters/AdapterLoader'; import { LoggerController } from './Controllers/LoggerController'; +import { LogsRouter } from './Routers/LogsRouter'; +import { PublicAPIRouter } from './Routers/PublicAPIRouter'; import { PushController } from './Controllers/PushController'; import { PushRouter } from './Routers/PushRouter'; +import { randomString } from './cryptoUtils'; import { RolesRouter } from './Routers/RolesRouter'; import { S3Adapter } from './Adapters/Files/S3Adapter'; import { SchemasRouter } from './Routers/SchemasRouter'; import { SessionsRouter } from './Routers/SessionsRouter'; +import { setFeature } from './features'; +import { UserController } from './Controllers/UserController'; import { UsersRouter } from './Routers/UsersRouter'; -import requiredParameter from './requiredParameter'; -import { randomString } from './cryptoUtils'; // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -106,11 +106,11 @@ function ParseServer({ passwordResetSuccess: undefined }, }) { - + setFeature('serverVersion', parseServerPackage.version); // Initialize the node client SDK automatically Parse.initialize(appId, javascriptKey || 'unused', masterKey); Parse.serverURL = serverURL; - + if (databaseAdapter) { DatabaseAdapter.setAdapter(databaseAdapter); } @@ -144,7 +144,7 @@ function ParseServer({ const hooksController = new HooksController(appId, collectionPrefix); const userController = new UserController(emailControllerAdapter, appId, { verifyUserEmails }); - + cache.apps.set(appId, { masterKey: masterKey, serverURL: serverURL, @@ -173,7 +173,7 @@ function ParseServer({ if (process.env.FACEBOOK_APP_ID) { cache.apps.get(appId)['facebookAppIds'].push(process.env.FACEBOOK_APP_ID); } - + Config.validate(cache.apps.get(appId)); // This app serves the Parse API directly. @@ -186,7 +186,7 @@ function ParseServer({ })); api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressApp()); - + // TODO: separate this from the regular ParseServer object if (process.env.TESTING == 1) { api.use('/', require('./testing-routes').router); @@ -215,17 +215,17 @@ function ParseServer({ if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) { routers.push(new GlobalConfigRouter()); } - + if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) { routers.push(new HooksRouter()); } - + let routes = routers.reduce((memo, router) => { return memo.concat(router.routes); }, []); let appRouter = new PromiseRouter(routes); - + batch.mountOnto(appRouter); api.use(appRouter.expressApp());