Skip to content

Commit

Permalink
Adds Mixpanel analytics provider (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
pushchris authored Mar 13, 2024
1 parent cde8f8c commit 898595a
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 27 deletions.
14 changes: 13 additions & 1 deletion apps/platform/src/providers/analytics/AnalyticsProvider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { camelcase, titleize } from '../../render/Helpers/String'
import { UserEventParams } from '../../users/UserEvent'
import { snakeCase } from '../../utilities'
import Provider from '../Provider'

export type AnalyticsProviderName = 'segment'
export type AnalyticsProviderName = 'segment' | 'mixpanel' | 'posthog'

export type AnalyticsUserEvent = UserEventParams & { external_id: string }

export type Convention = 'snake_case' | 'camel_case' | 'title_case'

export abstract class AnalyticsProvider extends Provider {
abstract track(event: AnalyticsUserEvent): Promise<void>

tranformEventName(event: string, convention: Convention) {
switch (convention) {
case 'camel_case': return camelcase(event)
case 'snake_case': return snakeCase(event)
case 'title_case': return titleize(event)
}
}
}
70 changes: 70 additions & 0 deletions apps/platform/src/providers/analytics/MixpanelProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ProviderControllers, ProviderParams, ProviderSchema } from '../Provider'
import { AnalyticsProvider, AnalyticsUserEvent } from './AnalyticsProvider'
import { createController } from '../ProviderService'
import { logger } from '../../config/logger'

type Convention = 'snake_case' | 'camel_case' | 'title_case'

interface MixpanelDataParams {
project_token: string
region?: string
is_default: boolean
event_name_convention: Convention
}

interface MixpanelProviderParams extends ProviderParams {
data: MixpanelDataParams
}

export default class MixpanelAnalyticsProvider extends AnalyticsProvider {
project_token!: string
region?: string
event_name_convention: Convention = 'snake_case'

static namespace = 'mixpanel'
static meta = {
name: 'Mixpanel',
url: 'https://mixpanel.com',
icon: 'https://parcelvoy.com/providers/mixpanel.svg',
}

static schema = ProviderSchema<MixpanelProviderParams, MixpanelDataParams>('mixpanelAnalyticsProviderParams', {
type: 'object',
required: ['project_token', 'is_default'],
properties: {
project_token: { type: 'string' },
region: { type: 'string', nullable: true, enum: ['api', 'api-eu'] },
is_default: { type: 'boolean' },
event_name_convention: { type: 'string', enum: ['snake_case', 'camel_case', 'title_case'] },
},
})

async track(event: AnalyticsUserEvent) {

const response = await fetch(`https://${this.region}.mixpanel.com/import`, {
method: 'POST',
headers: {
Authorization: `Basic ${btoa(this.project_token + ':')}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': 'parcelvoy/v1 (+https://github.com/parcelvoy/platform)',
},
body: JSON.stringify([{
event: this.tranformEventName(event.name, this.event_name_convention),
properties: {
...event.data,
distinct_id: event.external_id,
},
}]),
})

if (!response.ok) {
const responseBody = await response.json()
logger.error('Mixpanel error', responseBody)
}
}

static controllers(): ProviderControllers {
return { admin: createController('analytics', this) }
}
}
14 changes: 1 addition & 13 deletions apps/platform/src/providers/analytics/PosthogProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { PostHog } from 'posthog-node'
import { ProviderControllers, ProviderParams, ProviderSchema } from '../Provider'
import { AnalyticsProvider, AnalyticsUserEvent } from './AnalyticsProvider'
import { AnalyticsProvider, AnalyticsUserEvent, Convention } from './AnalyticsProvider'
import { createController } from '../ProviderService'
import { snakeCase } from '../../utilities'
import { camelcase, titleize } from '../../render/Helpers/String'

type Convention = 'snake_case' | 'camel_case' | 'title_case'

interface PostHogDataParams {
api_key: string
Expand Down Expand Up @@ -59,14 +55,6 @@ export default class PostHogAnalyticsProvider extends AnalyticsProvider {
})
}

tranformEventName(event: string, convention: Convention) {
switch (convention) {
case 'camel_case': return camelcase(event)
case 'snake_case': return snakeCase(event)
case 'title_case': return titleize(event)
}
}

static controllers(): ProviderControllers {
return { admin: createController('analytics', this) }
}
Expand Down
14 changes: 1 addition & 13 deletions apps/platform/src/providers/analytics/SegmentProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Analytics as Segment } from '@segment/analytics-node'
import { ProviderControllers, ProviderParams, ProviderSchema } from '../Provider'
import { AnalyticsProvider, AnalyticsUserEvent } from './AnalyticsProvider'
import { AnalyticsProvider, AnalyticsUserEvent, Convention } from './AnalyticsProvider'
import { createController } from '../ProviderService'
import { snakeCase } from '../../utilities'
import { camelcase, titleize } from '../../render/Helpers/String'

type Convention = 'snake_case' | 'camel_case' | 'title_case'

interface SegmentDataParams {
write_key: string
Expand Down Expand Up @@ -54,14 +50,6 @@ export default class SegmentAnalyticsProvider extends AnalyticsProvider {
})
}

tranformEventName(event: string, convention: Convention) {
switch (convention) {
case 'camel_case': return camelcase(event)
case 'snake_case': return snakeCase(event)
case 'title_case': return titleize(event)
}
}

static controllers(): ProviderControllers {
return { admin: createController('analytics', this) }
}
Expand Down
2 changes: 2 additions & 0 deletions apps/platform/src/providers/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { loadDefaultProvider } from '../ProviderRepository'
import { loadControllers } from '../ProviderService'
import Analytics from './Analytics'
import { AnalyticsProvider, AnalyticsProviderName } from './AnalyticsProvider'
import MixpanelAnalyticsProvider from './MixpanelProvider'
import PostHogAnalyticsProvider from './PosthogProvider'
import SegmentAnalyticsProvider from './SegmentProvider'

const typeMap = {
segment: SegmentAnalyticsProvider,
posthog: PostHogAnalyticsProvider,
mixpanel: MixpanelAnalyticsProvider,
}

export const providerMap = (record: { type: AnalyticsProviderName }): AnalyticsProvider => {
Expand Down

0 comments on commit 898595a

Please sign in to comment.