Skip to content

Commit bc4900a

Browse files
authored
Reset Password Union Introduction (canada-ca#1778)
* create password union file * progress * Finish up reset password objects * finish up the reset password union * refactor reset password to use new union type * update faker * update mutation query string * update reset page functionality to handle error returns
1 parent 7a2e9a5 commit bc4900a

13 files changed

Lines changed: 564 additions & 84 deletions

api-js/src/user/mutations/__tests__/reset-password.test.js

Lines changed: 310 additions & 59 deletions
Large diffs are not rendered by default.

api-js/src/user/mutations/reset-password.js

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { GraphQLNonNull, GraphQLString } from 'graphql'
22
import { mutationWithClientMutationId } from 'graphql-relay'
33
import { t } from '@lingui/macro'
44

5+
import { resetPasswordUnion } from '../unions'
6+
57
export const resetPassword = new mutationWithClientMutationId({
68
name: 'ResetPassword',
79
description:
@@ -22,13 +24,11 @@ export const resetPassword = new mutationWithClientMutationId({
2224
},
2325
}),
2426
outputFields: () => ({
25-
status: {
26-
type: GraphQLString,
27+
result: {
28+
type: resetPasswordUnion,
2729
description:
28-
'Informs the user if the password reset was successful, and to redirect to sign in page.',
29-
resolve: async (payload) => {
30-
return payload.status
31-
},
30+
'`ResetPasswordUnion` returning either a `ResetPasswordResult`, or `ResetPasswordError` object.',
31+
resolve: (payload) => payload,
3232
},
3333
}),
3434
mutateAndGetPayload: async (
@@ -57,7 +57,11 @@ export const resetPassword = new mutationWithClientMutationId({
5757
console.warn(
5858
`When resetting password user attempted to verify account, but userKey is not located in the token parameters.`,
5959
)
60-
throw new Error(i18n._(t`Unable to reset password. Please try again.`))
60+
return {
61+
_type: 'error',
62+
code: 400,
63+
description: i18n._(t`Unable to reset password. Please try again.`),
64+
}
6165
}
6266

6367
// Check if user exists
@@ -67,33 +71,49 @@ export const resetPassword = new mutationWithClientMutationId({
6771
console.warn(
6872
`A user attempted to reset the password for ${tokenParameters.userKey}, however there is no associated account.`,
6973
)
70-
throw new Error(i18n._(t`Unable to reset password. Please try again.`))
74+
return {
75+
_type: 'error',
76+
code: 400,
77+
description: i18n._(t`Unable to reset password. Please try again.`),
78+
}
7179
}
7280

7381
// Check if password in token matches token in db
7482
if (tokenParameters.currentPassword !== user.password) {
7583
console.warn(
7684
`User: ${user._key} attempted to reset password, however the current password does not match the current hashed password in the db.`,
7785
)
78-
throw new Error(i18n._(t`Unable to reset password. Please try again.`))
86+
return {
87+
_type: 'error',
88+
code: 400,
89+
description: i18n._(t`Unable to reset password. Please try again.`),
90+
}
7991
}
8092

8193
// Check to see if newly submitted passwords match
8294
if (password !== confirmPassword) {
8395
console.warn(
8496
`User: ${user._key} attempted to reset their password, however the submitted passwords do not match.`,
8597
)
86-
throw new Error(i18n._(t`New passwords do not match. Please try again.`))
98+
return {
99+
_type: 'error',
100+
code: 400,
101+
description: i18n._(t`New passwords do not match. Please try again.`),
102+
}
87103
}
88104

89105
// Check to see if password meets GoC requirements
90106
if (password.length < 12) {
91107
console.warn(
92108
`User: ${user._key} attempted to reset their password, however the submitted password is not long enough.`,
93109
)
94-
throw new Error(
95-
i18n._(t`Password is not strong enough. Please try again.`),
96-
)
110+
return {
111+
_type: 'error',
112+
code: 400,
113+
description: i18n._(
114+
t`Password is not strong enough. Please try again.`,
115+
),
116+
}
97117
}
98118

99119
// Update users password in db
@@ -114,6 +134,7 @@ export const resetPassword = new mutationWithClientMutationId({
114134
console.info(`User: ${user._key} successfully reset their password.`)
115135

116136
return {
137+
_type: 'regular',
117138
status: i18n._(t`Password was successfully reset.`),
118139
}
119140
},
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { GraphQLInt, GraphQLString } from 'graphql'
2+
3+
import { resetPasswordErrorType } from '../reset-password-error'
4+
5+
describe('given the resetPasswordErrorType object', () => {
6+
describe('testing the field definitions', () => {
7+
it('has an code field', () => {
8+
const demoType = resetPasswordErrorType.getFields()
9+
10+
expect(demoType).toHaveProperty('code')
11+
expect(demoType.code.type).toMatchObject(GraphQLInt)
12+
})
13+
it('has a description field', () => {
14+
const demoType = resetPasswordErrorType.getFields()
15+
16+
expect(demoType).toHaveProperty('description')
17+
expect(demoType.description.type).toMatchObject(GraphQLString)
18+
})
19+
})
20+
21+
describe('testing the field resolvers', () => {
22+
describe('testing the code resolver', () => {
23+
it('returns the resolved field', () => {
24+
const demoType = resetPasswordErrorType.getFields()
25+
26+
expect(demoType.code.resolve({ code: 400 })).toEqual(400)
27+
})
28+
})
29+
describe('testing the description field', () => {
30+
it('returns the resolved value', () => {
31+
const demoType = resetPasswordErrorType.getFields()
32+
33+
expect(
34+
demoType.description.resolve({ description: 'description' }),
35+
).toEqual('description')
36+
})
37+
})
38+
})
39+
})
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { GraphQLString } from 'graphql'
2+
3+
import { resetPasswordResultType } from '../reset-password-result'
4+
5+
describe('given the resetPasswordErrorType object', () => {
6+
describe('testing the field definitions', () => {
7+
it('has an status field', () => {
8+
const demoType = resetPasswordResultType.getFields()
9+
10+
expect(demoType).toHaveProperty('status')
11+
expect(demoType.status.type).toMatchObject(GraphQLString)
12+
})
13+
})
14+
15+
describe('testing the field resolvers', () => {
16+
describe('testing the status resolver', () => {
17+
it('returns the resolved field', () => {
18+
const demoType = resetPasswordResultType.getFields()
19+
20+
expect(demoType.status.resolve({ status: 'status' })).toEqual('status')
21+
})
22+
})
23+
})
24+
})

api-js/src/user/objects/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './auth-result'
2+
export * from './reset-password-error'
3+
export * from './reset-password-result'
24
export * from './sign-in-error'
35
export * from './tfa-sign-in-result'
46
export * from './user-personal'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { GraphQLInt, GraphQLObjectType, GraphQLString } from 'graphql'
2+
3+
export const resetPasswordErrorType = new GraphQLObjectType({
4+
name: 'ResetPasswordError',
5+
description:
6+
'This object is used to inform the user if any errors occurred while resetting their password.',
7+
fields: () => ({
8+
code: {
9+
type: GraphQLInt,
10+
description: 'Error code to inform user what the issue is related to.',
11+
resolve: ({ code }) => code,
12+
},
13+
description: {
14+
type: GraphQLString,
15+
description: 'Description of the issue that was encountered.',
16+
resolve: ({ description }) => description,
17+
},
18+
}),
19+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { GraphQLObjectType, GraphQLString } from 'graphql'
2+
3+
export const resetPasswordResultType = new GraphQLObjectType({
4+
name: 'ResetPasswordResult',
5+
description:
6+
'This object is used to inform the user that no errors were encountered while resetting their password.',
7+
fields: () => ({
8+
status: {
9+
type: GraphQLString,
10+
description:
11+
'Informs the user if the password reset was successful, and to redirect to sign in page.',
12+
resolve: (payload) => payload.status,
13+
},
14+
}),
15+
})
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { resetPasswordErrorType, resetPasswordResultType } from '../../objects/index'
2+
import { resetPasswordUnion } from '../reset-password-union'
3+
4+
describe('given the resetPasswordUnion', () => {
5+
describe('testing the field types', () => {
6+
it('contains resetPasswordResultType', () => {
7+
const demoType = resetPasswordUnion.getTypes()
8+
9+
expect(demoType).toContain(resetPasswordResultType)
10+
})
11+
it('contains resetPasswordErrorType', () => {
12+
const demoType = resetPasswordUnion.getTypes()
13+
14+
expect(demoType).toContain(resetPasswordErrorType)
15+
})
16+
})
17+
describe('testing the field selection', () => {
18+
describe('testing the resetPasswordResultType', () => {
19+
it('returns the correct type', () => {
20+
const obj = {
21+
_type: 'regular',
22+
authResult: {},
23+
}
24+
25+
expect(resetPasswordUnion.resolveType(obj)).toMatchObject(
26+
resetPasswordResultType,
27+
)
28+
})
29+
})
30+
describe('testing the resetPasswordErrorType', () => {
31+
it('returns the correct type', () => {
32+
const obj = {
33+
_type: 'error',
34+
error: 'sign-in-error',
35+
code: 401,
36+
description: 'text',
37+
}
38+
39+
expect(resetPasswordUnion.resolveType(obj)).toMatchObject(
40+
resetPasswordErrorType,
41+
)
42+
})
43+
})
44+
})
45+
})

api-js/src/user/unions/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './reset-password-union'
12
export * from './sign-in-union'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { GraphQLUnionType } from 'graphql'
2+
import { resetPasswordErrorType, resetPasswordResultType } from '../objects'
3+
4+
export const resetPasswordUnion = new GraphQLUnionType({
5+
name: 'ResetPasswordUnion',
6+
description:
7+
'This union is used with the `ResetPassword` mutation, allowing for users to reset their password, and support any errors that may occur',
8+
types: [resetPasswordErrorType, resetPasswordResultType],
9+
resolveType({ _type }) {
10+
if (_type === 'regular') {
11+
return resetPasswordResultType
12+
} else {
13+
return resetPasswordErrorType
14+
}
15+
},
16+
})

0 commit comments

Comments
 (0)