From 7c1d2bd5d82bc090b8adbecfcbe1d1c94cfbb0ac Mon Sep 17 00:00:00 2001 From: Sam Peters Date: Wed, 13 Sep 2023 12:48:02 -0500 Subject: [PATCH] chore: initial set notifications settings --- bats | 0 dev/apollo-federation/supergraph.graphql | 29 ++++++++++++ src/domain/primitives/index.types.d.ts | 2 + src/graphql/public/mutations.ts | 3 ++ ...count-update-push-notification-settings.ts | 41 +++++++++++++++++ src/graphql/public/schema.graphql | 21 +++++++++ ...count-update-push-notification-settings.ts | 18 ++++++++ .../scalar/push-notification-sub-type.ts | 30 +++++++++++++ .../types/scalar/push-notification-type.ts | 28 ++++++++++++ ...ount-update-push-notification-settings.gql | 10 +++++ test/bats/push-notification-settings.bats | 44 +++++++++++++++++++ 11 files changed, 226 insertions(+) create mode 100644 bats create mode 100644 src/graphql/public/root/mutation/account-update-push-notification-settings.ts create mode 100644 src/graphql/public/types/payload/account-update-push-notification-settings.ts create mode 100644 src/graphql/shared/types/scalar/push-notification-sub-type.ts create mode 100644 src/graphql/shared/types/scalar/push-notification-type.ts create mode 100644 test/bats/gql/account-update-push-notification-settings.gql create mode 100644 test/bats/push-notification-settings.bats diff --git a/bats b/bats new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 185834839e2..9b8a5a54ef8 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -120,6 +120,20 @@ type AccountUpdateDisplayCurrencyPayload errors: [Error!]! } +input AccountUpdatePushNotificationSettingsInput + @join__type(graph: PUBLIC) +{ + notificationSettings: [PushNotifcationSettingsInput]! + notificationsEnabled: Boolean! +} + +type AccountUpdatePushNotificationSettingsPayload + @join__type(graph: PUBLIC) +{ + account: ConsumerAccount + errors: [Error!]! +} + """An Opaque Bearer token""" scalar AuthToken @join__type(graph: PUBLIC) @@ -818,6 +832,7 @@ type Mutation accountDelete: AccountDeletePayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + accountUpdatePushNotificationSettings(input: AccountUpdatePushNotificationSettingsInput!): AccountUpdatePushNotificationSettingsPayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! captchaCreateChallenge: CaptchaCreateChallengePayload! @@ -1220,6 +1235,20 @@ type PublicWallet walletCurrency: WalletCurrency! } +input PushNotifcationSettingsInput + @join__type(graph: PUBLIC) +{ + disabledSubtypes: [PushNotificationSubType]! + enabled: Boolean! + type: PushNotificationType! +} + +scalar PushNotificationSubType + @join__type(graph: PUBLIC) + +scalar PushNotificationType + @join__type(graph: PUBLIC) + type Query @join__type(graph: PUBLIC) { diff --git a/src/domain/primitives/index.types.d.ts b/src/domain/primitives/index.types.d.ts index dc599ebed38..f6322b85752 100644 --- a/src/domain/primitives/index.types.d.ts +++ b/src/domain/primitives/index.types.d.ts @@ -11,6 +11,8 @@ type MilliSeconds = number & { readonly brand: unique symbol } type Days = number & { readonly brand: unique symbol } type JwtToken = string & { readonly brand: unique symbol } // short lived asymmetric token from oathkeeper type Memo = string & { readonly brand: unique symbol } +type PushNotificationType = string & { readonly brand: unique symbol } +type PushNotificationSubType = string & { readonly brand: unique symbol } type XOR = | (T1 & { [k in Exclude]?: never }) diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index f3655b8e4c9..9188aea5931 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -49,6 +49,7 @@ import UserTotpDeleteMutation from "@graphql/public/root/mutation/user-totp-dele import CallbackEndpointAdd from "./root/mutation/callback-endpoint-add" import CallbackEndpointDelete from "./root/mutation/callback-endpoint-delete" +import AccountUpdatePushNotificationSettingsMutation from "./root/mutation/account-update-push-notification-settings" // TODO: // const fields: { [key: string]: GraphQLFieldConfig } export const mutationFields = { @@ -86,6 +87,8 @@ export const mutationFields = { userContactUpdateAlias: UserContactUpdateAliasMutation, accountUpdateDefaultWalletId: AccountUpdateDefaultWalletIdMutation, accountUpdateDisplayCurrency: AccountUpdateDisplayCurrencyMutation, + accountUpdatePushNotificationSettings: + AccountUpdatePushNotificationSettingsMutation, accountDelete: AccountDeleteMutation, feedbackSubmit: FeedbackSubmitMutation, diff --git a/src/graphql/public/root/mutation/account-update-push-notification-settings.ts b/src/graphql/public/root/mutation/account-update-push-notification-settings.ts new file mode 100644 index 00000000000..8323f646df5 --- /dev/null +++ b/src/graphql/public/root/mutation/account-update-push-notification-settings.ts @@ -0,0 +1,41 @@ +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" + +const PushNotifcationSettingsInput = GT.Input({ + name: "PushNotifcationSettingsInput", + fields: () => ({ + type: { type: GT.NonNull(PushNotificationType) }, + enabled: { type: GT.NonNull(GT.Boolean) }, + disabledSubtypes: { type: GT.NonNull(GT.List(PushNotificationSubType)) }, + }), +}) + +const AccountUpdatePushNotificationSettingsInput = GT.Input({ + name: "AccountUpdatePushNotificationSettingsInput", + fields: () => ({ + notificationsEnabled: { type: GT.NonNull(GT.Boolean) }, + notificationSettings: { + type: GT.NonNull(GT.List(PushNotifcationSettingsInput)), + }, + }), +}) + +const AccountUpdatePushNotificationSettingsMutation = GT.Field({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(AccountUpdatePushNotificationSettingsPayload), + args: { + input: { type: GT.NonNull(AccountUpdatePushNotificationSettingsInput) }, + }, + resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { + return { + errors: [], + } + }, +}) + +export default AccountUpdatePushNotificationSettingsMutation diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index d343f462cb9..6b7d1412047 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -79,6 +79,16 @@ type AccountUpdateDisplayCurrencyPayload { errors: [Error!]! } +input AccountUpdatePushNotificationSettingsInput { + notificationSettings: [PushNotifcationSettingsInput]! + notificationsEnabled: Boolean! +} + +type AccountUpdatePushNotificationSettingsPayload { + account: ConsumerAccount + errors: [Error!]! +} + """An Opaque Bearer token""" scalar AuthToken @@ -622,6 +632,7 @@ type Mutation { accountDelete: AccountDeletePayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! + accountUpdatePushNotificationSettings(input: AccountUpdatePushNotificationSettingsInput!): AccountUpdatePushNotificationSettingsPayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! captchaCreateChallenge: CaptchaCreateChallengePayload! @@ -959,6 +970,16 @@ type PublicWallet { walletCurrency: WalletCurrency! } +input PushNotifcationSettingsInput { + disabledSubtypes: [PushNotificationSubType]! + enabled: Boolean! + type: PushNotificationType! +} + +scalar PushNotificationSubType + +scalar PushNotificationType + type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") diff --git a/src/graphql/public/types/payload/account-update-push-notification-settings.ts b/src/graphql/public/types/payload/account-update-push-notification-settings.ts new file mode 100644 index 00000000000..61bf2a84df9 --- /dev/null +++ b/src/graphql/public/types/payload/account-update-push-notification-settings.ts @@ -0,0 +1,18 @@ +import { GT } from "@graphql/index" + +import IError from "../../../shared/types/abstract/error" +import ConsumerAccount from "../object/consumer-account" + +const AccountUpdatePushNotificationSettingsPayload = GT.Object({ + name: "AccountUpdatePushNotificationSettingsPayload", + fields: () => ({ + errors: { + type: GT.NonNullList(IError), + }, + account: { + type: ConsumerAccount, + }, + }), +}) + +export default AccountUpdatePushNotificationSettingsPayload diff --git a/src/graphql/shared/types/scalar/push-notification-sub-type.ts b/src/graphql/shared/types/scalar/push-notification-sub-type.ts new file mode 100644 index 00000000000..c695b5c36e1 --- /dev/null +++ b/src/graphql/shared/types/scalar/push-notification-sub-type.ts @@ -0,0 +1,30 @@ +import { InputValidationError } from "@graphql/error" +import { GT } from "@graphql/index" + +const PushNotificationSubType = GT.Scalar({ + name: "PushNotificationSubType", + parseValue(value) { + if (typeof value !== "string") { + return new InputValidationError({ + message: "Invalid type for PushNotificationSubType", + }) + } + return validPushNotificationSubType(value) + }, + parseLiteral(ast) { + if (ast.kind === GT.Kind.STRING) { + return validPushNotificationSubType(ast.value) + } + return new InputValidationError({ + message: "Invalid type for PushNotificationSubType", + }) + }, +}) + +function validPushNotificationSubType( + value: string, +): PushNotificationSubType | InputValidationError { + return value as PushNotificationSubType +} + +export default PushNotificationSubType diff --git a/src/graphql/shared/types/scalar/push-notification-type.ts b/src/graphql/shared/types/scalar/push-notification-type.ts new file mode 100644 index 00000000000..4731c2f84ae --- /dev/null +++ b/src/graphql/shared/types/scalar/push-notification-type.ts @@ -0,0 +1,28 @@ +import { InputValidationError } from "@graphql/error" +import { GT } from "@graphql/index" + +const PushNotificationType = GT.Scalar({ + name: "PushNotificationType", + parseValue(value) { + if (typeof value !== "string") { + return new InputValidationError({ + message: "Invalid type for PushNotificationType", + }) + } + return validPushNotificationType(value) + }, + parseLiteral(ast) { + if (ast.kind === GT.Kind.STRING) { + return validPushNotificationType(ast.value) + } + return new InputValidationError({ message: "Invalid type for PushNotificationType" }) + }, +}) + +function validPushNotificationType( + value: string, +): PushNotificationType | InputValidationError { + return value as PushNotificationType +} + +export default PushNotificationType diff --git a/test/bats/gql/account-update-push-notification-settings.gql b/test/bats/gql/account-update-push-notification-settings.gql new file mode 100644 index 00000000000..8aa9d826d3a --- /dev/null +++ b/test/bats/gql/account-update-push-notification-settings.gql @@ -0,0 +1,10 @@ +mutation accountUpdatePushNotificationSettings($input: AccountUpdatePushNotificationSettingsInput!) { + accountUpdatePushNotificationSettings(input: $input) { + errors { + message + } + account { + defaultWalletId + } + } +} diff --git a/test/bats/push-notification-settings.bats b/test/bats/push-notification-settings.bats new file mode 100644 index 00000000000..93d37d043c1 --- /dev/null +++ b/test/bats/push-notification-settings.bats @@ -0,0 +1,44 @@ +#!/usr/bin/env bats + +load "helpers/setup-and-teardown" + +setup_file() { + clear_cache + reset_redis + + bitcoind_init + start_trigger + start_server + + initialize_user_from_onchain "$ALICE_TOKEN_NAME" "$ALICE_PHONE" "$CODE" +} + +teardown_file() { + stop_trigger + stop_server +} + + +@test "push-notification-settings: set and get" { + token_name="$ALICE_TOKEN_NAME" + + # notification_setting="{type: \"Circles\", enabled: false, disabledSubtypes: []}" + notification_setting=$( + jq -n \ + --arg type "Circles" \ + --argjson enabled false \ + --argjson disabledSubtypes "[]" \ + '{type: $type, enabled: $enabled, disabledSubtypes: $disabledSubtypes}' + ) + + variables=$( + jq -n \ + --argjson notification_setting "$notification_setting" \ + '{input: {notificationsEnabled: true, notificationSettings: [$notification_setting]}}' + ) + + exec_graphql "$token_name" 'account-update-push-notification-settings' "$variables" + + wallet_id="$(graphql_output '.data.accountUpdatePushNotificationSettings.account.defaultWalletId')" + [[ "$wallet_id" == "1232" ]] || exit 1 +}