Skip to content

Commit a64bd74

Browse files
authored
Merge pull request #39343 from github/repo-sync
Repo sync
2 parents 2ec7ac7 + 1f0d51b commit a64bd74

File tree

7 files changed

+160
-4
lines changed

7 files changed

+160
-4
lines changed

src/audit-logs/components/GroupedEvents.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import type { AuditLogEventT } from '../types'
77
type Props = {
88
auditLogEvents: AuditLogEventT[]
99
category: string
10+
categoryNote?: string
1011
}
1112

12-
export default function GroupedEvents({ auditLogEvents, category }: Props) {
13+
export default function GroupedEvents({ auditLogEvents, category, categoryNote }: Props) {
1314
const { t } = useTranslation('audit_logs')
1415
const eventSlug = slug(category)
1516

@@ -39,6 +40,11 @@ export default function GroupedEvents({ auditLogEvents, category }: Props) {
3940
<HeadingLink as="h3" slug={eventSlug}>
4041
{category}
4142
</HeadingLink>
43+
{categoryNote && (
44+
<div className="category-note mb-3 p-3 color-border-default border rounded-2">
45+
<p className="mb-0">{categoryNote}</p>
46+
</div>
47+
)}
4248
<div>
4349
{auditLogEvents.map((event) => (
4450
<div key={event.action} style={{ marginBottom: '3rem' }}>

src/audit-logs/lib/config.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,11 @@
33
"apiOnlyEvents": "This event is not available in the web interface, only via the REST API, audit log streaming, or JSON/CSV exports.",
44
"apiRequestEvent": "This event is only available via audit log streaming."
55
},
6+
"_categoryNotesDocumentation": "Category notes provide additional context for audit log event categories. Currently, notes are plain text and not version-specific. Future enhancement: add versioning support for different deployment types (GHEC, GHES, FPT).",
7+
"categoryNotes": {
8+
"members_can_create_pages": "For more information, see \"Managing the publication of GitHub Pages sites for your organization.\"",
9+
"git": "Note: Git events have special access requirements and retention policies that differ from other audit log events. For GitHub Enterprise Cloud, access Git events via the REST API only with 7-day retention. For GitHub Enterprise Server, Git events must be enabled in audit log configuration and are not included in search results.",
10+
"sso_redirect": "Note: Automatically redirecting users to sign in is currently in beta for Enterprise Managed Users and subject to change."
11+
},
612
"sha": "30f9be27cbe4d9f3729f8fb335ce8b254ca3b54a"
7-
}
13+
}

src/audit-logs/lib/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import type {
88
CategorizedEvents,
99
VersionedAuditLogData,
1010
RawAuditLogEventT,
11+
CategoryNotes,
12+
AuditLogConfig,
1113
} from '../types'
14+
import config from './config.json'
1215

1316
export const AUDIT_LOG_DATA_DIR = 'src/audit-logs/data'
1417

@@ -21,6 +24,12 @@ type PipelineConfig = {
2124
appendedDescriptions: Record<string, string>
2225
}
2326

27+
// get category notes from config
28+
export function getCategoryNotes(): CategoryNotes {
29+
const auditLogConfig = config as AuditLogConfig
30+
return auditLogConfig.categoryNotes || {}
31+
}
32+
2433
type TitleResolutionContext = {
2534
pages: Record<string, any>
2635
redirects: Record<string, string>

src/audit-logs/pages/audit-log-events.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@ import {
1414
import { AutomatedPage } from '@/automated-pipelines/components/AutomatedPage'
1515
import { HeadingLink } from '@/frame/components/article/HeadingLink'
1616
import GroupedEvents from '../components/GroupedEvents'
17-
import type { CategorizedEvents } from '../types'
17+
import type { CategorizedEvents, CategoryNotes } from '../types'
1818

1919
type Props = {
2020
mainContext: MainContextT
2121
automatedPageContext: AutomatedPageContextT
2222
auditLogEvents: CategorizedEvents
23+
categoryNotes: CategoryNotes
2324
}
2425

2526
export default function AuditLogEvents({
2627
mainContext,
2728
automatedPageContext,
2829
auditLogEvents,
30+
categoryNotes,
2931
}: Props) {
3032
const content = (
3133
<>
@@ -38,6 +40,7 @@ export default function AuditLogEvents({
3840
key={category}
3941
category={category}
4042
auditLogEvents={auditLogEvents[category]}
43+
categoryNote={categoryNotes[category]}
4144
/>
4245
)
4346
})}
@@ -55,7 +58,7 @@ export default function AuditLogEvents({
5558

5659
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
5760
const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items')
58-
const { getCategorizedAuditLogEvents } = await import('../lib')
61+
const { getCategorizedAuditLogEvents, getCategoryNotes } = await import('../lib')
5962

6063
const req = context.req as object
6164
const res = context.res as object
@@ -77,6 +80,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
7780
auditLogEvents = getCategorizedAuditLogEvents('organization', currentVersion)
7881
}
7982

83+
const categoryNotes = getCategoryNotes()
84+
8085
const auditLogEventsMiniTocs = await getAutomatedPageMiniTocItems(
8186
Object.keys(auditLogEvents).map((category) => category),
8287
context,
@@ -86,6 +91,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
8691
return {
8792
props: {
8893
auditLogEvents,
94+
categoryNotes,
8995
mainContext,
9096
automatedPageContext: getAutomatedPageContextFromRequest(req),
9197
},

src/audit-logs/tests/rendering.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,49 @@ describe('audit log events docs', () => {
9898
const $lead = $(leadSelector)
9999
expect($lead.length).toBe(1)
100100
})
101+
102+
test('category notes are rendered when present', async () => {
103+
// Test organization page which should have category notes
104+
const $ = await getDOM(
105+
'/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/audit-log-events-for-your-organization',
106+
)
107+
108+
// Look for category note elements - they should appear before tables
109+
const categoryNotes = $('.category-note')
110+
111+
// If there are categories with notes configured, we should see them rendered
112+
if (categoryNotes.length > 0) {
113+
categoryNotes.each((_, note) => {
114+
const $note = $(note)
115+
expect($note.text().length).toBeGreaterThan(0)
116+
117+
// Should be followed by a div (the category events)
118+
const $nextDiv = $note.next('div')
119+
expect($nextDiv.length).toBe(1)
120+
})
121+
}
122+
})
123+
124+
test('git category note is rendered on appropriate pages', async () => {
125+
// Test enterprise page which should have git category note for GHES
126+
const $ = await getDOM(
127+
'/enterprise-server@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/audit-log-events-for-your-enterprise',
128+
)
129+
130+
// Look for git category heading
131+
const gitHeading = $('#git')
132+
if (gitHeading.length > 0) {
133+
// Should have a category note before the div
134+
const $noteOrTable = gitHeading.next()
135+
136+
// Either the next element is a note (followed by div) or directly a div
137+
if ($noteOrTable.hasClass('category-note')) {
138+
expect($noteOrTable.text()).toContain('Git events')
139+
expect($noteOrTable.next('div').length).toBe(1)
140+
} else if ($noteOrTable.is('div')) {
141+
// Direct div is fine too - means no note for this category
142+
expect($noteOrTable.is('div')).toBe(true)
143+
}
144+
}
145+
})
101146
})
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { describe, expect, test } from 'vitest'
2+
3+
import { getCategorizedAuditLogEvents } from '../../lib'
4+
import config from '../../lib/config.json'
5+
6+
describe('audit log category notes', () => {
7+
test('config contains expected category notes', () => {
8+
expect(config.categoryNotes).toBeDefined()
9+
expect(typeof config.categoryNotes).toBe('object')
10+
11+
// Check that we have the specific category notes mentioned in the issue
12+
expect(config.categoryNotes).toHaveProperty('members_can_create_pages')
13+
expect(config.categoryNotes).toHaveProperty('git')
14+
expect(config.categoryNotes).toHaveProperty('sso_redirect')
15+
})
16+
17+
test('category notes are strings', () => {
18+
if (config.categoryNotes) {
19+
Object.values(config.categoryNotes).forEach((note) => {
20+
expect(typeof note).toBe('string')
21+
expect(note.length).toBeGreaterThan(0)
22+
})
23+
}
24+
})
25+
26+
test('members_can_create_pages note contains reference to GitHub Pages', () => {
27+
const note = config.categoryNotes?.['members_can_create_pages']
28+
expect(note).toContain('GitHub Pages')
29+
expect(note).toContain('organization')
30+
})
31+
32+
test('git category note contains REST API information', () => {
33+
const note = config.categoryNotes?.['git']
34+
expect(note).toContain('REST API')
35+
expect(note).toContain('7-day retention')
36+
})
37+
38+
test('sso_redirect note mentions beta status', () => {
39+
const note = config.categoryNotes?.['sso_redirect']
40+
expect(note).toContain('beta')
41+
expect(note).toContain('Enterprise Managed Users')
42+
})
43+
44+
test('category notes do not interfere with event categorization', () => {
45+
// Test that adding category notes doesn't break existing functionality
46+
const organizationEvents = getCategorizedAuditLogEvents('organization', 'free-pro-team@latest')
47+
const enterpriseEvents = getCategorizedAuditLogEvents('enterprise', 'enterprise-cloud@latest')
48+
49+
// Should still have categorized events
50+
expect(Object.keys(organizationEvents).length).toBeGreaterThan(0)
51+
expect(Object.keys(enterpriseEvents).length).toBeGreaterThan(0)
52+
53+
// Each category should still contain arrays of events
54+
Object.values(organizationEvents).forEach((events) => {
55+
expect(Array.isArray(events)).toBe(true)
56+
if (events.length > 0) {
57+
expect(events[0]).toHaveProperty('action')
58+
expect(events[0]).toHaveProperty('description')
59+
}
60+
})
61+
})
62+
63+
test('category notes are properly typed', () => {
64+
// This test will pass once we update the types
65+
const notes = config.categoryNotes
66+
if (notes) {
67+
expect(notes).toEqual(
68+
expect.objectContaining({
69+
members_can_create_pages: expect.any(String),
70+
git: expect.any(String),
71+
sso_redirect: expect.any(String),
72+
}),
73+
)
74+
}
75+
})
76+
})

src/audit-logs/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export type CategorizedEvents = Record<string, AuditLogEventT[]>
22

3+
export type CategoryNotes = Record<string, string>
4+
35
export type AuditLogEventT = {
46
action: string
57
description: string
@@ -19,3 +21,9 @@ export type RawAuditLogEventT = {
1921
}
2022

2123
export type VersionedAuditLogData = Record<string, Record<string, AuditLogEventT[]>>
24+
25+
export type AuditLogConfig = {
26+
sha: string
27+
appendedDescriptions: Record<string, string>
28+
categoryNotes?: CategoryNotes
29+
}

0 commit comments

Comments
 (0)