diff --git a/src/Model/Definition.ts b/src/Model/Definition.ts index b727bac..71daac9 100644 --- a/src/Model/Definition.ts +++ b/src/Model/Definition.ts @@ -28,7 +28,7 @@ export class Definition * @param type - The model property type. * @param options - Property definition options. */ -export function SDefine(type: Type, options: DefinitionOptions = {}) +export function SDefine(type: Type, options: DefinitionOptions = {}): Definition { - return new Definition(type, options); + return new Definition(type, options); } diff --git a/src/Model/Types/ArrayType.ts b/src/Model/Types/ArrayType.ts index b717d9a..2dfe381 100644 --- a/src/Model/Types/ArrayType.ts +++ b/src/Model/Types/ArrayType.ts @@ -6,7 +6,7 @@ import {Type} from "./Type"; export class ArrayType extends Type { /** - * Constructs a new array type of Sharkitek model property. + * Constructs a new array type of a Sharkitek model property. * @param valueType - Type of the array values. */ constructor(protected valueType: Type) diff --git a/src/Model/Types/ObjectType.ts b/src/Model/Types/ObjectType.ts new file mode 100644 index 0000000..22c85fb --- /dev/null +++ b/src/Model/Types/ObjectType.ts @@ -0,0 +1,77 @@ +import {Type} from "./Type"; +import {Definition} from "../Definition"; + +/** + * Type of a simple object. + */ +export class ObjectType extends Type, Record> +{ + /** + * Constructs a new object type of a Sharkitek model property. + * @param fieldsTypes Object fields types. + */ + constructor(protected fieldsTypes: Record>) + { + super(); + } + + deserialize(value: Record): Record + { + if (value === undefined) return undefined; + if (value === null) return null; + + return Object.fromEntries( + // For each defined field, deserialize its value according to its type. + (Object.entries(this.fieldsTypes) as [Keys, Definition][]).map(([fieldName, fieldDefinition]) => ( + // Return an entry with the current field name and the deserialized value. + [fieldName, fieldDefinition.type.deserialize(value[fieldName])] + )) + ) as Record; + } + + serialize(value: Record): Record + { + if (value === undefined) return undefined; + if (value === null) return null; + + return Object.fromEntries( + // For each defined field, serialize its value according to its type. + (Object.entries(this.fieldsTypes) as [Keys, Definition][]).map(([fieldName, fieldDefinition]) => ( + // Return an entry with the current field name and the serialized value. + [fieldName, fieldDefinition.type.serialize(value[fieldName])] + )) + ) as Record; + } + + serializeDiff(value: Record): Record + { + if (value === undefined) return undefined; + if (value === null) return null; + + return Object.fromEntries( + // For each defined field, serialize its diff value according to its type. + (Object.entries(this.fieldsTypes) as [Keys, Definition][]).map(([fieldName, fieldDefinition]) => ( + // Return an entry with the current field name and the serialized diff value. + [fieldName, fieldDefinition.type.serializeDiff(value[fieldName])] + )) + ) as Record; + } + + resetDiff(value: Record): void + { + // For each field, reset its diff. + (Object.entries(this.fieldsTypes) as [Keys, Definition][]).forEach(([fieldName, fieldDefinition]) => { + // Reset diff of the current field. + fieldDefinition.type.resetDiff(value[fieldName]); + }); + } +} + +/** + * Type of a simple object. + * @param fieldsTypes Object fields types. + */ +export function SObject(fieldsTypes: Record>): ObjectType +{ + return new ObjectType(fieldsTypes); +} diff --git a/src/index.ts b/src/index.ts index fa04563..db9c7c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,4 +10,5 @@ export * from "./Model/Types/DateType"; export * from "./Model/Types/DecimalType"; export * from "./Model/Types/ModelType"; export * from "./Model/Types/NumericType"; +export * from "./Model/Types/ObjectType"; export * from "./Model/Types/StringType"; diff --git a/tests/Model.test.ts b/tests/Model.test.ts index a5a4112..e29da48 100644 --- a/tests/Model.test.ts +++ b/tests/Model.test.ts @@ -10,6 +10,7 @@ import { ModelDefinition, SDefine, ModelIdentifier } from "../src"; +import {SObject} from "../src/Model/Types/ObjectType"; /** * Another test model. @@ -54,6 +55,9 @@ class Article extends Model
authors: Author[] = []; text: string; evaluation: number; + tags: { + name: string; + }[]; protected SIdentifier(): ModelIdentifier
{ @@ -68,6 +72,11 @@ class Article extends Model
authors: SDefine(SArray(SModel(Author))), text: SDefine(SString), evaluation: SDefine(SDecimal), + tags: SDefine(SArray( + SObject({ + name: SDefine(SString), + }) + )), }; } } @@ -82,6 +91,7 @@ it("deserialize", () => { ], text: "this is a long test.", evaluation: "25.23", + tags: [ {name: "test"}, {name: "foo"} ], }).serialize()).toStrictEqual({ id: 1, title: "this is a test", @@ -91,6 +101,7 @@ it("deserialize", () => { ], text: "this is a long test.", evaluation: "25.23", + tags: [ {name: "test"}, {name: "foo"} ], }); }); @@ -104,6 +115,9 @@ it("create and check state then serialize", () => { ]; article.text = "this is a long test."; article.evaluation = 25.23; + article.tags = []; + article.tags.push({name: "test"}); + article.tags.push({name: "foo"}); expect(article.isNew()).toBeTruthy(); expect(article.getIdentifier()).toStrictEqual(1); @@ -116,6 +130,7 @@ it("create and check state then serialize", () => { ], text: "this is a long test.", evaluation: "25.23", + tags: [ {name: "test"}, {name: "foo"} ], }); }); @@ -130,6 +145,7 @@ it("deserialize then save", () => { ], text: "this is a long test.", evaluation: "25.23", + tags: [ {name: "test"}, {name: "foo"} ], }); expect(article.isNew()).toBeFalsy(); @@ -156,6 +172,7 @@ it("save with modified submodels", () => { ], text: "this is a long test.", evaluation: "25.23", + tags: [ {name: "test"}, {name: "foo"} ], }); article.authors = article.authors.map((author) => {