diff --git a/lib/entity/entity-config.base.ts b/lib/entity/entity-config.base.ts
index 8c8fa78..ba8cac5 100644
--- a/lib/entity/entity-config.base.ts
+++ b/lib/entity/entity-config.base.ts
@@ -1,11 +1,11 @@
import {EntityFields} from "./entity-fields";
-import {Field} from "./entity-field";
import {Immutability} from "../services/immutability";
import {DataEntityConstructor} from "./data-entity.base";
import {ParisConfig} from "../config/paris-config";
import {ModelBase} from "../models/model.base";
import {HttpOptions} from "../services/http.service";
import {EntityId} from "../models/entity-id.type";
+import {Field} from "./entity-field";
const DEFAULT_VALUE_ID = "__default";
diff --git a/lib/entity/entity-field.config.ts b/lib/entity/entity-field.config.ts
new file mode 100644
index 0000000..29b2f46
--- /dev/null
+++ b/lib/entity/entity-field.config.ts
@@ -0,0 +1,141 @@
+import {DataEntityType} from "./data-entity.base";
+import {ParisConfig} from "../config/paris-config";
+import {DataQuery} from "../dataset/data-query";
+
+/**
+ * Configuration for a model field decorator
+ */
+export interface FieldConfig{
+ /**
+ * An ID for the field. By default, the ID is the property's name.
+ */
+ id?:string,
+
+ /**
+ * Optional name to assign to the field. May be used for reflection, debugging, etc.
+ */
+ name?:string,
+
+ /**
+ * Specifies which property in the raw data should be assigned to the model's property.
+ * By default, Paris looks for a property of the same name as the property in the model. i.e:
+ *
+ * If an entity has the following property definition:
+ *
+ * @EntityField()
+ * name:string;
+ *
+ * Then when creating the model, if the raw data contains a `name` property with value 'Anna', the resulting model will have a `name` property with value 'Anna'.
+ *
+ * @example
Mapping from a different raw data property with `data`
+ * If your raw data has properties in snake-case rather than camel-case, you'd need to map the properties:
+ *
+ * @EntityField({ data: "creation_date" })
+ * creationData: Date;
+ *
+ * @example Using the first available value from the raw data for the model's property
+ * If an array of strings is provided for `data`, Paris will assign to the model's property value the first value from the raw data which isn't undefined or null:
+ *
+ * @EntityField({ data: ['creation_date', 'init_date', 'start_date'] })
+ * date: Date;
+ *
+ * If the raw data is:
+ * {
+ * "creation_date": null,
+ * "start_date": 1532422166428
+ * }
+ *
+ * Then the model's `date` property will have a value of Date(1532422166428), since both creation_date and init_date have no value in the data.
+ *
+ * @example Using '__self' for data to pass the whole raw data
+ * In the case when we want to separate some properties of the raw data to a sub-model, it's possible to use the special value '__self' for the `data` field configuration.
+ * This passes the whole raw data object to the field's creation, rather than just the value of a property. e.g:
+ *
+ * Person extends EntityModelBase{
+ * @EntityField()
+ * name:string;
+ *
+ * @EntityField({ data: '__self' })
+ * address:Address;
+ * }
+ *
+ * In case we want to separate all address properties from a user into an encapsulated object, for the following raw data:
+ *
+ * {
+ * "name": "Anna",
+ * "street": "Prinsengracht 263-267",
+ * "zip": "1016 GV",
+ * "city": "Amsterdam",
+ * "country": "Holland"
+ * }
+ */
+ data?:"__self" | string | Array,
+
+ /**
+ * A value to assign to the property if the raw data is `null` or undefined.
+ */
+ defaultValue?:any,
+
+ /**
+ * `arrayOf` is required when the property's type is an array of a sub-model type.
+ * It's required because the ES6 Reflect-metadata module that Paris uses to infer the types of properties doesn't support generics.
+ *
+ * @example Using a model field's arrayOf configuration for assigning an array sub-model
+ * // Without the arrayOf, addresses won't be modeled by Paris.
+ * @EntityField({ arrayOf: Address })
+ * addresses: Array
+ */
+ arrayOf?:DataEntityType,
+
+ /**
+ * If a field's `required` is set to `true`, it means that the property must have a value for the whole model to be created.
+ * If `required` is `true` and the property has a value of `null` or `undefined`, then the model itself will be null.
+ * @default false
+ */
+ required?:boolean,
+
+ /**
+ * A condition that has to be satisfied in order to assign value to the property.
+ *
+ * @example Assigning ZIP code only if street exists
+ * @EntityField({ require: "street" })
+ * zip:string;
+ *
+ * @example Assigning ZIP code only if both street and country exist
+ * @EntityField({ require: (data:AddressRawData) => data.street && data.country })
+ * zip:string;
+ */
+ require?:((data:any, config?:ParisConfig) => any) | string,
+
+ /**
+ * Parses the raw data before it's used by Paris to create the property's value
+ * Sometimes the value in the raw data is not formatted as we'd like, or more information might be needed to create the desired value. A field's `parse` configuration is available for changing the raw data before it's passed to Paris.
+ * Important: `parse` should return a new RAW data, not a Paris model.
+ *
+ * @example Parsing a bitwise value into an array
+ * @EntityField({
+ * arrayOf: NotificationFormat,
+ * parse: (formatBitWise: number) => {
+ * return notificationFormatValues.reduce((formats: Array, notificationFormat) => {
+ * return notificationFormat.id > 0 && (formatBitWise & notificationFormat.id) ? [...formats, notificationFormat.id] : formats;
+ * }, []);
+ * },
+ * })
+ * formatFlavor: Array;
+ *
+ * @param fieldData The field's data from the raw data
+ * @param itemData The whole object's raw data
+ * @param {DataQuery} query The query (if any) that was used for getting the data
+ * @returns {any} new raw data.
+ */
+ parse?:(fieldData?:any, itemData?:any, query?: DataQuery) => any,
+
+ /**
+ * A method used to serialize the model field's data back into raw data, to be used when saving the model to backend.
+ * `serialize` may also be set to `false`, in which case the field won't be included in the serialized model.
+ */
+ serialize?: false | ((itemData:any, serializationData?:any) => any)
+}
+
+export const FIELD_DATA_SELF = "__self";
+export type EntityFieldConfigFunctionOrValue = ((data:any, config?:ParisConfig) => string) | string;
diff --git a/lib/entity/entity-field.decorator.ts b/lib/entity/entity-field.decorator.ts
index 09ff6cb..4f7cf3e 100644
--- a/lib/entity/entity-field.decorator.ts
+++ b/lib/entity/entity-field.decorator.ts
@@ -1,18 +1,19 @@
import {DataEntityType} from "./data-entity.base";
-import {Field} from "./entity-field";
+import {FieldConfig} from "./entity-field.config";
import {entityFieldsService} from "../services/entity-fields.service";
+import {Field} from "./entity-field";
-export function EntityField(fieldConfig?:Field):PropertyDecorator {
+export function EntityField(fieldConfig?:FieldConfig):PropertyDecorator {
return function (entityPrototype: DataEntityType, propertyKey: string | symbol) {
let propertyConstructor:Function = (Reflect).getMetadata("design:type", entityPrototype, propertyKey);
fieldConfig = fieldConfig || {};
- let fieldConfigCopy:Field = Object.assign({}, fieldConfig);
- if (!fieldConfigCopy.id)
- fieldConfigCopy.id = String(propertyKey);
+ let field:Field = Object.assign({}, fieldConfig);
+ if (!field.id)
+ field.id = String(propertyKey);
- fieldConfigCopy.type = fieldConfig.arrayOf || propertyConstructor;
- fieldConfigCopy.isArray = propertyConstructor === Array;
- entityFieldsService.addField(entityPrototype, fieldConfigCopy);
+ field.type = fieldConfig.arrayOf || propertyConstructor;
+ field.isArray = propertyConstructor === Array;
+ entityFieldsService.addField(entityPrototype, field);
}
}
diff --git a/lib/entity/entity-field.ts b/lib/entity/entity-field.ts
index f9f8cca..e16e8c6 100644
--- a/lib/entity/entity-field.ts
+++ b/lib/entity/entity-field.ts
@@ -1,21 +1,8 @@
+import {FieldConfig} from "./entity-field.config";
import {DataEntityType} from "./data-entity.base";
-import {ParisConfig} from "../config/paris-config";
-import {DataQuery} from "../dataset/data-query";
-export interface Field{
- id?:string,
- name?:string,
- data?:"__self" | string | Array,
+export interface Field extends FieldConfig{
entity?:DataEntityType,
type?:Function,
- defaultValue?:any,
- arrayOf?:DataEntityType,
isArray?:boolean,
- required?:boolean,
- require?:((data:any, config?:ParisConfig) => any) | string,
- parse?:(fieldData?:any, itemData?:any, query?: DataQuery) => any,
- serialize?: false | ((itemData:any, serializationData?:any) => any)
}
-
-export const FIELD_DATA_SELF = "__self";
-export type EntityFieldConfigFunctionOrValue = ((data:any, config?:ParisConfig) => string) | string;
diff --git a/lib/main.ts b/lib/main.ts
index d557687..266a0fa 100644
--- a/lib/main.ts
+++ b/lib/main.ts
@@ -28,6 +28,7 @@ export {EntityEvent} from "./events/entity.event";
export {SaveEntityEvent} from "./events/save-entity.event";
export {RemoveEntitiesEvent} from "./events/remove-entities.event";
export {EntityErrorEvent, EntityErrorTypes} from "./events/entity-error.event";
+export {FieldConfig} from "./entity/entity-field.config";
export {Field} from "./entity/entity-field";
export {ApiCall} from "./entity/api-call.decorator";
export {ApiCallBackendConfigInterface} from "./models/api-call-backend-config.interface";
diff --git a/lib/repository/readonly-repository.ts b/lib/repository/readonly-repository.ts
index 3e8432e..49cf26d 100644
--- a/lib/repository/readonly-repository.ts
+++ b/lib/repository/readonly-repository.ts
@@ -12,7 +12,7 @@ import {EntityBackendConfig, ModelEntity} from "../entity/entity.config";
import {DataEntityConstructor, DataEntityType} from "../entity/data-entity.base";
import {Paris} from "../services/paris";
import {DataAvailability} from "../dataset/data-availability.enum";
-import {Field, FIELD_DATA_SELF} from "../entity/entity-field";
+import {Field} from "../entity/entity-field";
import {DataCache} from "../services/cache";
import {Index} from "../models";
import {EntityConfigBase, EntityGetMethod, IEntityConfigBase} from "../entity/entity-config.base";
@@ -24,6 +24,7 @@ import {AjaxError} from "rxjs/ajax";
import {EntityErrorEvent, EntityErrorTypes} from "../events/entity-error.event";
import {catchError, map, mergeMap, tap} from "rxjs/operators";
import {IReadonlyRepository} from "./repository.interface";
+import {FIELD_DATA_SELF} from "../entity/entity-field.config";
export class ReadonlyRepository, TRawData = object> implements IReadonlyRepository{
protected _errorSubject$: Subject;