diff --git a/assets/images/help/settings/github-mobile-active-sessions.png b/assets/images/help/settings/github-mobile-active-sessions.png new file mode 100644 index 0000000000..d1081695b8 Binary files /dev/null and b/assets/images/help/settings/github-mobile-active-sessions.png differ diff --git a/assets/images/help/settings/revoke-mobile-session.png b/assets/images/help/settings/revoke-mobile-session.png new file mode 100644 index 0000000000..bce3edaa1a Binary files /dev/null and b/assets/images/help/settings/revoke-mobile-session.png differ diff --git a/assets/images/help/settings/revoke-session.png b/assets/images/help/settings/revoke-session.png new file mode 100644 index 0000000000..0d72d2b46c Binary files /dev/null and b/assets/images/help/settings/revoke-session.png differ diff --git a/assets/images/help/settings/saml-active-sessions.png b/assets/images/help/settings/saml-active-sessions.png index 8ed58b9133..86eadf4283 100644 Binary files a/assets/images/help/settings/saml-active-sessions.png and b/assets/images/help/settings/saml-active-sessions.png differ diff --git a/components/article/AutomatedPage.tsx b/components/article/AutomatedPage.tsx index f05f2e983a..0bc51eb83f 100644 --- a/components/article/AutomatedPage.tsx +++ b/components/article/AutomatedPage.tsx @@ -6,13 +6,14 @@ import { ArticleGridLayout } from './ArticleGridLayout' import { MiniTocs } from 'components/ui/MiniTocs' import { useAutomatedPageContext } from 'components/context/AutomatedPageContext' import { ClientSideHighlight } from 'components/ClientSideHighlight' +import { Callout } from 'components/ui/Callout' type Props = { children: React.ReactNode } export const AutomatedPage = ({ children }: Props) => { - const { title, intro, renderedPage, miniTocItems } = useAutomatedPageContext() + const { title, intro, renderedPage, miniTocItems, product } = useAutomatedPageContext() return ( @@ -22,11 +23,21 @@ export const AutomatedPage = ({ children }: Props) => { {title}} intro={ - intro && ( - - {intro} - - ) + <> + {intro && ( + + {intro} + + )} + + {product && ( + + )} + } toc={miniTocItems.length > 1 && } > diff --git a/components/context/AutomatedPageContext.tsx b/components/context/AutomatedPageContext.tsx index 745dbd892f..2f41951da5 100644 --- a/components/context/AutomatedPageContext.tsx +++ b/components/context/AutomatedPageContext.tsx @@ -6,6 +6,7 @@ export type AutomatedPageContextT = { intro: string renderedPage: string | JSX.Element[] miniTocItems: Array + product?: string } export const AutomatedPageContext = createContext(null) @@ -30,5 +31,6 @@ export const getAutomatedPageContextFromRequest = (req: any): AutomatedPageConte intro: page.intro, renderedPage: req.context.renderedPage || '', miniTocItems: req.context.miniTocItems || [], + product: page.product || '', } } diff --git a/components/parameter-table/ChildBodyParametersRows.tsx b/components/parameter-table/ChildBodyParametersRows.tsx index 66bd672ae1..19362497df 100644 --- a/components/parameter-table/ChildBodyParametersRows.tsx +++ b/components/parameter-table/ChildBodyParametersRows.tsx @@ -7,6 +7,7 @@ import type { ChildParameter } from './types' import styles from './ChildBodyParametersRows.module.scss' type Props = { + open: boolean slug: string childParamsGroups: ChildParameter[] parentName: string @@ -14,6 +15,7 @@ type Props = { } export function ChildBodyParametersRows({ + open, slug, parentName, parentType, @@ -24,24 +26,27 @@ export function ChildBodyParametersRows({ return ( -
+
- +
- {childParamsGroups.map((childParam) => { + {childParamsGroups.map((childParam, index) => { return ( ) })} diff --git a/components/parameter-table/ParameterRow.tsx b/components/parameter-table/ParameterRow.tsx index f5d31e79e7..ed13fdfb6c 100644 --- a/components/parameter-table/ParameterRow.tsx +++ b/components/parameter-table/ParameterRow.tsx @@ -1,6 +1,7 @@ import cx from 'classnames' import { useTranslation } from 'components/hooks/useTranslation' +import { KeyboardEventHandler } from 'react' import { ChildBodyParametersRows } from './ChildBodyParametersRows' import type { ChildParameter } from './types' @@ -10,14 +11,38 @@ type Props = { numPreviews?: number isChild?: boolean rowIndex?: number + bodyParamExpandCallback?: KeyboardEventHandler | undefined + clickedBodyParameterName?: string | undefined } +// Webhooks have these same properties in common that we describe separately in its +// own section on the webhooks page: +// +// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#webhook-payload-object-common-properties +// +// Since there's more details for these particular properties, we chose not +// show their child properties for each webhook and we also don't grab this +// information from the schema. +// +// We use this list of common properties to make sure we don't try and request +// the child properties for these specific properties. +const NO_CHILD_WEBHOOK_PROPERTIES = [ + 'action', + 'enterprise', + 'installation', + 'organization', + 'repository', + 'sender', +] + export function ParameterRow({ rowParams, slug, numPreviews = 0, - rowIndex = 0, isChild = false, + rowIndex = 0, + bodyParamExpandCallback = undefined, + clickedBodyParameterName = undefined, }: Props) { const { t } = useTranslation(['parameter_table', 'products']) @@ -97,8 +122,40 @@ export function ParameterRow({ parentName={rowParams.name} parentType={rowParams.type} childParamsGroups={rowParams.childParamsGroups} + open={rowParams.name === clickedBodyParameterName} /> )} + + {/* These conditions tell us: + + 1. the param is an object or array AND: + 2. the param has no child param groups AND: + 3. the param isn't one of the common webhook properties + + If all these are true, then that means we haven't yet loaded the + nested parameters so we show a stub
element that triggers + an API request to get the nested parameter data. + */} + {(rowParams.type === 'object' || rowParams.type.includes('array of')) && + rowParams.childParamsGroups && + rowParams.childParamsGroups.length === 0 && + !NO_CHILD_WEBHOOK_PROPERTIES.includes(rowParams.name) && ( +
+ + + )} ) } diff --git a/components/parameter-table/ParameterTable.tsx b/components/parameter-table/ParameterTable.tsx index a6c326f70f..bd9e0a9fc3 100644 --- a/components/parameter-table/ParameterTable.tsx +++ b/components/parameter-table/ParameterTable.tsx @@ -1,5 +1,6 @@ import cx from 'classnames' import { useTranslation } from 'components/hooks/useTranslation' +import { KeyboardEventHandler } from 'react' import { ParameterRow } from './ParameterRow' import { BodyParameter, ChildParameter, Parameter } from './types' @@ -8,20 +9,24 @@ import styles from './ParameterTable.module.scss' type Props = { slug: string - numPreviews: number - heading: string - headers: Array - parameters: Array + numPreviews?: number + heading?: string + headers?: Array + parameters?: Array bodyParameters: Array + bodyParamExpandCallback?: KeyboardEventHandler | undefined + clickedBodyParameterName?: string | undefined } export function ParameterTable({ slug, - numPreviews, + numPreviews = 0, heading = '', headers = [], - parameters, + parameters = [], bodyParameters, + bodyParamExpandCallback = undefined, + clickedBodyParameterName = '', }: Props) { const { t } = useTranslation(['parameter_table', 'products']) const queryParams = parameters.filter((param) => param.in === 'query') @@ -41,7 +46,11 @@ export function ParameterTable({ > - @@ -122,7 +131,9 @@ export function ParameterTable({ {bodyParameters.length > 0 && ( <> - @@ -131,7 +142,13 @@ export function ParameterTable({ {bodyParameters.map((param, index) => ( - + ))} )} diff --git a/components/webhooks/Webhook.tsx b/components/webhooks/Webhook.tsx new file mode 100644 index 0000000000..6c2be2be11 --- /dev/null +++ b/components/webhooks/Webhook.tsx @@ -0,0 +1,195 @@ +import { ActionList, ActionMenu, Flash } from '@primer/react' +import { useState, KeyboardEvent } from 'react' +import useSWR from 'swr' +import { slug } from 'github-slugger' +import cx from 'classnames' + +import { useMainContext } from 'components/context/MainContext' +import { useVersion } from 'components/hooks/useVersion' +import { LinkIconHeading } from 'components/article/LinkIconHeading' +import { useTranslation } from 'components/hooks/useTranslation' +import type { WebhookAction, WebhookData } from './types' +import { ParameterTable } from 'components/parameter-table/ParameterTable' + +import styles from './WebhookPayloadExample.module.scss' + +type Props = { + webhook: WebhookAction +} + +// fetcher passed to useSWR() to get webhook data using the given URL +async function webhookFetcher(url: string) { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`${response.status} on ${url}`) + } + + return response.json() +} + +// We manually created decorated webhooks files for GHES versions older than +// 3.7, returns whether the given version is one of these versions of GHES. +// +// TODO: once 3.7 is the oldest supported version of GHES, we won't need this +// anymore. +function isScrapedGhesVersion(version: ReturnType) { + const scrapedVersions = ['3.6', '3.5', '3.4', '3.3', '3.2'] + + if (!version.isEnterprise) return false + + // getting the number part e.g. '3.6' from a version string like + // 'enterprise-server@3.6' + const versionNumber = version.currentVersion.split('@')[1] + + return scrapedVersions.includes(versionNumber) +} + +export function Webhook({ webhook }: Props) { + // Get version for requests to switch webhook action type + const version = useVersion() + const { t } = useTranslation('products') + + const context = useMainContext() + // Get more user friendly language for the different availability options in + // the webhook schema (we can't change it directly in the schema). Note that + // we specifically don't want to translate these strings with useTranslation() + // like we usually do with strings from data/ui.yml. + const rephraseAvailability = context.data.ui.products.webhooks.rephrase_availability + + // The param that was clicked so we can expand its property
element + const [clickedBodyParameterName, setClickedBodyParameterName] = useState('') + // The selected webhook action type the user selects via a dropdown + const [selectedWebhookActionType, setSelectedWebhookActionType] = useState('') + const webhookSlug = slug(webhook.data.category) + const webhookFetchUrl = `/api/webhooks/v1?${new URLSearchParams({ + category: webhook.data.category, + version: version.currentVersion, + })}` + + // callback for the action type dropdown -- besides setting the action type + // state, we also want to clear the clicked body param so that no properties + // are expanded when we re-render the webhook + function handleActionTypeChange(type: string) { + setClickedBodyParameterName('') + setSelectedWebhookActionType(type) + } + + // callback to trigger useSWR() hook after a nested property is clicked + function handleBodyParamExpansion(event: KeyboardEvent) { + // need to cast it because 'closest' isn't necessarily available on + // event.target + const target = event.target as HTMLElement + setClickedBodyParameterName(target.closest('details')?.dataset.nestedParamId) + } + + // fires when the webhook action type changes or someone clicks on a nested + // body param for the first time. In either case, we now have all the data + // for a webhook (i.e. all the data for each action type and all of their + // nested parameters) + const { data, error } = useSWR( + clickedBodyParameterName || selectedWebhookActionType ? webhookFetchUrl : null, + webhookFetcher, + { + revalidateOnFocus: false, + } + ) + + const currentWebhookActionType = selectedWebhookActionType || webhook.data.action + const currentWebhookAction = (data && data[currentWebhookActionType]) || webhook.data + + return ( +
+

+ + {currentWebhookAction.category} +

+
+
+ +

{t('webhooks.availability')}

+
    + {currentWebhookAction.availability.map((availability) => { + // TODO: once 3.7 is the oldest supported version of GHES, we won't need this anymore. + if (isScrapedGhesVersion(version)) { + return ( +
  • + ) + } else { + return ( +
  • + {rephraseAvailability[availability] ?? availability} +
  • + ) + } + })} +
+

{t('webhooks.webhook_payload_object')}

+ {error && ( + +

{t('webhooks.action_type_switch_error')}

+

+ + {error.toString()} + +

+
+ )} + {webhook.actionTypes.length > 1 && ( +
+

{t('webhooks.action_type')}

+
+ + + {currentWebhookActionType} + + + + {webhook.actionTypes.map((type) => { + return ( + handleActionTypeChange(type)} + > + {type} + + ) + })} + + + +
+
+ )} +
+
+ +
+
+ + {webhook.data.payloadExample && ( + <> +

{t('webhooks.webhook_payload_example')}

+
+ {JSON.stringify(webhook.data.payloadExample, null, 2)} +
+ + )} +
+ ) +} diff --git a/components/webhooks/WebhookPayloadExample.module.scss b/components/webhooks/WebhookPayloadExample.module.scss new file mode 100644 index 0000000000..05121c8aa5 --- /dev/null +++ b/components/webhooks/WebhookPayloadExample.module.scss @@ -0,0 +1,16 @@ +@import "@primer/css/support/index.scss"; + +.payloadExample { + overflow: auto; + margin-bottom: 1rem; + line-height: 1.45; + background-color: var(--color-canvas-subtle); + font-size: 90%; + max-height: 32rem; + + code { + background-color: transparent; + padding: 8px 8px 16px; + white-space: pre; + } +} diff --git a/components/webhooks/types.ts b/components/webhooks/types.ts new file mode 100644 index 0000000000..f898dd0232 --- /dev/null +++ b/components/webhooks/types.ts @@ -0,0 +1,34 @@ +import { Parameter, StatusCode, CodeSample, BodyParameter } from '../rest/types' + +export interface WebhookT { + actions: string[] + webhookActions: WebhookAction[] +} + +interface WebhookActionData { + verb: string + title: string + descriptionHtml: string + summaryHtml: string + previews: Array + requestPath: string + serverUrl: string + statusCodes: Array + parameters: Array + bodyParameters: Array + category: string + subcategory: string + codeExamples: Array + availability: Array + action: string + payloadExample?: Object +} +export interface WebhookAction { + name: string + actionTypes: string[] + data: WebhookActionData +} + +export type WebhookData = { + [key: string]: WebhookActionData +} diff --git a/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md b/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md index 689c1eac97..cf62edb6c3 100644 --- a/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md +++ b/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md @@ -1,6 +1,6 @@ --- title: Viewing and managing your active SAML sessions -intro: You can view and revoke your active SAML sessions in your security settings. +intro: You can view and revoke your active SAML sessions in your settings. redirect_from: - /articles/viewing-and-managing-your-active-saml-sessions - /github/authenticating-to-github/viewing-and-managing-your-active-saml-sessions @@ -9,16 +9,24 @@ versions: ghec: '*' topics: - SSO +type: how_to shortTitle: Active SAML sessions --- + +You can view a list of devices that have logged into your account, and revoke any SAML sessions that you don't recognize. + {% data reusables.user-settings.access_settings %} -{% data reusables.user-settings.security %} -3. Under "Sessions," you can see your active SAML sessions. - ![List of active SAML sessions](/assets/images/help/settings/saml-active-sessions.png) -4. To see the session details, click **See more**. - ![Button to open SAML session details](/assets/images/help/settings/saml-expand-session-details.png) -5. To revoke a session, click **Revoke SAML**. - ![Button to revoke a SAML session](/assets/images/help/settings/saml-revoke-session.png) +{% data reusables.user-settings.sessions %} +1. Under "Web sessions," you can see your active SAML sessions. + + ![Screenshot of the list of active SAML sessions](/assets/images/help/settings/saml-active-sessions.png) + +1. To see the session details, click **See more**. + ![Screenshot of the active SAML sessions with the button to open SAML session details emphasized](/assets/images/help/settings/saml-expand-session-details.png) + +1. To revoke a session, click **Revoke SAML**. + + ![Screenshot of the Session details page with the button to revoke a SAML session emphasized](/assets/images/help/settings/saml-revoke-session.png) {% note %} diff --git a/content/authentication/keeping-your-account-and-data-secure/index.md b/content/authentication/keeping-your-account-and-data-secure/index.md index 3e202cd327..b2431aab33 100644 --- a/content/authentication/keeping-your-account-and-data-secure/index.md +++ b/content/authentication/keeping-your-account-and-data-secure/index.md @@ -32,6 +32,7 @@ children: - /githubs-ssh-key-fingerprints - /sudo-mode - /preventing-unauthorized-access + - /viewing-and-managing-your-sessions shortTitle: Account security --- diff --git a/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md b/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md new file mode 100644 index 0000000000..8e92306bc6 --- /dev/null +++ b/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md @@ -0,0 +1,44 @@ +--- +title: Viewing and managing your sessions +intro: You can view and revoke your active sessions in your settings. +versions: + feature: device-and-settings-management-page +type: how_to +topics: + - SSO +shortTitle: Viewing and managing sessions +--- + +You can view a list of devices that have logged into your account, and revoke any sessions that you don't recognize. + +{% data reusables.user-settings.access_settings %} +{% data reusables.user-settings.sessions %} +1. Under "Web sessions", you can see your active web sessions. + + ![Screenshot of the list of active sessions](/assets/images/help/settings/saml-active-sessions.png) +{% ifversion fpt or ghec %} + Under "{% data variables.product.prodname_mobile %} sessions", you can see a list of devices that have logged into your account via the {% data variables.product.prodname_mobile %} app. + + ![Screenshot of the list of active sessions](/assets/images/help/settings/github-mobile-active-sessions.png){% endif %} + +1. To see the web session details, click **See more**. + + ![Screenshot of the Sessions page with the button to open session details emphasized](/assets/images/help/settings/saml-expand-session-details.png) + +1. To revoke a web session, click **Revoke session**. + + ![Screenshot of the Sessions details page with the button to revoke a session emphasized](/assets/images/help/settings/revoke-session.png) + +{% ifversion fpt or ghec %} +1. Optionally, to revoke a {% data variables.product.prodname_mobile %} session, go back to the Sessions overview page and click **Revoke** next to the device you want to revoke. + + {% note %} + + **Note:** Revoking a mobile session signs you out of the {% data variables.product.prodname_mobile %} application on that device and removes it as a second-factor option. + + {% endnote %} + + ![Screenshot of the Sessions page with the button to revoke a mobile session emphasized](/assets/images/help/settings/revoke-mobile-session.png) + +{% endif %} + diff --git a/content/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.md b/content/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.md index 7f68a7bb88..fdfc96b388 100644 --- a/content/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.md +++ b/content/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.md @@ -1,6 +1,6 @@ --- title: Webhook events and payloads -intro: 'For each webhook event, you can review when the event occurs, an example payload, and descriptions about the payload object parameters.' +intro: 'Learn about when each webhook event occurs and what the payload contains.' product: '{% data reusables.gated-features.enterprise_account_webhooks %}' redirect_from: - /early-access/integrations/webhooks @@ -97,1568 +97,4 @@ Also, the `User-Agent` for the requests will have the prefix `GitHub-Hookshot/`. > } ``` -{% ifversion fpt or ghes > 3.3 or ghae or ghec %} -## branch_protection_rule - -Activity related to a branch protection rule. For more information, see "[About branch protection rules](/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#about-branch-protection-rules)." - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Administration** repository permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `created`, `edited`, or `deleted`. -`rule` | `object` | The branch protection rule. Includes a `name` and all the [branch protection settings](/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#about-branch-protection-settings) applied to branches that match the name. Binary settings are boolean. Multi-level configurations are one of `off`, `non_admins`, or `everyone`. Actor and build lists are arrays of strings. -`changes` | `object` | If the action was `edited`, the changes to the rule. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.branch_protection_rule.edited }} -{% endif %} - -{% ifversion ghes > 3.3 %} -## cache_sync - -A Git ref has been successfully synced to a cache replica. For more information, see "[About repository caching](/admin/enterprise-management/caching-repositories/about-repository-caching)." - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`cache_location` |`string` | The location of the cache server that has been updated. -`ref` | `string` | The ref that has been updated. -`before` | `string` | The OID of the ref on the cache replica before it was updated. -`after` | `string` | The OID of the ref on the cache replica after the update. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.cache_sync.synced }} -{% endif %} - -## check_run - -{% data reusables.webhooks.check_run_short_desc %} - -{% data reusables.apps.undetected-pushes-to-a-forked-repository-for-check-suites %} - -### Availability - -- Repository webhooks only receive payloads for the `created` and `completed` event types in a repository -- Organization webhooks only receive payloads for the `created` and `completed` event types in repositories -- {% data variables.product.prodname_github_apps %} with **Checks** read permission receive payloads for the `created` and `completed` events that occur in the repository where the app is installed. The app must have **Checks** write permission to receive the `rerequested` and `requested_action` event types. The `rerequested` and `requested_action` event type payloads are only sent to the {% data variables.product.prodname_github_app %} being requested. {% data variables.product.prodname_github_apps %} with **Checks** write permission are automatically subscribed to this webhook event. - -### Webhook payload object - -{% data reusables.webhooks.check_run_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.check_run.created }} - -## check_suite - -{% data reusables.webhooks.check_suite_short_desc %} - -{% data reusables.apps.undetected-pushes-to-a-forked-repository-for-check-suites %} - -### Availability - -- Repository webhooks only receive payloads for the `completed` event types in a repository -- Organization webhooks only receive payloads for the `completed` event types in repositories -- {% data variables.product.prodname_github_apps %} with **Checks** read permission receive payloads for the `created` and `completed` events that occur in the repository where the app is installed. The app must have **Checks** write permission to receive the `requested` and `rerequested` event types. The `requested` and `rerequested` event type payloads are only sent to the {% data variables.product.prodname_github_app %} being requested. {% data variables.product.prodname_github_apps %} with **Checks** write permission are automatically subscribed to this webhook event. - -### Webhook payload object - -{% data reusables.webhooks.check_suite_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.check_suite.completed }} - -## code_scanning_alert - -{% data reusables.webhooks.code_scanning_alert_event_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Code scanning alerts** permission - -### Webhook payload object - -{% data reusables.webhooks.code_scanning_alert_event_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -`sender` | `object` | If the `action` is `reopened_by_user` or `closed_by_user`, the `sender` object will be the user that triggered the event. The `sender` object is {% ifversion fpt or ghec %}`github`{% elsif ghes or ghae %}`github-enterprise`{% else %}empty{% endif %} for all other actions. - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.code_scanning_alert.reopened }} - -## commit_comment - -{% data reusables.webhooks.commit_comment_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.commit_comment_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.commit_comment.created }} - -{% ifversion ghes < 3.4 %} -## content_reference - -{% data reusables.webhooks.content_reference_short_desc %} - -Webhook events are triggered based on the specificity of the domain you register. For example, if you register a subdomain (`https://subdomain.example.com`) then only URLs for the subdomain trigger this event. If you register a domain (`https://example.com`) then URLs for domain and all subdomains trigger this event. See "[Create a content attachment](/rest/reference/apps#create-a-content-attachment)" to create a new content attachment. - -### Availability - -- {% data variables.product.prodname_github_apps %} with the `content_references:write` permission - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.content_reference.created }} - -{% endif %} -## create - -{% data reusables.webhooks.create_short_desc %} - -{% note %} - -**Note:** You will not receive a webhook for this event when you create more than three tags at once. - -{% endnote %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.create_properties %} -{% data reusables.webhooks.pusher_type_desc %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.create }} - -## delete - -{% data reusables.webhooks.delete_short_desc %} - -{% note %} - -**Note:** You will not receive a webhook for this event when you delete more than three tags at once. - -{% endnote %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.delete_properties %} -{% data reusables.webhooks.pusher_type_desc %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.delete }} - -{% ifversion fpt or ghec %} -## dependabot_alert - -{% data reusables.webhooks.dependabot_alert_description %} - -### Availability - -{% data reusables.webhooks.dependabot_alert_availability %} - -### Webhook payload object - -{% data reusables.webhooks.dependabot_alert_payload %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.dependabot_alert.fixed }} -{% endif %} - -## deploy_key - -{% data reusables.webhooks.deploy_key_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -{% data reusables.webhooks.deploy_key_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.deploy_key.created }} - -## deployment - -{% data reusables.webhooks.deployment_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Deployments** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `created`. -`deployment` |`object` | The [deployment](/rest/reference/deployments#list-deployments). -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.deployment }} - -## deployment_status - -{% data reusables.webhooks.deployment_status_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Deployments** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `created`. -`deployment_status` |`object` | The [deployment status](/rest/reference/deployments#list-deployment-statuses). -`deployment_status["state"]` |`string` | The new state. Can be `pending`, `success`, `failure`, or `error`. -`deployment_status["target_url"]` |`string` | The optional link added to the status. -`deployment_status["description"]`|`string` | The optional human-readable description added to the status. -`deployment` |`object` | The [deployment](/rest/reference/deployments#list-deployments) that this status is associated with. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.deployment_status }} - -{% ifversion fpt or ghec %} -## discussion - -{% data reusables.webhooks.discussions-webhooks-beta %} - -Activity related to a discussion. For more information, see the "[Using the GraphQL API for discussions](/graphql/guides/using-the-graphql-api-for-discussions)." -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Discussions** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `created`, `edited`, `deleted`, `pinned`, `unpinned`, `locked`, `unlocked`, `transferred`, `category_changed`, `answered`, `unanswered`, `labeled`, or `unlabeled`. -{% data reusables.webhooks.discussion_desc %} -{% data reusables.webhooks.repo_desc_graphql %} -{% data reusables.webhooks.org_desc_graphql %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.discussion.created }} - -## discussion_comment - -{% data reusables.webhooks.discussions-webhooks-beta %} - -Activity related to a comment in a discussion. For more information, see "[Using the GraphQL API for discussions](/graphql/guides/using-the-graphql-api-for-discussions)." - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Discussions** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `created`, `edited`, or `deleted`. -`comment` | `object` | The [`discussion comment`](/graphql/guides/using-the-graphql-api-for-discussions#discussioncomment) resource. -{% data reusables.webhooks.discussion_desc %} -{% data reusables.webhooks.repo_desc_graphql %} -{% data reusables.webhooks.org_desc_graphql %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.discussion_comment.created }} -{% endif %} - -{% ifversion ghes or ghae %} - -## enterprise - -{% data reusables.webhooks.enterprise_short_desc %} - -### Availability - -- GitHub Enterprise webhooks. For more information, "[Global webhooks](/rest/reference/enterprise-admin#global-webhooks/)." - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `anonymous_access_enabled` or `anonymous_access_disabled`. - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.enterprise.anonymous_access_enabled }} - -{% endif %} - -## fork - -{% data reusables.webhooks.fork_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.fork_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.fork }} - -## github_app_authorization - -When someone revokes their authorization of a {% data variables.product.prodname_github_app %}, this event occurs. A {% data variables.product.prodname_github_app %} receives this webhook by default and cannot unsubscribe from this event. - -{% data reusables.webhooks.authorization_event %} For details about user-to-server requests, which require {% data variables.product.prodname_github_app %} authorization, see "[Identifying and authorizing users for {% data variables.product.prodname_github_apps %}](/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/)." - -### Availability - -- {% data variables.product.prodname_github_apps %} - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `revoked`. -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.github_app_authorization.revoked }} - -## gollum - -{% data reusables.webhooks.gollum_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.gollum_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.gollum }} - -## installation - -{% data reusables.webhooks.installation_short_desc %} - -### Availability - -- {% data variables.product.prodname_github_apps %} - -### Webhook payload object - -{% data reusables.webhooks.installation_properties %} -{% data reusables.webhooks.app_always_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.installation.deleted }} - -## installation_repositories - -{% data reusables.webhooks.installation_repositories_short_desc %} - -### Availability - -- {% data variables.product.prodname_github_apps %} - -### Webhook payload object - -{% data reusables.webhooks.installation_repositories_properties %} -{% data reusables.webhooks.app_always_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.installation_repositories.added }} - -## issue_comment - -{% data reusables.webhooks.issue_comment_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Issues** permission - -### Webhook payload object - -{% data reusables.webhooks.issue_comment_webhook_properties %} -{% data reusables.webhooks.issue_comment_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.issue_comment.created }} - -## issues - -{% data reusables.webhooks.issues_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Issues** permission - -### Webhook payload object - -{% data reusables.webhooks.issue_webhook_properties %} -{% data reusables.webhooks.issue_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example when someone edits an issue - -{{ webhookPayloadsForCurrentVersion.issues.edited }} - -## label - -{% data reusables.webhooks.label_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Metadata** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action`|`string` | The action that was performed. Can be `created`, `edited`, or `deleted`. -`label`|`object` | The label that was added. -`changes`|`object`| The changes to the label if the action was `edited`. -`changes[name][from]`|`string` | The previous version of the name if the action was `edited`. -`changes[color][from]`|`string` | The previous version of the color if the action was `edited`. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.label.deleted }} - -{% ifversion fpt or ghec %} -## marketplace_purchase - -Activity related to a GitHub Marketplace purchase. {% data reusables.webhooks.action_type_desc %} For more information, see the "[GitHub Marketplace](/marketplace/)." - -### Availability - -- {% data variables.product.prodname_github_apps %} - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` | `string` | The action performed for a [GitHub Marketplace](https://github.com/marketplace) plan. Can be one of:
  • `purchased` - Someone purchased a GitHub Marketplace plan. The change should take effect on the account immediately.
  • `pending_change` - You will receive the `pending_change` event when someone has downgraded or cancelled a GitHub Marketplace plan to indicate a change will occur on the account. The new plan or cancellation takes effect at the end of the billing cycle. The `cancelled` or `changed` event type will be sent when the billing cycle has ended and the cancellation or new plan should take effect.
  • `pending_change_cancelled` - Someone has cancelled a pending change. Pending changes include plan cancellations and downgrades that will take effect at the end of a billing cycle.
  • `changed` - Someone has upgraded or downgraded a GitHub Marketplace plan and the change should take effect on the account immediately.
  • `cancelled` - Someone cancelled a GitHub Marketplace plan and the last billing cycle has ended. The change should take effect on the account immediately.
- -For a detailed description of this payload and the payload for each type of `action`, see [{% data variables.product.prodname_marketplace %} webhook events](/marketplace/integrating-with-the-github-marketplace-api/github-marketplace-webhook-events/). - -### Webhook payload example when someone purchases the plan - -{{ webhookPayloadsForCurrentVersion.marketplace_purchase.purchased }} - -{% endif %} - -## member - -{% data reusables.webhooks.member_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Members** permission - -### Webhook payload object - -{% data reusables.webhooks.member_webhook_properties %} -{% data reusables.webhooks.member_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.member.added }} - -## membership - -{% data reusables.webhooks.membership_short_desc %} - -### Availability - -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Members** permission - -### Webhook payload object - -{% data reusables.webhooks.membership_properties %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.membership.removed }} - -{% ifversion fpt or ghec %} - -## merge_group - -{% data reusables.pull_requests.merge-queue-beta %} - -Activity related to merge groups in a merge queue. The type of activity is specified in the action property of the payload object. - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Merge queues** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action`|`string` | The action that was performed. Currently, can only be `checks_requested`. -`merge_group`|`object` | The merge group. -`merge_group[head_sha]`|`string` | The SHA of the merge group. -`merge_group[head_ref]`|`string` | The full ref of the merge group. -`merge_group[base_sha]`|`string` | The SHA of the merge group's parent commit. -`merge_group[base_ref]`|`string` | The full ref of the branch the merge group will be merged into. -`merge_group[head_commit]`|`object` | An expanded representation of the `head_sha` commit. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.merge_group.checks_requested }} - -{% endif %} - -## meta - -The webhook this event is configured on was deleted. This event will only listen for changes to the particular hook the event is installed on. Therefore, it must be selected for each hook that you'd like to receive meta events for. - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action performed. Can be `deleted`. -`hook_id` |`integer` | The id of the modified webhook. -`hook` |`object` | The modified webhook. This will contain different keys based on the type of webhook it is: repository, organization, business, app, or GitHub Marketplace. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.meta.deleted }} - -## milestone - -{% data reusables.webhooks.milestone_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pull requests** permission - -### Webhook payload object - -{% data reusables.webhooks.milestone_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.milestone.created }} - -## organization - -{% data reusables.webhooks.organization_short_desc %} - -### Availability - -{% ifversion ghes or ghae %} -- GitHub Enterprise webhooks only receive `created` and `deleted` events. For more information, "[Global webhooks](/rest/reference/enterprise-admin#global-webhooks/).{% endif %} -- Organization webhooks only receive the `deleted`, `added`, `removed`, `renamed`, and `invited` events -- {% data variables.product.prodname_github_apps %} with **Members** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action that was performed. Can be one of:{% ifversion ghes or ghae %} `created`,{% endif %} `deleted`, `renamed`, `member_added`, `member_removed`, or `member_invited`. -`invitation` |`object` | The invitation for the user or email if the action is `member_invited`. -`membership` |`object` | The membership between the user and the organization. Not present when the action is `member_invited`. -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.organization.member_added }} - -{% ifversion fpt or ghec %} - -## org_block - -{% data reusables.webhooks.org_block_short_desc %} - -### Availability - -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Administration** organization permission - -### Webhook payload object - -Key | Type | Description -----|------|------------ -`action` | `string` | The action performed. Can be `blocked` or `unblocked`. -`blocked_user` | `object` | Information about the user that was blocked or unblocked. -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.org_block.blocked }} - -{% endif %} - -## package - -Activity related to {% data variables.product.prodname_registry %}. {% data reusables.webhooks.action_type_desc %} For more information, see "[Managing packages with {% data variables.product.prodname_registry %}](/github/managing-packages-with-github-packages)" to learn more about {% data variables.product.prodname_registry %}. - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -{% data reusables.webhooks.package_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.package.published }} - -## page_build - -{% data reusables.webhooks.page_build_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pages** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------ -`id` | `integer` | The unique identifier of the page build. -`build` | `object` | The [List GitHub Pages builds](/rest/reference/pages#list-github-pages-builds) itself. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.page_build }} - -## ping - -{% data reusables.webhooks.ping_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} receive a ping event with an `app_id` used to register the app - -### Webhook payload object - -Key | Type | Description -----|------|------------ -`zen` | `string` | Random string of GitHub zen. -`hook_id` | `integer` | The ID of the webhook that triggered the ping. -`hook` | `object` | The [webhook configuration](/rest/reference/webhooks#get-a-repository-webhook). -`hook[app_id]` | `integer` | When you register a new {% data variables.product.prodname_github_app %}, {% data variables.product.product_name %} sends a ping event to the **webhook URL** you specified during registration. The event contains the `app_id`, which is required for [authenticating](/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/) an app. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.ping }} - -## project - -{% data reusables.webhooks.project_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Projects** repository or organization permission - -{% ifversion projects-v2 %} -{% note %} - -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. - -{% endnote %} -{% endif %} - -### Webhook payload object - -{% data reusables.webhooks.project_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.project.created }} - -## project_card - -{% data reusables.webhooks.project_card_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Projects** repository or organization permission - -{% ifversion projects-v2 %} -{% note %} - -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. - -{% endnote %} -{% endif %} - -### Webhook payload object - -{% data reusables.webhooks.project_card_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.project_card.created }} - -## project_column - -{% data reusables.webhooks.project_column_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Projects** repository or organization permission - -{% ifversion projects-v2 %} -{% note %} - -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. - -{% endnote %} -{% endif %} - -### Webhook payload object - -{% data reusables.webhooks.project_column_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.project_column.created }} - -{% ifversion project-beta-webhooks %} - -## projects_v2_item - -{% note %} - -**Note:** Webhook events for {% data variables.projects.projects_v2 %} are currently in beta and subject to change. To share feedback about {% data variables.projects.projects_v2 %} webhooks with {% data variables.product.product_name %}, see the [Projects webhook feedback discussion](https://github.com/orgs/community/discussions/17405). - -{% endnote %} - -Activity related to items in a {% data variables.projects.project_v2 %}. {% data reusables.webhooks.action_type_desc %} For more information, see "[About {% data variables.projects.projects_v2 %}](/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects)." - -### Availability - -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Projects** organization permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action`|`string` | The action that was performed on the project item. Can be one of `archived`, `converted`, `created`, `edited`, `restored`, `deleted`, or `reordered`. -`projects_v2_item`|`object` | The project item itself. To find more information about the project item, you can use `node_id` (the node ID of the project item) and `project_node_id` (the node ID of the project) to query information in the GraphQL API. For more information, see "[Using the API to manage projects](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects)." -`changes`|`object` | The changes to the project item. -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.projects_v2_item.created }} - -{% endif %} - -## public - -{% data reusables.webhooks.public_short_desc %} -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Metadata** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.public }} - -## pull_request - -{% data reusables.webhooks.pull_request_short_desc %} - -{% ifversion fpt or ghec %} - {% data reusables.pull_requests.merge-queue-beta %} -{% endif %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pull requests** permission - -### Webhook payload object - -{% data reusables.webhooks.pull_request_webhook_properties %} -{% data reusables.webhooks.pull_request_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -Deliveries for `review_requested` and `review_request_removed` events will have an additional field called `requested_reviewer`. - -{{ webhookPayloadsForCurrentVersion.pull_request.opened }} - -## pull_request_review - -{% data reusables.webhooks.pull_request_review_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pull requests** permission - -### Webhook payload object - -{% data reusables.webhooks.pull_request_review_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.pull_request_review.submitted }} - -## pull_request_review_comment - -{% data reusables.webhooks.pull_request_review_comment_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pull requests** permission - -### Webhook payload object - -{% data reusables.webhooks.pull_request_review_comment_webhook_properties %} -{% data reusables.webhooks.pull_request_review_comment_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.pull_request_review_comment.created }} - -## pull_request_review_thread - -{% data reusables.webhooks.pull_request_review_thread_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Pull requests** permission - -### Webhook payload object - -{% data reusables.webhooks.pull_request_thread_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.pull_request_review_thread.resolved }} - -## push - -{% data reusables.webhooks.push_short_desc %} - -{% note %} - -**Note:** You will not receive a webhook for this event when you push more than three tags at once. - -{% endnote %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`ref`|`string` | The full [`git ref`](/rest/reference/git#refs) that was pushed. Example: `refs/heads/main` or `refs/tags/v3.14.1`. -`before`|`string` | The SHA of the most recent commit on `ref` before the push. -`after`|`string` | The SHA of the most recent commit on `ref` after the push. -`created`|`boolean` | Whether this push created the `ref`. -`deleted`|`boolean` | Whether this push deleted the `ref`. -`forced`|`boolean` | Whether this push was a force push of the `ref`. -`head_commit`|`object` | For pushes where `after` is or points to a commit object, an expanded representation of that commit. For pushes where `after` refers to an annotated tag object, an expanded representation of the commit pointed to by the annotated tag. -`compare`|`string` | URL that shows the changes in this `ref` update, from the `before` commit to the `after` commit. For a newly created `ref` that is directly based on the default branch, this is the comparison between the head of the default branch and the `after` commit. Otherwise, this shows all commits until the `after` commit. -`commits`|`array` | An array of commit objects describing the pushed commits. (Pushed commits are all commits that are included in the `compare` between the `before` commit and the `after` commit.) -`commits[][id]`|`string` | The SHA of the commit. -`commits[][timestamp]`|`string` | The ISO 8601 timestamp of the commit. -`commits[][message]`|`string` | The commit message. -`commits[][author]`|`object` | The git author of the commit. -`commits[][author][name]`|`string` | The git author's name. -`commits[][author][email]`|`string` | The git author's email address. -`commits[][url]`|`url` | URL that points to the commit API resource. -`commits[][distinct]`|`boolean` | Whether this commit is distinct from any that have been pushed before. -`commits[][added]`|`array` | An array of files added in the commit. For extremely large commits where {% data variables.product.product_name %} is unable to calculate this list in a timely manner, this may be empty even if files were added. -`commits[][modified]`|`array` | An array of files modified by the commit. For extremely large commits where {% data variables.product.product_name %} is unable to calculate this list in a timely manner, this may be empty even if files were modified. -`commits[][removed]`|`array` | An array of files removed in the commit. For extremely large commits where {% data variables.product.product_name %} is unable to calculate this list in a timely manner, this may be empty even if files were removed. -`pusher` | `object` | The user who pushed the commits. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.push }} - -## release - -{% data reusables.webhooks.release_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -{% data reusables.webhooks.release_webhook_properties %} -{% data reusables.webhooks.release_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.release.published }} - -## repository_dispatch - -This event occurs when a {% data variables.product.prodname_github_app %} sends a `POST` request to the "[Create a repository dispatch event](/rest/reference/repos#create-a-repository-dispatch-event)" endpoint. - -### Availability - -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.repository_dispatch }} - -## repository - -{% data reusables.webhooks.repository_short_desc %} - -### Availability - -- Repository webhooks receive all event types except `deleted` -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Metadata** permission receive all event types except `deleted` - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action that was performed. This can be one of:
  • `created` - A repository is created.
  • `deleted` - A repository is deleted.
  • `archived` - A repository is archived.
  • `unarchived` - A repository is unarchived.
  • {% ifversion ghes or ghae %}
  • `anonymous_access_enabled` - A repository is [enabled for anonymous Git access](/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise), `anonymous_access_disabled` - A repository is [disabled for anonymous Git access](/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise)
  • {% endif %}
  • `edited` - A repository's information is edited.
  • `renamed` - A repository is renamed.
  • `transferred` - A repository is transferred.
  • `publicized` - A repository is made public.
  • `privatized` - A repository is made private.
-{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.repository.publicized }} - -{% ifversion fpt or ghec %} -## repository_import - -{% data reusables.webhooks.repository_import_short_desc %} To receive this event for a personal repository, you must create an empty repository prior to the import. This event can be triggered using either the [GitHub Importer](/articles/importing-a-repository-with-github-importer/) or the [Source imports API](/rest/reference/migrations#source-imports). - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -{% data reusables.webhooks.repository_import_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.repository_import }} - -## repository_vulnerability_alert - -{% data reusables.webhooks.repository_vulnerability_alert_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -{% data reusables.webhooks.repository_vulnerability_alert_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.repository_vulnerability_alert.create }} - -{% endif %} - -{% ifversion ghes or ghec %} - -## secret_scanning_alert - -{% data reusables.webhooks.secret_scanning_alert_event_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Secret scanning alerts** permission - -### Webhook payload object - -{% data reusables.webhooks.secret_scanning_alert_event_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -`sender` | `object` | If the `action` is `resolved` or `reopened`, the `sender` object will be the user that triggered the event. The `sender` object is empty for all other actions. - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.secret_scanning_alert.reopened }} -{% endif %} - -{% ifversion ghes > 3.4 or ghec or ghae > 3.4 %} -## secret_scanning_alert_location - -{% data reusables.webhooks.secret_scanning_alert_location_event_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Secret scanning alerts** permission - -### Webhook payload object - -{% data reusables.webhooks.secret_scanning_alert_location_event_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.secret_scanning_alert_location.created }} -{% endif %} - -{% ifversion fpt or ghes or ghec %} -## security_advisory - -Activity related to a security advisory that has been reviewed by {% data variables.product.company_short %}. A {% data variables.product.company_short %}-reviewed security advisory provides information about security-related vulnerabilities in software on {% data variables.product.prodname_dotcom %}. - -The security advisory dataset also powers the GitHub {% data variables.product.prodname_dependabot_alerts %}. For more information, see "[About {% data variables.product.prodname_dependabot_alerts %}](/github/managing-security-vulnerabilities/about-alerts-for-vulnerable-dependencies/)." - -### Availability - -- {% data variables.product.prodname_github_apps %} - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action that was performed. The action can be one of `published`, `updated`, `performed`, or `withdrawn` for all new events. -`security_advisory` |`object` | The details of the security advisory, including summary, description, and severity. - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.security_advisory.published }} - -{% endif %} - -{% ifversion ghas-enablement-webhook %} - -## security_and_analysis - -Activity related to enabling or disabling code security and analysis features for a repository or organization. - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Administration** repository permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`changes`|`object` | The changes that were made to the code security and analysis features. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.security_and_analysis }} - -{% endif %} - -{% ifversion fpt or ghec %} -## sponsorship - -{% data reusables.webhooks.sponsorship_short_desc %} - -You can only create a sponsorship webhook on {% data variables.product.prodname_dotcom %}. For more information, see "[Configuring webhooks for events in your sponsored account](/sponsors/integrating-with-github-sponsors/configuring-webhooks-for-events-in-your-sponsored-account)". - -### Availability - -- Sponsored accounts - -### Webhook payload object - -{% data reusables.webhooks.sponsorship_webhook_properties %} -{% data reusables.webhooks.sponsorship_properties %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example when someone creates a sponsorship - -{{ webhookPayloadsForCurrentVersion.sponsorship.created }} - -### Webhook payload example when someone downgrades a sponsorship - -{{ webhookPayloadsForCurrentVersion.sponsorship.downgraded }} - -{% endif %} - -## star - -{% data reusables.webhooks.star_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks - -### Webhook payload object - -{% data reusables.webhooks.star_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.star.created }} - -## status - -{% data reusables.webhooks.status_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Commit statuses** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`id` | `integer` | The unique identifier of the status. -`sha`|`string` | The Commit SHA. -`state`|`string` | The new state. Can be `pending`, `success`, `failure`, or `error`. -`description`|`string` | The optional human-readable description added to the status. -`target_url`|`string` | The optional link added to the status. -`branches`|`array` | An array of branch objects containing the status' SHA. Each branch contains the given SHA, but the SHA may or may not be the head of the branch. The array includes a maximum of 10 branches. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.status }} - -## team - -{% data reusables.webhooks.team_short_desc %} - -### Availability - -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Members** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`action` |`string` | The action that was performed. Can be one of `created`, `deleted`, `edited`, `added_to_repository`, or `removed_from_repository`. -`team` |`object` | The team itself. -`changes`|`object` | The changes to the team if the action was `edited`. -`changes[description][from]` |`string` | The previous version of the description if the action was `edited`. -`changes[name][from]` |`string` | The previous version of the name if the action was `edited`. -`changes[privacy][from]` |`string` | The previous version of the team's privacy if the action was `edited`. -`changes[repository][permissions][from][admin]` | `boolean` | The previous version of the team member's `admin` permission on a repository, if the action was `edited`. -`changes[repository][permissions][from][pull]` | `boolean` | The previous version of the team member's `pull` permission on a repository, if the action was `edited`. -`changes[repository][permissions][from][push]` | `boolean` | The previous version of the team member's `push` permission on a repository, if the action was `edited`. -`repository`|`object` | The repository that was added or removed from to the team's purview if the action was `added_to_repository`, `removed_from_repository`, or `edited`. For `edited` actions, `repository` also contains the team's new permission levels for the repository. -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.team.added_to_repository }} - -## team_add - -{% data reusables.webhooks.team_add_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Members** permission - -### Webhook payload object - -Key | Type | Description -----|------|------------- -`team`|`object` | The [team](/rest/reference/teams) that was modified. **Note:** Older events may not include this in the payload. -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.team_add }} - -{% ifversion ghes or ghae %} - -## user - -When a user is `created` or `deleted`. - -### Availability -- GitHub Enterprise webhooks. For more information, "[Global webhooks](/rest/reference/enterprise-admin#global-webhooks/)." - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.user.created }} - -{% endif %} - -## watch - -{% data reusables.webhooks.watch_short_desc %} - -The event’s actor is the [user](/rest/reference/users) who starred a repository, and the event’s repository is the [repository](/rest/reference/repos) that was starred. - -### Availability - -- Repository webhooks -- Organization webhooks -- {% data variables.product.prodname_github_apps %} with **Metadata** permission - -### Webhook payload object - -{% data reusables.webhooks.watch_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.app_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.watch.started }} - -{% ifversion fpt or ghes or ghec %} -## workflow_dispatch - -This event occurs when someone triggers a workflow run on GitHub or sends a `POST` request to the "[Create a workflow dispatch event](/rest/reference/actions/#create-a-workflow-dispatch-event)" endpoint. For more information, see "[Events that trigger workflows](/actions/reference/events-that-trigger-workflows#workflow_dispatch)." - -### Availability - -- {% data variables.product.prodname_github_apps %} with **Contents** permission - -### Webhook payload object - -| Key | Type | Description | -|-----|-----|-----| -| `inputs` | `object` | Inputs to the workflow. Each key represents the name of the input while its value represents the value of that input. | -{% data reusables.webhooks.org_desc %} -| `ref` | `string` | The branch or tag from which the workflow was run. | -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.sender_desc %} -| `workflow` | `string` | Relative path to the workflow file which contains the workflow. | - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.workflow_dispatch }} -{% endif %} - -## workflow_job - -{% data reusables.webhooks.workflow_job_short_desc %} - -### Availability - -- Repository webhooks -- Organization webhooks -- Enterprise webhooks - -### Webhook payload object - -{% data reusables.webhooks.workflow_job_properties %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.workflow_job }} - -{% ifversion fpt or ghes or ghec %} -## workflow_run - -When a {% data variables.product.prodname_actions %} workflow run is requested or completed. For more information, see "[Events that trigger workflows](/actions/reference/events-that-trigger-workflows#workflow_run)." - -### Availability - -- {% data variables.product.prodname_github_apps %} with **Actions** or **Contents** permissions - -### Webhook payload object - -{% data reusables.webhooks.workflow_run_properties %} -{% data reusables.webhooks.workflow_desc %} -{% data reusables.webhooks.org_desc %} -{% data reusables.webhooks.repo_desc %} -{% data reusables.webhooks.sender_desc %} - -### Webhook payload example - -{{ webhookPayloadsForCurrentVersion.workflow_run }} -{% endif %} + diff --git a/data/features/device-and-settings-management-page.yml b/data/features/device-and-settings-management-page.yml new file mode 100644 index 0000000000..2c951b7a84 --- /dev/null +++ b/data/features/device-and-settings-management-page.yml @@ -0,0 +1,7 @@ +# Reference: #8482. +# Device and session management settings page +versions: + fpt: '*' + ghec: '*' + ghes: '>=3.8' + ghae: '>= 3.8' diff --git a/data/reusables/user-settings/sessions.md b/data/reusables/user-settings/sessions.md new file mode 100644 index 0000000000..743dcff89c --- /dev/null +++ b/data/reusables/user-settings/sessions.md @@ -0,0 +1 @@ +1. In the "Access" section of the sidebar, click **{% octicon "broadcast" aria-label="The broadcast icon" %} Sessions**. \ No newline at end of file diff --git a/data/ui.yml b/data/ui.yml index dfb6e87ff8..d2a5e24987 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -101,6 +101,7 @@ parameter_table: see_preview_notice: See preview notice see_preview_notices: See preview notices type: Type + single_enum_description: Value products: graphql: reference: @@ -141,6 +142,25 @@ products: preview_notice_to_change: This API is under preview and subject to change works_with: Works with api_reference: REST API reference + enum_description_title: Can be one of + required: Required + headers: Headers + query: Query parameters + path: Path parameters + body: Body parameters + webhooks: + action_type_switch_error: There was an error switching webhook action types. + action_type: Action type + availability: Availability + webhook_payload_object: Webhook payload object + webhook_payload_example: Webhook payload example + rephrase_availability: + repository: Repositories + organization: Organizations + app: GitHub Apps + business: Enterprises + marketplace: GitHub Marketplace + sponsors_listing: Sponsored accounts footer: all_rights_reserved: All rights reserved terms: Terms diff --git a/lib/webhooks/index.js b/lib/webhooks/index.js index 9932cd0b3c..c6e1a96e2d 100644 --- a/lib/webhooks/index.js +++ b/lib/webhooks/index.js @@ -3,9 +3,92 @@ import path from 'path' import fs from 'fs' import walk from 'walk-sync' import { set } from 'lodash-es' + +import { allVersions } from '../all-versions.js' +import { readCompressedJsonFileFallback } from '../read-json-file.js' + const __dirname = path.dirname(fileURLToPath(import.meta.url)) const staticDir = path.join(__dirname, 'static') +const schemasPath = path.join(__dirname, 'static/decorated') +// cache for webhook data per version +const webhooksCache = new Map() +// cache for webhook data for when you first visit the webhooks page where we +// show all webhooks for the current version but only 1 action type per webhook +// and also no nested parameters +const initialWebhooksCache = new Map() + +// return the webhoook data as described for `initialWebhooksCache` for the given +// version +export async function getInitialPageWebhooks(version) { + if (initialWebhooksCache.has(version)) { + return initialWebhooksCache.get(version) + } + const allWebhooks = await getWebhooks(version) + const initialWebhooks = [] + + // The webhooks page shows all webhooks but for each webhook only a single + // webhook action type at a time. We pick the first webhook type from each + // webhook's set of action types to show. + for (const [key, webhook] of Object.entries(allWebhooks)) { + const actionTypes = Object.keys(webhook) + const defaultAction = actionTypes ? actionTypes[0] : null + + const initialWebhook = { + name: key, + actionTypes, + data: webhook[defaultAction], + } + + // remove all nested params for the initial webhooks page, we'll load + // them by request + if (initialWebhook.data.bodyParameters) { + initialWebhook.data.bodyParameters.forEach((bodyParam) => { + if (bodyParam.childParamsGroups) { + bodyParam.childParamsGroups = [] + } + }) + } + + initialWebhooks.push({ ...initialWebhook }) + } + initialWebhooksCache.set(version, initialWebhooks) + return initialWebhooks +} + +// returns the webhook data for the given version and webhook category (e.g. +// `check_run`) -- this includes all the data per webhook action type and all +// nested parameters +export async function getWebhook(version, webhookCategory) { + const webhooks = await getWebhooks(version) + return webhooks[webhookCategory] +} + +// returns all the webhook data for the given version +export async function getWebhooks(version) { + const openApiVersion = getOpenApiVersion(version) + if (!webhooksCache.has(openApiVersion)) { + const filename = `${openApiVersion}.json` + + // The `readCompressedJsonFileFallback()` function + // will check for both a .br and .json extension. + webhooksCache.set( + openApiVersion, + readCompressedJsonFileFallback(path.join(schemasPath, filename)) + ) + } + + return webhooksCache.get(openApiVersion) +} + +function getOpenApiVersion(version) { + if (!(version in allVersions)) { + throw new Error(`Unrecognized version '${version}'. Not found in ${Object.keys(allVersions)}`) + } + return allVersions[version].openApiVersionName +} + +// TODO: docs-eng#1937: delete this function export default function getWebhookPayloads() { // Compile contents of individual .payload.json files into a single // object, with versions as top-level keys. @@ -40,6 +123,7 @@ export default function getWebhookPayloads() { return payloads } +// TODO: docs-eng#1937: delete this function function formatAsJsonCodeBlock(payloadObj) { // Note the use of `data-highlight="json"`. This is important because // done like this, it tells the rehype processor to NOT bother syntax diff --git a/middleware/api/index.js b/middleware/api/index.js index f95df82537..833e6f0392 100644 --- a/middleware/api/index.js +++ b/middleware/api/index.js @@ -3,10 +3,12 @@ import { createProxyMiddleware } from 'http-proxy-middleware' import events from './events.js' import search from './search.js' +import webhooks from './webhooks.js' const router = express.Router() router.use('/events', events) +router.use('/webhooks', webhooks) // The purpose of this is for convenience to everyone who runs this code // base locally but don't have an Elasticsearch server locally. diff --git a/middleware/api/webhooks.js b/middleware/api/webhooks.js new file mode 100644 index 0000000000..3db0c3b60a --- /dev/null +++ b/middleware/api/webhooks.js @@ -0,0 +1,42 @@ +import express from 'express' +import { getWebhook } from '../../lib/webhooks/index.js' +import { allVersions } from '../../lib/all-versions.js' +import { defaultCacheControl } from '../cache-control.js' + +const router = express.Router() + +// Returns a webhook for the given category and version +// +// Example request: +// +// /api/webhooks/v1?category=check_run&version=free-pro-team%40latest +router.get('/v1', async function webhooks(req, res, next) { + if (!req.query.category) { + return res.status(400).json({ error: "Missing 'category' in query string" }) + } + if (!req.query.version) { + return res.status(400).json({ error: "Missing 'version' in query string" }) + } + + const webhookVersion = Object.values(allVersions).find( + (version) => version.version === req.query.version + )?.version + const notFoundError = 'No webhook found for given category and version' + + if (!webhookVersion) { + return res.status(404).json({ error: notFoundError }) + } + + const webhook = await getWebhook(webhookVersion, req.query.category) + + if (webhook) { + if (process.env.NODE_ENV !== 'development') { + defaultCacheControl(res) + } + return res.status(200).send(webhook) + } else { + res.status(404).json({ error: notFoundError }) + } +}) + +export default router diff --git a/middleware/contextualizers/webhooks.js b/middleware/contextualizers/webhooks.js index 6dc7591c74..d79a66872c 100644 --- a/middleware/contextualizers/webhooks.js +++ b/middleware/contextualizers/webhooks.js @@ -5,6 +5,7 @@ import { allVersions } from '../../lib/all-versions.js' let webhookPayloads = null +// TODO: docs-eng#1937: webhooks-delete-1937: delete this file export default function webhooksContext(req, res, next) { const currentVersionObj = allVersions[req.context.currentVersion] // ignore requests to non-webhook reference paths diff --git a/middleware/index.js b/middleware/index.js index e3132ca537..bee098fc2d 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -38,6 +38,7 @@ import triggerError from './trigger-error.js' import ghesReleaseNotes from './contextualizers/ghes-release-notes.js' import ghaeReleaseNotes from './contextualizers/ghae-release-notes.js' import whatsNewChangelog from './contextualizers/whats-new-changelog.js' +// TODO: docs-eng#1937: delete this line import webhooks from './contextualizers/webhooks.js' import layout from './contextualizers/layout.js' import currentProductTree from './contextualizers/current-product-tree.js' @@ -261,6 +262,7 @@ export default function (app) { // *** Preparation for render-page: contextualizers *** app.use(asyncMiddleware(instrument(ghesReleaseNotes, './contextualizers/ghes-release-notes'))) app.use(asyncMiddleware(instrument(ghaeReleaseNotes, './contextualizers/ghae-release-notes'))) + // TODO: docs-eng#1937: delete this line app.use(instrument(webhooks, './contextualizers/webhooks')) app.use(asyncMiddleware(instrument(whatsNewChangelog, './contextualizers/whats-new-changelog'))) app.use(instrument(layout, './contextualizers/layout')) diff --git a/pages/[versionId]/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.tsx b/pages/[versionId]/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.tsx new file mode 100644 index 0000000000..ec4230cf78 --- /dev/null +++ b/pages/[versionId]/developers/webhooks-and-events/webhooks/webhook-events-and-payloads.tsx @@ -0,0 +1,68 @@ +import { GetServerSideProps } from 'next' +import { getInitialPageWebhooks } from 'lib/webhooks' +import { getMainContext, MainContext, MainContextT } from 'components/context/MainContext' +import { + getAutomatedPageContextFromRequest, + AutomatedPageContext, + AutomatedPageContextT, +} from 'components/context/AutomatedPageContext' +import { WebhookAction } from 'components/webhooks/types' +import { Webhook } from 'components/webhooks/Webhook' +import { getAutomatedPageMiniTocItems } from 'lib/get-mini-toc-items' +import { AutomatedPage } from 'components/article/AutomatedPage' + +type Props = { + mainContext: MainContextT + automatedPageContext: AutomatedPageContextT + webhooks: WebhookAction[] +} + +export default function WebhooksEventsAndPayloads({ + mainContext, + automatedPageContext, + webhooks, +}: Props) { + const content = webhooks.map((webhook: WebhookAction, index) => { + return ( +
+ +
+ ) + }) + return ( + + + {content} + + + ) +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + const req = context.req as object + const res = context.res as object + const currentVersion = context.query.versionId as string + const mainContext = await getMainContext(req, res) + const { miniTocItems } = getAutomatedPageContextFromRequest(req) + + // Get data for initial webhooks page (i.e. only 1 action type per webhook and + // no nested parameters) + const webhooks = (await getInitialPageWebhooks(currentVersion)) as WebhookAction[] + + // Build the minitocs for the webhooks page which is based on the webhook + // categories in addition to the Markdown in the webhook-events-and-payloads.md + // content file + const webhooksMiniTocs = await getAutomatedPageMiniTocItems( + webhooks.map((webhook) => webhook.data.category), + context + ) + webhooksMiniTocs && miniTocItems.push(...webhooksMiniTocs) + + return { + props: { + webhooks, + mainContext, + automatedPageContext: getAutomatedPageContextFromRequest(req), + }, + } +} diff --git a/tests/content/webhooks.js b/tests/content/webhooks.js index 0c7fe64887..1c03e60cbd 100644 --- a/tests/content/webhooks.js +++ b/tests/content/webhooks.js @@ -1,9 +1,10 @@ import { difference } from 'lodash-es' -import { getJSON } from '../helpers/e2etest.js' +import { get, getJSON } from '../helpers/e2etest.js' +import { SURROGATE_ENUMS } from '../../middleware/set-fastly-surrogate-key.js' import { latest } from '../../lib/enterprise-server-releases.js' import { allVersions } from '../../lib/all-versions.js' import getWebhookPayloads from '../../lib/webhooks' -import { jest } from '@jest/globals' +import { describe, expect, jest } from '@jest/globals' const allVersionValues = Object.values(allVersions) @@ -22,6 +23,66 @@ const ghaePayloadVersion = allVersionValues.find( (version) => version.plan === 'github-ae' ).miscVersionName +describe('webhooks middleware', () => { + test('basic get webhook', async () => { + const sp = new URLSearchParams() + // Based on live data which isn't ideal but it should rarely change at least. + // Just check that we find the webhook and that the result has the `category` + // field which all webhook types should have. + sp.set('category', 'branch_protection_rule') + sp.set('version', 'free-pro-team@latest') + const res = await get('/api/webhooks/v1?' + sp) + expect(res.statusCode).toBe(200) + const results = JSON.parse(res.text) + const actionTypes = Object.keys(results) + expect(actionTypes.length).toBeGreaterThan(2) + expect(Object.keys(results[actionTypes[0]]).includes('category')).toBeTruthy() + + // Check that it can be cached at the CDN + expect(res.headers['set-cookie']).toBeUndefined() + expect(res.headers['cache-control']).toContain('public') + expect(res.headers['cache-control']).toMatch(/max-age=[1-9]/) + expect(res.headers['surrogate-control']).toContain('public') + expect(res.headers['surrogate-control']).toMatch(/max-age=[1-9]/) + expect(res.headers['surrogate-key']).toBe(SURROGATE_ENUMS.DEFAULT) + }) + + test('get non-fpt version webhook', async () => { + const sp = new URLSearchParams() + sp.set('category', 'branch_protection_rule') + sp.set('version', 'enterprise-cloud@latest') + const res = await get('/api/webhooks/v1?' + sp) + expect(res.statusCode).toBe(200) + const results = JSON.parse(res.text) + const actionTypes = Object.keys(results) + expect(actionTypes.length).toBeGreaterThan(2) + expect(Object.keys(results[actionTypes[0]]).includes('category')).toBeTruthy() + + expect(res.statusCode).toBe(200) + }) + + test('unknown webhook category', async () => { + const sp = new URLSearchParams() + sp.set('category', 'no-such-category') + sp.set('version', 'free-pro-team@latest') + const res = await get('/api/webhooks/v1?' + sp) + + expect(res.statusCode).toBe(404) + expect(JSON.parse(res.text).error).toBeTruthy() + }) + + test('unknown version', async () => { + const sp = new URLSearchParams() + sp.set('category', 'branch_protection_rule') + sp.set('version', 'no-such-version') + const res = await get('/api/webhooks/v1?' + sp) + + expect(res.statusCode).toBe(404) + expect(JSON.parse(res.text).error).toBeTruthy() + }) +}) + +// TODO: docs-eng#1937: delete this test suite describe('webhook payloads', () => { jest.setTimeout(3 * 60 * 1000) diff --git a/tests/rendering/webhooks.js b/tests/rendering/webhooks.js index e418adbc9f..9bbd93aaed 100644 --- a/tests/rendering/webhooks.js +++ b/tests/rendering/webhooks.js @@ -1,11 +1,65 @@ import { jest } from '@jest/globals' import { getDOM } from '../helpers/e2etest.js' import { allVersions } from '../../lib/all-versions.js' +import { getWebhooks } from '../../lib/webhooks/index.js' describe('webhooks events and payloads', () => { jest.setTimeout(300 * 1000) - describe('rendering', () => { + test('loads webhook schema data for all versions', async () => { + for (const version in allVersions) { + const webhooks = await getWebhooks(version) + const webhookNames = Object.keys(webhooks) + const $ = await getDOM( + `/en/${version}/developers/webhooks-and-events/webhooks/webhook-events-and-payloads` + ) + const domH2Ids = $('h2') + .map((i, h2) => $(h2).attr('id')) + .get() + + webhookNames.forEach((webhookName) => { + expect(domH2Ids.includes(webhookName)) + }) + } + }) + + test('Non-GHES versions do not load GHES only webhook', async () => { + // available since 3.4, only in GHES (technically also GHAE which is based + // off of GHES) + const ghesOnlyWebhook = 'cache_sync' + + for (const version in allVersions) { + if (!version.includes('enterprise-server') && !version.includes('github-ae')) { + const $ = await getDOM( + `/en/${version}/developers/webhooks-and-events/webhooks/webhook-events-and-payloads` + ) + const domH2Ids = $('h2') + .map((i, h2) => $(h2).attr('id')) + .get() + + expect(domH2Ids.length).toBeGreaterThan(0) + expect(domH2Ids.includes(ghesOnlyWebhook)).toBe(false) + } + } + }) + + test('Webhooks events and payloads page has DOM markers needed for extracting search content', async () => { + const $ = await getDOM( + '/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads' + ) + const rootSelector = '[data-search=article-body]' + const $root = $(rootSelector) + expect($root.length).toBe(1) + + // on the webhooks page the lead is separate from the article body (unlike + // the REST pages for example) + const leadSelector = '[data-search=lead] p' + const $lead = $(leadSelector) + expect($lead.length).toBe(1) + }) + + // All webhook types don't yet have examples in the schema. + describe.skip('rendering', () => { test('every webhook event has at least one payload example', async () => { const versions = Object.values(allVersions).map((value) => value.version)
{`${t('name')}, ${t('type')}, ${t('description')}`}
+
+ +
+
+ {t('headers')}
+ {/* webhooks don't have a 'Parameters' table heading text so + we adjust the size of the body params heading in that case */} + {t('body')}