File

src/app/core/entity/schema/entity-schema.service.ts

Description

Transform between entity instances and database objects based on the dataType set for properties in Entity classes using the DatabaseField annotation.

You can inject the EntitySchemaService in your code to register your custom DefaultDatatype implementations.

This service is used by the EntityMapperService to internally transform objects. You should normally use the EntityMapperService instead of transforming objects yourself with the EntitySchemaService.

also see the How-To Guides:

Index

Methods

Constructor

constructor(injector: Injector)
Parameters :
Name Type Optional
injector Injector No

Methods

getComponent
getComponent(propertySchema: EntitySchemaField, mode: "view" | "edit")

Get the name of the component that should display this property. The edit component has to be a registered component. Components that are registered contain the DynamicComponent decorator

Parameters :
Name Type Optional Default value Description
propertySchema EntitySchemaField No

The schema definition of the attribute for which a component should be get

mode "view" | "edit" No "view"

(Optional) The mode for which a component is required. Default is "view".

Returns : string

string The name of the component which should display this property

Public getDatatypeOrDefault
getDatatypeOrDefault(datatypeName: string, failSilently)

Get the datatype for the giving name (or the default datatype if no other registered type fits)

Parameters :
Name Type Optional Default value Description
datatypeName string No

The key/name of the datatype

failSilently No false

If set to 'true' no error is thrown if datatype does not exist

Returns : any
Public loadDataIntoEntity
loadDataIntoEntity(entity: E, data: any)
Type parameters :
  • E

Helper function to assign the giving data to the given entity instance after transforming it according to the schema.

Parameters :
Name Type Optional Description
entity E No

An entity instance whose properties will be overwritten with the transformed data

data any No

The database object that will be transformed and assigned to the entity

Returns : E
Public transformDatabaseToEntityFormat
transformDatabaseToEntityFormat(data: any, schema: EntitySchema)
Type parameters :
  • T

Transform a database object to entity format according to the schema.

Parameters :
Name Type Optional Description
data any No

The database object that will be transformed to the given entity format

schema EntitySchema No

A schema defining the transformation

Returns : T
Public transformEntityToDatabaseFormat
transformEntityToDatabaseFormat(entity: Entity, schema?: EntitySchema)

Transform an entity instance to a database object according to the schema.

Parameters :
Name Type Optional Description
entity Entity No

The object (an instance of an entity type)

schema EntitySchema Yes

The schema of the entity (if not explicitly defined the schema of the given entity is used)

Returns : any
valueToDatabaseFormat
valueToDatabaseFormat(value: any, schemaField: EntitySchemaField, entity?: Entity)

Transform a single value into database format

Parameters :
Name Type Optional
value any No
schemaField EntitySchemaField No
entity Entity Yes
Returns : any
valueToEntityFormat
valueToEntityFormat(value: any, schemaField: EntitySchemaField, dataObject?: any)

Transform a single value into entity format

Parameters :
Name Type Optional
value any No
schemaField EntitySchemaField No
dataObject any Yes
Returns : any
import { Entity } from "../model/entity";
import { Injectable, Injector } from "@angular/core";
import { EntitySchema } from "./entity-schema";
import { EntitySchemaField } from "./entity-schema-field";
import { DefaultDatatype } from "../default-datatype/default.datatype";
import { asArray } from "app/utils/asArray";

/**
 * Transform between entity instances and database objects
 * based on the dataType set for properties in Entity classes using the {@link DatabaseField} annotation.
 *
 * You can inject the EntitySchemaService in your code to register your custom {@link DefaultDatatype} implementations.
 *
 * This service is used by the {@link EntityMapperService} to internally transform objects.
 * You should normally use the EntityMapperService instead of transforming objects yourself with the EntitySchemaService.
 *
 * also see the How-To Guides:
 * - [Create A New Entity Type]{@link /additional-documentation/how-to-guides/create-a-new-entity-type.html}
 */
@Injectable({ providedIn: "root" })
export class EntitySchemaService {
  /**
   * Internal cache of datatype implementations.
   */
  private schemaTypes = new Map<string, DefaultDatatype>();

  private defaultDatatype: DefaultDatatype = new DefaultDatatype();

  constructor(private injector: Injector) {}

  /**
   * Get the datatype for the giving name (or the default datatype if no other registered type fits)
   * @param datatypeName The key/name of the datatype
   * @param failSilently If set to 'true' no error is thrown if datatype does not exist
   */
  public getDatatypeOrDefault(datatypeName: string, failSilently = false) {
    if (!datatypeName) {
      return this.defaultDatatype;
    }
    if (this.schemaTypes.has(datatypeName)) {
      return this.schemaTypes.get(datatypeName);
    }

    // use Injector instead of normal dependency injection in the constructor, because some Datatypes use the SchemaService (--> Circular Dependency)
    const dataTypes: DefaultDatatype[] = this.injector.get(
      DefaultDatatype,
    ) as unknown as DefaultDatatype[];

    let dataType = dataTypes.find((d) => d.dataType === datatypeName);
    if (dataType) {
      this.schemaTypes.set(datatypeName, dataType);
      return dataType;
    } else if (!failSilently) {
      throw new Error(`Data type "${datatypeName}" does not exist`);
    }
  }

