From 191a5fa52302b5f3c33a9eab326428cfc5aa38ed Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 06:35:00 -0400 Subject: [PATCH 1/8] create password union file --- api-js/src/user/objects/index.js | 1 + .../src/user/objects/reset-password-error.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 api-js/src/user/objects/reset-password-error.js diff --git a/api-js/src/user/objects/index.js b/api-js/src/user/objects/index.js index fb1f3b3688..dce1e7ef13 100644 --- a/api-js/src/user/objects/index.js +++ b/api-js/src/user/objects/index.js @@ -1,4 +1,5 @@ export * from './auth-result' +export * from './reset-password-error' export * from './sign-in-error' export * from './tfa-sign-in-result' export * from './user-personal' diff --git a/api-js/src/user/objects/reset-password-error.js b/api-js/src/user/objects/reset-password-error.js new file mode 100644 index 0000000000..120c75c2d0 --- /dev/null +++ b/api-js/src/user/objects/reset-password-error.js @@ -0,0 +1,18 @@ +import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql' + +export const resetPasswordError = new GraphQLObjectType({ + name: 'ResetPasswordError', + description: '', + fields: () => ({ + code: { + type: GraphQLInt, + description: '', + resolve: ({ code }) => code, + }, + description: { + type: GraphQLString, + description: '', + resolve: ({ description }) => description, + }, + }), +}) From 438a7bc90714ab17d305899210bb91debf65e2f1 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 06:47:47 -0400 Subject: [PATCH 2/8] progress --- api-js/src/user/mutations/reset-password.js | 14 +++++++------- api-js/src/user/objects/index.js | 1 + api-js/src/user/objects/reset-password-result.js | 14 ++++++++++++++ api-js/src/user/unions/index.js | 1 + api-js/src/user/unions/reset-password-union.js | 16 ++++++++++++++++ 5 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 api-js/src/user/objects/reset-password-result.js create mode 100644 api-js/src/user/unions/reset-password-union.js diff --git a/api-js/src/user/mutations/reset-password.js b/api-js/src/user/mutations/reset-password.js index cc4e547807..3925331850 100644 --- a/api-js/src/user/mutations/reset-password.js +++ b/api-js/src/user/mutations/reset-password.js @@ -2,6 +2,8 @@ import { GraphQLNonNull, GraphQLString } from 'graphql' import { mutationWithClientMutationId } from 'graphql-relay' import { t } from '@lingui/macro' +import { resetPasswordUnion } from '../unions' + export const resetPassword = new mutationWithClientMutationId({ name: 'ResetPassword', description: @@ -22,13 +24,10 @@ export const resetPassword = new mutationWithClientMutationId({ }, }), outputFields: () => ({ - status: { - type: GraphQLString, - description: - 'Informs the user if the password reset was successful, and to redirect to sign in page.', - resolve: async (payload) => { - return payload.status - }, + result: { + type: resetPasswordUnion, + description: '', + resetPassword: (payload) => payload, }, }), mutateAndGetPayload: async ( @@ -114,6 +113,7 @@ export const resetPassword = new mutationWithClientMutationId({ console.info(`User: ${user._key} successfully reset their password.`) return { + _type: 'regular', status: i18n._(t`Password was successfully reset.`), } }, diff --git a/api-js/src/user/objects/index.js b/api-js/src/user/objects/index.js index dce1e7ef13..298ffd09ac 100644 --- a/api-js/src/user/objects/index.js +++ b/api-js/src/user/objects/index.js @@ -1,5 +1,6 @@ export * from './auth-result' export * from './reset-password-error' +export * from './reset-password-result' export * from './sign-in-error' export * from './tfa-sign-in-result' export * from './user-personal' diff --git a/api-js/src/user/objects/reset-password-result.js b/api-js/src/user/objects/reset-password-result.js new file mode 100644 index 0000000000..2ac756fbf8 --- /dev/null +++ b/api-js/src/user/objects/reset-password-result.js @@ -0,0 +1,14 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql' + +export const resetPasswordResult = new GraphQLObjectType({ + name: 'ResetPasswordResult', + description: '', + fields: () => ({ + status: { + type: GraphQLString, + description: + 'Informs the user if the password reset was successful, and to redirect to sign in page.', + resolve: (payload) => payload.status, + }, + }), +}) diff --git a/api-js/src/user/unions/index.js b/api-js/src/user/unions/index.js index 0eb396c567..41def62a31 100644 --- a/api-js/src/user/unions/index.js +++ b/api-js/src/user/unions/index.js @@ -1 +1,2 @@ +export * from './reset-password-union' export * from './sign-in-union' diff --git a/api-js/src/user/unions/reset-password-union.js b/api-js/src/user/unions/reset-password-union.js new file mode 100644 index 0000000000..3c1d343e81 --- /dev/null +++ b/api-js/src/user/unions/reset-password-union.js @@ -0,0 +1,16 @@ +import { GraphQLUnionType } from 'graphql' +import { resetPasswordError, resetPasswordResult } from '../objects' + +export const resetPasswordUnion = new GraphQLUnionType({ + name: 'ResetPasswordUnion', + description: + 'This union is used with the `resetPassword` mutation, allowing for users to reset their password, and support any errors that may occur', + types: [resetPasswordError, resetPasswordResult], + resolveType({ _type }) { + if (_type === 'regular') { + return resetPasswordResult + } else { + return resetPasswordError + } + }, +}) From 0f1a0d97c40b39c4aae0a4adc33e80026099d36d Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:33:11 -0400 Subject: [PATCH 3/8] Finish up reset password objects --- .../__tests__/reset-password-error.test.js | 39 +++++++++++++++++++ .../__tests__/reset-password-result.test.js | 24 ++++++++++++ .../src/user/objects/reset-password-error.js | 9 +++-- .../src/user/objects/reset-password-result.js | 5 ++- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 api-js/src/user/objects/__tests__/reset-password-error.test.js create mode 100644 api-js/src/user/objects/__tests__/reset-password-result.test.js diff --git a/api-js/src/user/objects/__tests__/reset-password-error.test.js b/api-js/src/user/objects/__tests__/reset-password-error.test.js new file mode 100644 index 0000000000..4e779680cf --- /dev/null +++ b/api-js/src/user/objects/__tests__/reset-password-error.test.js @@ -0,0 +1,39 @@ +import { GraphQLInt, GraphQLString } from 'graphql' + +import { resetPasswordErrorType } from '../reset-password-error' + +describe('given the resetPasswordErrorType object', () => { + describe('testing the field definitions', () => { + it('has an code field', () => { + const demoType = resetPasswordErrorType.getFields() + + expect(demoType).toHaveProperty('code') + expect(demoType.code.type).toMatchObject(GraphQLInt) + }) + it('has a description field', () => { + const demoType = resetPasswordErrorType.getFields() + + expect(demoType).toHaveProperty('description') + expect(demoType.description.type).toMatchObject(GraphQLString) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the code resolver', () => { + it('returns the resolved field', () => { + const demoType = resetPasswordErrorType.getFields() + + expect(demoType.code.resolve({ code: 400 })).toEqual(400) + }) + }) + describe('testing the description field', () => { + it('returns the resolved value', () => { + const demoType = resetPasswordErrorType.getFields() + + expect( + demoType.description.resolve({ description: 'description' }), + ).toEqual('description') + }) + }) + }) +}) diff --git a/api-js/src/user/objects/__tests__/reset-password-result.test.js b/api-js/src/user/objects/__tests__/reset-password-result.test.js new file mode 100644 index 0000000000..351d768a0f --- /dev/null +++ b/api-js/src/user/objects/__tests__/reset-password-result.test.js @@ -0,0 +1,24 @@ +import { GraphQLString } from 'graphql' + +import { resetPasswordResultType } from '../reset-password-result' + +describe('given the resetPasswordErrorType object', () => { + describe('testing the field definitions', () => { + it('has an status field', () => { + const demoType = resetPasswordResultType.getFields() + + expect(demoType).toHaveProperty('status') + expect(demoType.status.type).toMatchObject(GraphQLString) + }) + }) + + describe('testing the field resolvers', () => { + describe('testing the status resolver', () => { + it('returns the resolved field', () => { + const demoType = resetPasswordResultType.getFields() + + expect(demoType.status.resolve({ status: 'status' })).toEqual('status') + }) + }) + }) +}) diff --git a/api-js/src/user/objects/reset-password-error.js b/api-js/src/user/objects/reset-password-error.js index 120c75c2d0..d0353e04ba 100644 --- a/api-js/src/user/objects/reset-password-error.js +++ b/api-js/src/user/objects/reset-password-error.js @@ -1,17 +1,18 @@ import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql' -export const resetPasswordError = new GraphQLObjectType({ +export const resetPasswordErrorType = new GraphQLObjectType({ name: 'ResetPasswordError', - description: '', + description: + 'This object is used to inform the user if any errors occurred while resetting their password.', fields: () => ({ code: { type: GraphQLInt, - description: '', + description: 'Error code to inform user what the issue is related to.', resolve: ({ code }) => code, }, description: { type: GraphQLString, - description: '', + description: 'Description of the issue that was encountered.', resolve: ({ description }) => description, }, }), diff --git a/api-js/src/user/objects/reset-password-result.js b/api-js/src/user/objects/reset-password-result.js index 2ac756fbf8..658ee9f284 100644 --- a/api-js/src/user/objects/reset-password-result.js +++ b/api-js/src/user/objects/reset-password-result.js @@ -1,8 +1,9 @@ import { GraphQLObjectType, GraphQLString } from 'graphql' -export const resetPasswordResult = new GraphQLObjectType({ +export const resetPasswordResultType = new GraphQLObjectType({ name: 'ResetPasswordResult', - description: '', + description: + 'This object is used to inform the user that no errors were encountered while resetting their password.', fields: () => ({ status: { type: GraphQLString, From d33e2495f3ee1781054069ffadd5305364a08890 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:33:23 -0400 Subject: [PATCH 4/8] finish up the reset password union --- .../__tests__/reset-password-union.test.js | 45 +++++++++++++++++++ .../src/user/unions/reset-password-union.js | 10 ++--- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 api-js/src/user/unions/__tests__/reset-password-union.test.js diff --git a/api-js/src/user/unions/__tests__/reset-password-union.test.js b/api-js/src/user/unions/__tests__/reset-password-union.test.js new file mode 100644 index 0000000000..9535275e7f --- /dev/null +++ b/api-js/src/user/unions/__tests__/reset-password-union.test.js @@ -0,0 +1,45 @@ +import { resetPasswordErrorType, resetPasswordResultType } from '../../objects/index' +import { resetPasswordUnion } from '../reset-password-union' + +describe('given the resetPasswordUnion', () => { + describe('testing the field types', () => { + it('contains resetPasswordResultType', () => { + const demoType = resetPasswordUnion.getTypes() + + expect(demoType).toContain(resetPasswordResultType) + }) + it('contains resetPasswordErrorType', () => { + const demoType = resetPasswordUnion.getTypes() + + expect(demoType).toContain(resetPasswordErrorType) + }) + }) + describe('testing the field selection', () => { + describe('testing the resetPasswordResultType', () => { + it('returns the correct type', () => { + const obj = { + _type: 'regular', + authResult: {}, + } + + expect(resetPasswordUnion.resolveType(obj)).toMatchObject( + resetPasswordResultType, + ) + }) + }) + describe('testing the resetPasswordErrorType', () => { + it('returns the correct type', () => { + const obj = { + _type: 'error', + error: 'sign-in-error', + code: 401, + description: 'text', + } + + expect(resetPasswordUnion.resolveType(obj)).toMatchObject( + resetPasswordErrorType, + ) + }) + }) + }) +}) diff --git a/api-js/src/user/unions/reset-password-union.js b/api-js/src/user/unions/reset-password-union.js index 3c1d343e81..ef85cb1e3f 100644 --- a/api-js/src/user/unions/reset-password-union.js +++ b/api-js/src/user/unions/reset-password-union.js @@ -1,16 +1,16 @@ import { GraphQLUnionType } from 'graphql' -import { resetPasswordError, resetPasswordResult } from '../objects' +import { resetPasswordErrorType, resetPasswordResultType } from '../objects' export const resetPasswordUnion = new GraphQLUnionType({ name: 'ResetPasswordUnion', description: - 'This union is used with the `resetPassword` mutation, allowing for users to reset their password, and support any errors that may occur', - types: [resetPasswordError, resetPasswordResult], + 'This union is used with the `ResetPassword` mutation, allowing for users to reset their password, and support any errors that may occur', + types: [resetPasswordErrorType, resetPasswordResultType], resolveType({ _type }) { if (_type === 'regular') { - return resetPasswordResult + return resetPasswordResultType } else { - return resetPasswordError + return resetPasswordErrorType } }, }) From 83a8222df1571d573a4d4ab59ffa7e29a933925d Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:33:35 -0400 Subject: [PATCH 5/8] refactor reset password to use new union type --- .../__tests__/reset-password.test.js | 369 +++++++++++++++--- api-js/src/user/mutations/reset-password.js | 39 +- 2 files changed, 340 insertions(+), 68 deletions(-) diff --git a/api-js/src/user/mutations/__tests__/reset-password.test.js b/api-js/src/user/mutations/__tests__/reset-password.test.js index 87c965fc7c..84605bd905 100644 --- a/api-js/src/user/mutations/__tests__/reset-password.test.js +++ b/api-js/src/user/mutations/__tests__/reset-password.test.js @@ -129,7 +129,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -155,7 +163,9 @@ describe('reset users password', () => { const expectedResponse = { data: { resetPassword: { - status: 'Password was successfully reset.', + result: { + status: 'Password was successfully reset.', + }, }, }, } @@ -248,7 +258,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -299,7 +317,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -322,11 +348,18 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError('Unable to reset password. Please try again.'), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'Unable to reset password. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `When resetting password user attempted to verify account, but userKey is not located in the token parameters.`, ]) @@ -349,7 +382,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -372,11 +413,18 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError('Unable to reset password. Please try again.'), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'Unable to reset password. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `When resetting password user attempted to verify account, but userKey is not located in the token parameters.`, ]) @@ -399,7 +447,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -422,11 +478,18 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError('Unable to reset password. Please try again.'), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'Unable to reset password. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `A user attempted to reset the password for 1, however there is no associated account.`, ]) @@ -459,7 +522,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -482,11 +553,18 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError('Unable to reset password. Please try again.'), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'Unable to reset password. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset password, however the current password does not match the current hashed password in the db.`, ]) @@ -516,7 +594,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -539,11 +625,18 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError('New passwords do not match. Please try again.'), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'New passwords do not match. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset their password, however the submitted passwords do not match.`, ]) @@ -573,7 +666,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -596,13 +697,19 @@ describe('reset users password', () => { }, ) - const error = [ - new GraphQLError( - 'Password is not strong enough. Please try again.', - ), - ] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: + 'Password is not strong enough. Please try again.', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset their password, however the submitted password is not long enough.`, ]) @@ -636,7 +743,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -700,7 +815,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -774,7 +897,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -800,7 +931,9 @@ describe('reset users password', () => { const expectedResponse = { data: { resetPassword: { - status: 'todo', + result: { + status: 'todo', + }, }, }, } @@ -888,7 +1021,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -911,9 +1052,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `When resetting password user attempted to verify account, but userKey is not located in the token parameters.`, ]) @@ -936,7 +1086,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -959,9 +1117,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `When resetting password user attempted to verify account, but userKey is not located in the token parameters.`, ]) @@ -984,7 +1151,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -1007,9 +1182,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `A user attempted to reset the password for 1, however there is no associated account.`, ]) @@ -1042,7 +1226,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -1065,9 +1257,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset password, however the current password does not match the current hashed password in the db.`, ]) @@ -1097,7 +1298,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -1120,9 +1329,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset their password, however the submitted passwords do not match.`, ]) @@ -1152,7 +1370,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -1175,9 +1401,18 @@ describe('reset users password', () => { }, ) - const error = [new GraphQLError('todo')] + const error = { + data: { + resetPassword: { + result: { + code: 400, + description: 'todo', + }, + }, + }, + } - expect(response.errors).toEqual(error) + expect(response).toEqual(error) expect(consoleOutput).toEqual([ `User: ${user._key} attempted to reset their password, however the submitted password is not long enough.`, ]) @@ -1211,7 +1446,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, @@ -1273,7 +1516,15 @@ describe('reset users password', () => { resetToken: "${resetToken}" } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } `, diff --git a/api-js/src/user/mutations/reset-password.js b/api-js/src/user/mutations/reset-password.js index 3925331850..0f4704a997 100644 --- a/api-js/src/user/mutations/reset-password.js +++ b/api-js/src/user/mutations/reset-password.js @@ -26,8 +26,9 @@ export const resetPassword = new mutationWithClientMutationId({ outputFields: () => ({ result: { type: resetPasswordUnion, - description: '', - resetPassword: (payload) => payload, + description: + '`ResetPasswordUnion` returning either a `ResetPasswordResult`, or `ResetPasswordError` object.', + resolve: (payload) => payload, }, }), mutateAndGetPayload: async ( @@ -56,7 +57,11 @@ export const resetPassword = new mutationWithClientMutationId({ console.warn( `When resetting password user attempted to verify account, but userKey is not located in the token parameters.`, ) - throw new Error(i18n._(t`Unable to reset password. Please try again.`)) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to reset password. Please try again.`), + } } // Check if user exists @@ -66,7 +71,11 @@ export const resetPassword = new mutationWithClientMutationId({ console.warn( `A user attempted to reset the password for ${tokenParameters.userKey}, however there is no associated account.`, ) - throw new Error(i18n._(t`Unable to reset password. Please try again.`)) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to reset password. Please try again.`), + } } // Check if password in token matches token in db @@ -74,7 +83,11 @@ export const resetPassword = new mutationWithClientMutationId({ console.warn( `User: ${user._key} attempted to reset password, however the current password does not match the current hashed password in the db.`, ) - throw new Error(i18n._(t`Unable to reset password. Please try again.`)) + return { + _type: 'error', + code: 400, + description: i18n._(t`Unable to reset password. Please try again.`), + } } // Check to see if newly submitted passwords match @@ -82,7 +95,11 @@ export const resetPassword = new mutationWithClientMutationId({ console.warn( `User: ${user._key} attempted to reset their password, however the submitted passwords do not match.`, ) - throw new Error(i18n._(t`New passwords do not match. Please try again.`)) + return { + _type: 'error', + code: 400, + description: i18n._(t`New passwords do not match. Please try again.`), + } } // Check to see if password meets GoC requirements @@ -90,9 +107,13 @@ export const resetPassword = new mutationWithClientMutationId({ console.warn( `User: ${user._key} attempted to reset their password, however the submitted password is not long enough.`, ) - throw new Error( - i18n._(t`Password is not strong enough. Please try again.`), - ) + return { + _type: 'error', + code: 400, + description: i18n._( + t`Password is not strong enough. Please try again.`, + ), + } } // Update users password in db From 8e3fdf2fa6da8c9fea26d10361c7e7bddf32a97d Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:39:18 -0400 Subject: [PATCH 6/8] update faker --- frontend/schema.faker.graphql | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/schema.faker.graphql b/frontend/schema.faker.graphql index c7ea167d86..5afd192cf9 100644 --- a/frontend/schema.faker.graphql +++ b/frontend/schema.faker.graphql @@ -1402,6 +1402,14 @@ type RequestScanPayload { clientMutationId: String } +# This object is used to inform the user if any errors occurred while resetting their password. +type ResetPasswordError { + # Error code to inform user what the issue is related to. + code: Int + # Description of the issue that was encountered. + description: String +} + input ResetPasswordInput { # The users new password. password: String! @@ -1413,11 +1421,21 @@ input ResetPasswordInput { } type ResetPasswordPayload { + # `ResetPasswordUnion` returning either a `ResetPasswordResult`, or `ResetPasswordError` object. + result: ResetPasswordUnion + clientMutationId: String +} + +# This object is used to inform the user that no errors were encountered while resetting their password. +type ResetPasswordResult { # Informs the user if the password reset was successful, and to redirect to sign in page. status: String - clientMutationId: String } +# This union is used with the `ResetPassword` mutation, allowing for users to +# reset their password, and support any errors that may occur +union ResetPasswordUnion = ResetPasswordError | ResetPasswordResult + # An enum used to assign, and test users roles. enum RoleEnums { # A user who has been given access to view an organization. From 2b286cf194c4b0d131733c25e243f498ab188abe Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:39:27 -0400 Subject: [PATCH 7/8] update mutation query string --- frontend/src/graphql/mutations.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 0d2fca387c..6d48623ebd 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -81,7 +81,15 @@ export const RESET_PASSWORD = gql` resetToken: $resetToken } ) { - status + result { + ... on ResetPasswordError { + code + description + } + ... on ResetPasswordResult { + status + } + } } } ` From fdfe9dbdca0f64c52333c402630037d9e0dab598 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Thu, 4 Mar 2021 07:39:43 -0400 Subject: [PATCH 8/8] update reset page functionality to handle error returns --- frontend/src/ResetPasswordPage.js | 41 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/frontend/src/ResetPasswordPage.js b/frontend/src/ResetPasswordPage.js index 396c2656a2..1e3db604cc 100644 --- a/frontend/src/ResetPasswordPage.js +++ b/frontend/src/ResetPasswordPage.js @@ -35,16 +35,37 @@ export default function ResetPasswordPage() { position: 'top-left', }) }, - onCompleted() { - history.push('/sign-in') - toast({ - title: t`Password Updated`, - description: t`You may now sign in with your new password`, - status: 'success', - duration: 9000, - isClosable: true, - position: 'top-left', - }) + onCompleted({ resetPassword }) { + if (resetPassword.result.__typename === 'ResetPasswordResult') { + history.push('/sign-in') + toast({ + title: t`Password Updated`, + description: t`You may now sign in with your new password`, + status: 'success', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else if (resetPassword.result.__typename === 'ResetPasswordError') { + toast({ + title: t`Unable to reset your password, please try again.`, + description: resetPassword.result.description, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + } else { + toast({ + title: t`Incorrect send method received.`, + description: t`Incorrect resetPassword.result typename.`, + status: 'error', + duration: 9000, + isClosable: true, + position: 'top-left', + }) + console.log('Incorrect resetPassword.result typename.') + } }, })