UNPKG

@harishreddym/baqend

Version:

Baqend JavaScript SDK

340 lines (291 loc) 9.91 kB
'use strict'; const binding = require('../binding'); const SingularAttribute = require('./SingularAttribute'); const BasicType = require('./BasicType'); const Type = require('./Type'); const ManagedType = require('./ManagedType'); const Permission = require('../util/Permission'); const Metadata = require('../util/Metadata'); /** * @alias metamodel.EntityType * @extends metamodel.ManagedType */ class EntityType extends ManagedType { /** * @inheritDoc * @return {Type.PersistenceType} */ get persistenceType() { return Type.PersistenceType.ENTITY; } /** * @type metamodel.SingularAttribute */ get id() { return this.declaredId || this.superType.id; } /** * @type metamodel.SingularAttribute */ get version() { return this.declaredVersion || this.superType.version; } /** * @type metamodel.SingularAttribute */ get acl() { return this.declaredAcl || this.superType.acl; } /** * @param {string} ref * @param {metamodel.EntityType} superType * @param {Class<binding.Entity>=} typeConstructor */ constructor(ref, superType, typeConstructor) { super(ref, typeConstructor); /** @type metamodel.SingularAttribute */ this.declaredId = null; /** @type metamodel.SingularAttribute */ this.declaredVersion = null; /** @type metamodel.SingularAttribute */ this.declaredAcl = null; /** @type metamodel.EntityType */ this.superType = superType; /** @type util.Permission */ this.loadPermission = new Permission(); /** @type util.Permission */ this.updatePermission = new Permission(); /** @type util.Permission */ this.deletePermission = new Permission(); /** @type util.Permission */ this.queryPermission = new Permission(); /** @type util.Permission */ this.schemaSubclassPermission = new Permission(); /** @type util.Permission */ this.insertPermission = new Permission(); } /** * @inheritDoc */ createProxyClass() { let Class = this.superType.typeConstructor; if (Class === Object) { switch (this.name) { case 'User': Class = binding.User; break; case 'Role': Class = binding.Role; break; default: Class = binding.Entity; break; } } return this.enhancer.createProxy(Class); } /** * Gets all on this class referencing attributes * * @param {EntityManager} db The instances will be found by this EntityManager * @param {Object} [options] Some options to pass * @param {Array.<string>} [options.classes] An array of class names to filter for, null for no filter * @return {Map.<metamodel.ManagedType, Set.<string>>} A map from every referencing class to a set of its referencing * attribute names */ getReferencing(db, options) { const opts = Object.assign({}, options); const entities = db.metamodel.entities; const referencing = new Map(); const names = Object.keys(entities); for (let i = 0, len = names.length; i < len; i += 1) { const name = names[i]; // Skip class if not in class filter if (!opts.classes || opts.classes.indexOf(name) !== -1) { const entity = entities[name]; const iter = entity.attributes(); for (let el = iter.next(); !el.done; el = iter.next()) { const attr = el.value; // Filter only referencing singular and collection attributes if (attr.type === this || attr.elementType === this) { const typeReferences = referencing.get(attr.declaringType) || new Set(); typeReferences.add(attr.name); referencing.set(attr.declaringType, typeReferences); } } } } return referencing; } /** * @inheritDoc */ createObjectFactory(db) { switch (this.name) { case 'User': return binding.UserFactory.create(this, db); case 'Device': return binding.DeviceFactory.create(this, db); case 'Object': return undefined; default: return binding.EntityFactory.create(this, db); } } /** * @param {util.Metadata} state The root object state, can be <code>null</code> if a currentObject is provided * @param {json} jsonObject The json data to merge * @param {*} currentObject The object where the jsonObject will be merged into, if the current object is null, * a new instance will be created * @param {Object=} options The options used to apply the json * @param {boolean} [options.persisting=false] indicates if the current state will be persisted. * Used to update the internal change tracking state of collections and mark the object persistent or dirty afterwards * @param {boolean} [options.onlyMetadata=false] Indicates if only the metadata should be updated * @return {*} The merged entity instance */ fromJsonValue(state, jsonObject, currentObject, options) { // handle references if (typeof jsonObject === 'string') { return state.db.getReference(jsonObject); } if (!jsonObject || typeof jsonObject !== 'object') { return null; } const opt = Object.assign({ persisting: false, onlyMetadata: false, }, options); let obj; let objectState; if (currentObject) { const currentObjectState = Metadata.get(currentObject); // merge state into the current object if: // 1. The provided json does not contains an id and we have an already created object for it // 2. The object was created without an id and was later fetched from the server (e.g. User/Role) // 3. The provided json has the same id as the current object, they can differ on embedded json for a reference if (!jsonObject.id || !currentObjectState.id || jsonObject.id === currentObjectState.id) { obj = currentObject; objectState = currentObjectState; } } if (!obj) { obj = state.db.getReference(this.typeConstructor, jsonObject.id); objectState = Metadata.get(obj); } // deserialize our properties objectState.enable(false); super.fromJsonValue(objectState, jsonObject, obj, opt); objectState.enable(true); if (opt.persisting) { objectState.setPersistent(); } else if (!opt.onlyMetadata) { objectState.setDirty(); } return obj; } /** * Converts the given object to json * @param {util.Metadata} state The root object state * @param {*} object The object to convert * @param {Object} [options=false] to json options by default excludes the metadata * @param {boolean} [options.excludeMetadata=false] Excludes the metadata form the serialized json * @param {number|boolean} [options.depth=0] Includes up to depth referenced objects into the serialized json * @param {boolean} [options.persisting=false] indicates if the current state will be persisted. * Used to update the internal change tracking state of collections and mark the object persistent if its true * @return {json} JSON-Object */ toJsonValue(state, object, options) { const opt = Object.assign({ excludeMetadata: false, depth: 0, persisting: false, }, options); const isInDepth = opt.depth === true || opt.depth > -1; // check if object is already loaded in state const objectState = object && Metadata.get(object); if (isInDepth && objectState && objectState.isAvailable) { // serialize our properties objectState.enable(false); const json = super.toJsonValue(objectState, object, Object.assign({}, opt, { depth: opt.depth === true ? true : opt.depth - 1, })); objectState.enable(true); return json; } if (object instanceof this.typeConstructor) { object.attach(state.db); return object.id; } return null; } toString() { return 'EntityType(' + this.ref + ')'; } toJSON() { const json = super.toJSON(); json.acl.schemaSubclass = this.schemaSubclassPermission; json.acl.insert = this.insertPermission; json.acl.update = this.updatePermission; json.acl.delete = this.deletePermission; json.acl.query = this.queryPermission; return json; } } /** * @alias metamodel.EntityType.Object * @extends metamodel.EntityType */ class ObjectType extends EntityType { static get ref() { return '/db/Object'; } constructor() { super(EntityType.Object.ref, null, Object); this.declaredId = new class extends SingularAttribute { constructor() { super('id', BasicType.String, true); } getJsonValue(state) { return state.id || undefined; } setJsonValue(state, object, jsonValue) { if (!this.id) { state.id = jsonValue; } } }(); this.declaredId.init(this, 0); this.declaredId.isId = true; this.declaredVersion = new class extends SingularAttribute { constructor() { super('version', BasicType.Integer, true); } getJsonValue(state) { return state.version || undefined; } setJsonValue(state, object, jsonValue) { if (jsonValue) { state.version = jsonValue; } } }(); this.declaredVersion.init(this, 1); this.declaredVersion.isVersion = true; this.declaredAcl = new class extends SingularAttribute { constructor() { super('acl', BasicType.JsonObject, true); } getJsonValue(state) { return state.acl.toJSON(); } setJsonValue(state, object, jsonValue) { state.acl.fromJSON(jsonValue || {}); } }(); this.declaredAcl.init(this, 2); this.declaredAcl.isAcl = true; this.declaredAttributes = [this.declaredId, this.declaredVersion, this.declaredAcl]; } } EntityType.Object = ObjectType; module.exports = EntityType;