  /**
   * Transform a database object to entity format according to the schema.
   * @param data The database object that will be transformed to the given entity format
   * @param schema A schema defining the transformation
   */
  public transformDatabaseToEntityFormat<T = Entity>(
    data: any,
    schema: EntitySchema,
  ): T {
    const transformed = {};
    for (const key of schema.keys()) {
      const schemaField: EntitySchemaField = schema.get(key);

      if (data[key] === undefined) {
        continue;
      }

      const newValue = this.valueToEntityFormat(data[key], schemaField, data);
      if (newValue !== undefined) {
        transformed[key] = newValue;
      }

      if (schemaField.generateIndex) {
        throw new Error('schema option "isIndexed" not implemented yet');
      }
    }

    return transformed as T;
  }

  /**
   * Helper function to assign the giving data to the given entity instance after transforming it according to the schema.
   * @param entity An entity instance whose properties will be overwritten with the transformed data
   * @param data The database object that will be transformed and assigned to the entity
   */
  public loadDataIntoEntity<E extends Entity>(entity: E, data: any): E {
    const transformed = this.transformDatabaseToEntityFormat(
      data,
      (<typeof Entity>entity.constructor).schema,
    );
    return Object.assign(entity, transformed);
  }

  /**
   * Transform an entity instance to a database object according to the schema.
   * @param entity The object (an instance of an entity type)
   * @param schema The schema of the entity (if not explicitly defined the schema of the given entity is used)
   */
  public transformEntityToDatabaseFormat(
    entity: Entity,
    schema?: EntitySchema,
  ): any {
    if (!schema) {
      schema = entity.getSchema();
    }

    const data = {};

    for (const key of schema.keys()) {
      let value = entity[key];
      const schemaField: EntitySchemaField = schema.get(key);

      if (value === undefined) {
        // skip and keep undefined
        continue;
      }

      try {
        data[key] = this.valueToDatabaseFormat(value, schemaField, entity);
      } catch (err) {
        throw new Error(`Transformation for ${key} failed: ${err}`);
      }

      if (data[key] === undefined) {
        delete data[key];
      }
    }

    return data;
  }

  /**
   * Get the name of the component that should display this property.
   * The edit component has to be a registered component. Components that are registered contain the `DynamicComponent`
   * decorator
   *
   * @param propertySchema The schema definition of the attribute for which a component should be get
   * @param mode (Optional) The mode for which a component is required. Default is "view".
   * @returns string The name of the component which should display this property
   */
  getComponent(
    propertySchema: EntitySchemaField,
    mode: "view" | "edit" = "view",
  ): string {
    if (!propertySchema) {
      return undefined;
    }
    const componentAttribute =
      mode === "view" ? "viewComponent" : "editComponent";
    if (propertySchema[componentAttribute]) {
      return propertySchema[componentAttribute];
    }

    const dataType = this.getDatatypeOrDefault(propertySchema.dataType);
    if (dataType?.[componentAttribute]) {
      return dataType[componentAttribute];
    }
  }

  /**
   * Transform a single value into database format
   * @param value
   * @param schemaField
   * @param entity
   */
  valueToDatabaseFormat(
    value: any,
    schemaField: EntitySchemaField,
    entity?: Entity,
  ) {
    if (value === null) {
      // keep 'null' to be able to explicitly mark a value as being reset
      return null;
    }

    const dataType = this.getDatatypeOrDefault(schemaField.dataType);
    if (schemaField.isArray) {
      return asArray(value).map((v) =>
        dataType.transformToDatabaseFormat(v, schemaField, entity),
      );
    } else {
      return dataType.transformToDatabaseFormat(value, schemaField, entity);
    }
  }

  /**
   * Transform a single value into entity format
   * @param value
   * @param schemaField
   * @param dataObject
   */
  valueToEntityFormat(
    value: any,
    schemaField: EntitySchemaField,
    dataObject?: any,
  ) {
    if (value === null) {
      // keep 'null' to be able to explicitly mark a value as being reset
      return null;
    }

    const dataType = this.getDatatypeOrDefault(schemaField.dataType);
    if (schemaField.isArray) {
      return asArray(value).map((v) =>
        dataType.transformToObjectFormat(v, schemaField, dataObject),
      );
    } else {
      return dataType.transformToObjectFormat(value, schemaField, dataObject);
    }
  }
}

results matching ""

    No results matching ""