Skip to content

Commit

Permalink
refactor model classes
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Weirich <matthias.weirich@selectcode.de>
  • Loading branch information
vavido committed Jan 11, 2021
1 parent 328fae4 commit f587151
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 65 deletions.
42 changes: 14 additions & 28 deletions javascript/lib/api/src/model/model.ts
Expand Up @@ -14,7 +14,7 @@
/**
* Model to represent an Entity in Ditto.
*/
export abstract class EntityModel<T extends EntityModel<T>> {
export abstract class EntityModel {

/**
* Turns an object into an array of the type A. It contains one Entity for every key of the original object
Expand All @@ -24,7 +24,7 @@ export abstract class EntityModel<T extends EntityModel<T>> {
* @param parser - A method to parse instances of the desired Entity.
* @returns The array of Entities.
*/
static objectToArray<T extends EntityWithId<T>>(o: Object, parser: (obj: Object, key: string) => T): T[] {
static objectToArray<T extends EntityWithId>(o: Object, parser: (obj: Object, key: string) => T): T[] {
return Object.keys(o).map((key: string) => {
// @ts-ignore
return parser(o[key], key);
Expand All @@ -51,7 +51,7 @@ export abstract class EntityModel<T extends EntityModel<T>> {
return JSON.stringify(this.toObject());
}

equals(toCompare: T): boolean {
equals(toCompare: EntityModel): boolean {
// @ts-ignore
return toCompare !== undefined && (this === toCompare || this.toJson() === toCompare.toJson());
}
Expand All @@ -67,34 +67,21 @@ export abstract class EntityModel<T extends EntityModel<T>> {
/**
* Entity with an 'id' field
*/
export abstract class EntityWithId<T extends EntityWithId<T>> extends EntityModel<T> {
export abstract class EntityWithId extends EntityModel {
id!: string;

equals(toCompare: T): boolean {
equals(toCompare: EntityWithId): boolean {
return super.equals(toCompare) && this.id === toCompare.id;
}
}

/**
* Type that can be used for entities with unknown property keys but known property values.
* E.g. an entity that has user-defined keys but the values are always of the same type.
* In typescript such interfaces are called 'index signatures'.
*/
export interface IndexedEntityModelType<T> {
[index: string]: T;
}

/**
* Abstract entity model class that consists of entities of the same type but with unknown property keys.
* @param <T> - Type of the implementing class.
* @param <V> - Type of the entities that are stored by the model.
*/
export abstract class IndexedEntityModel<T extends IndexedEntityModel<T, V>, V>
extends EntityModel<IndexedEntityModel<T, V>> {
export abstract class IndexedEntityModel<EntryType extends EntityModel> implements Record<string, EntryType> {

protected constructor(private readonly _elements: IndexedEntityModelType<V> | undefined) {
super();
}
[key: string]: EntryType;

/**
* Map the object to an indexed object with types. Iterates all keys and maps all values with {@code mapValue}.
Expand All @@ -103,9 +90,9 @@ export abstract class IndexedEntityModel<T extends IndexedEntityModel<T, V>, V>
* @param mapValue - how to get the value from an objects value.
* @param mapKey - how to get the key from an objects key.
*/
static fromPlainObject<T>(objectToMap: Object | undefined,
mapValue: (value: any, key: string) => T,
mapKey: (key: string, value: any) => string = key => key): IndexedEntityModelType<T> | undefined {
static fromPlainObject<T extends EntityModel>(objectToMap: Object | undefined,
mapValue: (value: any, key: string) => T,
mapKey: (key: string, value: any) => string = key => key): IndexedEntityModel<T> | undefined {
if (objectToMap) {
const entries = Object.keys(objectToMap)
.map(k => {
Expand All @@ -126,7 +113,7 @@ export abstract class IndexedEntityModel<T extends IndexedEntityModel<T, V>, V>
* @param objectToMap - the object to map.
* @param removeType - function that is called to remove the type of the objects values.
*/
static toPlainObject<T>(objectToMap: IndexedEntityModelType<T>, removeType: (typedObject: T) => any) {
static toPlainObject<T extends EntityModel>(objectToMap: IndexedEntityModel<T>, removeType: (typedObject: T) => any) {
const entries = Object.keys(objectToMap)
.map(k => {
return { [k]: removeType(objectToMap[k]) };
Expand All @@ -139,10 +126,9 @@ export abstract class IndexedEntityModel<T extends IndexedEntityModel<T, V>, V>
*
* @returns The object representation of the Entity.
*/
toObject(): Object | undefined {
if (this._elements) {
// @ts-ignore
return IndexedEntityModel.toPlainObject(this._elements, element => element.toObject());
static toObject<T extends EntityModel>(entityModel: IndexedEntityModel<T>): Object | undefined {
if (entityModel != null) {
return IndexedEntityModel.toPlainObject(entityModel, element => (<T> element).toObject());
}
return undefined;
}
Expand Down
59 changes: 22 additions & 37 deletions javascript/lib/api/src/model/things.model.ts
Expand Up @@ -16,17 +16,17 @@ import { EntityModel, EntityWithId, IndexedEntityModel } from './model';
/**
* Representation of a Thing
*/
export class Thing extends EntityWithId<Thing> {
export class Thing extends EntityWithId {

public static readonly NAMESPACE_SEPARATION_REGEX = /([^:]*):(.*)/;

public constructor(private readonly _thingId: string,
private readonly _policyId?: string,
private readonly _attributes?: object,
private readonly _features?: Features,
private readonly __revision?: number,
private readonly __modified?: string,
private readonly _acl?: Acl) {
private readonly _policyId?: string,
private readonly _attributes?: object,
private readonly _features?: Features,
private readonly __revision?: number,
private readonly __modified?: string,
private readonly _acl?: Acl) {
super();
}

Expand All @@ -51,8 +51,8 @@ export class Thing extends EntityWithId<Thing> {
}

public toObject(): object {
const featuresObj = this.features ? this.features.toObject() : undefined;
const aclObj = this._acl ? this._acl.toObject() : undefined;
const featuresObj = this.features ? Features.toObject(this.features) : undefined;
const aclObj = this._acl ? Acl.toObject(this._acl) : undefined;
return EntityModel.buildObject(new Map<string, any>([
['thingId', this.thingId],
['policyId', this.policyId],
Expand Down Expand Up @@ -115,41 +115,34 @@ export class Thing extends EntityWithId<Thing> {
}
}

interface FeaturesType {
[featureId: string]: Feature;
}

/**
* Representation of Features
*/
export class Features extends IndexedEntityModel<Features, Feature> {

public constructor(readonly features?: FeaturesType) {
super(features);
}
export class Features extends IndexedEntityModel<Feature> {

/**
* Parses Features.
*
* @param o - The object to parse.
* @returns The Features
*/
public static fromObject(o: any): Features {
public static fromObject(o: any): Features | undefined {
if (o === undefined) {
return o;
}
return new Features(IndexedEntityModel.fromPlainObject(o, Feature.fromObject));
return IndexedEntityModel.fromPlainObject<Feature>(o, Feature.fromObject);
}
}

/**
* Representation of a Feature
*/
export class Feature extends EntityWithId<Feature> {
export class Feature extends EntityWithId {

public constructor(private readonly _id: string,
private readonly _definition?: string[],
private readonly _properties?: object) {
private readonly _definition?: string[],
private readonly _properties?: object) {
super();
}

Expand Down Expand Up @@ -188,36 +181,28 @@ export class Feature extends EntityWithId<Feature> {
}
}

interface AclType {
[aclEntryId: string]: AclEntry;
}

export class Acl extends IndexedEntityModel<Acl, AclEntry> {

public constructor(readonly aclEntries?: AclType) {
super(aclEntries);
}
export class Acl extends IndexedEntityModel<AclEntry> {

/**
* Parses Acl.
*
* @param o - The object to parse.
* @returns The Acl
*/
public static fromObject(o: any): Acl {
public static fromObject(o: any): Acl | undefined {
if (o === undefined) {
return o;
}
return new Acl(IndexedEntityModel.fromPlainObject(o, AclEntry.fromObject));
return IndexedEntityModel.fromPlainObject<AclEntry>(o, AclEntry.fromObject);
}
}

export class AclEntry extends EntityWithId<AclEntry> {
export class AclEntry extends EntityWithId {

public constructor(private readonly _id: string,
private readonly _read: boolean,
private readonly _write: boolean,
private readonly _administrate: boolean) {
private readonly _read: boolean,
private readonly _write: boolean,
private readonly _administrate: boolean) {
super();
}

Expand Down

0 comments on commit f587151

Please sign in to comment.