Skip to content

Commit

Permalink
feat: add push notification filtering settings (#3207)
Browse files Browse the repository at this point in the history
- add admin mutation for filtered push notifications
- add mutations for changing notification settings
- filter various push notifications from payments
  • Loading branch information
UncleSamtoshi authored Sep 20, 2023
1 parent 28c220a commit 79bf33b
Show file tree
Hide file tree
Showing 58 changed files with 1,395 additions and 75 deletions.
61 changes: 61 additions & 0 deletions dev/apollo-federation/supergraph.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface Account
id: ID!
level: AccountLevel!
limits: AccountLimits!
notificationSettings: NotificationSettings!
realtimePrice: RealtimePrice!
transactions(
"""Returns the items in the list that come after the specified cursor."""
Expand All @@ -56,6 +57,32 @@ type AccountDeletePayload
success: Boolean!
}

input AccountDisableNotificationCategoryInput
@join__type(graph: PUBLIC)
{
category: NotificationCategory!
channel: NotificationChannel
}

input AccountDisableNotificationChannelInput
@join__type(graph: PUBLIC)
{
channel: NotificationChannel!
}

input AccountEnableNotificationCategoryInput
@join__type(graph: PUBLIC)
{
category: NotificationCategory!
channel: NotificationChannel
}

input AccountEnableNotificationChannelInput
@join__type(graph: PUBLIC)
{
channel: NotificationChannel!
}

enum AccountLevel
@join__type(graph: PUBLIC)
{
Expand Down Expand Up @@ -120,6 +147,13 @@ type AccountUpdateDisplayCurrencyPayload
errors: [Error!]!
}

type AccountUpdateNotificationSettingsPayload
@join__type(graph: PUBLIC)
{
account: ConsumerAccount
errors: [Error!]!
}

"""An Opaque Bearer token"""
scalar AuthToken
@join__type(graph: PUBLIC)
Expand Down Expand Up @@ -267,6 +301,7 @@ type ConsumerAccount implements Account
id: ID!
level: AccountLevel!
limits: AccountLimits!
notificationSettings: NotificationSettings!

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
Expand Down Expand Up @@ -816,6 +851,10 @@ type Mutation
@join__type(graph: PUBLIC)
{
accountDelete: AccountDeletePayload!
accountDisableNotificationCategory(input: AccountDisableNotificationCategoryInput!): AccountUpdateNotificationSettingsPayload!
accountDisableNotificationChannel(input: AccountDisableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload!
accountEnableNotificationCategory(input: AccountEnableNotificationCategoryInput!): AccountUpdateNotificationSettingsPayload!
accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload!
accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload!
accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload!
callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload!
Expand Down Expand Up @@ -949,6 +988,28 @@ enum Network
testnet @join__enumValue(graph: PUBLIC)
}

scalar NotificationCategory
@join__type(graph: PUBLIC)

enum NotificationChannel
@join__type(graph: PUBLIC)
{
PUSH @join__enumValue(graph: PUBLIC)
}

type NotificationChannelSettings
@join__type(graph: PUBLIC)
{
disabledCategories: [NotificationCategory!]!
enabled: Boolean!
}

type NotificationSettings
@join__type(graph: PUBLIC)
{
push: NotificationChannelSettings!
}

"""An address for an on-chain bitcoin destination"""
scalar OnChainAddress
@join__type(graph: PUBLIC)
Expand Down
31 changes: 31 additions & 0 deletions src/app/accounts/disable-notification-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
checkedToNotificationCategory,
disableNotificationCategory as domainDisableNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const disableNotificationCategory = async ({
accountId,
notificationChannel,
notificationCategory,
}: {
accountId: AccountId
notificationChannel?: NotificationChannel
notificationCategory: string
}): Promise<Account | ApplicationError> => {
const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory)
if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainDisableNotificationCategory({
notificationSettings: account.notificationSettings,
notificationChannel,
notificationCategory: checkedNotificationCategory,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
22 changes: 22 additions & 0 deletions src/app/accounts/disable-notification-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { disableNotificationChannel as domainDisableNotificationChannel } from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const disableNotificationChannel = async ({
accountId,
notificationChannel,
}: {
accountId: AccountId
notificationChannel: NotificationChannel
}): Promise<Account | ApplicationError> => {
const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainDisableNotificationChannel({
notificationSettings: account.notificationSettings,
notificationChannel,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
31 changes: 31 additions & 0 deletions src/app/accounts/enable-notification-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
checkedToNotificationCategory,
enableNotificationCategory as domainEnableNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const enableNotificationCategory = async ({
accountId,
notificationChannel,
notificationCategory,
}: {
accountId: AccountId
notificationChannel?: NotificationChannel
notificationCategory: string
}): Promise<Account | ApplicationError> => {
const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory)
if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainEnableNotificationCategory({
notificationSettings: account.notificationSettings,
notificationChannel,
notificationCategory: checkedNotificationCategory,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
22 changes: 22 additions & 0 deletions src/app/accounts/enable-notification-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { enableNotificationChannel as domainEnableNotificationChannel } from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const enableNotificationChannel = async ({
accountId,
notificationChannel,
}: {
accountId: AccountId
notificationChannel: NotificationChannel
}): Promise<Account | ApplicationError> => {
const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainEnableNotificationChannel({
notificationSettings: account.notificationSettings,
notificationChannel,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
4 changes: 4 additions & 0 deletions src/app/accounts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export * from "./update-display-currency"
export * from "./username-available"
export * from "./delete-business-map-info"
export * from "./upgrade-device-account"
export * from "./disable-notification-category"
export * from "./enable-notification-category"
export * from "./enable-notification-channel"
export * from "./disable-notification-channel"

const accounts = AccountsRepository()

Expand Down
1 change: 1 addition & 0 deletions src/app/accounts/send-default-wallet-balance-to-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const sendDefaultWalletBalanceToAccounts = async () => {
deviceTokens: user.deviceTokens,
displayBalanceAmount: displayAmount,
recipientLanguage: user.language,
notificationSettings: account.notificationSettings,
})

if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) {
Expand Down
18 changes: 15 additions & 3 deletions src/app/admin/send-admin-push-notification.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { checkedToAccountUuid } from "@domain/accounts"
import {
GaloyNotificationCategories,
checkedToNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose/accounts"
import { UsersRepository } from "@services/mongoose/users"
import { NotificationsService } from "@services/notifications"
Expand All @@ -8,12 +12,20 @@ export const sendAdminPushNotification = async ({
title,
body,
data,
notificationCategory,
}: {
accountId: string
title: string
body: string
data?: { [key: string]: string }
notificationCategory?: string
}): Promise<true | ApplicationError> => {
const checkedNotificationCategory = notificationCategory
? checkedToNotificationCategory(notificationCategory)
: GaloyNotificationCategories.AdminPushNotification

if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const accountId = checkedToAccountUuid(accountIdRaw)
if (accountId instanceof Error) return accountId

Expand All @@ -26,14 +38,14 @@ export const sendAdminPushNotification = async ({
const user = await usersRepo.findById(kratosUserId)
if (user instanceof Error) return user

const success = await NotificationsService().adminPushNotificationSend({
const success = await NotificationsService().adminPushNotificationFilteredSend({
deviceTokens: user.deviceTokens,
title,
body,
data,
notificationCategory: checkedNotificationCategory,
notificationSettings: account.notificationSettings,
})

if (success instanceof Error) return success

return success
}
1 change: 1 addition & 0 deletions src/app/payments/send-intraledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ const executePaymentViaIntraledger = async <
recipientDeviceTokens: recipientUser.deviceTokens,
recipientLanguage: recipientUser.language,
paymentAmount: { amount, currency: recipientWallet.currency },
recipientNotificationSettings: recipientAccount.notificationSettings,
displayPaymentAmount: recipientDisplayAmount,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/payments/send-lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ const executePaymentViaIntraledger = async <
displayPaymentAmount: recipientDisplayAmount,
paymentHash,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/add-pending-on-chain-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const addPendingTransaction = async ({
displayPaymentAmount: settlementDisplayAmount,
txHash: txId,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: account.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/add-settled-on-chain-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ const addSettledTransactionBeforeFinally = async ({
displayPaymentAmount: displayAmount,
txHash,
recipientDeviceTokens: user.deviceTokens,
recipientNotificationSettings: account.notificationSettings,
recipientLanguage: user.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/send-on-chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ const executePaymentViaIntraledger = async <
paymentAmount: { amount, currency: recipientWalletCurrency },
displayPaymentAmount: recipientDisplayAmount,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/settle-payout-txn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const settlePayout = async (
displayPaymentAmount,
txHash,
senderDeviceTokens: user.deviceTokens,
senderNotificationSettings: account.notificationSettings,
senderLanguage: user.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/update-pending-invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ const updatePendingInvoiceBeforeFinally = async ({
displayPaymentAmount,
paymentHash,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/domain/accounts/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type Account = {
readonly isEditor: boolean
readonly quizQuestions: UserQuizQuestion[] // deprecated
readonly quiz: Quiz[]
notificationSettings: NotificationSettings
kratosUserId: UserId
displayCurrency: DisplayCurrency
// temp
Expand Down
2 changes: 2 additions & 0 deletions src/domain/notifications/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export class NotificationsServiceUnreachableServerError extends NotificationsSer
export class UnknownNotificationsServiceError extends NotificationsError {
level = ErrorLevel.Critical
}

export class InvalidPushNotificationSettingError extends NotificationsError {}
Loading

0 comments on commit 79bf33b

Please sign in to comment.