Skip to content

Commit

Permalink
moved chared zod schemas, updated password generation for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianboros committed Sep 16, 2024
1 parent 92681d5 commit 3cffcbb
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 146 deletions.
8 changes: 0 additions & 8 deletions packages/wallet/backend/src/asset/validation.ts

This file was deleted.

12 changes: 6 additions & 6 deletions packages/wallet/backend/src/auth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { validate } from '@/shared/validate'
import { NextFunction, Request } from 'express'
import { AuthService } from './service'
import {
logInSchema,
signUpSchema,
resendVerifyEmailSchema
logInBodySchema,
signUpBodySchema,
emailBodySchema
} from './validation'
import { UserService } from '@/user/service'
import { Controller, toSuccessResponse, Unauthorized } from '@shared/backend'
Expand All @@ -27,7 +27,7 @@ export class AuthController implements IAuthController {
try {
const {
body: { email, password }
} = await validate(signUpSchema, req)
} = await validate(signUpBodySchema, req)

await this.authService.signUp({ email, password })

Expand All @@ -43,7 +43,7 @@ export class AuthController implements IAuthController {
try {
const {
body: { email, password }
} = await validate(logInSchema, req)
} = await validate(logInBodySchema, req)

const { user, session } = await this.authService.authorize({
email,
Expand Down Expand Up @@ -109,7 +109,7 @@ export class AuthController implements IAuthController {
try {
const {
body: { email }
} = await validate(resendVerifyEmailSchema, req)
} = await validate(emailBodySchema, req)

await this.authService.resendVerifyEmail({ email })

Expand Down
34 changes: 7 additions & 27 deletions packages/wallet/backend/src/auth/validation.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
import { z } from 'zod'
import { emailSchema, loginSchema, signUpSchema } from '@wallet/shared'

export const signUpSchema = z.object({
body: z
.object({
email: z.string().email({ message: 'Email is required' }),
password: z
.string()
.min(6, { message: 'Password should be at least 6 characters long' }),
confirmPassword: z.string()
})
.superRefine(({ password, confirmPassword }, ctx) => {
if (password !== confirmPassword) {
ctx.addIssue({
code: 'custom',
message: `Passwords do not match`,
path: ['confirmPassword']
})
}
})
export const signUpBodySchema = z.object({
body: signUpSchema
})

export const logInSchema = z.object({
body: z.object({
email: z.string().email(),
password: z.string()
})
export const logInBodySchema = z.object({
body: loginSchema
})

export const resendVerifyEmailSchema = z.object({
body: z.object({
email: z.string().email()
})
export const emailBodySchema = z.object({
body: emailSchema
})
4 changes: 2 additions & 2 deletions packages/wallet/backend/src/rafiki/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Logger } from 'winston'
import { RatesService } from '@/rates/service'
import { validate } from '@/shared/validate'
import { RafikiService } from './service'
import { ratesSchema, webhookSchema } from './validation'
import { RatesResponse } from '@wallet/shared'
import { webhookSchema } from './validation'
import { ratesSchema, RatesResponse } from '@wallet/shared'

interface IRafikiController {
getRates: (
Expand Down
9 changes: 0 additions & 9 deletions packages/wallet/backend/src/rafiki/validation.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { z } from 'zod'
import { EventType, PaymentType } from './service'

export const ratesSchema = z.object({
query: z.object({
base: z
.string()
.length(3)
.transform((v) => v.toLocaleUpperCase())
})
})

export const webhookSchema = z.object({
body: z.object({
id: z.string({ required_error: 'id is required' }),
Expand Down
9 changes: 0 additions & 9 deletions packages/wallet/backend/src/rapyd/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,6 @@ export const walletSchema = z.object({
})
})

export const ratesSchema = z.object({
query: z.object({
base: z
.string()
.length(3)
.transform((v) => v.toLocaleUpperCase())
})
})

export interface RapydResponse<T> {
status: Status
data: T
Expand Down
10 changes: 0 additions & 10 deletions packages/wallet/backend/src/rates/validation.ts

This file was deleted.

14 changes: 9 additions & 5 deletions packages/wallet/backend/tests/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker'
import { logInSchema, signUpSchema } from '@/auth/validation'
import { logInBodySchema, signUpBodySchema } from '@/auth/validation'
import z from 'zod'
import { PartialModelObject } from 'objection'
import { Transaction } from '../src/transaction/model'
Expand All @@ -11,15 +11,16 @@ import {
RapydProfile,
walletSchema
} from '@/rapyd/schemas'
import { ratesSchema, webhookSchema } from '@/rafiki/validation'
import { webhookSchema } from '@/rafiki/validation'
import { EventType, WebHook } from '@/rafiki/service'
import {
incomingPaymentSchema,
paymentDetailsSchema
} from '@/incomingPayment/validation'
import { outgoingPaymentSchema } from '@/outgoingPayment/validation'
import { ratesSchema } from '@wallet/shared'

export type LogInRequest = z.infer<typeof logInSchema>
export type LogInRequest = z.infer<typeof logInBodySchema>
export type GetRatesRequest = z.infer<typeof ratesSchema>
export type OnWebHook = z.infer<typeof webhookSchema>
export type IncomingPaymentCreated = z.infer<typeof incomingPaymentSchema>
Expand All @@ -29,7 +30,10 @@ export type GetPaymentDetailsByUrl = z.infer<typeof paymentDetailsSchema>
export const fakeLoginData = () => {
return {
email: faker.internet.email(),
password: faker.internet.password()
password: faker.internet.password({
length: 20,
pattern: /[0-9a-zA-Z`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/

Check failure on line 35 in packages/wallet/backend/tests/mocks.ts

View workflow job for this annotation

GitHub Actions / ESLint and Prettier checks

Unnecessary escape character: \[

Check failure on line 35 in packages/wallet/backend/tests/mocks.ts

View workflow job for this annotation

GitHub Actions / ESLint and Prettier checks

Unnecessary escape character: \/
})
}
}

Expand All @@ -42,7 +46,7 @@ export const mockLogInRequest = (
}
})

type SignUpRequest = z.infer<typeof signUpSchema>
type SignUpRequest = z.infer<typeof signUpBodySchema>
export const mockSignUpRequest = (
overrides?: Partial<SignUpRequest['body']>
): SignUpRequest => {
Expand Down
76 changes: 8 additions & 68 deletions packages/wallet/frontend/src/lib/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,14 @@ import {
} from '../httpClient'
import { ACCEPTED_IMAGE_TYPES } from '@/utils/constants'
import { SelectOption } from '@/ui/forms/Select'
import { UserResponse, ValidTokenResponse } from '@wallet/shared'
import { emailSchema } from '@wallet/shared/src'

const isValidPassword = (password: string): boolean => {
if (typeof password !== 'string') return false
if (password.length < 8) return false

const containsUppercase = (ch: string) => /[A-Z]/.test(ch)
const containsLowercase = (ch: string) => /[a-z]/.test(ch)
const containsSpecialChar = (ch: string) =>
// eslint-disable-next-line no-useless-escape
/[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(ch)
let countOfUpperCase = 0,
countOfLowerCase = 0,
countOfNumbers = 0,
countOfSpecialChar = 0
for (let i = 0; i < password.length; i++) {
const ch = password.charAt(i)
if (!isNaN(+ch)) countOfNumbers++
else if (containsUppercase(ch)) countOfUpperCase++
else if (containsLowercase(ch)) countOfLowerCase++
else if (containsSpecialChar(ch)) countOfSpecialChar++
}

if (
countOfLowerCase < 1 ||
countOfUpperCase < 1 ||
countOfSpecialChar < 1 ||
countOfNumbers < 1
) {
return false
}

return true
}

export const signUpSchema = z
.object({
email: z.string().email({ message: 'Email is required' }),
password: z
.string()
.min(8, { message: 'Password should be at least 8 characters long' }),
confirmPassword: z.string()
})
.superRefine(({ password }, ctx) => {
if (!isValidPassword(password)) {
ctx.addIssue({
code: 'custom',
message:
'Password must contain at least one number and one special character and have a mixture of uppercase and lowercase letters',
path: ['password']
})
}
})
.superRefine(({ confirmPassword, password }, ctx) => {
if (confirmPassword !== password) {
ctx.addIssue({
code: 'custom',
message: 'Passwords must match',
path: ['confirmPassword']
})
}
})

export const loginSchema = z.object({
email: z.string().email({ message: 'Email is required' }),
password: z.string().min(1, { message: 'Password is required' })
})
import {
UserResponse,
ValidTokenResponse,
emailSchema,
isValidPassword,
signUpSchema,
loginSchema
} from '@wallet/shared'

export const personalDetailsSchema = z.object({
firstName: z.string().min(1, { message: 'First name is required' }),
Expand Down
3 changes: 2 additions & 1 deletion packages/wallet/frontend/src/pages/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { Link } from '@/ui/Link'
import { Play } from '@/components/icons/Play'
import { useRouter } from 'next/router'
import Image from 'next/image'
import { loginSchema, userService } from '@/lib/api/user'
import { userService } from '@/lib/api/user'
import { useDialog } from '@/lib/hooks/useDialog'
import { SuccessDialog } from '@/components/dialogs/SuccessDialog'
import { ErrorDialog } from '@/components/dialogs/ErrorDialog'
import { NextPageWithLayout } from '@/lib/types/app'
import { useEffect } from 'react'
import { useTheme } from 'next-themes'
import { loginSchema } from '@wallet/shared'

const LoginPage: NextPageWithLayout = () => {
const [openDialog, closeDialog] = useDialog()
Expand Down
3 changes: 2 additions & 1 deletion packages/wallet/frontend/src/pages/auth/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { useZodForm } from '@/lib/hooks/useZodForm'
import { Input } from '@/ui/forms/Input'
import { Link } from '@/ui/Link'
import { Play } from '@/components/icons/Play'
import { signUpSchema, userService } from '@/lib/api/user'
import { userService } from '@/lib/api/user'
import { useDialog } from '@/lib/hooks/useDialog'
import { SuccessDialog } from '@/components/dialogs/SuccessDialog'
import { getObjectKeys } from '@/utils/helpers'
import { NextPageWithLayout } from '@/lib/types/app'
import { useEffect } from 'react'
import { cx } from 'class-variance-authority'
import { useTheme } from 'next-themes'
import { signUpSchema } from '@wallet/shared'

const SignUpPage: NextPageWithLayout = () => {
const [openDialog, closeDialog] = useDialog()
Expand Down
11 changes: 11 additions & 0 deletions packages/wallet/shared/src/types/rate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import { z } from 'zod'

export interface RatesResponse {
base: string
rates: Record<string, number>
}

export const ratesSchema = z.object({
query: z.object({
base: z
.string()
.length(3)
.transform((v) => v.toLocaleUpperCase())
})
})
65 changes: 65 additions & 0 deletions packages/wallet/shared/src/types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,68 @@ export type ValidTokenResponse = {
export const emailSchema = z.object({
email: z.string().email({ message: 'Email is required' })
})

export const isValidPassword = (password: string): boolean => {
if (typeof password !== 'string') return false
if (password.length < 8) return false

const containsUppercase = (ch: string) => /[A-Z]/.test(ch)
const containsLowercase = (ch: string) => /[a-z]/.test(ch)
const containsSpecialChar = (ch: string) =>
// eslint-disable-next-line no-useless-escape
/[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(ch)
let countOfUpperCase = 0,
countOfLowerCase = 0,
countOfNumbers = 0,
countOfSpecialChar = 0
for (let i = 0; i < password.length; i++) {
const ch = password.charAt(i)
if (!isNaN(+ch)) countOfNumbers++
else if (containsUppercase(ch)) countOfUpperCase++
else if (containsLowercase(ch)) countOfLowerCase++
else if (containsSpecialChar(ch)) countOfSpecialChar++
}

if (
countOfLowerCase < 1 ||
countOfUpperCase < 1 ||
countOfSpecialChar < 1 ||
countOfNumbers < 1
) {
return false
}
return true
}

export const signUpSchema = z
.object({
email: z.string().email({ message: 'Email is required' }),
password: z
.string()
.min(8, { message: 'Password should be at least 8 characters long' }),
confirmPassword: z.string()
})
.superRefine(({ password }, ctx) => {
if (!isValidPassword(password)) {
ctx.addIssue({
code: 'custom',
message:
'Password must contain at least one number and one special character and have a mixture of uppercase and lowercase letters',
path: ['password']
})
}
})
.superRefine(({ confirmPassword, password }, ctx) => {
if (confirmPassword !== password) {
ctx.addIssue({
code: 'custom',
message: 'Passwords must match',
path: ['confirmPassword']
})
}
})

export const loginSchema = z.object({
email: z.string().email({ message: 'Email is required' }),
password: z.string().min(1, { message: 'Password is required' })
})

0 comments on commit 3cffcbb

Please sign in to comment.