From ed207751ada7ecee51c6d9021f1aebb3752d17f3 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 16 Oct 2024 15:16:33 +0800 Subject: [PATCH] Update telemetry calls for docs to support PH (#29749) * Update telemetry calls for docs to support PH * Update telemetry calls for www to support PH * Add ts ignore * Remove use of useRouter for docs * Add credentials include for www and docs for telemetry calls * Update TELEMETRY_CONSENT in common for www and docs to reset telemetry opt in preference * Update common telemetry to use new endpoint and payload, and trigger reset request when opting out of telemetry from ui patterns PrigacySettings * Fix * Fix * Fix * Fix build issue in docs * Fix build issue in docs * I hope this fixes the build issues * once more... * Fix * Add credentials include * Fix --- .../features/telemetry/telemetry.client.tsx | 54 ++++++++++--------- apps/docs/lib/fetch/fetchWrappers.ts | 2 + apps/docs/lib/telemetry.ts | 31 ++++++++--- apps/www/lib/fetchWrapper.ts | 5 +- apps/www/lib/telemetry.ts | 51 +++++++++--------- apps/www/pages/_app.tsx | 46 +++++++++++----- packages/common/constants/local-storage.ts | 2 +- packages/common/fetchWrappers.tsx | 7 ++- packages/common/hooks/useTelemetryProps.ts | 15 +++++- packages/common/telemetry.ts | 31 ++++++++--- .../ui-patterns/PrivacySettings/index.tsx | 14 ++++- 11 files changed, 172 insertions(+), 86 deletions(-) diff --git a/apps/docs/features/telemetry/telemetry.client.tsx b/apps/docs/features/telemetry/telemetry.client.tsx index a0854d7c172..b32c4ad9c75 100644 --- a/apps/docs/features/telemetry/telemetry.client.tsx +++ b/apps/docs/features/telemetry/telemetry.client.tsx @@ -1,38 +1,42 @@ 'use client' -import { useTelemetryProps } from 'common' +import { isBrowser } from 'common' import { usePathname } from 'next/navigation' import { useCallback, useEffect } from 'react' import { useConsent } from 'ui-patterns/ConsentToast' -import { BASE_PATH, IS_PLATFORM } from '~/lib/constants' +import { IS_PLATFORM } from '~/lib/constants' import { unauthedAllowedPost } from '~/lib/fetch/fetchWrappers' const useSendPageTelemetryWithConsent = () => { + const pathname = usePathname() const { hasAcceptedConsent } = useConsent() - const telemetryProps = useTelemetryProps() - const sendPageTelemetry = useCallback( - (route: string) => { - if (!(IS_PLATFORM && hasAcceptedConsent)) return + const sendPageTelemetry = useCallback(() => { + if (!(IS_PLATFORM && hasAcceptedConsent)) return - unauthedAllowedPost('/platform/telemetry/page', { - body: { - // @ts-ignore [JOSHEN] To be fixed for PH - referrer: document.referrer, - title: document.title, - route: `${BASE_PATH}${route}`, - ga: { - screen_resolution: telemetryProps?.screenResolution, - language: telemetryProps?.language, - session_id: '', - }, + const title = typeof document !== 'undefined' ? document?.title : '' + const referrer = typeof document !== 'undefined' ? document?.referrer : '' + + unauthedAllowedPost('/platform/telemetry/page', { + body: { + pathname, + page_url: isBrowser ? window.location.href : '', + page_title: title, + ph: { + referrer, + language: navigator.language ?? 'en-US', + user_agent: navigator.userAgent, + search: isBrowser ? window.location.search : '', + viewport_height: isBrowser ? window.innerHeight : 0, + viewport_width: isBrowser ? window.innerWidth : 0, }, - }).catch((e) => { - console.error('Problem sending telemetry:', e) - }) - }, - [telemetryProps, hasAcceptedConsent] - ) + }, + headers: { Version: '2' }, + credentials: 'include', + }).catch((e) => { + console.error('Problem sending telemetry:', e) + }) + }, [pathname, hasAcceptedConsent]) return sendPageTelemetry } @@ -42,9 +46,7 @@ const PageTelemetry = () => { const sendPageTelemetry = useSendPageTelemetryWithConsent() useEffect(() => { - if (pathname) { - sendPageTelemetry(pathname) - } + if (pathname) sendPageTelemetry() }, [pathname, sendPageTelemetry]) return null diff --git a/apps/docs/lib/fetch/fetchWrappers.ts b/apps/docs/lib/fetch/fetchWrappers.ts index 440ac83dc8b..d978d4b06f2 100644 --- a/apps/docs/lib/fetch/fetchWrappers.ts +++ b/apps/docs/lib/fetch/fetchWrappers.ts @@ -49,6 +49,7 @@ export const post: typeof _post = async (url, init) => { const headers = await constructHeaders(init?.headers) return await _post(url, { + credentials: 'include', ...init, headers, }) @@ -58,6 +59,7 @@ export const unauthedAllowedPost: typeof _post = async (url, init) => { const headers = await constructHeaders(init?.headers, { allowUnauthenticated: true }) return await _post(url, { + credentials: 'include', ...init, headers, }) diff --git a/apps/docs/lib/telemetry.ts b/apps/docs/lib/telemetry.ts index 222dea30901..bfc8b89a73e 100644 --- a/apps/docs/lib/telemetry.ts +++ b/apps/docs/lib/telemetry.ts @@ -1,7 +1,6 @@ +import { isBrowser } from 'common' import { usePathname } from 'next/navigation' - import { useConsent } from 'ui-patterns/ConsentToast' - import { unauthedAllowedPost } from './fetch/fetchWrappers' type TelemetryEvent = { @@ -18,20 +17,36 @@ const noop = () => {} * Checks for user consent to telemetry before sending. */ const useSendTelemetryEvent = () => { - const { hasAcceptedConsent } = useConsent() const pathname = usePathname() + const { hasAcceptedConsent } = useConsent() if (!hasAcceptedConsent) return noop + const title = typeof document !== 'undefined' ? document?.title : '' + const referrer = typeof document !== 'undefined' ? document?.referrer : '' + return (event: TelemetryEvent) => unauthedAllowedPost('/platform/telemetry/event', { - // @ts-ignore - endpoint will accept this just fine body: { - ...event, - page_title: document?.title, - // @ts-ignore [JOSHEN] To be fixed for PH - page_location: pathname, + pathname, + action: event.action, + page_url: isBrowser ? window.location.href : '', + page_title: title, + ph: { + referrer, + language: navigator.language ?? 'en-US', + user_agent: navigator.userAgent, + search: isBrowser ? window.location.search : '', + viewport_height: isBrowser ? window.innerHeight : 0, + viewport_width: isBrowser ? window.innerWidth : 0, + }, + custom_properties: { + category: event.category, + label: event.label, + } as any, }, + headers: { Version: '2' }, + credentials: 'include', }) .then(({ error }) => { if (error) console.error(error) diff --git a/apps/www/lib/fetchWrapper.ts b/apps/www/lib/fetchWrapper.ts index e5ba7a29072..b129e8ab7e6 100644 --- a/apps/www/lib/fetchWrapper.ts +++ b/apps/www/lib/fetchWrapper.ts @@ -25,11 +25,13 @@ export async function get(url: string, options?: RequestInit) { } export async function post(url: string, data: DataProps, options?: RequestInit) { + const { headers: optionHeaders, ...otherOptions } = options || {} const accessToken = await getAccessToken() let headers = new Headers({ 'Content-Type': 'application/json', Accept: 'application/json', + ...optionHeaders, }) if (accessToken) { @@ -39,8 +41,9 @@ export async function post(url: string, data: DataProps, options?: RequestInit) return fetch(url, { method: 'POST', headers, + credentials: 'include', referrerPolicy: 'no-referrer-when-downgrade', body: JSON.stringify(data), - ...options, + ...otherOptions, }) } diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts index 31f2eb90343..6255a45aa4c 100644 --- a/apps/www/lib/telemetry.ts +++ b/apps/www/lib/telemetry.ts @@ -1,7 +1,7 @@ -import { post } from '~/lib/fetchWrapper' -import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants' +import { LOCAL_STORAGE_KEYS, TelemetryProps } from 'common' +import { API_URL, IS_PREVIEW, IS_PROD } from 'lib/constants' import { NextRouter } from 'next/router' -import { LOCAL_STORAGE_KEYS } from 'common' +import { post } from '~/lib/fetchWrapper' export interface TelemetryEvent { category: string @@ -10,17 +10,12 @@ export interface TelemetryEvent { value?: string } -export interface TelemetryProps { - screenResolution?: string - language: string -} - const noop = () => {} // This event is the same as in studio/lib/telemetry.tx // but uses different ENV variables for www -const sendEvent = (event: TelemetryEvent, gaProps: TelemetryProps, router: NextRouter) => { +const sendEvent = (event: TelemetryEvent, telemetryProps: TelemetryProps, router: NextRouter) => { const consent = typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT) @@ -32,22 +27,30 @@ const sendEvent = (event: TelemetryEvent, gaProps: TelemetryProps, router: NextR if (blockEvent) return noop const { category, action, label, value } = event + const title = typeof document !== 'undefined' ? document?.title : '' + const referrer = typeof document !== 'undefined' ? document?.referrer : '' - return post(`${API_URL}/telemetry/event`, { - action: action, - category: category, - label: label, - value: value, - page_referrer: document?.referrer, - page_title: document?.title, - page_location: router.asPath, - ga: { - screen_resolution: gaProps?.screenResolution, - language: gaProps?.language, + const { page_url, search, language, viewport_height, viewport_width } = telemetryProps + + return post( + `${API_URL}/telemetry/event`, + { + page_url, + action: action, + page_title: title, + pathname: router.pathname, + ph: { + search, + referrer, + language, + viewport_height, + viewport_width, + user_agent: navigator.userAgent, + }, + custom_properties: { category, label, value } as any, }, - }) + { headers: { Version: '2' }, credentials: 'include' } + ) } -export default { - sendEvent, -} +export default { sendEvent } diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index a2884f5133d..4720fd4810a 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -3,7 +3,14 @@ import 'config/code-hike.scss' import '../styles/index.css' import { SessionContextProvider } from '@supabase/auth-helpers-react' -import { AuthProvider, IS_PROD, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common' +import { + AuthProvider, + IS_PROD, + isBrowser, + ThemeProvider, + useTelemetryProps, + useThemeSandbox, +} from 'common' import { DefaultSeo } from 'next-seo' import { AppProps } from 'next/app' import Head from 'next/head' @@ -30,25 +37,38 @@ export default function App({ Component, pageProps }: AppProps) { const IS_DEV = !IS_PROD && !IS_PREVIEW const blockEvents = IS_DEV || !hasAcceptedConsent + const title = typeof document !== 'undefined' ? document?.title : '' + const referrer = typeof document !== 'undefined' ? document?.referrer : '' + + const { search, language, viewport_height, viewport_width } = telemetryProps + useThemeSandbox() - function handlePageTelemetry(route: string) { - return post(`${API_URL}/telemetry/page`, { - referrer: document.referrer, - title: document.title, - route, - ga: { - screen_resolution: telemetryProps?.screenResolution, - language: telemetryProps?.language, + function handlePageTelemetry(url: string) { + return post( + `${API_URL}/telemetry/page`, + { + page_url: url, + page_title: title, + pathname: router.pathname, + ph: { + referrer, + language, + search, + viewport_height, + viewport_width, + user_agent: navigator.userAgent, + }, }, - }) + { headers: { Version: '2' }, credentials: 'include' } + ) } useEffect(() => { if (blockEvents) return - function handleRouteChange(url: string) { - handlePageTelemetry(url) + function handleRouteChange() { + handlePageTelemetry(window.location.href) } // Listen for page changes after a navigation or when the query changes @@ -64,7 +84,7 @@ export default function App({ Component, pageProps }: AppProps) { * Send page telemetry on first page load */ if (router.isReady) { - handlePageTelemetry(router.asPath) + handlePageTelemetry(window.location.href) } }, [router.isReady, consentValue]) diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index 46b45969e9c..3a3faca6fbf 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -1,5 +1,5 @@ export const LOCAL_STORAGE_KEYS = { - TELEMETRY_CONSENT: 'supabase-consent', + TELEMETRY_CONSENT: 'supabase-consent-ph', HIDE_PROMO_TOAST: 'supabase-hide-promo-toast', BLOG_VIEW: 'supabase-blog-view', } diff --git a/packages/common/fetchWrappers.tsx b/packages/common/fetchWrappers.tsx index 0313e49565f..54b8c1879f4 100644 --- a/packages/common/fetchWrappers.tsx +++ b/packages/common/fetchWrappers.tsx @@ -2,16 +2,19 @@ interface DataProps { [prop: string]: any } -export const post = (url: string, data: DataProps, options = {}) => { +export const post = (url: string, data: DataProps, options = {} as { [key: string]: any }) => { + const { optionHeaders, ...otherOptions } = options return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', + ...optionHeaders, }, + credentials: 'include', referrerPolicy: 'no-referrer-when-downgrade', body: JSON.stringify(data), - ...options, + ...otherOptions, }).catch((error) => { console.error('Error at fetchWrapper - post:', error) }) diff --git a/packages/common/hooks/useTelemetryProps.ts b/packages/common/hooks/useTelemetryProps.ts index e780615654c..88a16ef8b8f 100644 --- a/packages/common/hooks/useTelemetryProps.ts +++ b/packages/common/hooks/useTelemetryProps.ts @@ -3,12 +3,25 @@ import { useRouter } from 'next/compat/router' import { isBrowser } from '../helpers' -export function useTelemetryProps() { +export interface TelemetryProps { + screenResolution?: string + page_url: string + search: string + language: string + viewport_height: number + viewport_width: number +} + +export function useTelemetryProps(): TelemetryProps { const router = useRouter() const locale = router ? router.locale : undefined return { screenResolution: isBrowser ? `${window.innerWidth}x${window.innerHeight}` : undefined, + page_url: isBrowser ? window.location.href : '', + search: isBrowser ? window.location.search : '', + viewport_height: isBrowser ? window.innerHeight : 0, + viewport_width: isBrowser ? window.innerWidth : 0, language: locale ?? 'en-US', } } diff --git a/packages/common/telemetry.ts b/packages/common/telemetry.ts index eb536b448d6..a2a8857d875 100644 --- a/packages/common/telemetry.ts +++ b/packages/common/telemetry.ts @@ -1,13 +1,28 @@ import { post } from './fetchWrappers' export function handlePageTelemetry(API_URL: string, route: string, telemetryProps: any) { - return post(`${API_URL}/telemetry/page`, { - referrer: document.referrer, - title: document.title, - route, - ga: { - screen_resolution: telemetryProps?.screenResolution, - language: telemetryProps?.language, + const { page_url, search, language, viewport_height, viewport_width } = telemetryProps + return post( + `${API_URL}/telemetry/page`, + { + page_url, + page_title: document.title, + pathname: route, + ph: { + referrer: document.referrer, + user_agent: navigator.userAgent, + search, + language, + viewport_height, + viewport_width, + }, }, - }) + { + headers: { Version: '2' }, + } + ) +} + +export function handleResetTelemetry(API_URL: string) { + return post(`${API_URL}/telemetry/reset`, {}) } diff --git a/packages/ui-patterns/PrivacySettings/index.tsx b/packages/ui-patterns/PrivacySettings/index.tsx index a8e20482901..d6b34a5ddd9 100644 --- a/packages/ui-patterns/PrivacySettings/index.tsx +++ b/packages/ui-patterns/PrivacySettings/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { LOCAL_STORAGE_KEYS } from 'common' +import { handleResetTelemetry, LOCAL_STORAGE_KEYS } from 'common' import Link from 'next/link' import { PropsWithChildren, useEffect, useState } from 'react' import { Modal, Toggle } from 'ui' @@ -30,6 +30,10 @@ export const PrivacySettings = ({ setIsOpen(false) } + const handleOptOutTelemetry = async () => { + handleResetTelemetry(process.env.NEXT_PUBLIC_API_URL!) + } + return ( <>