Skip to content

Commit

Permalink
satellite/analytics: add event Arrived From Source
Browse files Browse the repository at this point in the history
Add new analytics event, Arrived From Source. This event is triggered
during the setup phase of the frontend app if the 'source' query
parameter exists. On the backend we define a map of allowed sources
based on constants. This will allow us to identify what brought the user
to the satellite console; in this case, email links.

updates: storj/storj-private#609

Change-Id: I620450c3db2986564d8344cd7e30be5392c9c053
  • Loading branch information
cam-a committed Mar 19, 2024
1 parent 971d1e0 commit 13e69e4
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 8 deletions.
20 changes: 19 additions & 1 deletion satellite/analytics/service.go
Expand Up @@ -17,6 +17,11 @@ import (
)

const (
// SourceTrialExpiringNotice is the trial expiring notice source.
SourceTrialExpiringNotice = "trial_expiring_notice"
// SourceTrialExpiredNotice is the trial expired notice source.
SourceTrialExpiredNotice = "trial_expired_notice"

eventInviteLinkClicked = "Invite Link Clicked"
eventInviteLinkSignup = "Invite Link Signup"
eventAccountCreated = "Account Created"
Expand Down Expand Up @@ -111,6 +116,7 @@ const (
eventBusinessSelected = "Business Selected"
eventUserUpgraded = "User Upgraded"
eventUpgradeClicked = "Upgrade Clicked"
eventArrivedFromSource = "Arrived From Source"
)

var (
Expand Down Expand Up @@ -173,6 +179,7 @@ type Service struct {
config Config
satelliteName string
clientEvents map[string]bool
sources map[string]interface{}

segment segment.Client
hubspot *HubSpotEvents
Expand All @@ -185,6 +192,7 @@ func NewService(log *zap.Logger, config Config, satelliteName string) *Service {
config: config,
satelliteName: satelliteName,
clientEvents: make(map[string]bool),
sources: make(map[string]interface{}),
hubspot: NewHubSpotEvents(log.Named("hubspotclient"), config.HubSpot, satelliteName),
}
if config.Enabled {
Expand All @@ -204,10 +212,13 @@ func NewService(log *zap.Logger, config Config, satelliteName string) *Service {
eventProjectStorageLimitUpdated, eventProjectBandwidthLimitUpdated, eventProjectInvitationAccepted, eventProjectInvitationDeclined,
eventGalleryViewClicked, eventResendInviteClicked, eventRemoveProjectMemberCLicked, eventCopyInviteLinkClicked, eventUserSignUp,
eventPersonalInfoSubmitted, eventBusinessInfoSubmitted, eventUseCaseSelected, eventOnboardingCompleted, eventOnboardingAbandoned,
eventPersonalSelected, eventBusinessSelected, eventUserUpgraded, eventUpgradeClicked} {
eventPersonalSelected, eventBusinessSelected, eventUserUpgraded, eventUpgradeClicked, eventArrivedFromSource} {
service.clientEvents[name] = true
}

service.sources[SourceTrialExpiredNotice] = struct{}{}
service.sources[SourceTrialExpiringNotice] = struct{}{}

return service
}

Expand Down Expand Up @@ -657,6 +668,13 @@ func (service *Service) TrackEvent(eventName string, userID uuid.UUID, email str
return
}

if v, ok := customProps["source"]; ok {
if _, ok = service.sources[v]; !ok {
service.log.Error("Event source is not in allowed list", zap.String("eventName", eventName), zap.String("source", v))
return
}
}

props := segment.NewProperties()
props.Set("email", email)

Expand Down
6 changes: 4 additions & 2 deletions satellite/console/emailreminders/chore.go
Expand Up @@ -5,6 +5,7 @@ package emailreminders

import (
"context"
"fmt"
"strings"
"time"

Expand All @@ -14,6 +15,7 @@ import (

"storj.io/common/sync2"
"storj.io/storj/private/post"
"storj.io/storj/satellite/analytics"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/console/consoleauth"
"storj.io/storj/satellite/console/consoleweb/consoleapi"
Expand Down Expand Up @@ -144,7 +146,7 @@ func (chore *Chore) sendExpirationNotifications(ctx context.Context) (err error)
mon.IntVal("expiring_needing_reminder").Observe(int64(len(users)))

expirationWarning := &console.TrialExpirationReminderEmail{
SignInLink: chore.address + "login?source=trial_expiring_notice",
SignInLink: chore.address + fmt.Sprintf("login?source=%s", analytics.SourceTrialExpiringNotice),
Origin: chore.address,
ContactInfoURL: chore.supportURL,
ScheduleMeetingLink: chore.scheduleMeetingURL,
Expand All @@ -171,7 +173,7 @@ func (chore *Chore) sendExpirationNotifications(ctx context.Context) (err error)
mon.IntVal("expired_needing_notice").Observe(int64(len(users)))

expirationNotice := &console.TrialExpiredEmail{
SignInLink: chore.address + "login?source=trial_expired_notice",
SignInLink: chore.address + fmt.Sprintf("login?source=%s", analytics.SourceTrialExpiredNotice),
Origin: chore.address,
ContactInfoURL: chore.supportURL,
ScheduleMeetingLink: chore.scheduleMeetingURL,
Expand Down
7 changes: 7 additions & 0 deletions web/satellite/src/App.vue
Expand Up @@ -77,6 +77,7 @@ const user = computed<User>(() => usersStore.state.user);
*/
async function setup() {
isLoading.value = true;
const source = new URLSearchParams(window.location.search).get('source');
try {
await usersStore.getUser();
const promises: Promise<void | object | string>[] = [
Expand All @@ -93,6 +94,12 @@ async function setup() {
const invites = projectsStore.state.invitations;
const projects = projectsStore.state.projects;
if (source) {
const props = new Map();
props.set('source', source);
analyticsStore.eventTriggered(AnalyticsEvent.ARRIVED_FROM_SOURCE, props);
}
if (appStore.state.hasJustLoggedIn && !invites.length && projects.length <= 1) {
if (!projects.length) {
await projectsStore.createDefaultProject(usersStore.state.user.id);
Expand Down
10 changes: 5 additions & 5 deletions web/satellite/src/store/modules/analyticsStore.ts
Expand Up @@ -34,16 +34,16 @@ export const useAnalyticsStore = defineStore('analytics', () => {
} catch (_) { /*empty*/ }
}

function eventTriggered(eventName: AnalyticsEvent, props?: {[p: string]: string}): void {
analytics.eventTriggered(eventName, props).catch(_ => {});
function eventTriggered(eventName: AnalyticsEvent, props?: Map<string, string>): void {
analytics.eventTriggered(eventName, props).catch(_ => { });
}

function linkEventTriggered(eventName: AnalyticsEvent, link: string): void {
analytics.linkEventTriggered(eventName, link).catch(_ => {});
analytics.linkEventTriggered(eventName, link).catch(_ => { });
}

function pageVisit(pagePath: string, source: string): void {
analytics.pageVisit(pagePath).catch(_ => {});
analytics.pageVisit(pagePath).catch(_ => { });

if (!plausible.value) {
return;
Expand Down Expand Up @@ -73,7 +73,7 @@ export const useAnalyticsStore = defineStore('analytics', () => {
}

function errorEventTriggered(source: AnalyticsErrorEventSource): void {
analytics.errorEventTriggered(source).catch(_ => {});
analytics.errorEventTriggered(source).catch(_ => { });
}

return {
Expand Down
1 change: 1 addition & 0 deletions web/satellite/src/utils/constants/analyticsEventNames.ts
Expand Up @@ -48,6 +48,7 @@ export enum AnalyticsEvent {
PERSONAL_SELECTED = 'Personal Selected',
BUSINESS_SELECTED = 'Business Selected',
UPGRADE_CLICKED = 'Upgrade Clicked',
ARRIVED_FROM_SOURCE = 'Arrived From Source',
}

export enum AnalyticsErrorEventSource {
Expand Down

0 comments on commit 13e69e4

Please sign in to comment.