From 29c86bb3b56d4eaf5ecfe4e7214f2a0c4ecbb3d4 Mon Sep 17 00:00:00 2001 From: Mike Cornwell Date: Wed, 19 Mar 2025 12:43:06 -0400 Subject: [PATCH 1/5] fix(validator): fix object validators inability to check for each property if undefined --- package.json | 2 +- src/validation.ts | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 20cad91..fbb2b8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functional-models", - "version": "3.0.12", + "version": "3.0.13", "description": "Functional models is ooey gooey framework for building and using awesome models EVERYWHERE.", "main": "index.js", "types": "index.d.ts", diff --git a/src/validation.ts b/src/validation.ts index cc67e02..311fa2e 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -1,6 +1,7 @@ import isEmpty from 'lodash/isEmpty' import merge from 'lodash/merge' import flatMap from 'lodash/flatMap' +import get from 'lodash/get' import { DataDescription, ModelInstance, @@ -436,16 +437,22 @@ const referenceTypeMatch = ( } } +/** + * A validator that can validate an entire object. + * + * @param required - If this object is required. + * @param keyToValidators - An object that has a dotted path key to the property to validate, and one or more validators for it. + */ const objectValidator = ({ required, keyToValidators, }: { required?: boolean - keyToValidators: { - [s: string]: - | ValuePropertyValidatorComponent - | ValuePropertyValidatorComponent[] - } + keyToValidators: Record< + string, + | ValuePropertyValidatorComponent + | ValuePropertyValidatorComponent[] + > }): ValuePropertyValidatorComponent => { return (obj: T) => { if (!obj) { @@ -459,16 +466,13 @@ const objectValidator = ({ return isNotObj } return ( - Object.entries(obj) - .reduce((acc, [key, value]) => { - const validators = keyToValidators[key] - if (!validators) { - return acc - } - const validator = Array.isArray(validators) - ? multiValidator(validators) - : validators - const error = validator(value) + Object.entries(keyToValidators) + .reduce((acc, [key, validator]) => { + const theValidator = Array.isArray(validator) + ? multiValidator(validator) + : (validator as ValuePropertyValidatorComponent) + const value = get(obj, key) + const error = theValidator(value) if (error) { return acc.concat(`${key}: ${error}`) } From b31067fc8934b73bcc7e16ee88576242ea4d96fa Mon Sep 17 00:00:00 2001 From: Mike Cornwell Date: Sat, 22 Mar 2025 08:04:15 -0400 Subject: [PATCH 2/5] fix(jsonable): jsonable should allow undefined --- package.json | 2 +- src/types.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fbb2b8d..b7722a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functional-models", - "version": "3.0.13", + "version": "3.0.14", "description": "Functional models is ooey gooey framework for building and using awesome models EVERYWHERE.", "main": "index.js", "types": "index.d.ts", diff --git a/src/types.ts b/src/types.ts index c52305e..f8b804a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,7 +29,7 @@ type Arrayable = T | readonly T[] * A JSON compliant object. */ type JsonObj = Readonly<{ - [s: string]: JsonAble | null + [s: string]: JsonAble | null | undefined }> /** @@ -42,6 +42,7 @@ type JsonAble = | string | boolean | null + | undefined /** * This is a fully Json compliant version of a DataDescription From fdfeec04a8e9063f6cd82b347e590db3269c1672 Mon Sep 17 00:00:00 2001 From: Mike Cornwell Date: Sat, 22 Mar 2025 16:57:43 -0400 Subject: [PATCH 3/5] fix(reftype): all ability to pass in string or number or undefined as a value for a reference type --- package.json | 2 +- src/validation.ts | 14 ++++++++++++-- test/src/validation.test.ts | 8 ++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b7722a0..5d148e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functional-models", - "version": "3.0.14", + "version": "3.0.15", "description": "Functional models is ooey gooey framework for building and using awesome models EVERYWHERE.", "main": "index.js", "types": "index.d.ts", diff --git a/src/validation.ts b/src/validation.ts index 311fa2e..cda2053 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -418,8 +418,17 @@ const referenceTypeMatch = ( any > => { return (value?: ModelInstance) => { - if (!value) { - return 'Must include a value' + const theType = typeof value + switch (theType) { + case 'string': + case 'number': + case 'undefined': + return undefined + default: + break + } + if (value === null) { + return undefined } // This needs to stay here, as it delays the creation long enough for // self referencing types. @@ -427,6 +436,7 @@ const referenceTypeMatch = ( typeof referencedModel === 'function' ? referencedModel() : referencedModel + const hasGetModel = get(value, 'getModel') // Assumption: By the time this is received, value === a model instance. const eModel = model.getName() const aModel = value.getModel().getName() diff --git a/test/src/validation.test.ts b/test/src/validation.test.ts index 34e4dd3..f6a80fc 100644 --- a/test/src/validation.test.ts +++ b/test/src/validation.test.ts @@ -769,7 +769,7 @@ describe('/src/validation.ts', () => { }) }) describe('#referenceTypeMatch()', () => { - it('should return an error if undefined is passed as a value', () => { + it('should return undefined if undefined is passed as a value', () => { const myModel = TestModel1.create({}) const actual = referenceTypeMatch(TestModel1)( // @ts-ignore @@ -778,9 +778,9 @@ describe('/src/validation.ts', () => { {}, {} ) - assert.isOk(actual) + assert.isUndefined(actual) }) - it('should return an error if null is passed as a value', () => { + it('should return undefined if null is passed as a value', () => { const myModel = TestModel1.create({}) const actual = referenceTypeMatch(TestModel1)( // @ts-ignore @@ -789,7 +789,7 @@ describe('/src/validation.ts', () => { {}, {} ) - assert.isOk(actual) + assert.isUndefined(actual) }) it('should allow a function for a model', async () => { const myModel = EMPTY_MODEL.create({}) From 3aa7a137f4c501a58814a3bdaca398bca8be30c6 Mon Sep 17 00:00:00 2001 From: Mike Cornwell Date: Thu, 27 Mar 2025 12:57:45 -0400 Subject: [PATCH 4/5] feat(date): defaults to date type --- package.json | 2 +- src/orm/query.ts | 12 ++++++------ src/orm/types.ts | 2 ++ src/properties.ts | 2 +- test/src/orm/query.test.ts | 8 ++++---- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 5d148e1..5cc64b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functional-models", - "version": "3.0.15", + "version": "3.0.16", "description": "Functional models is ooey gooey framework for building and using awesome models EVERYWHERE.", "main": "index.js", "types": "index.d.ts", diff --git a/src/orm/query.ts b/src/orm/query.ts index 8d97aad..a081a99 100644 --- a/src/orm/query.ts +++ b/src/orm/query.ts @@ -94,7 +94,7 @@ const _builderV2 = (data: OrmSearch): InnerBuilderV2 => { const thisDatesBefore = ( key: string, jsDate: Date | string, - { valueType = DatastoreValueType.string, equalToAndBefore = true } = {} + { valueType = DatastoreValueType.date, equalToAndBefore = true } = {} ) => { const p = datesBefore(key, jsDate, { valueType, equalToAndBefore }) return _link(merge(data, { query: data.query.concat(p) })) @@ -103,7 +103,7 @@ const _builderV2 = (data: OrmSearch): InnerBuilderV2 => { const thisDatesAfter = ( key: string, jsDate: Date | string, - { valueType = DatastoreValueType.string, equalToAndAfter = true } = {} + { valueType = DatastoreValueType.date, equalToAndAfter = true } = {} ) => { const p = datesAfter(key, jsDate, { valueType, equalToAndAfter }) return _link(merge(data, { query: data.query.concat(p) })) @@ -235,11 +235,11 @@ const datesAfter = ( key: string, jsDate: Date | string, options: { valueType: DatastoreValueType; equalToAndAfter: boolean } = { - valueType: DatastoreValueType.string, + valueType: DatastoreValueType.date, equalToAndAfter: true, } ): DatesAfterQuery => { - const { valueType = DatastoreValueType.string, equalToAndAfter = true } = + const { valueType = DatastoreValueType.date, equalToAndAfter = true } = options return { type: 'datesAfter', @@ -271,11 +271,11 @@ const datesBefore = ( key: string, jsDate: Date | string, options: { valueType: DatastoreValueType; equalToAndBefore: boolean } = { - valueType: DatastoreValueType.string, + valueType: DatastoreValueType.date, equalToAndBefore: true, } ): DatesBeforeQuery => { - const { valueType = DatastoreValueType.string, equalToAndBefore = true } = + const { valueType = DatastoreValueType.date, equalToAndBefore = true } = options return { type: 'datesBefore', diff --git a/src/orm/types.ts b/src/orm/types.ts index 34c4637..060e005 100644 --- a/src/orm/types.ts +++ b/src/orm/types.ts @@ -745,6 +745,7 @@ type InnerBuilderV2 = { complex: (subBuilderFunc: SubBuilderFunction) => BuilderV2Link /** * Searches for elements that are after the given date. + * NOTE: It can be very important to set the valueType to either string or Date depending on what datastore you are using. * @param key - The property name/key to use. * @param jsDate - The date to search. * @param options - Additional options. @@ -756,6 +757,7 @@ type InnerBuilderV2 = { ) => BuilderV2Link /** * Searches for elements that are before the given date. + * NOTE: It can be very important to set the valueType to either string or Date depending on what datastore you are using. * @param key - The property name/key to use. * @param jsDate - The date to search. * @param options - Additional options. diff --git a/src/properties.ts b/src/properties.ts index 825d310..df47148 100644 --- a/src/properties.ts +++ b/src/properties.ts @@ -192,7 +192,7 @@ type DatePropertyConfig> = { formatFunction?: (date: Date, format?: string) => string /** * The format the date should be in. This is a framework agnostic format, and should be based on your format function. - * NOTE: If a formatFunction is not provided, this is completely ignored. For dates YYYY/MM/DD is the default and for Datetimes it is ISOString() + * NOTE: If a formatFunction is not provided, this is completely ignored. For dates YYYY-MM-DD is the default and for Datetimes it is ISOString() */ format?: string } & PropertyConfig diff --git a/test/src/orm/query.test.ts b/test/src/orm/query.test.ts index 4357ced..e485372 100644 --- a/test/src/orm/query.test.ts +++ b/test/src/orm/query.test.ts @@ -446,7 +446,7 @@ describe('/src/orm/query.ts', () => { type: 'datesBefore', key: 'my-key', date: '2025-01-01T00:00:00.000Z', - valueType: DatastoreValueType.string, + valueType: DatastoreValueType.date, options: { equalToAndBefore: true, }, @@ -497,7 +497,7 @@ describe('/src/orm/query.ts', () => { type: 'datesAfter', key: 'my-key', date: '2025-01-01T00:00:00.000Z', - valueType: DatastoreValueType.string, + valueType: DatastoreValueType.date, options: { equalToAndAfter: true, }, @@ -725,7 +725,7 @@ describe('/src/orm/query.ts', () => { type: 'datesBefore', key: 'my-key', date: '2020-01-01', - valueType: 'string', + valueType: 'date', options: { equalToAndBefore: true, }, @@ -746,7 +746,7 @@ describe('/src/orm/query.ts', () => { type: 'datesAfter', key: 'my-key', date: '2020-01-01', - valueType: 'string', + valueType: 'date', options: { equalToAndAfter: true, }, From b197c67fccd00d39d911cc1be26c37a0d5f31a01 Mon Sep 17 00:00:00 2001 From: Mike Cornwell Date: Sat, 26 Apr 2025 18:59:36 -0400 Subject: [PATCH 5/5] style(fix): fix style --- src/validation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/validation.ts b/src/validation.ts index cda2053..c513900 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -436,7 +436,6 @@ const referenceTypeMatch = ( typeof referencedModel === 'function' ? referencedModel() : referencedModel - const hasGetModel = get(value, 'getModel') // Assumption: By the time this is received, value === a model instance. const eModel = model.getName() const aModel = value.getModel().getName()