From 9702346edc7677367dcb6e2591ebb3d1e3dbc3b6 Mon Sep 17 00:00:00 2001 From: June Kelly Date: Tue, 1 Mar 2022 09:54:13 +0000 Subject: [PATCH] Merge pull request #6628 from overleaf/jk-heartbeat-editor-type-metadata Add editor metadata to editing-session pings GitOrigin-RevId: f42be772c13f71380d59bae7c447645fe0e16a17 --- .../Features/Analytics/AnalyticsController.js | 22 ++++++++++++++++++- .../Features/Analytics/AnalyticsManager.js | 3 ++- services/web/frontend/js/ide.js | 5 ++++- .../web/frontend/js/ide/editor/Document.js | 12 ++++++++++ .../frontend/js/ide/editor/EditorManager.js | 7 ++++++ services/web/frontend/js/main/event.js | 11 +++++----- .../src/Analytics/AnalyticsControllerTests.js | 7 +++++- .../src/Analytics/AnalyticsManagerTests.js | 5 ++++- 8 files changed, 62 insertions(+), 10 deletions(-) diff --git a/services/web/app/src/Features/Analytics/AnalyticsController.js b/services/web/app/src/Features/Analytics/AnalyticsController.js index f50470a2b3..67ab97f657 100644 --- a/services/web/app/src/Features/Analytics/AnalyticsController.js +++ b/services/web/app/src/Features/Analytics/AnalyticsController.js @@ -4,6 +4,20 @@ const SessionManager = require('../Authentication/SessionManager') const GeoIpLookup = require('../../infrastructure/GeoIpLookup') const Features = require('../../infrastructure/Features') +const getSegmentation = req => { + const segmentation = req.body ? req.body.segmentation : null + const cleanedSegmentation = {} + if ( + segmentation && + segmentation.editorType && + typeof segmentation.editorType === 'string' && + segmentation.editorType.length < 100 + ) { + cleanedSegmentation.editorType = segmentation.editorType + } + return cleanedSegmentation +} + module.exports = { updateEditingSession(req, res, next) { if (!Features.hasFeature('analytics')) { @@ -11,6 +25,7 @@ module.exports = { } const userId = SessionManager.getLoggedInUserId(req.session) const { projectId } = req.params + const segmentation = getSegmentation(req) let countryCode = null if (userId) { @@ -20,7 +35,12 @@ module.exports = { } else if (geoDetails && geoDetails.country_code) { countryCode = geoDetails.country_code } - AnalyticsManager.updateEditingSession(userId, projectId, countryCode) + AnalyticsManager.updateEditingSession( + userId, + projectId, + countryCode, + segmentation + ) }) } res.sendStatus(202) diff --git a/services/web/app/src/Features/Analytics/AnalyticsManager.js b/services/web/app/src/Features/Analytics/AnalyticsManager.js index 281d6a14c3..c4beeb6f23 100644 --- a/services/web/app/src/Features/Analytics/AnalyticsManager.js +++ b/services/web/app/src/Features/Analytics/AnalyticsManager.js @@ -123,7 +123,7 @@ async function setUserPropertyForSession(session, propertyName, propertyValue) { } } -function updateEditingSession(userId, projectId, countryCode) { +function updateEditingSession(userId, projectId, countryCode, segmentation) { if (!userId) { return } @@ -139,6 +139,7 @@ function updateEditingSession(userId, projectId, countryCode) { userId, projectId, countryCode, + segmentation, createdAt: new Date(), }) .then(() => { diff --git a/services/web/frontend/js/ide.js b/services/web/frontend/js/ide.js index 6f97b1ea05..de6e04568a 100644 --- a/services/web/frontend/js/ide.js +++ b/services/web/frontend/js/ide.js @@ -259,7 +259,10 @@ If the project has been renamed please look in your project list for a new proje }) ide.editingSessionHeartbeat = () => { - eventTracking.editingSessionHeartbeat() + const segmentation = { + editorType: ide.editorManager.getEditorType(), + } + eventTracking.editingSessionHeartbeat(segmentation) } $scope.$on('cursor:editor:update', () => { diff --git a/services/web/frontend/js/ide/editor/Document.js b/services/web/frontend/js/ide/editor/Document.js index 87b3a2fdf1..38418939a5 100644 --- a/services/web/frontend/js/ide/editor/Document.js +++ b/services/web/frontend/js/ide/editor/Document.js @@ -91,6 +91,18 @@ export default Document = (function () { this._bindToSocketEvents() } + editorType() { + if (this.ace) { + return 'ace' + } else if (this.cm6) { + return 'cm6' + } else if (this.cm) { + return 'cm-rich-text' + } else { + return null + } + } + attachToAce(ace) { this.ace = ace if (this.doc != null) { diff --git a/services/web/frontend/js/ide/editor/EditorManager.js b/services/web/frontend/js/ide/editor/EditorManager.js index a725a34a44..1ef1f7ea94 100644 --- a/services/web/frontend/js/ide/editor/EditorManager.js +++ b/services/web/frontend/js/ide/editor/EditorManager.js @@ -132,6 +132,13 @@ export default EditorManager = (function () { }) } + getEditorType() { + if (!this.$scope.editor.sharejs_doc) { + return null + } + return this.$scope.editor.sharejs_doc.editorType() + } + showRichText() { return ( this.localStorage(`editor.mode.${this.$scope.project_id}`) === diff --git a/services/web/frontend/js/main/event.js b/services/web/frontend/js/main/event.js index fcf2248bd7..748fb1183b 100644 --- a/services/web/frontend/js/main/event.js +++ b/services/web/frontend/js/main/event.js @@ -48,10 +48,11 @@ App.factory('eventTracking', function ($http, localStorage) { return localStorage(CACHE_KEY, curCache) } - const _sendEditingSessionHeartbeat = () => + const _sendEditingSessionHeartbeat = segmentation => $http({ url: `/editingSession/${window.project_id}`, method: 'PUT', + data: { segmentation }, headers: { 'X-CSRF-Token': window.csrfToken, }, @@ -69,14 +70,14 @@ App.factory('eventTracking', function ($http, localStorage) { } }, - editingSessionHeartbeat() { - sl_console.log('[Event] heartbeat trigger') + editingSessionHeartbeat(segmentation) { + sl_console.log('[Event] heartbeat trigger', segmentation) if (!(nextHeartbeat <= new Date())) { return } - sl_console.log('[Event] send heartbeat request') - _sendEditingSessionHeartbeat() + sl_console.log('[Event] send heartbeat request', segmentation) + _sendEditingSessionHeartbeat(segmentation) heartbeatsSent++ diff --git a/services/web/test/unit/src/Analytics/AnalyticsControllerTests.js b/services/web/test/unit/src/Analytics/AnalyticsControllerTests.js index 9a29cffbe7..ec1ec0b6db 100644 --- a/services/web/test/unit/src/Analytics/AnalyticsControllerTests.js +++ b/services/web/test/unit/src/Analytics/AnalyticsControllerTests.js @@ -43,6 +43,11 @@ describe('AnalyticsController', function () { projectId: 'a project id', }, session: {}, + body: { + segmentation: { + editorType: 'abc', + }, + }, } this.GeoIpLookup.getDetails = sinon .stub() @@ -54,7 +59,7 @@ describe('AnalyticsController', function () { this.controller.updateEditingSession(this.req, this.res) this.AnalyticsManager.updateEditingSession - .calledWith('1234', 'a project id', 'XY') + .calledWith('1234', 'a project id', 'XY', { editorType: 'abc' }) .should.equal(true) done() }) diff --git a/services/web/test/unit/src/Analytics/AnalyticsManagerTests.js b/services/web/test/unit/src/Analytics/AnalyticsManagerTests.js index 9ff02f29ba..70a0a14205 100644 --- a/services/web/test/unit/src/Analytics/AnalyticsManagerTests.js +++ b/services/web/test/unit/src/Analytics/AnalyticsManagerTests.js @@ -146,10 +146,12 @@ describe('AnalyticsManager', function () { it('updateEditingSession', function () { const projectId = '789ghi' const countryCode = 'fr' + const segmentation = { editorType: 'abc' } this.AnalyticsManager.updateEditingSession( this.fakeUserId, projectId, - countryCode + countryCode, + segmentation ) sinon.assert.calledWithMatch( this.analyticsEditingSessionQueue.add, @@ -158,6 +160,7 @@ describe('AnalyticsManager', function () { userId: this.fakeUserId, projectId, countryCode, + segmentation, } ) })