Add new fields to analytics event context: status, page_type, page_document_type (#21115)
* Add new fields to analytics event context: status, page-type, page-document-type * Update schema-event.js * Typescript * Add status meta to error pages * Update DefaultLayout.tsx * Update DefaultLayout.tsx * Update building-and-testing-nodejs-or-python.tsx
This commit is contained in:
@@ -11,7 +11,7 @@ import { useTranslation } from './hooks/useTranslation'
|
|||||||
|
|
||||||
type Props = { children?: React.ReactNode }
|
type Props = { children?: React.ReactNode }
|
||||||
export const DefaultLayout = (props: Props) => {
|
export const DefaultLayout = (props: Props) => {
|
||||||
const { page, error, isHomepageVersion, currentPathWithoutLanguage, fullUrl } = useMainContext()
|
const { page, error, isHomepageVersion, currentPathWithoutLanguage, fullUrl, status } = useMainContext()
|
||||||
const { t } = useTranslation('errors')
|
const { t } = useTranslation('errors')
|
||||||
return (
|
return (
|
||||||
<div className="d-lg-flex">
|
<div className="d-lg-flex">
|
||||||
@@ -26,6 +26,7 @@ export const DefaultLayout = (props: Props) => {
|
|||||||
{/* For Google and Bots */}
|
{/* For Google and Bots */}
|
||||||
{page.introPlainText && <meta name="description" content={page.introPlainText} />}
|
{page.introPlainText && <meta name="description" content={page.introPlainText} />}
|
||||||
|
|
||||||
|
{/* For local site search indexing */}
|
||||||
{page.topics.length > 0 && <meta name="keywords" content={page.topics.join(',')} />}
|
{page.topics.length > 0 && <meta name="keywords" content={page.topics.join(',')} />}
|
||||||
|
|
||||||
{page.hidden && <meta name="robots" content="noindex" />}
|
{page.hidden && <meta name="robots" content="noindex" />}
|
||||||
@@ -41,6 +42,11 @@ export const DefaultLayout = (props: Props) => {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* For analytics events */}
|
||||||
|
{status && <meta name="status" content={status.toString()} />}
|
||||||
|
{page.type && <meta name="page-type" content={page.type} />}
|
||||||
|
{page.documentType && <meta name="page-document-type" content={page.documentType} />}
|
||||||
|
|
||||||
{page.fullTitle && (
|
{page.fullTitle && (
|
||||||
<>
|
<>
|
||||||
<meta property="og:site_name" content="GitHub Docs" />
|
<meta property="og:site_name" content="GitHub Docs" />
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export function GenericError() {
|
|||||||
<div className="min-h-screen d-flex flex-column">
|
<div className="min-h-screen d-flex flex-column">
|
||||||
<Head>
|
<Head>
|
||||||
<title>GitHub Documentation</title>
|
<title>GitHub Documentation</title>
|
||||||
|
<meta name="status" content="500" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SimpleHeader />
|
<SimpleHeader />
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export type MainContextT = {
|
|||||||
featureFlags: FeatureFlags
|
featureFlags: FeatureFlags
|
||||||
page: {
|
page: {
|
||||||
documentType: string
|
documentType: string
|
||||||
|
type?: string
|
||||||
languageVariants: Array<{ name: string; code: string; hreflang: string; href: string }>
|
languageVariants: Array<{ name: string; code: string; hreflang: string; href: string }>
|
||||||
topics: Array<string>
|
topics: Array<string>
|
||||||
title: string
|
title: string
|
||||||
@@ -104,10 +105,12 @@ export type MainContextT = {
|
|||||||
|
|
||||||
searchVersions: Record<string, string>
|
searchVersions: Record<string, string>
|
||||||
nonEnterpriseDefaultVersion: string
|
nonEnterpriseDefaultVersion: string
|
||||||
|
|
||||||
|
status: number
|
||||||
fullUrl: string
|
fullUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMainContextFromRequest = (req: any): MainContextT => {
|
export const getMainContext = (req: any, res: any): MainContextT => {
|
||||||
return {
|
return {
|
||||||
breadcrumbs: req.context.breadcrumbs || {},
|
breadcrumbs: req.context.breadcrumbs || {},
|
||||||
activeProducts: req.context.activeProducts,
|
activeProducts: req.context.activeProducts,
|
||||||
@@ -134,6 +137,7 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
|
|||||||
page: {
|
page: {
|
||||||
languageVariants: req.context.page.languageVariants,
|
languageVariants: req.context.page.languageVariants,
|
||||||
documentType: req.context.page.documentType,
|
documentType: req.context.page.documentType,
|
||||||
|
type: req.context.page.type || null,
|
||||||
title: req.context.page.title,
|
title: req.context.page.title,
|
||||||
fullTitle: req.context.page.fullTitle,
|
fullTitle: req.context.page.fullTitle,
|
||||||
topics: req.context.page.topics || [],
|
topics: req.context.page.topics || [],
|
||||||
@@ -166,6 +170,7 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
|
|||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
searchVersions: req.context.searchVersions,
|
searchVersions: req.context.searchVersions,
|
||||||
nonEnterpriseDefaultVersion: req.context.nonEnterpriseDefaultVersion,
|
nonEnterpriseDefaultVersion: req.context.nonEnterpriseDefaultVersion,
|
||||||
|
status: res.statusCode,
|
||||||
fullUrl: req.protocol + '://' + req.get('host') + req.originalUrl,
|
fullUrl: req.protocol + '://' + req.get('host') + req.originalUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ type SendEventProps = {
|
|||||||
preference_value?: string
|
preference_value?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMetaContent(name: string) {
|
||||||
|
const metaTag = document.querySelector(`meta[name="${name}"]`) as HTMLMetaElement
|
||||||
|
return metaTag?.content
|
||||||
|
}
|
||||||
|
|
||||||
export function sendEvent({ type, version = '1.0.0', ...props }: SendEventProps) {
|
export function sendEvent({ type, version = '1.0.0', ...props }: SendEventProps) {
|
||||||
let site_language = location.pathname.split('/')[1]
|
let site_language = location.pathname.split('/')[1]
|
||||||
if (location.pathname.startsWith('/playground')) {
|
if (location.pathname.startsWith('/playground')) {
|
||||||
@@ -91,6 +96,9 @@ export function sendEvent({ type, version = '1.0.0', ...props }: SendEventProps)
|
|||||||
search: location.search,
|
search: location.search,
|
||||||
href: location.href,
|
href: location.href,
|
||||||
site_language,
|
site_language,
|
||||||
|
page_document_type: getMetaContent('page-document-type'),
|
||||||
|
page_type: getMetaContent('page-type'),
|
||||||
|
status: Number(getMetaContent('status') || 0),
|
||||||
|
|
||||||
// Device information
|
// Device information
|
||||||
// os, os_version, browser, browser_version:
|
// os, os_version, browser, browser_version:
|
||||||
|
|||||||
@@ -63,6 +63,22 @@ const context = {
|
|||||||
description: 'The language the user is viewing.',
|
description: 'The language the user is viewing.',
|
||||||
enum: Object.keys(languages),
|
enum: Object.keys(languages),
|
||||||
},
|
},
|
||||||
|
page_document_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The generic page document type based on URL path.',
|
||||||
|
enum: ['homepage', 'early-access', 'product', 'category', 'mapTopic', 'article'], // get-document-type.js
|
||||||
|
},
|
||||||
|
page_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Optional page type from the content frontmatter.',
|
||||||
|
enum: ['overview', 'quick_start', 'tutorial', 'how_to', 'reference'], // frontmatter.js
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: 'number',
|
||||||
|
description: 'The HTTP response status code of the main page HTML.',
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 999,
|
||||||
|
},
|
||||||
|
|
||||||
// Device information
|
// Device information
|
||||||
os: {
|
os: {
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ router.post('/', async function postEvents(req, res, next) {
|
|||||||
const fields = omit(req.body, '_csrf')
|
const fields = omit(req.body, '_csrf')
|
||||||
|
|
||||||
if (!ajv.validate(schema, fields)) {
|
if (!ajv.validate(schema, fields)) {
|
||||||
if (isDev) console.log(ajv.errorsText())
|
return res.status(400).json(isDev ? ajv.errorsText() : {})
|
||||||
return res.status(400).json({})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.hydro.maySend()) {
|
if (req.hydro.maySend()) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const Custom404 = () => {
|
|||||||
<div className="min-h-screen d-flex flex-column">
|
<div className="min-h-screen d-flex flex-column">
|
||||||
<Head>
|
<Head>
|
||||||
<title>404 - Page not found</title>
|
<title>404 - Page not found</title>
|
||||||
|
<meta name="status" content="404" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SimpleHeader />
|
<SimpleHeader />
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import displayToolSpecificContent from 'components/lib/display-tool-specific-con
|
|||||||
import localization from 'components/lib/localization'
|
import localization from 'components/lib/localization'
|
||||||
import wrapCodeTerms from 'components/lib/wrap-code-terms'
|
import wrapCodeTerms from 'components/lib/wrap-code-terms'
|
||||||
|
|
||||||
import {
|
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
||||||
MainContextT,
|
|
||||||
MainContext,
|
|
||||||
getMainContextFromRequest,
|
|
||||||
} from 'components/context/MainContext'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getProductLandingContextFromRequest,
|
getProductLandingContextFromRequest,
|
||||||
@@ -100,10 +96,11 @@ export default GlobalPage
|
|||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const req = context.req as any
|
const req = context.req as any
|
||||||
|
const res = context.res as any
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: getMainContextFromRequest(req),
|
mainContext: getMainContext(req, res),
|
||||||
productLandingContext: getProductLandingContextFromRequest(req),
|
productLandingContext: getProductLandingContextFromRequest(req),
|
||||||
productSubLandingContext: getProductSubLandingContextFromRequest(req),
|
productSubLandingContext: getProductSubLandingContextFromRequest(req),
|
||||||
tocLandingContext: getTocLandingContextFromRequest(req),
|
tocLandingContext: getTocLandingContextFromRequest(req),
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
import { BeakerIcon, ZapIcon } from '@primer/octicons-react'
|
import { BeakerIcon, ZapIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
import {
|
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
||||||
MainContextT,
|
|
||||||
MainContext,
|
|
||||||
getMainContextFromRequest,
|
|
||||||
} from 'components/context/MainContext'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PlaygroundContextProvider,
|
PlaygroundContextProvider,
|
||||||
@@ -88,10 +84,11 @@ function PageInner() {
|
|||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const req = context.req as any
|
const req = context.req as any
|
||||||
|
const res = context.res as any
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: getMainContextFromRequest(req),
|
mainContext: getMainContext(req, res),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
import { Liquid } from 'liquidjs'
|
import { Liquid } from 'liquidjs'
|
||||||
import {
|
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
||||||
MainContextT,
|
|
||||||
MainContext,
|
|
||||||
getMainContextFromRequest,
|
|
||||||
} from 'components/context/MainContext'
|
|
||||||
import { DefaultLayout } from 'components/DefaultLayout'
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
import { GHAEReleaseNotes } from 'components/release-notes/GHAEReleaseNotes'
|
import { GHAEReleaseNotes } from 'components/release-notes/GHAEReleaseNotes'
|
||||||
import { GHESReleaseNotes } from 'components/release-notes/GHESReleaseNotes'
|
import { GHESReleaseNotes } from 'components/release-notes/GHESReleaseNotes'
|
||||||
@@ -40,11 +36,12 @@ export default function ReleaseNotes({
|
|||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const req = context.req as any
|
const req = context.req as any
|
||||||
|
const res = context.res as any
|
||||||
const currentVersion = req.context.allVersions[req.context.currentVersion]
|
const currentVersion = req.context.allVersions[req.context.currentVersion]
|
||||||
const { latestPatch = '', latestRelease = '' } = req.context
|
const { latestPatch = '', latestRelease = '' } = req.context
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: getMainContextFromRequest(req),
|
mainContext: getMainContext(req, res),
|
||||||
currentVersion,
|
currentVersion,
|
||||||
ghesContext: {
|
ghesContext: {
|
||||||
currentVersion,
|
currentVersion,
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
|
|
||||||
import {
|
import { MainContextT, MainContext, getMainContext } from 'components/context/MainContext'
|
||||||
MainContextT,
|
|
||||||
MainContext,
|
|
||||||
getMainContextFromRequest,
|
|
||||||
} from 'components/context/MainContext'
|
|
||||||
import { Breadcrumbs } from 'components/Breadcrumbs'
|
import { Breadcrumbs } from 'components/Breadcrumbs'
|
||||||
import { DefaultLayout } from 'components/DefaultLayout'
|
import { DefaultLayout } from 'components/DefaultLayout'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
@@ -55,10 +51,11 @@ export default function GQLExplorer({ mainContext, graphqlExplorerUrl }: Props)
|
|||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const req = context.req as any
|
const req = context.req as any
|
||||||
|
const res = context.res as any
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: getMainContextFromRequest(req),
|
mainContext: getMainContext(req, res),
|
||||||
graphqlExplorerUrl: req.context.graphql.explorerUrl,
|
graphqlExplorerUrl: req.context.graphql.explorerUrl,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { GetServerSideProps } from 'next'
|
|||||||
import {
|
import {
|
||||||
MainContextT,
|
MainContextT,
|
||||||
MainContext,
|
MainContext,
|
||||||
getMainContextFromRequest,
|
getMainContext,
|
||||||
useMainContext,
|
useMainContext,
|
||||||
} from 'components/context/MainContext'
|
} from 'components/context/MainContext'
|
||||||
|
|
||||||
@@ -134,10 +134,11 @@ function LandingPage(props: LandingPageProps) {
|
|||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
|
||||||
const req = context.req as any
|
const req = context.req as any
|
||||||
|
const res = context.res as any
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mainContext: getMainContextFromRequest(req),
|
mainContext: getMainContext(req, res),
|
||||||
gettingStartedLinks: req.context.featuredLinks.gettingStarted.map(
|
gettingStartedLinks: req.context.featuredLinks.gettingStarted.map(
|
||||||
({ title, href, intro }: any) => ({ title, href, intro })
|
({ title, href, intro }: any) => ({ title, href, intro })
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user