diff --git a/backend/src/api/eagleEyeContent/eagleEyeContentTrack.ts b/backend/src/api/eagleEyeContent/eagleEyeContentTrack.ts new file mode 100644 index 0000000000..c3ccdac065 --- /dev/null +++ b/backend/src/api/eagleEyeContent/eagleEyeContentTrack.ts @@ -0,0 +1,21 @@ +import Permissions from '../../security/permissions' +import PermissionChecker from '../../services/user/permissionChecker' +import track from '../../segment/track' + +export default async (req, res) => { + new PermissionChecker(req).validateHas(Permissions.values.eagleEyeContentRead) + track( + 'Eagle Eye post clicked', + { + url: req.body.url, + platform: req.body.platform, + }, + { ...req }, + ) + + const out = { + Success: true, + } + + await req.responseHandler.success(req, res, out) +} diff --git a/backend/src/api/eagleEyeContent/index.ts b/backend/src/api/eagleEyeContent/index.ts index 7fd10c716c..3a1321ab59 100644 --- a/backend/src/api/eagleEyeContent/index.ts +++ b/backend/src/api/eagleEyeContent/index.ts @@ -15,6 +15,12 @@ export default (app) => { safeWrap(require('./eagleEyeContentUpsert').default), ) + app.post( + `/tenant/:tenantId/eagleEyeContent/track`, + featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'), + safeWrap(require('./eagleEyeContentTrack').default), + ) + app.get( `/tenant/:tenantId/eagleEyeContent/search`, featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'), diff --git a/backend/src/services/eagleEyeActionService.ts b/backend/src/services/eagleEyeActionService.ts index 05ad1e0c27..79a5bbd956 100644 --- a/backend/src/services/eagleEyeActionService.ts +++ b/backend/src/services/eagleEyeActionService.ts @@ -5,6 +5,7 @@ import Error404 from '../errors/Error404' import { EagleEyeAction, EagleEyeActionType } from '../types/eagleEyeTypes' import { IServiceOptions } from './IServiceOptions' import { LoggingBase } from './loggingBase' +import track from '../segment/track' export default class EagleEyeActionService extends LoggingBase { options: IServiceOptions @@ -27,6 +28,18 @@ export default class EagleEyeActionService extends LoggingBase { throw new Error404(this.options.language, 'errors.eagleEye.contentNotFound') } + // Tracking here so we have access to url and platform + track( + `Eagle Eye post ${data.type === EagleEyeActionType.BOOKMARK ? 'bookmarked' : 'voted'}`, + { + type: data.type, + url: content.url, + platform: content.platform, + action: 'create', + }, + { ...this.options }, + ) + const existingUserActions: EagleEyeAction[] = content.actions.filter( (a) => a.actionById === this.options.currentUser.id, ) @@ -50,7 +63,10 @@ export default class EagleEyeActionService extends LoggingBase { await EagleEyeActionRepository.removeActionFromContent( EagleEyeActionType.THUMBS_UP, contentId, - this.options, + { + ...this.options, + transaction, + }, ) } else if ( data.type === EagleEyeActionType.THUMBS_UP && @@ -59,16 +75,18 @@ export default class EagleEyeActionService extends LoggingBase { await EagleEyeActionRepository.removeActionFromContent( EagleEyeActionType.THUMBS_DOWN, contentId, - this.options, + { + ...this.options, + transaction, + }, ) } // add new action - const record = await EagleEyeActionRepository.createActionForContent( - data, - contentId, - this.options, - ) + const record = await EagleEyeActionRepository.createActionForContent(data, contentId, { + ...this.options, + transaction, + }) await SequelizeRepository.commitTransaction(transaction) @@ -96,5 +114,17 @@ export default class EagleEyeActionService extends LoggingBase { if (content.actions.length === 0) { await EagleEyeContentRepository.destroy(contentId, this.options) } + + // Tracking here so we have access to url and platform + track( + `Eagle Eye post ${action.type === EagleEyeActionType.BOOKMARK ? 'bookmarked' : 'voted'}`, + { + type: action.type, + url: content.url, + platform: content.platform, + action: 'destroy', + }, + { ...this.options }, + ) } } diff --git a/backend/src/services/eagleEyeSettingsService.ts b/backend/src/services/eagleEyeSettingsService.ts index 7753f2f13d..211494cf35 100644 --- a/backend/src/services/eagleEyeSettingsService.ts +++ b/backend/src/services/eagleEyeSettingsService.ts @@ -2,6 +2,7 @@ import lodash from 'lodash' import SequelizeRepository from '../database/repositories/sequelizeRepository' import UserRepository from '../database/repositories/userRepository' import Error400 from '../errors/Error400' +import track from '../segment/track' import { EagleEyeSettings, EagleEyeFeedSettings, @@ -146,6 +147,41 @@ export default class EagleEyeSettingsService extends LoggingBase { await SequelizeRepository.commitTransaction(transaction) + // Track the events in Segment + const settingsOut: EagleEyeSettings = userOut.eagleEyeSettings + + if (data.emailDigestActive) { + track( + 'Eagle Eye email settings updated', + { + email: settingsOut.emailDigest.email, + frequency: settingsOut.emailDigest.frequency, + time: settingsOut.emailDigest.time, + matchFeedSettings: settingsOut.emailDigest.matchFeedSettings, + platforms: settingsOut.emailDigest.feed.platforms, + publishedDate: settingsOut.emailDigest.feed.publishedDate, + keywords: settingsOut.emailDigest.feed.keywords, + exactKeywords: settingsOut.emailDigest.feed.exactKeywords, + excludeKeywords: settingsOut.emailDigest.feed.excludedKeywords, + }, + { ...this.options }, + ) + } else { + track( + 'Eagle Eye settings updated', + { + onboarded: settingsOut.onboarded, + emailDigestActive: settingsOut.emailDigestActive, + platforms: settingsOut.feed.platforms, + publishedDate: settingsOut.feed.publishedDate, + keywords: settingsOut.feed.keywords, + exactKeywords: settingsOut.feed.exactKeywords, + excludeKeywords: settingsOut.feed.excludedKeywords, + }, + { ...this.options }, + ) + } + return userOut.eagleEyeSettings } catch (error) { await SequelizeRepository.rollbackTransaction(transaction) diff --git a/backend/src/services/premium/enrichment/memberEnrichmentService.ts b/backend/src/services/premium/enrichment/memberEnrichmentService.ts index 93b3075795..3c529eea67 100644 --- a/backend/src/services/premium/enrichment/memberEnrichmentService.ts +++ b/backend/src/services/premium/enrichment/memberEnrichmentService.ts @@ -29,7 +29,7 @@ import RedisPubSubEmitter from '../../../utils/redis/pubSubEmitter' import { createRedisClient } from '../../../utils/redis' import { ApiWebsocketMessage } from '../../../types/mq/apiWebsocketMessage' import MemberEnrichmentCacheRepository from '../../../database/repositories/memberEnrichmentCacheRepository' -import track from '../../../segment/telemetryTrack' +import track from '../../../segment/track' export default class MemberEnrichmentService extends LoggingBase { options: IServiceOptions diff --git a/frontend/src/premium/eagle-eye/components/list/eagle-eye-result-card.vue b/frontend/src/premium/eagle-eye/components/list/eagle-eye-result-card.vue index 8fef282ce8..caafbc80f1 100644 --- a/frontend/src/premium/eagle-eye/components/list/eagle-eye-result-card.vue +++ b/frontend/src/premium/eagle-eye/components/list/eagle-eye-result-card.vue @@ -164,6 +164,7 @@ import { formatDateToTimeAgo } from '@/utils/date' import { computed, defineProps } from 'vue' import platformOptions from '@/premium/eagle-eye/constants/eagle-eye-platforms.json' +import { EagleEyeService } from '../../eagle-eye-service' import { withHttp } from '@/utils/string' import { mapActions, @@ -217,12 +218,17 @@ const bookmarkTooltip = computed(() => { }) // Open post in origin url -const onCardClick = (e) => { +const onCardClick = async (e) => { if (!props.result.url || e.target.localName === 'a') { return } window.open(withHttp(props.result.url), '_blank') + + await EagleEyeService.trackClick({ + url: props.result.url, + platform: props.result.platform + }) } // If opposite thumbs up is set, remove before creating the new action diff --git a/frontend/src/premium/eagle-eye/eagle-eye-service.js b/frontend/src/premium/eagle-eye/eagle-eye-service.js index 7582cbfd3c..bbc15f1f7a 100644 --- a/frontend/src/premium/eagle-eye/eagle-eye-service.js +++ b/frontend/src/premium/eagle-eye/eagle-eye-service.js @@ -41,6 +41,18 @@ export class EagleEyeService { return response.data } + static async trackClick({ url, platform }) { + const tenantId = AuthCurrentTenant.get() + const response = await authAxios.post( + `/tenant/${tenantId}/eagleEyeContent/track`, + { + url, + platform + } + ) + return response.data + } + static async addAction({ postId, actionData }) { const tenantId = AuthCurrentTenant.get() diff --git a/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue b/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue index 2a71f20696..81cce7c30e 100644 --- a/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue +++ b/frontend/src/premium/eagle-eye/pages/eagle-eye-page.vue @@ -41,7 +41,7 @@ import AppEagleEyeSettings from '@/premium/eagle-eye/components/list/eagle-eye-s import AppEagleEyeList from '@/premium/eagle-eye/components/list/eagle-eye-list.vue' import AppEagleEyeLoadingState from '@/premium/eagle-eye/components/list/eagle-eye-loading-state.vue' import { mapGetters } from '@/shared/vuex/vuex.helpers' -import { computed, onMounted } from 'vue' +import { computed, onMounted } from 'vue' import { useStore } from 'vuex' const { showBanner } = mapGetters('tenant')