Skip to content

Commit 3e65a9b

Browse files
committed
Add links to Hydrated events
1 parent c7d8a09 commit 3e65a9b

4 files changed

Lines changed: 49 additions & 37 deletions

File tree

src/Share/BackgroundJobs/Webhooks/Worker.hs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ data WebhookEventPayload jwt = WebhookEventPayload
139139
-- | The topic of the notification event.
140140
topic :: NotificationTopic,
141141
-- | The data associated with the notification event.
142-
data_ :: HydratedEventPayload,
142+
data_ :: HydratedEvent,
143143
-- | A signed token containing all of the same data.
144144
jwt :: jwt
145145
}
@@ -176,7 +176,7 @@ instance FromJSON (WebhookEventPayload ()) where
176176
<*> pure ()
177177

178178
tryWebhook ::
179-
NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEventPayload ->
179+
NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEvent ->
180180
NotificationWebhookId ->
181181
Background (Maybe WebhookSendFailure)
182182
tryWebhook event webhookId = UnliftIO.handleAny (\someException -> pure $ Just $ InvalidRequest event.eventId webhookId someException) do
@@ -267,12 +267,12 @@ instance ToJSON (MessageContent 'Discord) where
267267
]
268268
]
269269

270-
buildWebhookRequest :: NotificationWebhookId -> URI -> NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEventPayload -> WebhookEventPayload JWTParam -> Background (Either WebhookSendFailure HTTPClient.Request)
270+
buildWebhookRequest :: NotificationWebhookId -> URI -> NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEvent -> WebhookEventPayload JWTParam -> Background (Either WebhookSendFailure HTTPClient.Request)
271271
buildWebhookRequest webhookId uri event defaultPayload = do
272272
if
273-
| isSlackWebhook uri -> buildChatAppPayload (Proxy @Slack) uri
274-
| isDiscordWebhook uri -> buildChatAppPayload (Proxy @Discord) uri
275-
| otherwise -> pure $ buildDefaultPayload
273+
| isSlackWebhook uri -> buildChatAppPayload (Proxy @Slack) uri
274+
| isDiscordWebhook uri -> buildChatAppPayload (Proxy @Discord) uri
275+
| otherwise -> pure $ buildDefaultPayload
276276
where
277277
isSlackWebhook :: URI -> Bool
278278
isSlackWebhook uri =
@@ -307,18 +307,18 @@ buildWebhookRequest webhookId uri event defaultPayload = do
307307
actorAuthor = maybe "" (<> " ") actorName <> actorHandle
308308
actorAvatarUrl = event.eventActor ^. DisplayInfo.avatarUrl_
309309
actorLink <- Links.userProfilePage (event.eventActor ^. DisplayInfo.handle_)
310-
messageContent :: MessageContent provider <- case event.eventData of
310+
let mainLink = event.eventData.hydratedEventLink
311+
messageContent :: MessageContent provider <- case event.eventData.payload of
311312
HydratedProjectBranchUpdatedPayload payload -> do
312313
let pbShorthand = (projectBranchShortHandFromParts payload.projectInfo.projectShortHand payload.branchInfo.branchShortHand)
313314
title = "Branch " <> IDs.toText pbShorthand <> " was just updated."
314315
preText = title
315-
link <- Links.notificationLink event.eventData
316316
pure $
317317
MessageContent
318318
{ preText = preText,
319319
content = "Branch updated",
320320
title = title,
321-
mainLink = link,
321+
mainLink,
322322
authorName = actorAuthor,
323323
authorLink = actorLink,
324324
authorAvatarUrl = actorAvatarUrl,
@@ -330,13 +330,12 @@ buildWebhookRequest webhookId uri event defaultPayload = do
330330
title = payload.contributionInfo.contributionTitle
331331
description = fromMaybe "" $ payload.contributionInfo.contributionDescription
332332
preText = "New Contribution in " <> IDs.toText pbShorthand
333-
link <- Links.notificationLink event.eventData
334333
pure $
335334
MessageContent
336335
{ preText = preText,
337336
content = description,
338337
title = title,
339-
mainLink = link,
338+
mainLink,
340339
authorName = actorAuthor,
341340
authorLink = actorLink,
342341
authorAvatarUrl = actorAvatarUrl,
@@ -364,13 +363,13 @@ cutOffText maxLength text =
364363

365364
attemptWebhookSend ::
366365
AuthZ.AuthZReceipt ->
367-
(NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEventPayload -> NotificationWebhookId -> IO (Maybe WebhookSendFailure)) ->
366+
(NotificationEvent NotificationEventId UnifiedDisplayInfo UTCTime HydratedEvent -> NotificationWebhookId -> IO (Maybe WebhookSendFailure)) ->
368367
NotificationEventId ->
369368
NotificationWebhookId ->
370369
PG.Transaction e (Maybe WebhookSendFailure)
371370
attemptWebhookSend _authZReceipt tryWebhookIO eventId webhookId = do
372371
event <- NQ.expectEvent eventId
373-
hydratedEvent <- forOf eventData_ event NQ.hydrateEventData
372+
hydratedEvent <- forOf eventData_ event NQ.hydrateEventPayload
374373
populatedEvent <- hydratedEvent & DisplayInfoQ.unifiedDisplayInfoForUserOf eventUserInfo_
375374
PG.transactionUnsafeIO (tryWebhookIO populatedEvent webhookId) >>= \case
376375
Just err -> do

src/Share/Notifications/Ops.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Share.Notifications.Ops
33
createWebhookDeliveryMethod,
44
updateWebhookDeliveryMethod,
55
deleteWebhookDeliveryMethod,
6+
hydrateEvent,
67
)
78
where
89

@@ -16,6 +17,7 @@ import Share.Prelude
1617
import Share.Utils.URI (URIParam (..))
1718
import Share.Web.App (WebApp)
1819
import Share.Web.Errors (respondError)
20+
import Share.Web.UI.Links qualified as Links
1921

2022
listNotificationDeliveryMethods :: UserId -> Maybe NotificationSubscriptionId -> WebApp [NotificationDeliveryMethod]
2123
listNotificationDeliveryMethods userId maySubscriptionId = do
@@ -75,3 +77,8 @@ deleteWebhookDeliveryMethod notificationUser webhookDeliveryMethodId = do
7577
Right _ -> do
7678
PG.runTransaction $ do
7779
NotifQ.deleteWebhookDeliveryMethod notificationUser webhookDeliveryMethodId
80+
81+
hydrateEvent :: HydratedEventPayload -> WebApp HydratedEvent
82+
hydrateEvent hydratedEventPayload = do
83+
hydratedEventLink <- Links.notificationLink hydratedEventPayload
84+
pure $ HydratedEvent {hydratedEventPayload, hydratedEventLink}

src/Share/Notifications/Queries.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Share.Notifications.Queries
22
( recordEvent,
33
expectEvent,
4-
listNotificationHubEntries,
4+
listNotificationHubEntryPayloads,
55
updateNotificationHubEntries,
66
addSubscriptionDeliveryMethods,
77
removeSubscriptionDeliveryMethods,
@@ -17,7 +17,7 @@ module Share.Notifications.Queries
1717
deleteNotificationSubscription,
1818
updateNotificationSubscription,
1919
getNotificationSubscription,
20-
hydrateEventData,
20+
hydrateEventPayload,
2121
)
2222
where
2323

@@ -54,8 +54,8 @@ expectEvent eventId = do
5454
WHERE id = #{eventId}
5555
|]
5656

