From 5de03ce9635b6b25cdb6445a95574f724946b2e7 Mon Sep 17 00:00:00 2001 From: Matthew Callens Date: Fri, 30 Jun 2023 16:49:12 -0400 Subject: [PATCH] add more user centric mutations to gql (#4258) --- .../src/routes/graphql/clients/hasura.ts | 60 +++++++++++++++++++ .../backpack-api/src/routes/graphql/index.ts | 44 +++++--------- .../graphql/resolvers/mutation/index.ts | 1 + .../routes/graphql/resolvers/mutation/user.ts | 26 ++++++++ .../graphql/resolvers/mutation/wallets.ts | 24 ++++++++ .../backpack-api/src/routes/graphql/types.ts | 28 +++++++++ .../native/backpack-api/src/schema.graphql | 10 ++++ .../data-components/src/apollo/graphql.ts | 16 +++++ 8 files changed, 181 insertions(+), 28 deletions(-) create mode 100644 backend/native/backpack-api/src/routes/graphql/resolvers/mutation/user.ts diff --git a/backend/native/backpack-api/src/routes/graphql/clients/hasura.ts b/backend/native/backpack-api/src/routes/graphql/clients/hasura.ts index 067019d2e5..9e6062dd27 100644 --- a/backend/native/backpack-api/src/routes/graphql/clients/hasura.ts +++ b/backend/native/backpack-api/src/routes/graphql/clients/hasura.ts @@ -470,6 +470,37 @@ export class Hasura { return createConnection(nodes, false, false); } + /** + * Delete a public key table entry for the user matching the arguments. + * @param {string} userId + * @param {ProviderId} provider + * @param {string} address + * @returns {Promise} + * @memberof Hasura + */ + async removeUserPublicKey( + userId: string, + provider: ProviderId, + address: string + ): Promise { + const resp = await this.#chain("mutation")( + { + delete_auth_public_keys: [ + { + where: { + user_id: { _eq: userId }, + blockchain: { _eq: provider.toLowerCase() }, + public_key: { _eq: address }, + }, + }, + { affected_rows: true }, + ], + }, + { operationName: "RemoveUserPublicKey" } + ); + return resp.delete_auth_public_keys?.affected_rows ?? 0; + } + /** * Updates the notification cursor for the argued user if applicable. * @param {string} userId @@ -541,4 +572,33 @@ export class Hasura { ); return resp.update_auth_notifications?.affected_rows; } + + /** + * Update the argued user's avatar to the provider ID + * and NFT address combination provided in the arguments. + * @param {string} userId + * @param {ProviderId} providerId + * @param {string} nft + * @returns {Promise} + * @memberof Hasura + */ + async updateUserAvatar( + userId: string, + providerId: ProviderId, + nft: string + ): Promise { + const response = await this.#chain("mutation")( + { + update_auth_users: [ + { + _set: { avatar_nft: `${providerId.toLowerCase()}/${nft}` }, + where: { id: { _eq: userId } }, + }, + { affected_rows: true }, + ], + }, + { operationName: "UpdateUserAvatar" } + ); + return response.update_auth_users?.affected_rows ?? 0; + } } diff --git a/backend/native/backpack-api/src/routes/graphql/index.ts b/backend/native/backpack-api/src/routes/graphql/index.ts index 7d69ddcc27..1a348cc76c 100644 --- a/backend/native/backpack-api/src/routes/graphql/index.ts +++ b/backend/native/backpack-api/src/routes/graphql/index.ts @@ -4,21 +4,7 @@ import { applyMiddleware } from "graphql-middleware"; import { allow, shield } from "graphql-shield"; import { join } from "path"; -import { - authenticateMutationResolver, - deauthenticateMutationResolver, - friendshipTypeResolvers, - importPublicKeyMutationResolver, - jsonObjectScalar, - markNotificationsAsReadMutationResolver, - notificationTypeResolvers, - sendFriendRequestMutationResolver, - tokenListQueryResolver, - userQueryResolver, - userTypeResolvers, - walletQueryResolver, - walletTypeResolvers, -} from "./resolvers"; +import * as handlers from "./resolvers"; import { authorized } from "./rules"; import type { MutationResolvers, QueryResolvers, Resolvers } from "./types"; @@ -26,20 +12,22 @@ import type { MutationResolvers, QueryResolvers, Resolvers } from "./types"; * Root `Mutation` object resolver. */ const mutationResolvers: MutationResolvers = { - authenticate: authenticateMutationResolver, - deauthenticate: deauthenticateMutationResolver, - importPublicKey: importPublicKeyMutationResolver, - markNotificationsAsRead: markNotificationsAsReadMutationResolver, - sendFriendRequest: sendFriendRequestMutationResolver, + authenticate: handlers.authenticateMutationResolver, + deauthenticate: handlers.deauthenticateMutationResolver, + importPublicKey: handlers.importPublicKeyMutationResolver, + markNotificationsAsRead: handlers.markNotificationsAsReadMutationResolver, + removePublicKey: handlers.removePublicKeyMutationResolver, + sendFriendRequest: handlers.sendFriendRequestMutationResolver, + setAvatar: handlers.setAvatarMutationResolver, }; /** * Root `Query` object resolver. */ const queryResolvers: QueryResolvers = { - tokenList: tokenListQueryResolver, - user: userQueryResolver, - wallet: walletQueryResolver, + tokenList: handlers.tokenListQueryResolver, + user: handlers.userQueryResolver, + wallet: handlers.walletQueryResolver, }; /** @@ -48,11 +36,11 @@ const queryResolvers: QueryResolvers = { const resolvers: Resolvers = { Query: queryResolvers, Mutation: mutationResolvers, - Friendship: friendshipTypeResolvers, - Notification: notificationTypeResolvers, - User: userTypeResolvers, - Wallet: walletTypeResolvers, - JSONObject: jsonObjectScalar, + Friendship: handlers.friendshipTypeResolvers, + Notification: handlers.notificationTypeResolvers, + User: handlers.userTypeResolvers, + Wallet: handlers.walletTypeResolvers, + JSONObject: handlers.jsonObjectScalar, }; /** diff --git a/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/index.ts b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/index.ts index ac20ca9d1d..72b906d1fb 100644 --- a/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/index.ts +++ b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/index.ts @@ -1,4 +1,5 @@ export * from "./authentication"; export * from "./friendship"; export * from "./notifications"; +export * from "./user"; export * from "./wallets"; diff --git a/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/user.ts b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/user.ts new file mode 100644 index 0000000000..ec8b7d6d86 --- /dev/null +++ b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/user.ts @@ -0,0 +1,26 @@ +import type { GraphQLResolveInfo } from "graphql"; + +import type { ApiContext } from "../../context"; +import type { MutationResolvers, MutationSetAvatarArgs } from "../../types"; + +/** + * Handler for the mutation to import a new public key to a user account. + * @param {{}} _parent + * @param {MutationSetAvatarArgs} args + * @param {ApiContext} ctx + * @param {GraphQLResolveInfo} _info + * @returns {Promise} + */ +export const setAvatarMutationResolver: MutationResolvers["setAvatar"] = async ( + _parent: {}, + args: MutationSetAvatarArgs, + ctx: ApiContext, + _info: GraphQLResolveInfo +): Promise => { + const affectedRows = await ctx.dataSources.hasura.updateUserAvatar( + ctx.authorization.userId!, + args.providerId, + args.nft + ); + return affectedRows > 0; +}; diff --git a/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/wallets.ts b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/wallets.ts index 9af89b8b42..ce6d2ec15b 100644 --- a/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/wallets.ts +++ b/backend/native/backpack-api/src/routes/graphql/resolvers/mutation/wallets.ts @@ -10,6 +10,7 @@ import { CreatePublicKeys } from "../../../../validation/user"; import type { ApiContext } from "../../context"; import type { MutationImportPublicKeyArgs, + MutationRemovePublicKeyArgs, MutationResolvers, } from "../../types"; @@ -86,3 +87,26 @@ export const importPublicKeyMutationResolver: MutationResolvers["importPublicKey return resp.isPrimary; }; + +/** + * Handler for the mutation to remove a user public key from the database. + * @param {{}} _parent + * @param {MutationRemovePublicKeyArgs} args + * @param {ApiContext} ctx + * @param {GraphQLResolveInfo} _info + * @returns {Promise} + */ +export const removePublicKeyMutationResolver: MutationResolvers["removePublicKey"] = + async ( + _parent: {}, + args: MutationRemovePublicKeyArgs, + ctx: ApiContext, + _info: GraphQLResolveInfo + ): Promise => { + const affectedRows = await ctx.dataSources.hasura.removeUserPublicKey( + ctx.authorization.userId!, + args.providerId, + args.address + ); + return affectedRows > 0; + }; diff --git a/backend/native/backpack-api/src/routes/graphql/types.ts b/backend/native/backpack-api/src/routes/graphql/types.ts index 35d43cd264..2460ab9a39 100644 --- a/backend/native/backpack-api/src/routes/graphql/types.ts +++ b/backend/native/backpack-api/src/routes/graphql/types.ts @@ -177,8 +177,12 @@ export type Mutation = { importPublicKey?: Maybe; /** Set the `viewed` status of the argued notification IDs are `true`. */ markNotificationsAsRead: Scalars["Int"]; + /** Deletes a public key registered to the active user account. */ + removePublicKey: Scalars["Boolean"]; /** Allows users to send friend requests to another remote user. */ sendFriendRequest?: Maybe; + /** Set a user's avatar to a new image. */ + setAvatar: Scalars["Boolean"]; }; /** Root level mutation type. */ @@ -201,12 +205,24 @@ export type MutationMarkNotificationsAsReadArgs = { ids: Array; }; +/** Root level mutation type. */ +export type MutationRemovePublicKeyArgs = { + address: Scalars["String"]; + providerId: ProviderId; +}; + /** Root level mutation type. */ export type MutationSendFriendRequestArgs = { accept: Scalars["Boolean"]; otherUserId: Scalars["String"]; }; +/** Root level mutation type. */ +export type MutationSetAvatarArgs = { + nft: Scalars["String"]; + providerId: ProviderId; +}; + /** Generic NFT object type definition to provide on-chain and off-chain metadata. */ export type Nft = Node & { __typename?: "Nft"; @@ -1040,12 +1056,24 @@ export type MutationResolvers< ContextType, RequireFields >; + removePublicKey?: Resolver< + ResolversTypes["Boolean"], + ParentType, + ContextType, + RequireFields + >; sendFriendRequest?: Resolver< Maybe, ParentType, ContextType, RequireFields >; + setAvatar?: Resolver< + ResolversTypes["Boolean"], + ParentType, + ContextType, + RequireFields + >; }>; export type NftResolvers< diff --git a/backend/native/backpack-api/src/schema.graphql b/backend/native/backpack-api/src/schema.graphql index f7f4c122d5..6ce2ec7997 100644 --- a/backend/native/backpack-api/src/schema.graphql +++ b/backend/native/backpack-api/src/schema.graphql @@ -50,10 +50,20 @@ type Mutation { """ markNotificationsAsRead(ids: [Int!]!): Int! + """ + Deletes a public key registered to the active user account. + """ + removePublicKey(providerId: ProviderID!, address: String!): Boolean! + """ Allows users to send friend requests to another remote user. """ sendFriendRequest(otherUserId: String!, accept: Boolean!): Boolean + + """ + Set a user's avatar to a new image. + """ + setAvatar(providerId: ProviderID!, nft: String!): Boolean! } """ diff --git a/packages/data-components/src/apollo/graphql.ts b/packages/data-components/src/apollo/graphql.ts index 49f170a92d..898d9bf859 100644 --- a/packages/data-components/src/apollo/graphql.ts +++ b/packages/data-components/src/apollo/graphql.ts @@ -171,8 +171,12 @@ export type Mutation = { importPublicKey?: Maybe; /** Set the `viewed` status of the argued notification IDs are `true`. */ markNotificationsAsRead: Scalars["Int"]; + /** Deletes a public key registered to the active user account. */ + removePublicKey: Scalars["Boolean"]; /** Allows users to send friend requests to another remote user. */ sendFriendRequest?: Maybe; + /** Set a user's avatar to a new image. */ + setAvatar: Scalars["Boolean"]; }; /** Root level mutation type. */ @@ -195,12 +199,24 @@ export type MutationMarkNotificationsAsReadArgs = { ids: Array; }; +/** Root level mutation type. */ +export type MutationRemovePublicKeyArgs = { + address: Scalars["String"]; + providerId: ProviderId; +}; + /** Root level mutation type. */ export type MutationSendFriendRequestArgs = { accept: Scalars["Boolean"]; otherUserId: Scalars["String"]; }; +/** Root level mutation type. */ +export type MutationSetAvatarArgs = { + nft: Scalars["String"]; + providerId: ProviderId; +}; + /** Generic NFT object type definition to provide on-chain and off-chain metadata. */ export type Nft = Node & { __typename?: "Nft";