Created
February 13, 2023 16:29
-
-
Save ntotten/2d6ed938cc024b2026a3016d38ac27f7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ZuploRequest, ZuploContext, ResponseFactory, environment } from "@zuplo/runtime"; | |
import { SegmentClient } from "./segment"; | |
export async function postLogin( | |
request: ZuploRequest, | |
context: ZuploContext | |
): Promise<Response> { | |
if (!environment.SEGMENT_WRITE_KEY) { | |
throw new Error("SEGMENT_WRITE_KEY environment variable not set") | |
} | |
const client = new SegmentClient(environment.SEGMENT_WRITE_KEY); | |
const { user, stats, geoip, language, user_agent, ip } = await request.json() as Auth0LoginEvent; | |
await client.identity({ | |
userId: user.user_id, | |
traits: { | |
email: user.email, | |
emailVerified: user.email_verified, | |
username: user.username, | |
lastName: user.family_name, | |
firstName: user.given_name, | |
logins: stats?.logins_count, | |
createdAt: user.created_at, | |
phone: user.phone_number, | |
phoneVerified: user.phone_verified, | |
avatar: user.picture, | |
}, | |
context: { | |
active: true, | |
ip: ip, | |
location: { | |
longitude: geoip?.longitude, | |
latitude: geoip?.latitude, | |
city: geoip?.cityName, | |
country: geoip?.countryName, | |
}, | |
timezone: geoip?.timeZone, | |
locale: language, | |
userAgent: user_agent | |
} | |
}) | |
return new Response(null, { status: 200 }); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const API_URL = "https://api.segment.io/v1"; | |
export class SegmentClient { | |
private authHeader: string; | |
constructor(writeKey: string) { | |
this.authHeader = "Basic " + btoa(`${writeKey}:`); | |
} | |
identity(data: SegmentIdentity): Promise<void> { | |
return this.post("/identify", data); | |
} | |
track(data: SegmentTrack): Promise<void> { | |
return this.post("/track", data); | |
} | |
page(data: SegmentPage): Promise<void> { | |
return this.post("/page", data); | |
} | |
screen(data: SegmentScreen): Promise<void> { | |
return this.post("/screen", data); | |
} | |
group(data: SegmentGroup): Promise<void> { | |
return this.post("/group", data); | |
} | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
private async post(pathname: string, data: any): Promise<void> { | |
const response = await fetch(`${API_URL}${pathname}`, { | |
method: "POST", | |
headers: { | |
Authorization: this.authHeader, | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify(data), | |
}); | |
// Segment always returns a 200, unless the request is in an invalid | |
// format or it is too large (32KB). | |
if (response.status !== 200) { | |
throw new Error("Error posting to segment API"); | |
} | |
} | |
} | |
export interface SegmentBaseEvent { | |
userId?: string; | |
anonymousId?: string; | |
context?: Partial<SegmentContext>; | |
properties?: Partial<SegmentProperties>; | |
/** | |
* Timestamp when the message itself took place, | |
* defaulted to the current time by the Segment | |
* Tracking API, as a ISO-8601 format date string. | |
* If the event just happened, leave it out and | |
* we’ll use the server’s time. If you’re importing | |
* data from the past, make sure you to provide a | |
* timestamp. | |
* | |
* See the Timestamps fields docs for more detail. | |
*/ | |
timestamp?: string; | |
} | |
export interface SegmentPage extends SegmentBaseEvent { | |
/** | |
* Name of the page For example, most sites have a | |
* “Signup” page that can be useful to tag, | |
* so you can see users as they move through your funnel. | |
*/ | |
name: string; | |
} | |
export interface SegmentScreen extends SegmentBaseEvent { | |
/** | |
* Name of the screen See the Name field docs for more details. | |
*/ | |
name: string; | |
} | |
export interface SegmentTrack extends SegmentBaseEvent { | |
event: string; | |
} | |
export interface SegmentGroup extends SegmentIdentity { | |
groupId: string; | |
} | |
export interface SegmentIdentity { | |
userId?: string; | |
anonymousId?: string; | |
traits?: Partial<SegmentTraits>; | |
context?: Partial<SegmentContext>; | |
/** | |
* Timestamp when the message itself took place, | |
* defaulted to the current time by the Segment | |
* Tracking API, as a ISO-8601 format date string. | |
* If the event just happened, leave it out and | |
* we’ll use the server’s time. If you’re importing | |
* data from the past, make sure you to provide a | |
* timestamp. | |
* | |
* See the Timestamps fields docs for more detail. | |
*/ | |
timestamp?: string; | |
} | |
export interface SegmentContext { | |
/** | |
* Whether a user is active | |
* | |
* This is usually used to flag an .identify() call to just update the traits but not “last seen.” | |
*/ | |
active: boolean; | |
/** | |
* Information about the current application. | |
*/ | |
app: Partial<{ | |
name: string; | |
version: string; | |
build: string; | |
}>; | |
/** | |
* Dictionary of information about the campaign that resulted | |
* in the API call, containing name, source, medium, term, | |
* content, and any other custom UTM parameter. | |
* | |
* This maps directly to the common UTM campaign parameters. | |
*/ | |
campaign: Partial<{ | |
[key: string]: string | number; | |
name: string; | |
source: string; | |
medium: string; | |
term: string; | |
content: string; | |
}>; | |
device: Partial<{ | |
id: string; | |
advertisingId: string; | |
manufacturer: string; | |
model: string; | |
name: string; | |
type: string; | |
version: string; | |
}>; | |
ip: string; | |
library: Partial<{ | |
name: string; | |
version: string; | |
}>; | |
locale: string; | |
location: Partial<{ | |
city: string; | |
country: string; | |
latitude: string; | |
longitude: string; | |
region: string; | |
speed: string; | |
}>; | |
network: Partial<{ | |
bluetooth: string; | |
carrier: string; | |
cellular: string; | |
wifi: string; | |
}>; | |
os: Partial<{ | |
name: string; | |
version: string; | |
}>; | |
page: Partial<{ | |
path: string; | |
referrer: string; | |
search: string; | |
title: string; | |
url: string; | |
}>; | |
referrer: Partial<{ | |
type: string; | |
name: string; | |
url: string; | |
link: string; | |
}>; | |
screen: Partial<{ | |
density: number; | |
height: number; | |
width: number; | |
}>; | |
/** | |
* Timezones are sent as tzdata strings to add user | |
* timezone information which might be stripped | |
* from the timestamp, for example America/New_York | |
*/ | |
timezone: string; | |
/** | |
* Group / Account ID. | |
* This is useful in B2B use cases where you need to | |
* attribute your non-group calls to a company or | |
* account. It is relied on by several Customer | |
* Success and CRM tools. | |
*/ | |
groupId: string; | |
traits: Partial<SegmentTraits>; | |
userAgent: string; | |
} | |
export interface SegmentTraits { | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
[key: string]: any; | |
address: Partial<{ | |
city: string; | |
country: string; | |
postalCode: string; | |
state: string; | |
street: string; | |
}>; | |
age: number; | |
/** | |
* URL to an avatar image for the user | |
*/ | |
avatar: string; | |
/** | |
* ISO-8601 date string | |
*/ | |
birthday: string; | |
company: Partial<{ | |
name: string; | |
id: string | number; | |
industry: string; | |
employeeCount: number; | |
plan: string; | |
}>; | |
createdAt: string; | |
description: string; | |
email: string; | |
firstName: string; | |
lastName: string; | |
gender: string; | |
/** | |
* Unique ID in your database for a user | |
*/ | |
id: string; | |
/** | |
* Full name of a user. If you only pass a first and last name Segment automatically fills in the full name for you. | |
*/ | |
name: string; | |
phone: string; | |
/** | |
* Title of a user, usually related to their position at a specific company. Example: “VP of Engineering” | |
*/ | |
title: string; | |
/** | |
* User’s username. This should be unique to each user, like the usernames of Twitter or GitHub. | |
*/ | |
username: string; | |
website: string; | |
} | |
export interface SegmentProperties { | |
[key: string]: string | number | boolean; | |
revenue: number; | |
currency: string; | |
value: number; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment