Skip to content

Commit ab82175

Browse files
committed
Re-enable web push for github push events
1 parent 1db9d19 commit ab82175

File tree

6 files changed

+95
-36
lines changed

6 files changed

+95
-36
lines changed

src/app/domain/github/githubPush.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { GithubPush } from '../types/GithubPush'
66
import { executeAndCatchConditionalCheckFailed } from '../entityStore/entityStoreOperationSupport'
77
import { sendToEventBridge } from '../../outboundInterfaces/eventBridgeBus'
88
import { saveLatestPushes } from './githubLatestPushesPerRef'
9+
import { GithubUserId } from '../types/GithubUserId'
10+
import { getUserIdsForAccount } from './githubMembership'
911

1012
export async function processPushes(appState: AppState, pushes: GithubPush[], publishNotifications: boolean) {
1113
if (pushes.length > 0) {
@@ -38,3 +40,10 @@ async function savePushes(appState: AppState, pushes: GithubPush[]) {
3840
export function latestCommitInPush(push: Pick<GithubPush, 'commits'>) {
3941
return push.commits[push.commits.length - 1]
4042
}
43+
44+
export async function getRelatedMemberIdsForPush(
45+
appState: AppState,
46+
push: GithubPush
47+
): Promise<GithubUserId[]> {
48+
return getUserIdsForAccount(appState, push.accountId)
49+
}

src/app/domain/user/userNotifyable.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { loadCalculatedUserSettingsOrUseDefaults } from './calculatedUserSetting
66
import { UserScopeReferenceData } from '../types/UserScopeReferenceData'
77
import { GithubWorkflowRunEvent } from '../types/GithubWorkflowRunEvent'
88
import { GithubWorkflowSummary } from '../types/GithubSummaries'
9+
import { GithubRepoKey } from '../types/GithubKeys'
910

1011
export async function filterWorkflowNotifyEnabled(
1112
appState: AppState,
@@ -45,3 +46,40 @@ async function getWorkflowNotifyEnabledForUser(
4546
}
4647
return yesNotify
4748
}
49+
50+
export async function filterRepoNotifyEnabled(
51+
appState: AppState,
52+
refData: UserScopeReferenceData,
53+
userIds: GithubUserId[],
54+
repo: GithubRepoKey
55+
): Promise<GithubUserId[]> {
56+
const enabledUserIds: GithubUserId[] = []
57+
for (const userId of userIds) {
58+
if (
59+
// TODO - don't do this! This is a hack while all users have same visibility to
60+
// avoid a huge amount of queries for large accounts
61+
// What we want instead is some caching for underlying ref data objects
62+
await getRepoNotifyEnabledForUser(appState, repo, {
63+
...refData,
64+
userId
65+
})
66+
) {
67+
enabledUserIds.push(userId)
68+
}
69+
}
70+
return enabledUserIds
71+
}
72+
73+
async function getRepoNotifyEnabledForUser(
74+
appState: AppState,
75+
repo: GithubRepoKey,
76+
refData: UserScopeReferenceData
77+
) {
78+
const userSettings = await loadCalculatedUserSettingsOrUseDefaults(appState, refData)
79+
const yesNotify = userSettings.github.accounts[repo.accountId]?.repos[repo.repoId].notify
80+
if (yesNotify === undefined) {
81+
logger.warn(`No calculated user notify setting for repo`, { repo, userSettings })
82+
return false
83+
}
84+
return yesNotify
85+
}

src/app/domain/webPush/cicadaEventWebPushPublisher.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import {
1010
} from '../github/githubWorkflowRunEvent'
1111
import { isCicadaEventBridgeDetail } from '../../outboundInterfaces/eventBridgeBus'
1212
import { CicadaWebNotification } from '../../outboundInterfaces/webPushWrapper'
13-
import { filterWorkflowNotifyEnabled } from '../user/userNotifyable'
13+
import { filterRepoNotifyEnabled, filterWorkflowNotifyEnabled } from '../user/userNotifyable'
1414

1515
import { loadUserScopeReferenceData } from '../github/userScopeReferenceData'
16+
import { isGithubPush } from '../types/GithubPush'
17+
import { getRelatedMemberIdsForPush } from '../github/githubPush'
1618

1719
// TOEventually - these are going to create a lot of queries for subscription lookup for large organizations
1820
// May be better to have one table / index for this.
@@ -54,20 +56,25 @@ export function generateRunEventNotification(
5456
}
5557
}
5658

57-
// TOEventually - add this back now that notification preferences added, maybe
58-
// export async function handleNewPush(appState: AppState, eventDetail: unknown) {
59-
// if (!isCicadaEventBridgeDetail(eventDetail) || !isGithubPush(eventDetail.data)) {
60-
// logger.error(
61-
// `Event detail for detail-type ${EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_PUSH} was not of expected format`,
62-
// { commit: eventDetail }
63-
// )
64-
// return
65-
// }
66-
//
67-
// const push = eventDetail.data
68-
//
69-
// await publishToSubscriptionsForUsers(appState, await getRelatedMemberIdsForPush(appState, push), {
70-
// title: 'New Push',
71-
// body: `New push to ${push.repoName}:${push.ref} by ${push.actor.login}`
72-
// })
73-
// }
59+
export async function handleNewPush(appState: AppState, eventDetail: unknown) {
60+
if (!isCicadaEventBridgeDetail(eventDetail) || !isGithubPush(eventDetail.data)) {
61+
logger.error(
62+
`Event detail for detail-type ${EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_PUSH} was not of expected format`,
63+
{ commit: eventDetail }
64+
)
65+
return
66+
}
67+
68+
const push = eventDetail.data
69+
const userIds = await getRelatedMemberIdsForPush(appState, push)
70+
if (userIds.length === 0) return
71+
// TODO - this is a hack to avoid terrible performance - see todo in filterWorkflowNotifyEnabled
72+
const refData = await loadUserScopeReferenceData(appState, userIds[0])
73+
const notifyEnabledUserIds = await filterRepoNotifyEnabled(appState, refData, userIds, push)
74+
75+
await publishToSubscriptionsForUsers(appState, notifyEnabledUserIds, {
76+
title: 'New Push',
77+
body: `New push to ${push.repoName}:${push.ref} by ${push.actor.userName}`,
78+
data: { url: push.repoUrl ?? '' }
79+
})
80+
}

src/app/domain/webPush/webPushEventBridgeEventProcessor.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { AppState } from '../../environment/AppState'
22
import { EventBridgeEvent } from 'aws-lambda'
3-
import { EVENTBRIDGE_DETAIL_TYPES, EventBridgeDetailType } from '../../../multipleContexts/eventBridge'
3+
import {
4+
EVENTBRIDGE_DETAIL_TYPES,
5+
isWebPushEventBridgeDetailType,
6+
WebPushEventBridgeDetailType
7+
} from '../../../multipleContexts/eventBridge'
48
import { logger } from '../../util/logging'
5-
import { handleNewWorkflowRunEvent } from './cicadaEventWebPushPublisher'
9+
import { handleNewPush, handleNewWorkflowRunEvent } from './cicadaEventWebPushPublisher'
610
import { handleWebPushTest } from './webPushUserTest'
711

812
export async function processEventBridgeWebPushEvent(
@@ -18,18 +22,9 @@ export async function processEventBridgeWebPushEvent(
1822
await webPushEventProcessors[detailType](appState, event.detail)
1923
}
2024

21-
// We don't necessarily listen for all event bridge events
22-
type WebPushEventBridgeDetailType = Extract<
23-
EventBridgeDetailType,
24-
'GithubNewWorkflowRunEvent' | 'WebPushTest'
25-
>
26-
27-
function isWebPushEventBridgeDetailType(x: unknown): x is WebPushEventBridgeDetailType {
28-
return typeof x === 'string' && (x === 'GithubNewWorkflowRunEvent' || x === 'WebPushTest')
29-
}
30-
3125
const webPushEventProcessors: Record<WebPushEventBridgeDetailType, WebPushEventProcessor> = {
3226
[EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_WORKFLOW_RUN_EVENT]: handleNewWorkflowRunEvent,
27+
[EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_PUSH]: handleNewPush,
3328
[EVENTBRIDGE_DETAIL_TYPES.WEB_PUSH_TEST]: handleWebPushTest
3429
}
3530

src/cdk/stacks/main/userFacingWeb.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Duration } from 'aws-cdk-lib'
44
import { grantLambdaFunctionPermissionToPutEvents } from '../../support/eventbridge'
55
import { Rule } from 'aws-cdk-lib/aws-events'
66
import * as targets from 'aws-cdk-lib/aws-events-targets'
7-
import { EVENTBRIDGE_DETAIL_TYPES } from '../../../multipleContexts/eventBridge'
7+
import { WEBPUSH_EVENTBRIDGE_DETAIL_TYPES } from '../../../multipleContexts/eventBridge'
88
import { IdentitySource, LambdaIntegration, RequestAuthorizer, RestApi } from 'aws-cdk-lib/aws-apigateway'
99
import { HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2'
1010
import { MainStackProps } from './mainStackProps'
@@ -102,11 +102,8 @@ function defineWebPushPublisher(scope: Construct, props: UserFacingWebEndpointsP
102102
description: `Send Web Push notifications`,
103103
eventPattern: {
104104
source: [props.appName],
105-
detailType: [
106-
EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_WORKFLOW_RUN_EVENT,
107-
EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_PUSH,
108-
EVENTBRIDGE_DETAIL_TYPES.WEB_PUSH_TEST
109-
]
105+
// Need Array.from here otherwise a type error
106+
detailType: Array.from(WEBPUSH_EVENTBRIDGE_DETAIL_TYPES)
110107
},
111108
targets: [new targets.LambdaFunction(lambdaFunction)]
112109
})

src/multipleContexts/eventBridge.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// See comment at WebPushEventBridgeDetailTypes if adding more types here
2+
23
export const EVENTBRIDGE_DETAIL_TYPES = {
34
GITHUB_NEW_PUSH: 'GithubNewPush',
45
GITHUB_NEW_WORKFLOW_RUN_EVENT: 'GithubNewWorkflowRunEvent',
@@ -13,3 +14,15 @@ export type EventBridgeDetailType = (typeof EVENTBRIDGE_DETAIL_TYPES)[keyof type
1314
export function isEventBridgeDetailType(x: unknown): x is EventBridgeDetailType {
1415
return typeof x === 'string' && Object.values(EVENTBRIDGE_DETAIL_TYPES).includes(x as EventBridgeDetailType)
1516
}
17+
18+
export const WEBPUSH_EVENTBRIDGE_DETAIL_TYPES = [
19+
EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_PUSH,
20+
EVENTBRIDGE_DETAIL_TYPES.GITHUB_NEW_WORKFLOW_RUN_EVENT,
21+
EVENTBRIDGE_DETAIL_TYPES.WEB_PUSH_TEST
22+
] as const
23+
24+
export type WebPushEventBridgeDetailType = (typeof WEBPUSH_EVENTBRIDGE_DETAIL_TYPES)[number]
25+
26+
export function isWebPushEventBridgeDetailType(x: unknown): x is WebPushEventBridgeDetailType {
27+
return typeof x === 'string' && WEBPUSH_EVENTBRIDGE_DETAIL_TYPES.includes(x as WebPushEventBridgeDetailType)
28+
}

0 commit comments

Comments
 (0)