Models rewrite with new API for better typings and extensibility.
This commit is contained in:
parent
498d25a909
commit
e43e27e2e1
17 changed files with 430 additions and 386 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@sharkitek/core",
|
"name": "@sharkitek/core",
|
||||||
"version": "2.1.3",
|
"version": "3.0.0",
|
||||||
"description": "Sharkitek core models library.",
|
"description": "Sharkitek core models library.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"sharkitek",
|
"sharkitek",
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import {Type} from "./Types/Type";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options of a definition.
|
|
||||||
*/
|
|
||||||
export interface DefinitionOptions<SerializedType, SharkitekType>
|
|
||||||
{ //TODO implement some options, like `mandatory`.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Sharkitek model property definition.
|
|
||||||
*/
|
|
||||||
export class Definition<SerializedType, SharkitekType>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Initialize a property definition with the given type and options.
|
|
||||||
* @param type - The model property type.
|
|
||||||
* @param options - Property definition options.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
public type: Type<SerializedType, SharkitekType>,
|
|
||||||
public options: DefinitionOptions<SerializedType, SharkitekType> = {},
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a property definition with the given type and options.
|
|
||||||
* @param type - The model property type.
|
|
||||||
* @param options - Property definition options.
|
|
||||||
*/
|
|
||||||
export function SDefine<SerializedType, SharkitekType>(type: Type<SerializedType, SharkitekType>, options: DefinitionOptions<SerializedType, SharkitekType> = {}): Definition<SerializedType, SharkitekType>
|
|
||||||
{
|
|
||||||
return new Definition<SerializedType, SharkitekType>(type, options);
|
|
||||||
}
|
|
|
@ -1,85 +1,123 @@
|
||||||
import {Definition} from "./Definition";
|
import {Definition} from "./PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model properties definition type.
|
* Type definition of a model constructor.
|
||||||
*/
|
*/
|
||||||
export type ModelDefinition<T> = Partial<Record<keyof T, Definition<unknown, unknown>>>;
|
export type ConstructorOf<T extends object> = { new(): T; };
|
||||||
/**
|
|
||||||
* Model identifier type.
|
|
||||||
*/
|
|
||||||
export type ModelIdentifier<T> = keyof T;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Sharkitek model.
|
* Unknown property definition.
|
||||||
*/
|
*/
|
||||||
export abstract class Model<THIS>
|
export type UnknownDefinition = Definition<unknown, unknown>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model shape.
|
||||||
|
*/
|
||||||
|
export type ModelShape = Record<string, UnknownDefinition>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties of a model based on its shape.
|
||||||
|
*/
|
||||||
|
export type PropertiesModel<Shape extends ModelShape> = {
|
||||||
|
[k in keyof Shape]: Shape[k]["_sharkitek"];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized object type based on model shape.
|
||||||
|
*/
|
||||||
|
export type SerializedModel<Shape extends ModelShape> = {
|
||||||
|
[k in keyof Shape]: Shape[k]["_serialized"];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of a model object.
|
||||||
|
*/
|
||||||
|
export type Model<Shape extends ModelShape, IdentifierType = unknown> = ModelDefinition<Shape, IdentifierType> & PropertiesModel<Shape>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier type.
|
||||||
|
*/
|
||||||
|
export type IdentifierType<Shape extends ModelShape, K extends keyof Shape> = Shape[K]["_sharkitek"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface of a Sharkitek model definition.
|
||||||
|
*/
|
||||||
|
export interface ModelDefinition<Shape extends ModelShape, IdentifierType>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Model properties definition function.
|
* Get model identifier.
|
||||||
*/
|
*/
|
||||||
protected abstract SDefinition(): ModelDefinition<THIS>;
|
getIdentifier(): IdentifierType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of the model identifier property.
|
* Serialize the model.
|
||||||
*/
|
*/
|
||||||
protected SIdentifier(): ModelIdentifier<THIS>
|
serialize(): SerializedModel<Shape>;
|
||||||
{
|
/**
|
||||||
return undefined;
|
* Deserialize the model.
|
||||||
|
* @param obj Serialized object.
|
||||||
|
*/
|
||||||
|
deserialize(obj: SerializedModel<Shape>): Model<Shape, IdentifierType>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find out if the model is new (never deserialized) or not.
|
||||||
|
*/
|
||||||
|
isNew(): boolean;
|
||||||
|
/**
|
||||||
|
* Find out if the model is dirty or not.
|
||||||
|
*/
|
||||||
|
isDirty(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the difference between current model state and the original one.
|
||||||
|
*/
|
||||||
|
serializeDiff(): Partial<SerializedModel<Shape>>;
|
||||||
|
/**
|
||||||
|
* Set current properties values as original values.
|
||||||
|
*/
|
||||||
|
resetDiff(): void;
|
||||||
|
/**
|
||||||
|
* Get difference between original values and current ones, then reset it.
|
||||||
|
* Similar to call `serializeDiff()` then `resetDiff()`.
|
||||||
|
*/
|
||||||
|
save(): Partial<SerializedModel<Shape>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get given property definition.
|
* Define a Sharkitek model.
|
||||||
* @protected
|
* @param shape Model shape definition.
|
||||||
|
* @param identifier Identifier property name.
|
||||||
*/
|
*/
|
||||||
protected getPropertyDefinition(propertyName: string): Definition<unknown, unknown>
|
export function model<Shape extends ModelShape, Identifier extends keyof Shape = any>(
|
||||||
|
shape: Shape,
|
||||||
|
identifier?: Identifier,
|
||||||
|
): ConstructorOf<Model<Shape, IdentifierType<Shape, Identifier>>>
|
||||||
{
|
{
|
||||||
return (this.SDefinition() as any)?.[propertyName];
|
// Get shape entries.
|
||||||
|
const shapeEntries = Object.entries(shape) as [keyof Shape, UnknownDefinition][];
|
||||||
|
|
||||||
|
return class GenericModel implements ModelDefinition<Shape, IdentifierType<Shape, Identifier>>
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
// Initialize properties to undefined.
|
||||||
|
Object.assign(this,
|
||||||
|
// Build empty properties model from shape entries.
|
||||||
|
Object.fromEntries(shapeEntries.map(([key]) => [key, undefined])) as PropertiesModel<Shape>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of the model properties.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected getProperties(): string[]
|
|
||||||
{
|
|
||||||
return Object.keys(this.SDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calling a function for a defined property.
|
|
||||||
* @param propertyName - The property for which to check definition.
|
|
||||||
* @param callback - The function called when the property is defined.
|
|
||||||
* @param notProperty - The function called when the property is not defined.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected propertyWithDefinition(propertyName: string, callback: (propertyDefinition: Definition<unknown, unknown>) => void, notProperty: () => void = () => {}): unknown
|
|
||||||
{
|
|
||||||
// Getting the current property definition.
|
|
||||||
const propertyDefinition = this.getPropertyDefinition(propertyName);
|
|
||||||
if (propertyDefinition)
|
|
||||||
// There is a definition for the current property, calling the right callback.
|
|
||||||
return callback(propertyDefinition);
|
|
||||||
else
|
|
||||||
// No definition for the given property, calling the right callback.
|
|
||||||
return notProperty();
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Calling a function for each defined property.
|
* Calling a function for each defined property.
|
||||||
* @param callback - The function to call.
|
* @param callback - The function to call.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected forEachModelProperty(callback: (propertyName: string, propertyDefinition: Definition<unknown, unknown>) => unknown): any|void
|
protected forEachModelProperty<ReturnType>(callback: (propertyName: keyof Shape, propertyDefinition: UnknownDefinition) => ReturnType): ReturnType
|
||||||
{
|
{
|
||||||
for (const propertyName of this.getProperties())
|
for (const [propertyName, propertyDefinition] of shapeEntries)
|
||||||
{ // For each property, checking that its type is defined and calling the callback with its type.
|
{ // For each property, checking that its type is defined and calling the callback with its type.
|
||||||
const result = this.propertyWithDefinition(propertyName, (propertyDefinition) => {
|
|
||||||
// If the property is defined, calling the function with the property name and definition.
|
// If the property is defined, calling the function with the property name and definition.
|
||||||
const result = callback(propertyName, propertyDefinition);
|
const result = callback(propertyName, propertyDefinition);
|
||||||
|
|
||||||
// If there is a return value, returning it directly (loop is broken).
|
|
||||||
if (typeof result !== "undefined") return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
// If there is a return value, returning it directly (loop is broken).
|
// If there is a return value, returning it directly (loop is broken).
|
||||||
if (typeof result !== "undefined") return result;
|
if (typeof result !== "undefined") return result;
|
||||||
}
|
}
|
||||||
|
@ -90,30 +128,60 @@ export abstract class Model<THIS>
|
||||||
* The original properties values.
|
* The original properties values.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected _originalProperties: Record<string, any> = {};
|
protected _originalProperties: Partial<PropertiesModel<Shape>> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The original (serialized) object.
|
* The original (serialized) object.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected _originalObject: any = null;
|
protected _originalObject: SerializedModel<Shape>|null = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getIdentifier(): IdentifierType<Shape, Identifier>
|
||||||
|
{
|
||||||
|
return (this as PropertiesModel<Shape>)?.[identifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(): SerializedModel<Shape>
|
||||||
|
{
|
||||||
|
// Creating an empty (=> partial) serialized object.
|
||||||
|
const serializedObject: Partial<SerializedModel<Shape>> = {};
|
||||||
|
|
||||||
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
|
// For each defined model property, adding it to the serialized object.
|
||||||
|
serializedObject[propertyName] = propertyDefinition.type.serialize((this as PropertiesModel<Shape>)?.[propertyName]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializedObject as SerializedModel<Shape>; // Returning the serialized object.
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(obj: SerializedModel<Shape>): Model<Shape, IdentifierType<Shape, Identifier>>
|
||||||
|
{
|
||||||
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
|
// For each defined model property, assigning its deserialized value.
|
||||||
|
(this as PropertiesModel<Shape>)[propertyName] = propertyDefinition.type.deserialize(obj[propertyName]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset original property values.
|
||||||
|
this.resetDiff();
|
||||||
|
|
||||||
|
this._originalObject = obj; // The model is not a new one, but loaded from a deserialized one. Storing it.
|
||||||
|
|
||||||
|
return this as Model<Shape, IdentifierType<Shape, Identifier>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the model is new or not.
|
|
||||||
*/
|
|
||||||
isNew(): boolean
|
isNew(): boolean
|
||||||
{
|
{
|
||||||
return !this._originalObject;
|
return !this._originalObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the model is dirty or not.
|
|
||||||
*/
|
|
||||||
isDirty(): boolean
|
isDirty(): boolean
|
||||||
{
|
{
|
||||||
return this.forEachModelProperty((propertyName, propertyDefinition) => (
|
return this.forEachModelProperty((propertyName, propertyDefinition) => (
|
||||||
// For each property, checking if it is different.
|
// For each property, checking if it is different.
|
||||||
propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName])
|
propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as PropertiesModel<Shape>)[propertyName])
|
||||||
// There is a difference, we should return false.
|
// There is a difference, we should return false.
|
||||||
? true
|
? true
|
||||||
// There is no difference, returning nothing.
|
// There is no difference, returning nothing.
|
||||||
|
@ -121,97 +189,43 @@ export abstract class Model<THIS>
|
||||||
)) === true;
|
)) === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get model identifier.
|
|
||||||
*/
|
|
||||||
getIdentifier(): unknown
|
|
||||||
{
|
|
||||||
return (this as any)[this.SIdentifier()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
serializeDiff(): Partial<SerializedModel<Shape>>
|
||||||
* Set current properties values as original values.
|
|
||||||
*/
|
|
||||||
resetDiff()
|
|
||||||
{
|
{
|
||||||
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
// Creating an empty (=> partial) serialized object.
|
||||||
// For each property, set its original value to its current property value.
|
const serializedObject: Partial<SerializedModel<Shape>> = {};
|
||||||
this._originalProperties[propertyName] = (this as any)[propertyName];
|
|
||||||
propertyDefinition.type.resetDiff((this as any)[propertyName]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Serialize the difference between current model state and original one.
|
|
||||||
*/
|
|
||||||
serializeDiff(): any
|
|
||||||
{
|
|
||||||
// Creating a serialized object.
|
|
||||||
const serializedDiff: any = {};
|
|
||||||
|
|
||||||
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each defined model property, adding it to the serialized object if it has changed.
|
// For each defined model property, adding it to the serialized object if it has changed or if it is the identifier.
|
||||||
if (this.SIdentifier() == propertyName
|
if (
|
||||||
|| propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName]))
|
identifier == propertyName ||
|
||||||
// Adding the current property to the serialized object if it is the identifier or its value has changed.
|
propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as PropertiesModel<Shape>)[propertyName])
|
||||||
serializedDiff[propertyName] = propertyDefinition.type.serializeDiff((this as any)[propertyName]);
|
) // Adding the current property to the serialized object if it is the identifier or its value has changed.
|
||||||
})
|
serializedObject[propertyName] = propertyDefinition.type.serializeDiff((this as PropertiesModel<Shape>)?.[propertyName]);
|
||||||
|
|
||||||
return serializedDiff; // Returning the serialized object.
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get difference between original values and current ones, then reset it.
|
|
||||||
* Similar to call `serializeDiff()` then `resetDiff()`.
|
|
||||||
*/
|
|
||||||
save(): any
|
|
||||||
{
|
|
||||||
// Get the difference.
|
|
||||||
const diff = this.serializeDiff();
|
|
||||||
|
|
||||||
// Once the difference has been gotten, reset it.
|
|
||||||
this.resetDiff();
|
|
||||||
|
|
||||||
return diff; // Return the difference.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize the model.
|
|
||||||
*/
|
|
||||||
serialize(): any
|
|
||||||
{
|
|
||||||
// Creating a serialized object.
|
|
||||||
const serializedObject: any = {};
|
|
||||||
|
|
||||||
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
|
||||||
// For each defined model property, adding it to the serialized object.
|
|
||||||
serializedObject[propertyName] = propertyDefinition.type.serialize((this as any)[propertyName]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return serializedObject; // Returning the serialized object.
|
return serializedObject; // Returning the serialized object.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
resetDiff(): void
|
||||||
* Special operations on parse.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected parse(): void
|
|
||||||
{} // Nothing by default. TODO: create an event system to create functions like "beforeDeserialization" or "afterDeserialization".
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize the model.
|
|
||||||
*/
|
|
||||||
deserialize(serializedObject: any): THIS
|
|
||||||
{
|
{
|
||||||
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each defined model property, assigning its deserialized value to the model.
|
// For each property, set its original value to its current property value.
|
||||||
(this as any)[propertyName] = propertyDefinition.type.deserialize(serializedObject[propertyName]);
|
this._originalProperties[propertyName] = (this as PropertiesModel<Shape>)[propertyName];
|
||||||
|
propertyDefinition.type.resetDiff((this as PropertiesModel<Shape>)[propertyName]);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reset original property values.
|
save(): Partial<SerializedModel<Shape>>
|
||||||
|
{
|
||||||
|
// Get the difference.
|
||||||
|
const diff = this.serializeDiff();
|
||||||
|
|
||||||
|
// Once the difference has been obtained, reset it.
|
||||||
this.resetDiff();
|
this.resetDiff();
|
||||||
|
|
||||||
this._originalObject = serializedObject; // The model is not a new one, but loaded from a deserialized one.
|
return diff; // Return the difference.
|
||||||
|
}
|
||||||
|
|
||||||
return this as unknown as THIS; // Returning this, after deserialization.
|
} as unknown as ConstructorOf<Model<Shape, IdentifierType<Shape, Identifier>>>;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
10
src/Model/Properties.ts
Normal file
10
src/Model/Properties.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export {define} from "./PropertyDefinition";
|
||||||
|
|
||||||
|
export {array} from "./Types/ArrayType";
|
||||||
|
export {bool, boolean} from "./Types/BoolType";
|
||||||
|
export {date} from "./Types/DateType";
|
||||||
|
export {decimal} from "./Types/DecimalType";
|
||||||
|
export {model} from "./Types/ModelType";
|
||||||
|
export {numeric} from "./Types/NumericType";
|
||||||
|
export {object} from "./Types/ObjectType";
|
||||||
|
export {string} from "./Types/StringType";
|
26
src/Model/PropertyDefinition.ts
Normal file
26
src/Model/PropertyDefinition.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import {Type} from "./Types/Type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property definition class.
|
||||||
|
*/
|
||||||
|
export class Definition<SerializedType, ModelType>
|
||||||
|
{
|
||||||
|
readonly _sharkitek: ModelType;
|
||||||
|
readonly _serialized: SerializedType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a property definer instance.
|
||||||
|
* @param type Property type.
|
||||||
|
*/
|
||||||
|
constructor(public readonly type: Type<SerializedType, ModelType>)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New definition of a property of the given type.
|
||||||
|
* @param type Type of the property to define.
|
||||||
|
*/
|
||||||
|
export function define<SerializedType, ModelType>(type: Type<SerializedType, ModelType>): Definition<SerializedType, ModelType>
|
||||||
|
{
|
||||||
|
return new Definition(type);
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of an array of values.
|
* Type of an array of values.
|
||||||
|
@ -6,60 +7,60 @@ import {Type} from "./Type";
|
||||||
export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<SerializedValueType[], SharkitekValueType[]>
|
export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<SerializedValueType[], SharkitekValueType[]>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructs a new array type of a Sharkitek model property.
|
* Initialize a new array type of a Sharkitek model property.
|
||||||
* @param valueType - Type of the array values.
|
* @param valueDefinition Definition the array values.
|
||||||
*/
|
*/
|
||||||
constructor(protected valueType: Type<SerializedValueType, SharkitekValueType>)
|
constructor(protected valueDefinition: Definition<SerializedValueType, SharkitekValueType>)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: SharkitekValueType[]): SerializedValueType[]
|
serialize(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
return value.map((value) => (
|
return value.map((value) => (
|
||||||
// Serializing each value of the array.
|
// Serializing each value of the array.
|
||||||
this.valueType.serialize(value)
|
this.valueDefinition.type.serialize(value)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(value: SerializedValueType[]): SharkitekValueType[]
|
deserialize(value: SerializedValueType[]|null|undefined): SharkitekValueType[]|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
return value.map((serializedValue) => (
|
return value.map((serializedValue) => (
|
||||||
// Deserializing each value of the array.
|
// Deserializing each value of the array.
|
||||||
this.valueType.deserialize(serializedValue)
|
this.valueDefinition.type.deserialize(serializedValue)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeDiff(value: SharkitekValueType[]): any
|
serializeDiff(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
// Serializing diff of all elements.
|
// Serializing diff of all elements.
|
||||||
return value.map((value) => this.valueType.serializeDiff(value));
|
return value.map((value) => this.valueDefinition.type.serializeDiff(value) as SerializedValueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDiff(value: SharkitekValueType[]): void
|
resetDiff(value: SharkitekValueType[]|null|undefined): void
|
||||||
{
|
{
|
||||||
// Do nothing if it is not an array.
|
// Do nothing if it is not an array.
|
||||||
if (!Array.isArray(value)) return;
|
if (!Array.isArray(value)) return;
|
||||||
|
|
||||||
// Reset diff of all elements.
|
// Reset diff of all elements.
|
||||||
value.forEach((value) => this.valueType.resetDiff(value));
|
value.forEach((value) => this.valueDefinition.type.resetDiff(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of an array of values.
|
* New array property definition.
|
||||||
* @param valueType - Type of the array values.
|
* @param valueDefinition Array values type definition.
|
||||||
*/
|
*/
|
||||||
export function SArray<SerializedValueType, SharkitekValueType>(valueType: Type<SerializedValueType, SharkitekValueType>)
|
export function array<SerializedValueType, SharkitekValueType>(valueDefinition: Definition<SerializedValueType, SharkitekValueType>): Definition<SerializedValueType[], SharkitekValueType[]>
|
||||||
{
|
{
|
||||||
return new ArrayType<SerializedValueType, SharkitekValueType>(valueType);
|
return define(new ArrayType(valueDefinition));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,42 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any boolean value.
|
* Type of any boolean value.
|
||||||
*/
|
*/
|
||||||
export class BoolType extends Type<boolean, boolean>
|
export class BoolType extends Type<boolean, boolean>
|
||||||
{
|
{
|
||||||
deserialize(value: boolean): boolean
|
deserialize(value: boolean|null|undefined): boolean|null|undefined
|
||||||
{
|
{
|
||||||
|
// Keep NULL and undefined values.
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return !!value; // ensure bool type.
|
return !!value; // ensure bool type.
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: boolean): boolean
|
serialize(value: boolean|null|undefined): boolean|null|undefined
|
||||||
{
|
{
|
||||||
|
// Keep NULL and undefined values.
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return !!value; // ensure bool type.
|
return !!value; // ensure bool type.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any boolean value.
|
* New boolean property definition.
|
||||||
*/
|
*/
|
||||||
export const SBool = new BoolType();
|
export function bool(): Definition<boolean, boolean>
|
||||||
|
{
|
||||||
|
return define(new BoolType());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* New boolean property definition.
|
||||||
|
* Alias of bool.
|
||||||
|
*/
|
||||||
|
export function boolean(): ReturnType<typeof bool>
|
||||||
|
{
|
||||||
|
return bool();
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of dates.
|
* Type of dates.
|
||||||
*/
|
*/
|
||||||
export class DateType extends Type<string, Date>
|
export class DateType extends Type<string, Date>
|
||||||
{
|
{
|
||||||
deserialize(value: string): Date
|
deserialize(value: string|null|undefined): Date|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return new Date(value);
|
return new Date(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: Date): string
|
serialize(value: Date|null|undefined): string|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return value?.toISOString();
|
return value?.toISOString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of dates.
|
* New date property definition.
|
||||||
*/
|
*/
|
||||||
export const SDate = new DateType();
|
export function date(): Definition<string, Date>
|
||||||
|
{
|
||||||
|
return define(new DateType());
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of decimal numbers.
|
* Type of decimal numbers.
|
||||||
*/
|
*/
|
||||||
export class DecimalType extends Type<string, number>
|
export class DecimalType extends Type<string, number>
|
||||||
{
|
{
|
||||||
deserialize(value: string): number
|
deserialize(value: string|null|undefined): number|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return parseFloat(value);
|
return parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: number): string
|
serialize(value: number|null|undefined): string|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
return value?.toString();
|
return value?.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of decimal numbers.
|
* New decimal property definition.
|
||||||
*/
|
*/
|
||||||
export const SDecimal = new DecimalType();
|
export function decimal(): Definition<string, number>
|
||||||
|
{
|
||||||
|
return define(new DecimalType());
|
||||||
|
}
|
||||||
|
|
|
@ -1,44 +1,49 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
import {Model} from "../Model";
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
import {ConstructorOf, Model, ModelShape, SerializedModel} from "../Model";
|
||||||
/**
|
|
||||||
* Type definition of the constructor of a specific type.
|
|
||||||
*/
|
|
||||||
export type ConstructorOf<T> = { new(): T; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of a Sharkitek model value.
|
* Type of a Sharkitek model value.
|
||||||
*/
|
*/
|
||||||
export class ModelType<M extends Model<M>> extends Type<any, M>
|
export class ModelType<Shape extends ModelShape> extends Type<SerializedModel<Shape>, Model<Shape>>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructs a new model type of a Sharkitek model property.
|
* Initialize a new model type of a Sharkitek model property.
|
||||||
* @param modelConstructor - Constructor of the model.
|
* @param modelConstructor Model constructor.
|
||||||
*/
|
*/
|
||||||
constructor(protected modelConstructor: ConstructorOf<M>)
|
constructor(protected modelConstructor: ConstructorOf<Model<Shape>>)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: M|null): any
|
serialize(value: Model<Shape>|null|undefined): SerializedModel<Shape>|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
// Serializing the given model.
|
// Serializing the given model.
|
||||||
return value ? value.serialize() : null;
|
return value?.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(value: any): M|null
|
deserialize(value: SerializedModel<Shape>|null|undefined): Model<Shape>|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
// Deserializing the given object in the new model.
|
// Deserializing the given object in the new model.
|
||||||
return value ? (new this.modelConstructor()).deserialize(value) : null;
|
return (new this.modelConstructor()).deserialize(value) as Model<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeDiff(value: M): any
|
serializeDiff(value: Model<Shape>|null|undefined): Partial<SerializedModel<Shape>>|null|undefined
|
||||||
{
|
{
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (value === null) return null;
|
||||||
|
|
||||||
// Serializing the given model.
|
// Serializing the given model.
|
||||||
return value ? value.serializeDiff() : null;
|
return value?.serializeDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDiff(value: M): void
|
resetDiff(value: Model<Shape>|null|undefined): void
|
||||||
{
|
{
|
||||||
// Reset diff of the given model.
|
// Reset diff of the given model.
|
||||||
value?.resetDiff();
|
value?.resetDiff();
|
||||||
|
@ -46,10 +51,10 @@ export class ModelType<M extends Model<M>> extends Type<any, M>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of a Sharkitek model value.
|
* New model property definition.
|
||||||
* @param modelConstructor - Constructor of the model.
|
* @param modelConstructor Model constructor.
|
||||||
*/
|
*/
|
||||||
export function SModel<M extends Model<M>>(modelConstructor: ConstructorOf<M>)
|
export function model<Shape extends ModelShape>(modelConstructor: ConstructorOf<Model<Shape>>): Definition<SerializedModel<Shape>, Model<Shape>>
|
||||||
{
|
{
|
||||||
return new ModelType(modelConstructor);
|
return define(new ModelType(modelConstructor));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any numeric value.
|
* Type of any numeric value.
|
||||||
*/
|
*/
|
||||||
export class NumericType extends Type<number, number>
|
export class NumericType extends Type<number, number>
|
||||||
{
|
{
|
||||||
deserialize(value: number): number
|
deserialize(value: number|null|undefined): number|null|undefined
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: number): number
|
serialize(value: number|null|undefined): number|null|undefined
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any numeric value.
|
* New numeric property definition.
|
||||||
*/
|
*/
|
||||||
export const SNumeric = new NumericType();
|
export function numeric(): Definition<number, number>
|
||||||
|
{
|
||||||
|
return define(new NumericType());
|
||||||
|
}
|
||||||
|
|
|
@ -1,66 +1,67 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
import {Definition} from "../Definition";
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
import {ModelShape, PropertiesModel, SerializedModel, UnknownDefinition} from "../Model";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of a simple object.
|
* Type of a custom object.
|
||||||
*/
|
*/
|
||||||
export class ObjectType<Keys extends symbol|string> extends Type<Record<Keys, any>, Record<Keys, any>>
|
export class ObjectType<Shape extends ModelShape> extends Type<SerializedModel<Shape>, PropertiesModel<Shape>>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructs a new object type of a Sharkitek model property.
|
* Initialize a new object type of a Sharkitek model property.
|
||||||
* @param fieldsTypes Object fields types.
|
* @param shape
|
||||||
*/
|
*/
|
||||||
constructor(protected fieldsTypes: Record<Keys, Definition<unknown, unknown>>)
|
constructor(protected readonly shape: Shape)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(value: Record<Keys, any>): Record<Keys, any>
|
deserialize(value: SerializedModel<Shape>|null|undefined): PropertiesModel<Shape>|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
// For each defined field, deserialize its value according to its type.
|
// For each defined field, deserialize its value according to its type.
|
||||||
(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => (
|
(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => (
|
||||||
// Return an entry with the current field name and the deserialized value.
|
// Return an entry with the current field name and the deserialized value.
|
||||||
[fieldName, fieldDefinition.type.deserialize(value?.[fieldName])]
|
[fieldName, fieldDefinition.type.deserialize(value?.[fieldName])]
|
||||||
))
|
))
|
||||||
) as Record<Keys, any>;
|
) as PropertiesModel<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: Record<Keys, any>): Record<Keys, any>
|
serialize(value: PropertiesModel<Shape>|null|undefined): SerializedModel<Shape>|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
// For each defined field, serialize its value according to its type.
|
// For each defined field, serialize its value according to its type.
|
||||||
(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => (
|
(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => (
|
||||||
// Return an entry with the current field name and the serialized value.
|
// Return an entry with the current field name and the serialized value.
|
||||||
[fieldName, fieldDefinition.type.serialize(value?.[fieldName])]
|
[fieldName, fieldDefinition.type.serialize(value?.[fieldName])]
|
||||||
))
|
))
|
||||||
) as Record<Keys, any>;
|
) as PropertiesModel<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeDiff(value: Record<Keys, any>): Record<Keys, any>
|
serializeDiff(value: PropertiesModel<Shape>|null|undefined): Partial<SerializedModel<Shape>>|null|undefined
|
||||||
{
|
{
|
||||||
if (value === undefined) return undefined;
|
if (value === undefined) return undefined;
|
||||||
if (value === null) return null;
|
if (value === null) return null;
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
// For each defined field, serialize its diff value according to its type.
|
// For each defined field, serialize its diff value according to its type.
|
||||||
(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => (
|
(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => (
|
||||||
// Return an entry with the current field name and the serialized diff value.
|
// Return an entry with the current field name and the serialized diff value.
|
||||||
[fieldName, fieldDefinition.type.serializeDiff(value?.[fieldName])]
|
[fieldName, fieldDefinition.type.serializeDiff(value?.[fieldName])]
|
||||||
))
|
))
|
||||||
) as Record<Keys, any>;
|
) as PropertiesModel<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDiff(value: Record<Keys, any>): void
|
resetDiff(value: PropertiesModel<Shape>|null|undefined)
|
||||||
{
|
{
|
||||||
// For each field, reset its diff.
|
// For each field, reset its diff.
|
||||||
(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).forEach(([fieldName, fieldDefinition]) => {
|
(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).forEach(([fieldName, fieldDefinition]) => {
|
||||||
// Reset diff of the current field.
|
// Reset diff of the current field.
|
||||||
fieldDefinition.type.resetDiff(value?.[fieldName]);
|
fieldDefinition.type.resetDiff(value?.[fieldName]);
|
||||||
});
|
});
|
||||||
|
@ -68,10 +69,10 @@ export class ObjectType<Keys extends symbol|string> extends Type<Record<Keys, an
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of a simple object.
|
* New object property definition.
|
||||||
* @param fieldsTypes Object fields types.
|
* @param shape Shape of the object.
|
||||||
*/
|
*/
|
||||||
export function SObject<Keys extends symbol|string>(fieldsTypes: Record<Keys, Definition<unknown, unknown>>): ObjectType<Keys>
|
export function object<Shape extends ModelShape>(shape: Shape): Definition<SerializedModel<Shape>, PropertiesModel<Shape>>
|
||||||
{
|
{
|
||||||
return new ObjectType<Keys>(fieldsTypes);
|
return define(new ObjectType(shape));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
import {Type} from "./Type";
|
import {Type} from "./Type";
|
||||||
|
import {define, Definition} from "../PropertyDefinition";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any string value.
|
* Type of any string value.
|
||||||
*/
|
*/
|
||||||
export class StringType extends Type<string, string>
|
export class StringType extends Type<string, string>
|
||||||
{
|
{
|
||||||
deserialize(value: string): string
|
deserialize(value: string|null|undefined): string|null|undefined
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(value: string): string
|
serialize(value: string|null|undefined): string|null|undefined
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of any string value.
|
* New string property definition.
|
||||||
*/
|
*/
|
||||||
export const SString = new StringType();
|
export function string(): Definition<string, string>
|
||||||
|
{
|
||||||
|
return define(new StringType());
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
/**
|
/**
|
||||||
* Abstract class of a Sharkitek model property type.
|
* Abstract class of a Sharkitek model property type.
|
||||||
*/
|
*/
|
||||||
export abstract class Type<SerializedType, SharkitekType>
|
export abstract class Type<SerializedType, ModelType>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Serialize the given value of a Sharkitek model property.
|
* Serialize the given value of a Sharkitek model property.
|
||||||
* @param value - Value to serialize.
|
* @param value Value to serialize.
|
||||||
*/
|
*/
|
||||||
abstract serialize(value: SharkitekType): SerializedType;
|
abstract serialize(value: ModelType|null|undefined): SerializedType|null|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize the given value of a serialized Sharkitek model.
|
* Deserialize the given value of a serialized Sharkitek model.
|
||||||
* @param value - Value to deserialize.
|
* @param value - Value to deserialize.
|
||||||
*/
|
*/
|
||||||
abstract deserialize(value: SerializedType): SharkitekType;
|
abstract deserialize(value: SerializedType|null|undefined): ModelType|null|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize the given value only if it has changed.
|
* Serialize the given value only if it has changed.
|
||||||
* @param value - Value to deserialize.
|
* @param value - Value to deserialize.
|
||||||
*/
|
*/
|
||||||
serializeDiff(value: SharkitekType): SerializedType|null
|
serializeDiff(value: ModelType|null|undefined): Partial<SerializedType>|null|undefined
|
||||||
{
|
{
|
||||||
return this.serialize(value); // By default, nothing changes.
|
return this.serialize(value); // By default, nothing changes.
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export abstract class Type<SerializedType, SharkitekType>
|
||||||
* Reset the difference between the original value and the current one.
|
* Reset the difference between the original value and the current one.
|
||||||
* @param value - Value for which reset diff data.
|
* @param value - Value for which reset diff data.
|
||||||
*/
|
*/
|
||||||
resetDiff(value: SharkitekType): void
|
resetDiff(value: ModelType|null|undefined): void
|
||||||
{
|
{
|
||||||
// By default, nothing to do.
|
// By default, nothing to do.
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ export abstract class Type<SerializedType, SharkitekType>
|
||||||
* @param originalValue - Original property value.
|
* @param originalValue - Original property value.
|
||||||
* @param currentValue - Current property value.
|
* @param currentValue - Current property value.
|
||||||
*/
|
*/
|
||||||
propertyHasChanged(originalValue: SharkitekType, currentValue: SharkitekType): boolean
|
propertyHasChanged(originalValue: ModelType|null|undefined, currentValue: ModelType|null|undefined): boolean
|
||||||
{
|
{
|
||||||
return originalValue != currentValue;
|
return originalValue != currentValue;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export abstract class Type<SerializedType, SharkitekType>
|
||||||
* @param originalValue - Original serialized property value.
|
* @param originalValue - Original serialized property value.
|
||||||
* @param currentValue - Current serialized property value.
|
* @param currentValue - Current serialized property value.
|
||||||
*/
|
*/
|
||||||
serializedPropertyHasChanged(originalValue: SerializedType, currentValue: SerializedType): boolean
|
serializedPropertyHasChanged(originalValue: SerializedType|null|undefined, currentValue: SerializedType|null|undefined): boolean
|
||||||
{
|
{
|
||||||
return originalValue != currentValue;
|
return originalValue != currentValue;
|
||||||
}
|
}
|
||||||
|
|
14
src/Model/index.ts
Normal file
14
src/Model/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import * as property from "./Properties";
|
||||||
|
export { property };
|
||||||
|
|
||||||
|
export * from "./Model";
|
||||||
|
export {Definition} from "./PropertyDefinition";
|
||||||
|
|
||||||
|
export {ArrayType} from "./Types/ArrayType";
|
||||||
|
export {BoolType} from "./Types/BoolType";
|
||||||
|
export {DateType} from "./Types/DateType";
|
||||||
|
export {DecimalType} from "./Types/DecimalType";
|
||||||
|
export {ModelType} from "./Types/ModelType";
|
||||||
|
export {NumericType} from "./Types/NumericType";
|
||||||
|
export {ObjectType} from "./Types/ObjectType";
|
||||||
|
export {StringType} from "./Types/StringType";
|
18
src/index.ts
18
src/index.ts
|
@ -1,14 +1,4 @@
|
||||||
|
import * as s from "./Model";
|
||||||
export * from "./Model/Model";
|
export * from "./Model";
|
||||||
|
export { s };
|
||||||
export * from "./Model/Definition";
|
export default s;
|
||||||
|
|
||||||
export * from "./Model/Types/Type";
|
|
||||||
export * from "./Model/Types/ArrayType";
|
|
||||||
export * from "./Model/Types/BoolType";
|
|
||||||
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";
|
|
||||||
|
|
|
@ -1,39 +1,18 @@
|
||||||
import {
|
import {s} from "../src";
|
||||||
SArray,
|
|
||||||
SDecimal,
|
|
||||||
SModel,
|
|
||||||
SNumeric,
|
|
||||||
SString,
|
|
||||||
SDate,
|
|
||||||
SBool,
|
|
||||||
Model,
|
|
||||||
ModelDefinition,
|
|
||||||
SDefine, ModelIdentifier
|
|
||||||
} from "../src";
|
|
||||||
import {SObject} from "../src/Model/Types/ObjectType";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Another test model.
|
* Another test model.
|
||||||
*/
|
*/
|
||||||
class Author extends Model<Author>
|
class Author extends s.model({
|
||||||
|
name: s.property.string(),
|
||||||
|
firstName: s.property.string(),
|
||||||
|
email: s.property.string(),
|
||||||
|
createdAt: s.property.date(),
|
||||||
|
active: s.property.bool(),
|
||||||
|
})
|
||||||
{
|
{
|
||||||
name: string;
|
|
||||||
firstName: string;
|
|
||||||
email: string;
|
|
||||||
createdAt: Date;
|
|
||||||
active: boolean = true;
|
active: boolean = true;
|
||||||
|
|
||||||
protected SDefinition(): ModelDefinition<Author>
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
name: SDefine(SString),
|
|
||||||
firstName: SDefine(SString),
|
|
||||||
email: SDefine(SString),
|
|
||||||
createdAt: SDefine(SDate),
|
|
||||||
active: SDefine(SBool),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name: string = "", firstName: string = "", email: string = "", createdAt: Date = new Date())
|
constructor(name: string = "", firstName: string = "", email: string = "", createdAt: Date = new Date())
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -48,7 +27,18 @@ class Author extends Model<Author>
|
||||||
/**
|
/**
|
||||||
* A test model.
|
* A test model.
|
||||||
*/
|
*/
|
||||||
class Article extends Model<Article>
|
class Article extends s.model({
|
||||||
|
id: s.property.numeric(),
|
||||||
|
title: s.property.string(),
|
||||||
|
authors: s.property.array(s.property.model(Author)),
|
||||||
|
text: s.property.string(),
|
||||||
|
evaluation: s.property.decimal(),
|
||||||
|
tags: s.property.array(
|
||||||
|
s.property.object({
|
||||||
|
name: s.property.string(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}, "id")
|
||||||
{
|
{
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -58,27 +48,6 @@ class Article extends Model<Article>
|
||||||
tags: {
|
tags: {
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
protected SIdentifier(): ModelIdentifier<Article>
|
|
||||||
{
|
|
||||||
return "id";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SDefinition(): ModelDefinition<Article>
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
id: SDefine(SNumeric),
|
|
||||||
title: SDefine(SString),
|
|
||||||
authors: SDefine(SArray(SModel(Author))),
|
|
||||||
text: SDefine(SString),
|
|
||||||
evaluation: SDefine(SDecimal),
|
|
||||||
tags: SDefine(SArray(
|
|
||||||
SObject({
|
|
||||||
name: SDefine(SString),
|
|
||||||
})
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("deserialize", () => {
|
it("deserialize", () => {
|
||||||
|
@ -140,8 +109,8 @@ it("deserialize then save", () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "this is a test",
|
title: "this is a test",
|
||||||
authors: [
|
authors: [
|
||||||
{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: new Date(), active: true, },
|
{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: (new Date()).toISOString(), active: true, },
|
||||||
{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: new Date(), active: false, },
|
{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: (new Date()).toISOString(), active: false, },
|
||||||
],
|
],
|
||||||
text: "this is a long test.",
|
text: "this is a long test.",
|
||||||
evaluation: "25.23",
|
evaluation: "25.23",
|
||||||
|
@ -167,8 +136,8 @@ it("save with modified submodels", () => {
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "this is a test",
|
title: "this is a test",
|
||||||
authors: [
|
authors: [
|
||||||
{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: new Date(), active: true, },
|
{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: (new Date()).toISOString(), active: true, },
|
||||||
{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: new Date(), active: false, },
|
{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: (new Date()).toISOString(), active: false, },
|
||||||
],
|
],
|
||||||
text: "this is a long test.",
|
text: "this is a long test.",
|
||||||
evaluation: "25.23",
|
evaluation: "25.23",
|
||||||
|
|
Loading…
Reference in a new issue