57-
listNotificationHubEntries :: UserId -> Maybe Int -> Maybe UTCTime -> Maybe (NESet NotificationStatus) -> Transaction e [NotificationHubEntry UnifiedDisplayInfo HydratedEventPayload]
58-
listNotificationHubEntries notificationUserId mayLimit afterTime statusFilter = do
57+
listNotificationHubEntryPayloads :: UserId -> Maybe Int -> Maybe UTCTime -> Maybe (NESet NotificationStatus) -> Transaction e [NotificationHubEntry UnifiedDisplayInfo HydratedEventPayload]
58+
listNotificationHubEntryPayloads notificationUserId mayLimit afterTime statusFilter = do
5959
let limit = clamp (0, 1000) . fromIntegral @Int @Int32 . fromMaybe 50 $ mayLimit
6060
let statusFilterList = Foldable.toList <$> statusFilter
6161
dbNotifications <-
@@ -70,7 +70,7 @@ listNotificationHubEntries notificationUserId mayLimit afterTime statusFilter =
7070
ORDER BY hub.created_at DESC
7171
LIMIT #{limit}
7272
|]
73-
hydrated <- PG.pipelined $ forOf (traversed . traversed) dbNotifications hydrateEventData
73+
hydratedPayloads <- PG.pipelined $ forOf (traversed . traversed) dbNotifications hydrateEventData
7474
hydrated & DisplayInfoQ.unifiedDisplayInfoForUserOf (traversed . hubEntryUserInfo_)
7575

