Skip to content

Commit

Permalink
feat: set push notification settings
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleSamtoshi committed Sep 13, 2023
1 parent 7c1d2bd commit 2c45ce1
Show file tree
Hide file tree
Showing 21 changed files with 267 additions and 17 deletions.
Empty file removed bats
Empty file.
23 changes: 20 additions & 3 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!
pushNotificationSettings: PushNotificationSettings!
realtimePrice: RealtimePrice!
transactions(
"""Returns the items in the list that come after the specified cursor."""
Expand Down Expand Up @@ -123,8 +124,8 @@ type AccountUpdateDisplayCurrencyPayload
input AccountUpdatePushNotificationSettingsInput
@join__type(graph: PUBLIC)
{
notificationSettings: [PushNotifcationSettingsInput]!
notificationsEnabled: Boolean!
enabled: Boolean!
settings: [PushNotificationSettingsInput]!
}

type AccountUpdatePushNotificationSettingsPayload
Expand Down Expand Up @@ -281,6 +282,7 @@ type ConsumerAccount implements Account
id: ID!
level: AccountLevel!
limits: AccountLimits!
pushNotificationSettings: PushNotificationSettings!

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
Expand Down Expand Up @@ -1235,7 +1237,22 @@ type PublicWallet
walletCurrency: WalletCurrency!
}

input PushNotifcationSettingsInput
type PushNotificationSetting
@join__type(graph: PUBLIC)
{
disabledSubtypes: [PushNotificationSubType]!
enabled: Boolean!
type: PushNotificationType!
}

type PushNotificationSettings
@join__type(graph: PUBLIC)
{
enabled: Boolean!
settings: [PushNotificationSetting]!
}

