Skip to content

Commit

Permalink
feat: centralize password and googleIDToken field schema
Browse files Browse the repository at this point in the history
  • Loading branch information
trevor-anderson committed Mar 24, 2024
1 parent 2be61c8 commit 83b079e
Showing 1 changed file with 69 additions and 10 deletions.
79 changes: 69 additions & 10 deletions src/components/Form/helpers/yupCommonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { isValidEmail, isValidPassword } from "@nerdware/ts-string-helpers";
import { string as yupString } from "yup";

/**
* Base string schema: `yup.string().trim().max(250)`
* Base string schema: `yup.string().defined().trim().max(250).default("")`
* - Prohibits `undefined` values.
* - Trim whitespace from front and end of string values.
* - Default max 250 per field ensures strings fit within API/DB limitations.
* - Setting a default value of `''` ensures that calls to `Schema.getDefault()` and/or
* `Schema.cast()` do not result in fields with `undefined` values.
*/
const yupBaseStringSchema = yupString().trim().max(250).default("");
const yupBaseStringSchema = yupString().defined().trim().max(250).default("");

const yupNullableStringSchema = yupBaseStringSchema.nullable().default(null);

/**
* @note Yup order of operations:
Expand All @@ -17,10 +20,13 @@ const yupBaseStringSchema = yupString().trim().max(250).default("");
* 3. validations
*/
export const yupCommonSchema = {
/** Base string schema: `yup.string().trim().max(250).default('')` */
/** ### Base string schema: `yup.string().defined().trim().max(250).default("")` */
string: yupBaseStringSchema,
/** Nullable string schema: `yup.string().trim().max(250).nullable().default(null)` */
stringNullable: yupBaseStringSchema.nullable().default(null),

/** ### Nullable string schema: `yup.string().defined().trim().max(250).nullable().default(null)` */
stringNullable: yupNullableStringSchema,

/** ### Email string schema */
email: yupBaseStringSchema
/**
* Yup's `email` validation uses the following pattern:
Expand All @@ -34,13 +40,66 @@ export const yupCommonSchema = {
message: "Please enter a valid email",
test: (value) => isValidEmail(value),
}),

/** ### Phone string schema (for US phone numbers only) */
phone: yupBaseStringSchema.matches(
/^\([1-9]\d{2}\)\s?\d{3}\s?-\s?\d{4}$/,
"Must be a valid US phone number"
),
password: yupBaseStringSchema.test({
name: "is-valid-password",
message: "Please enter a valid password",
test: (value) => isValidPassword(value),
}),

/**
* ### Schema: Google OAuth2 ID Token (base64-encoded string)
* > **Note:** This cannot depend on the value of `password`, as that creates a circular dependency.
*/
googleIDToken: yupNullableStringSchema.max(Number.MAX_SAFE_INTEGER),

/**
* ### Schema: Password
*
* A nullable-string schema which depends on the value of `googleIDToken`:
* - WHEN `googleIDToken` is present/truthy, `password` is nullable.
* - WHEN `googleIDToken` is not present/falsy, `password` must meet the following conditions:
* - Contains at least one lowercase letter.
* - Contains at least one uppercase letter.
* - Contains at least one number.
* - Contains at least one of !, \@, #, $, %, ^, &, and/or *.
* - Is at least 6 characters long, and no more than 250 characters long.
*/
// prettier-ignore
password: yupString().defined().nullable().default(null).when("googleIDToken", {
is: (googleIDToken: string | null) => !!googleIDToken,
then: (schema) => schema,
otherwise: (schema) => schema.nonNullable().default("").trim().max(250)
.test({
name: "is-right-length",
message: "Passwords must be between 6-250 characters",
test: (value) => value?.length >= 6 && value?.length <= 250,
})
.test({
name: "contains-lowercase-char",
message: "Passwords must contain at least 1 lowercase letter",
test: (value) => /[a-z]/.test(value),
})
.test({
name: "contains-uppercase-char",
message: "Passwords must contain at least 1 uppercase letter",
test: (value) => /[A-Z]/.test(value),
})
.test({
name: "contains-number",
message: "Passwords must contain at least one number",
test: (value) => /\d/.test(value),
})
.test({
name: "contains-special-char",
message: "Must contain at least one special character: !, @, #, $, %, ^, &, or *",
test: (value) => /[!@#$%^&*]/.test(value),
})
// This last one should be unnecessary, but it's here just in case.
.test({
name: "fallback-is-valid-password",
message: "Your password does not meet platform requirements",
test: (value) => isValidPassword(value),
}),
}),
} as const;

0 comments on commit 83b079e

Please sign in to comment.