7676
updateNotificationHubEntries :: (QueryA m) => NESet NotificationHubEntryId -> NotificationStatus -> m ()
@@ -293,8 +293,8 @@ getNotificationSubscription subscriberUserId subscriptionId = do
293293
-- (preferably pipelined).
294294
--
295295
-- If need be we can write a batch job in plpgsql to hydrate them all at once.
296-
hydrateEventData :: forall m. (QueryA m) => NotificationEventData -> m HydratedEventPayload
297-
hydrateEventData = \case
296+
hydrateEventPayload :: forall m. (QueryA m) => NotificationEventData -> m HydratedEventPayload
297+
hydrateEventPayload = \case
298298
ProjectBranchUpdatedData
299299
(ProjectBranchData {projectId, branchId}) -> do
300300
HydratedProjectBranchUpdatedPayload <$> hydrateProjectBranchPayload projectId branchId

src/Share/Notifications/Types.hs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Share.Notifications.Types
1818
NotificationEmailDeliveryConfig (..),
1919
NotificationWebhookConfig (..),
2020
HydratedEventPayload (..),
21+
HydratedEvent (..),
2122
BranchPayload (..),
2223
ProjectPayload (..),
2324
ContributionPayload (..),
@@ -527,27 +528,32 @@ instance FromJSON ProjectContributionCreatedPayload where
527528
contributionInfo <- o .: "contribution"
528529
pure ProjectContributionCreatedPayload {projectInfo, contributionInfo}
529530

531+
data HydratedEvent = HydratedEvent
532+
{ hydratedEventPayload :: HydratedEventPayload,
533+
hydratedEventLink :: URI
534+
}
535+
deriving stock (Show, Eq)
536+
537+
instance ToJSON HydratedEvent where
538+
toJSON he@(HydratedEvent {hydratedEventPayload, hydratedEventLink}) =
539+
let kind :: Text = case hydratedEventTopic he of
540+
ProjectBranchUpdated -> "projectBranchUpdated"
541+
ProjectContributionCreated -> "projectContributionCreated"
542+
payload = case hydratedEventPayload of
543+
HydratedProjectBranchUpdatedPayload p -> Aeson.toJSON p
544+
HydratedProjectContributionCreatedPayload p -> Aeson.toJSON p
545+
in Aeson.object
546+
[ "payload" .= payload,
547+
"link" .= URIParam hydratedEventLink,
548+
"kind" .= kind
549+
]
550+
530551
data HydratedEventPayload
531552
= HydratedProjectBranchUpdatedPayload ProjectBranchUpdatedPayload
532553
| HydratedProjectContributionCreatedPayload ProjectContributionCreatedPayload
533554
deriving stock (Show, Eq)
534555

535-
hydratedEventTopic :: HydratedEventPayload -> NotificationTopic
536-
hydratedEventTopic = \case
556+
hydratedEventTopic :: HydratedEvent -> NotificationTopic
557+
hydratedEventTopic (HydratedEvent {hydratedEventPayload}) = case hydratedEventPayload of
537558
HydratedProjectBranchUpdatedPayload _ -> ProjectBranchUpdated
538559
HydratedProjectContributionCreatedPayload _ -> ProjectContributionCreated
539-
540-
instance ToJSON HydratedEventPayload where
541-
toJSON = \case
542-
(HydratedProjectBranchUpdatedPayload payload) ->
543-
Aeson.object ["kind" .= ("projectBranchUpdated" :: Text), "payload" .= payload]
544-
(HydratedProjectContributionCreatedPayload payload) ->
545-
Aeson.object ["kind" .= ("projectContributionCreated" :: Text), "payload" .= payload]
546-
547-
instance FromJSON HydratedEventPayload where
548-
parseJSON = Aeson.withObject "HydratedEventPayload" \o -> do
549-
kind <- o .: "kind"
550-
case kind of
551-
"projectBranchUpdated" -> HydratedProjectBranchUpdatedPayload <$> o .: "payload"
552-
"projectContributionCreated" -> HydratedProjectContributionCreatedPayload <$> o .: "payload"
553-
_ -> fail $ "Unknown event kind: " <> Text.unpack kind

0 commit comments

Comments
 (0)