input PushNotificationSettingsInput
@join__type(graph: PUBLIC)
{
disabledSubtypes: [PushNotificationSubType]!
Expand Down
1 change: 1 addition & 0 deletions src/app/accounts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from "./update-display-currency"
export * from "./username-available"
export * from "./delete-business-map-info"
export * from "./upgrade-device-account"
export * from "./update-push-notification-settings"

const accounts = AccountsRepository()

Expand Down
31 changes: 31 additions & 0 deletions src/app/accounts/update-push-notification-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { checkedToPushNotificationSettings } from "@domain/notifications"

import { AccountsRepository } from "@services/mongoose"

export const updatePushNotificationSettings = async ({
accountId,
notificationSettings,
}: {
accountId: AccountId
notificationSettings: {
enabled: boolean
settings: {
type: string
enabled: boolean
disabledSubtypes: string[]
}[]
}
}): Promise<Account | ApplicationError> => {
const checkedPushNotificationSettings =
checkedToPushNotificationSettings(notificationSettings)

if (checkedPushNotificationSettings instanceof Error)
return checkedPushNotificationSettings

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

account.pushNotificationSettings = checkedPushNotificationSettings

return AccountsRepository().update(account)
}
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[]
pushNotificationSettings: PushNotificationSettings // QUESTION: should this be here or should the notification service have its own storage of notification settings?
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 {}
43 changes: 43 additions & 0 deletions src/domain/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { InvalidPushNotificationSettingError } from "./errors"

export * from "./errors"

export const NotificationType = {
Expand All @@ -8,3 +10,44 @@ export const NotificationType = {
OnchainPayment: "onchain_payment",
LnInvoicePaid: "paid-invoice",
} as const

export const checkedToPushNotificationSettings = ({
enabled,
settings,
}: {
enabled: boolean
settings: {
type: string
enabled: boolean
disabledSubtypes: string[]
}[]
}): PushNotificationSettings | InvalidPushNotificationSettingError => {
const checkedSettings = settings.map(checkedToPushNotificationSetting)

const notificationTypes = checkedSettings.map((s) => s.type)
const uniqueTypes = [...new Set(notificationTypes)]
if (notificationTypes.length !== uniqueTypes.length) {
return new InvalidPushNotificationSettingError("Duplicate notification types")
}

return {
enabled,
settings: checkedSettings,
}
}

export const checkedToPushNotificationSetting = ({
type,
enabled,
disabledSubtypes,
}: {
type: string
enabled: boolean
disabledSubtypes: string[]
}): PushNotificationSetting => {
return {
type: type as PushNotificationType,
enabled,
disabledSubtypes: disabledSubtypes as PushNotificationSubType[],
}
}
11 changes: 11 additions & 0 deletions src/domain/notifications/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,14 @@ interface INotificationsService {
args: SendPushNotificationArgs,
): Promise<true | NotificationsServiceError>
}

type PushNotificationSettings = {
enabled: boolean
settings: PushNotificationSetting[]
}

type PushNotificationSetting = {
type: PushNotificationType
enabled: boolean
disabledSubtypes: PushNotificationSubType[]
}
4 changes: 4 additions & 0 deletions src/graphql/error-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ export const mapError = (error: ApplicationError): CustomApolloError => {
message = "Invalid quiz question id was passed."
return new ValidationInternalError({ message, logger: baseLogger })

case "InvalidPushNotificationSettingError":
message = "Invalid push notification setting was passed."
return new ValidationInternalError({ message, logger: baseLogger })

case "RewardAlreadyPresentError":
message = "Reward for quiz question was already claimed."
return new ValidationInternalError({ message, logger: baseLogger })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { GT } from "@graphql/index"
import PushNotificationType from "@graphql/shared/types/scalar/push-notification-type"
import PushNotificationSubType from "@graphql/shared/types/scalar/push-notification-sub-type"
import AccountUpdatePushNotificationSettingsPayload from "@graphql/public/types/payload/account-update-push-notification-settings"
import { Accounts } from "@app/index"
import { mapAndParseErrorForGqlResponse } from "@graphql/error-map"

const PushNotifcationSettingsInput = GT.Input({
name: "PushNotifcationSettingsInput",
const PushNotificationSettingsInput = GT.Input({
name: "PushNotificationSettingsInput",
fields: () => ({
type: { type: GT.NonNull(PushNotificationType) },
enabled: { type: GT.NonNull(GT.Boolean) },
Expand All @@ -16,14 +18,27 @@ const PushNotifcationSettingsInput = GT.Input({
const AccountUpdatePushNotificationSettingsInput = GT.Input({
name: "AccountUpdatePushNotificationSettingsInput",
fields: () => ({
notificationsEnabled: { type: GT.NonNull(GT.Boolean) },
notificationSettings: {
type: GT.NonNull(GT.List(PushNotifcationSettingsInput)),
enabled: { type: GT.NonNull(GT.Boolean) },
settings: {
type: GT.NonNull(GT.List(PushNotificationSettingsInput)),
},
}),
})

const AccountUpdatePushNotificationSettingsMutation = GT.Field({
const AccountUpdatePushNotificationSettingsMutation = GT.Field<
null,
GraphQLPublicContextAuth,
{
input: {
enabled: boolean
settings: {
type: string
enabled: boolean
disabledSubtypes: string[]
}[]
}
}
>({
extensions: {
complexity: 120,
},
Expand All @@ -32,8 +47,18 @@ const AccountUpdatePushNotificationSettingsMutation = GT.Field({
input: { type: GT.NonNull(AccountUpdatePushNotificationSettingsInput) },
},
resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => {
const result = await Accounts.updatePushNotificationSettings({
accountId: domainAccount.id,
notificationSettings: args.input,
})

if (result instanceof Error) {
return { errors: [mapAndParseErrorForGqlResponse(result)] }
}

return {
errors: [],
account: result,
}
},
})
Expand Down
19 changes: 16 additions & 3 deletions src/graphql/public/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface Account {
id: ID!
level: AccountLevel!
limits: AccountLimits!
pushNotificationSettings: PushNotificationSettings!
realtimePrice: RealtimePrice!
transactions(
"""Returns the items in the list that come after the specified cursor."""
Expand Down Expand Up @@ -80,8 +81,8 @@ type AccountUpdateDisplayCurrencyPayload {
}

input AccountUpdatePushNotificationSettingsInput {
notificationSettings: [PushNotifcationSettingsInput]!
notificationsEnabled: Boolean!
enabled: Boolean!
settings: [PushNotificationSettingsInput]!
}

type AccountUpdatePushNotificationSettingsPayload {
Expand Down Expand Up @@ -208,6 +209,7 @@ type ConsumerAccount implements Account {
id: ID!
level: AccountLevel!
limits: AccountLimits!
pushNotificationSettings: PushNotificationSettings!

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
Expand Down Expand Up @@ -970,7 +972,18 @@ type PublicWallet {
walletCurrency: WalletCurrency!
}

input PushNotifcationSettingsInput {
type PushNotificationSetting {
disabledSubtypes: [PushNotificationSubType]!
enabled: Boolean!
type: PushNotificationType!
}

type PushNotificationSettings {
enabled: Boolean!
settings: [PushNotificationSetting]!
}

input PushNotificationSettingsInput {
disabledSubtypes: [PushNotificationSubType]!
enabled: Boolean!
type: PushNotificationType!
Expand Down
4 changes: 4 additions & 0 deletions src/graphql/public/types/abstract/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AccountLevel from "../../../shared/types/scalar/account-level"

import Wallet from "../../../shared/types/abstract/wallet"
import CallbackEndpoint from "../object/callback-endpoint"
import { PushNotificationSettings } from "../object/push-notification-settings"

const IAccount = GT.Interface({
name: "Account",
Expand Down Expand Up @@ -56,6 +57,9 @@ const IAccount = GT.Interface({
},
},
},
pushNotificationSettings: {
type: GT.NonNull(PushNotificationSettings),
},

// FUTURE-PLAN: Support a `users: [User!]!` field here
}),
Expand Down
5 changes: 5 additions & 0 deletions src/graphql/public/types/object/business-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import AccountLevel from "../../../shared/types/scalar/account-level"
import { TransactionConnection } from "../../../shared/types/object/transaction"

import RealtimePrice from "./realtime-price"
import { PushNotificationSettings } from "./push-notification-settings"

const BusinessAccount = GT.Object({
name: "BusinessAccount",
Expand Down Expand Up @@ -159,6 +160,10 @@ const BusinessAccount = GT.Object({
)
},
},
pushNotificationSettings: {
type: GT.NonNull(PushNotificationSettings),
resolve: (source) => source.pushNotificationSettings,
},
}),
})

Expand Down
6 changes: 6 additions & 0 deletions src/graphql/public/types/object/consumer-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TransactionConnection } from "../../../shared/types/object/transaction"
import AccountLimits from "./account-limits"
import Quiz from "./quiz"
import CallbackEndpoint from "./callback-endpoint"
import { PushNotificationSettings } from "./push-notification-settings"

const ConsumerAccount = GT.Object<Account, GraphQLPublicContextAuth>({
name: "ConsumerAccount",
Expand Down Expand Up @@ -182,6 +183,11 @@ const ConsumerAccount = GT.Object<Account, GraphQLPublicContextAuth>({
)
},
},

pushNotificationSettings: {
type: GT.NonNull(PushNotificationSettings),
resolve: (source) => source.pushNotificationSettings,
},
}),
})

Expand Down
42 changes: 42 additions & 0 deletions src/graphql/public/types/object/push-notification-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { GT } from "@graphql/index"
import PushNotificationSubType from "@graphql/shared/types/scalar/push-notification-sub-type"
import PushNotificationType from "@graphql/shared/types/scalar/push-notification-type"

export const PushNotificationSettings = GT.Object<
PushNotificationSettings,
GraphQLPublicContextAuth
>({
name: "PushNotificationSettings",
fields: () => ({
enabled: {
type: GT.NonNull(GT.Boolean),
resolve: (source) => source.enabled,
},
settings: {
type: GT.NonNull(GT.List(PushNotificationSetting)),
resolve: (source) => source.settings,
},
}),
})

export const PushNotificationSetting = GT.Object<
PushNotificationSetting,
GraphQLPublicContextAuth
>({
name: "PushNotificationSetting",
fields: () => ({
type: {
type: GT.NonNull(PushNotificationType),

resolve: (source) => source.type,
},
enabled: {
type: GT.NonNull(GT.Boolean),
resolve: (source) => source.enabled,
},
disabledSubtypes: {
type: GT.NonNull(GT.List(PushNotificationSubType)),
resolve: (source) => source.disabledSubtypes,
},
}),
})
Loading

0 comments on commit 2c45ce1

Please sign in to comment.