Skip to content

Commit

Permalink
Merge pull request #98 from koyopro/feature/secure_password
Browse files Browse the repository at this point in the history
[accel-record] marge arrays in Mix
  • Loading branch information
koyopro authored Jan 6, 2025
2 parents 87210bc + 089e8bb commit b9949df
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/silly-apples-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"accel-record-core": patch
"accel-record": patch
---

Merge arrays in Mix()
43 changes: 24 additions & 19 deletions packages/accel-record-core/src/model/securePassword.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Model } from "../index.js";
import { compareSync, genSaltSync, hashSync } from "bcrypt-ts";
import { Model, Validator } from "../index.js";
import { isBlank } from "../validation/validator/presence.js";
import { validates } from "./validations.js";

// bcrypt-ts: The maximum input length is 72 bytes
const MAX_PASSWORD_LENGTH_ALLOWED = 72;
Expand Down Expand Up @@ -60,8 +61,30 @@ export function hasSecurePassword<T extends string = "password">(

// Create an alias for authenticate only when the attribute is "password"
const authenticateAlias = attribute == "password" ? "authenticate" : "__authenticate";

const CustomValidator = class extends Validator<Model> {
validate(): void {
if (!validations) return;
const password = _get(this.record, _attribute);
const confirm = _get(this.record, _cofirmAttribute);
const digest = _get(this.record, `${attribute}Digest`);
if (validations == true && isBlank(digest)) {
this.record.errors.add(attribute, "blank");
}
this.record.validates(attribute as any, {
length: { maximum: MAX_PASSWORD_LENGTH_ALLOWED },
});
if (confirm != undefined && password !== confirm) {
const humanAttributeName = this.record.class().humanAttributeName(attribute);
this.record.errors.add(confirmAttribute, "confirmation", {
attribute: humanAttributeName,
});
}
}
};
// @ts-ignore
return class SecurePassword {
static validations = validates(this, [CustomValidator]);
get [attribute]() {
return _get(this, _attribute);
}
Expand All @@ -84,24 +107,6 @@ export function hasSecurePassword<T extends string = "password">(
[authenticateAlias](this: any, password: string) {
return this[authenticate](password);
}
validateAttributes<T extends Model & SecurePassword>(this: T) {
if (!validations) return;
const password = _get(this, _attribute);
const confirm = _get(this, _cofirmAttribute);
const digest = _get(this, `${attribute}Digest`);
if (validations == true && isBlank(digest)) {
this.errors.add(attribute, "blank");
}
this.validates(attribute as any, {
length: { maximum: MAX_PASSWORD_LENGTH_ALLOWED },
});
if (confirm != undefined && password !== confirm) {
const humanAttributeName = this.class().humanAttributeName(attribute);
this.errors.add(confirmAttribute, "confirmation", {
attribute: humanAttributeName,
});
}
}
};
}

Expand Down
10 changes: 5 additions & 5 deletions packages/accel-record-core/src/model/validations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Collection } from "../associations/collectionProxy.js";
import { HasManyAssociation } from "../associations/hasManyAssociation.js";
import { HasOneAssociation } from "../associations/hasOneAssociation.js";
import { FormModel, Model, ModelBase } from "../index.js";
import { Model, ModelBase } from "../index.js";
import { Meta } from "../meta.js";
import { Errors } from "../validation/errors.js";
import { AcceptanceOptions, AcceptanceValidator } from "../validation/validator/acceptance.js";
Expand Down Expand Up @@ -183,10 +183,10 @@ export function validates<T extends typeof Model, K extends keyof Meta<T>["Creat
klass: T,
list: ValidateItem<T, K>[]
): ValidateItem<any, any>[];
export function validates<T extends typeof FormModel, K extends keyof InstanceType<T> & string>(
klass: T,
list: ValidateItem<T, K>[]
): ValidateItem<any, any>[];
export function validates<
T extends { new (...args: any): any },
K extends keyof InstanceType<T> & string,
>(klass: T, list: ValidateItem<T, K>[]): ValidateItem<any, any>[];
export function validates<T, K extends string>(
klass: T,
list: ValidateItem<T, K>[]
Expand Down
10 changes: 9 additions & 1 deletion packages/accel-record-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ const assign = (target: object, source: object, key: string) => {
const desc = Object.getOwnPropertyDescriptor(source, key);
const targetDesc = findMethod(target, key);

if (typeof targetDesc?.value == "function" && typeof desc?.value == "function") {
if (Array.isArray(targetDesc?.value) && Array.isArray(desc?.value)) {
// merge arrays
Object.defineProperty(target, key, {
value: [...targetDesc.value, ...desc.value],
enumerable: targetDesc.enumerable,
writable: true,
configurable: true,
});
} else if (typeof targetDesc?.value == "function" && typeof desc?.value == "function") {
// already has a method, so we need to wrap it
Object.defineProperty(target, key, {
value: function (this: any, ...args: any[]) {
Expand Down
6 changes: 6 additions & 0 deletions tests/models/mix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Mix } from "accel-record-core";
class A {
static a = "a";

static validations = ["a"];

a = "a";
errors: string[] = [];

Expand All @@ -24,6 +26,8 @@ class AB extends Mix(A, B) {}
class C {
static c = "c";

static validations = ["c"];

c = "c";

validate<T extends A>(this: T) {
Expand All @@ -41,6 +45,8 @@ test("mix", () => {
expect(ABC.a).toBe("a");
expect(ABC.c).toBe("c");

expect(ABC.validations).toEqual(["a", "c"]);

const ab = new AB();
expect(ab.a).toBe("a");
expect(ab.aa).toBe("aa");
Expand Down

0 comments on commit b9949df

Please sign in to comment.