diff --git a/src/Adapters/Analytics/AnalyticsAdapter.js b/src/Adapters/Analytics/AnalyticsAdapter.js new file mode 100644 index 0000000000..48dd272b3c --- /dev/null +++ b/src/Adapters/Analytics/AnalyticsAdapter.js @@ -0,0 +1,11 @@ +export class AnalyticsAdapter { + appOpened(parameters, req) { + return Promise.resolve({}); + } + + trackEvent(eventName, parameters, req) { + return Promise.resolve({}); + } +} + +export default AnalyticsAdapter; diff --git a/src/Config.js b/src/Config.js index 927319ab88..83cec09dcd 100644 --- a/src/Config.js +++ b/src/Config.js @@ -39,6 +39,7 @@ export class Config { this.preventLoginWithUnverifiedEmail = cacheInfo.preventLoginWithUnverifiedEmail; this.appName = cacheInfo.appName; + this.analyticsController = cacheInfo.analyticsController; this.cacheController = cacheInfo.cacheController; this.hooksController = cacheInfo.hooksController; this.filesController = cacheInfo.filesController; diff --git a/src/Controllers/AnalyticsController.js b/src/Controllers/AnalyticsController.js new file mode 100644 index 0000000000..8bcda298a6 --- /dev/null +++ b/src/Controllers/AnalyticsController.js @@ -0,0 +1,28 @@ +import AdaptableController from './AdaptableController'; +import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; + +export class AnalyticsController extends AdaptableController { + appOpened(req) { + return this.adapter.appOpened(req.body, req).then( + function(response) { + return { response: response }; + }).catch((err) => { + return { response: {} }; + }); + } + + trackEvent(req) { + return this.adapter.trackEvent(req.params.eventName, req.body, req).then( + function(response) { + return { response: response }; + }).catch((err) => { + return { response: {} }; + }); + } + + expectedAdapterType() { + return AnalyticsAdapter; + } +} + +export default AnalyticsController; diff --git a/src/ParseServer.js b/src/ParseServer.js index 7e96d7c55a..ce2471f8a0 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -25,7 +25,9 @@ import { AnalyticsRouter } from './Routers/AnalyticsRouter'; import { ClassesRouter } from './Routers/ClassesRouter'; import { FeaturesRouter } from './Routers/FeaturesRouter'; import { InMemoryCacheAdapter } from './Adapters/Cache/InMemoryCacheAdapter'; +import { AnalyticsController } from './Controllers/AnalyticsController'; import { CacheController } from './Controllers/CacheController'; +import { AnalyticsAdapter } from './Adapters/Analytics/AnalyticsAdapter'; import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; import { FilesController } from './Controllers/FilesController'; import { FilesRouter } from './Routers/FilesRouter'; @@ -65,6 +67,7 @@ const requiredUserFields = { fields: { ...SchemaController.defaultColumns._Defau // ParseServer works like a constructor of an express app. // The args that we understand are: +// "analyticsAdapter": an adapter class for analytics // "filesAdapter": a class like GridStoreAdapter providing create, get, // and delete // "loggerAdapter": a class like FileLoggerAdapter providing info, error, @@ -95,6 +98,7 @@ class ParseServer { appId = requiredParameter('You must provide an appId!'), masterKey = requiredParameter('You must provide a masterKey!'), appName, + analyticsAdapter = undefined, filesAdapter, push, loggerAdapter, @@ -137,7 +141,6 @@ class ParseServer { // Initialize the node client SDK automatically Parse.initialize(appId, javascriptKey || 'unused', masterKey); Parse.serverURL = serverURL; - if ((databaseOptions || databaseURI || collectionPrefix !== '') && databaseAdapter) { throw 'You cannot specify both a databaseAdapter and a databaseURI/databaseOptions/connectionPrefix.'; } else if (!databaseAdapter) { @@ -183,6 +186,7 @@ class ParseServer { const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter); const emailControllerAdapter = loadAdapter(emailAdapter); const cacheControllerAdapter = loadAdapter(cacheAdapter, InMemoryCacheAdapter, {appId: appId}); + const analyticsControllerAdapter = loadAdapter(analyticsAdapter, AnalyticsAdapter); // We pass the options and the base class for the adatper, // Note that passing an instance would work too @@ -194,6 +198,7 @@ class ParseServer { const cacheController = new CacheController(cacheControllerAdapter, appId); const databaseController = new DatabaseController(databaseAdapter); const hooksController = new HooksController(appId, databaseController, webhookKey); + const analyticsController = new AnalyticsController(analyticsControllerAdapter); // TODO: create indexes on first creation of a _User object. Otherwise it's impossible to // have a Parse app without it having a _User collection. @@ -225,6 +230,7 @@ class ParseServer { webhookKey: webhookKey, fileKey: fileKey, facebookAppIds: facebookAppIds, + analyticsController: analyticsController, cacheController: cacheController, filesController: filesController, pushController: pushController, diff --git a/src/Routers/AnalyticsRouter.js b/src/Routers/AnalyticsRouter.js index 76be8d8718..511781d807 100644 --- a/src/Routers/AnalyticsRouter.js +++ b/src/Routers/AnalyticsRouter.js @@ -1,17 +1,20 @@ // AnalyticsRouter.js import PromiseRouter from '../PromiseRouter'; -// Returns a promise that resolves to an empty object response -function ignoreAndSucceed(req) { - return Promise.resolve({ - response: {} - }); +function appOpened(req) { + const analyticsController = req.config.analyticsController; + return analyticsController.appOpened(req); +} + +function trackEvent(req) { + const analyticsController = req.config.analyticsController; + return analyticsController.trackEvent(req); } export class AnalyticsRouter extends PromiseRouter { mountRoutes() { - this.route('POST','/events/AppOpened', ignoreAndSucceed); - this.route('POST','/events/:eventName', ignoreAndSucceed); + this.route('POST','/events/AppOpened', appOpened); + this.route('POST','/events/:eventName', trackEvent); } }