|
@@ -2,412 +2,413 @@ const { MongoClient, ObjectID } = require("mongodb");
|
|
|
const {asynchronize, openPromise } = require('./asynchronize')
|
|
|
|
|
|
const mm = db => {
|
|
|
- class Savable {
|
|
|
- constructor(obj, ref, empty=false){
|
|
|
- this._id = null
|
|
|
- this._ref = ref
|
|
|
- this._class = this.__proto__.constructor.name
|
|
|
- this._empty = true
|
|
|
-
|
|
|
- Savable.addClass(this.__proto__.constructor)
|
|
|
-
|
|
|
- if (obj){
|
|
|
- this.populate(obj)
|
|
|
- this._empty = empty
|
|
|
+
|
|
|
+ function sliceSavable(userACL){
|
|
|
+ userACL = userACL.map(tag => tag.toString())
|
|
|
+
|
|
|
+ class Savable {
|
|
|
+ constructor(obj, ref, empty=false){
|
|
|
+ this._id = null
|
|
|
+ this._ref = ref
|
|
|
+ this._class = this.__proto__.constructor.name
|
|
|
+ this._empty = true
|
|
|
+
|
|
|
+ Savable.addClass(this.__proto__.constructor)
|
|
|
+
|
|
|
+ if (obj){
|
|
|
+ this.populate(obj)
|
|
|
+ this._empty = empty
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- backupRelations(){
|
|
|
- this._loadRelations = {};
|
|
|
- for (const relation in this.__proto__.constructor.relations){
|
|
|
- this._loadRelations[relation] = this[relation] instanceof Array ? [...this[relation]] : this[relation]
|
|
|
+
|
|
|
+ backupRelations(){
|
|
|
+ this._loadRelations = {};
|
|
|
+ for (const relation in this.__proto__.constructor.relations){
|
|
|
+ this._loadRelations[relation] = this[relation] instanceof Array ? [...this[relation]] : this[relation]
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- populate(obj){
|
|
|
- const convertSavables = (obj) => {
|
|
|
- for (const key in obj){
|
|
|
- if (Savable.isSavable(obj[key])){
|
|
|
- obj[key] = (this._ref &&
|
|
|
- Savable.equals(obj[key], this._ref)) ?
|
|
|
- this._ref :
|
|
|
- Savable.newSavable(obj[key], this)
|
|
|
- }
|
|
|
- else if (typeof obj[key] === 'object'){
|
|
|
- convertSavables(obj[key])
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ populate(obj){
|
|
|
+ const convertSavables = (obj) => {
|
|
|
+ for (const key in obj){
|
|
|
+ if (Savable.isSavable(obj[key])){
|
|
|
+ obj[key] = (this._ref &&
|
|
|
+ Savable.equals(obj[key], this._ref)) ?
|
|
|
+ this._ref :
|
|
|
+ Savable.newSavable(obj[key], this)
|
|
|
+ }
|
|
|
+ else if (typeof obj[key] === 'object'){
|
|
|
+ convertSavables(obj[key])
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ Object.assign(this, obj)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ convertSavables(this)
|
|
|
+
|
|
|
+ this.backupRelations()
|
|
|
+
|
|
|
}
|
|
|
-
|
|
|
- Object.assign(this, obj)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- convertSavables(this)
|
|
|
-
|
|
|
- this.backupRelations()
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- get _empty(){
|
|
|
- return !!this.then
|
|
|
- }
|
|
|
-
|
|
|
- set _empty(value){
|
|
|
- if (value){
|
|
|
-
|
|
|
-
|
|
|
- this.then = (cb, err) => {
|
|
|
-
|
|
|
- if (!this._id) err(new ReferenceError('Id is empty'))
|
|
|
- if (!this._class) err(new ReferenceError('Class is empty'))
|
|
|
-
|
|
|
- const promise = openPromise()
|
|
|
-
|
|
|
- this.collection.findOne(this._id).then( data => {
|
|
|
- if (!data){
|
|
|
- let error = new ReferenceError('Document Not Found')
|
|
|
- if (typeof err === 'function') err(error)
|
|
|
- else promise.reject(error)
|
|
|
- }
|
|
|
- else {
|
|
|
- delete this.then
|
|
|
- this.populate(data)
|
|
|
- if (typeof cb === 'function')
|
|
|
- promise.resolve(cb(this))
|
|
|
+
|
|
|
+ get _empty(){
|
|
|
+ return !!this.then
|
|
|
+ }
|
|
|
+
|
|
|
+ set _empty(value){
|
|
|
+ if (value){
|
|
|
+
|
|
|
+
|
|
|
+ this.then = (cb, err) => {
|
|
|
+
|
|
|
+ if (!this._id) err(new ReferenceError('Id is empty'))
|
|
|
+ if (!this._class) err(new ReferenceError('Class is empty'))
|
|
|
+
|
|
|
+ const promise = openPromise()
|
|
|
+
|
|
|
+ this.collection.findOne(this._id).then( data => {
|
|
|
+ if (!data){
|
|
|
+ let error = new ReferenceError('Document Not Found')
|
|
|
+ if (typeof err === 'function') err(error)
|
|
|
+ else promise.reject(error)
|
|
|
+ }
|
|
|
else {
|
|
|
- promise.resolve(this)
|
|
|
+ delete this.then
|
|
|
+ this.populate(data)
|
|
|
+ if (typeof cb === 'function')
|
|
|
+ promise.resolve(cb(this))
|
|
|
+ else {
|
|
|
+ promise.resolve(this)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
- return promise
|
|
|
+ })
|
|
|
+ return promise
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ delete this.then
|
|
|
}
|
|
|
}
|
|
|
- else {
|
|
|
- delete this.then
|
|
|
+
|
|
|
+ get createdAt(){
|
|
|
+ return this._id ? new Date(this._id.getTimestamp()) : null
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- get createdAt(){
|
|
|
- return this._id ? new Date(this._id.getTimestamp()) : null
|
|
|
- }
|
|
|
-
|
|
|
- get collection(){
|
|
|
- return db.collection(this._class)
|
|
|
- }
|
|
|
-
|
|
|
- async save(noSync=false){
|
|
|
- if (this.validate && !(await this.validate())){
|
|
|
- throw new SyntaxError(`validation error on entity ${this._id} of class ${this.constructor.name} (${this.name || this.key}) save`)
|
|
|
+
|
|
|
+ get collection(){
|
|
|
+ return db.collection(this._class)
|
|
|
}
|
|
|
- if (this.empty) return this;
|
|
|
-
|
|
|
- const syncRelations = async () => {
|
|
|
- if (noSync) return
|
|
|
- if (!(this && this.__proto__ && this.__proto__.constructor && this.__proto__.constructor.relations)) return
|
|
|
-
|
|
|
-
|
|
|
- async function getValueByField(field, savable) {
|
|
|
- let path = field.split('.');
|
|
|
- await savable
|
|
|
- let result = savable;
|
|
|
- let prev;
|
|
|
- let lastKey = path.pop()
|
|
|
- while (prev = result, result = result[path.shift()] && path.length);
|
|
|
- return {value: prev[lastKey], obj: prev, lastKey};
|
|
|
+
|
|
|
+ async save(noSync=false){
|
|
|
+ if (this.validate && !(await this.validate())){
|
|
|
+ throw new SyntaxError(`validation error on entity ${this._id} of class ${this.constructor.name} (${this.name || this.key}) save`)
|
|
|
}
|
|
|
-
|
|
|
- for (const relation in this.__proto__.constructor.relations){
|
|
|
- let backRef = this.__proto__.constructor.relations[relation]
|
|
|
- if (Array.isArray(backRef)) backRef = backRef[0]
|
|
|
-
|
|
|
- const loadRelation = this._loadRelations[relation]
|
|
|
- const loadRelationAsArray = loadRelation instanceof Savable ? [loadRelation] : loadRelation
|
|
|
-
|
|
|
- let {value, obj, lastKey: key} = await getValueByField(relation, this)
|
|
|
- const valueAsArray = value instanceof Savable ? [value] : value
|
|
|
- if (loadRelationAsArray){
|
|
|
- const removedRefs = valueAsArray ?
|
|
|
- loadRelationAsArray.filter(ref => !Savable.existsInArray(valueAsArray, ref)) :
|
|
|
- loadRelationAsArray
|
|
|
- for (const ref of removedRefs){
|
|
|
- try { await ref } catch (e) {console.log('SYNC RELATIONS REMOVE ERROR') }
|
|
|
-
|
|
|
- if (ref instanceof Savable)
|
|
|
- await ref.removeRelation(this, relation)
|
|
|
- }
|
|
|
+ if (this.empty) return this;
|
|
|
+
|
|
|
+ const syncRelations = async () => {
|
|
|
+ if (noSync) return
|
|
|
+ if (!(this && this.__proto__ && this.__proto__.constructor && this.__proto__.constructor.relations)) return
|
|
|
+
|
|
|
+
|
|
|
+ async function getValueByField(field, savable) {
|
|
|
+ let path = field.split('.');
|
|
|
+ await savable
|
|
|
+ let result = savable;
|
|
|
+ let prev;
|
|
|
+ let lastKey = path.pop()
|
|
|
+ while (prev = result, result = result[path.shift()] && path.length);
|
|
|
+ return {value: prev[lastKey], obj: prev, lastKey};
|
|
|
}
|
|
|
- if (valueAsArray){
|
|
|
- for (const foreignSavable of valueAsArray) {
|
|
|
- try { await foreignSavable } catch (e) {console.log('SYNC RELATIONS ADD ERROR') }
|
|
|
-
|
|
|
- let foreignLoadRelationsAsArray = Savable.arrize(foreignSavable._loadRelations[backRef])
|
|
|
- if (foreignSavable && !Savable.existsInArray(foreignLoadRelationsAsArray, this)){
|
|
|
- await foreignSavable.setRelation(this, relation)
|
|
|
+
|
|
|
+ for (const relation in this.__proto__.constructor.relations){
|
|
|
+ let backRef = this.__proto__.constructor.relations[relation]
|
|
|
+ if (Array.isArray(backRef)) backRef = backRef[0]
|
|
|
+
|
|
|
+ const loadRelation = this._loadRelations[relation]
|
|
|
+ const loadRelationAsArray = loadRelation instanceof Savable ? [loadRelation] : loadRelation
|
|
|
+
|
|
|
+ let {value, obj, lastKey: key} = await getValueByField(relation, this)
|
|
|
+ const valueAsArray = value instanceof Savable ? [value] : value
|
|
|
+ if (loadRelationAsArray){
|
|
|
+ const removedRefs = valueAsArray ?
|
|
|
+ loadRelationAsArray.filter(ref => !Savable.existsInArray(valueAsArray, ref)) :
|
|
|
+ loadRelationAsArray
|
|
|
+ for (const ref of removedRefs){
|
|
|
+ try { await ref } catch (e) {console.log('SYNC RELATIONS REMOVE ERROR') }
|
|
|
+
|
|
|
+ if (ref instanceof Savable)
|
|
|
+ await ref.removeRelation(this, relation)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (valueAsArray){
|
|
|
+ for (const foreignSavable of valueAsArray) {
|
|
|
+ try { await foreignSavable } catch (e) {console.log('SYNC RELATIONS ADD ERROR') }
|
|
|
+
|
|
|
+ let foreignLoadRelationsAsArray = Savable.arrize(foreignSavable._loadRelations[backRef])
|
|
|
+ if (foreignSavable && !Savable.existsInArray(foreignLoadRelationsAsArray, this)){
|
|
|
+ await foreignSavable.setRelation(this, relation)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- async function recursiveSlicer(obj){
|
|
|
- let result = obj instanceof Array ? [] : {}
|
|
|
- for (const key in obj){
|
|
|
-
|
|
|
- if (obj[key] && typeof obj[key] === 'object'){
|
|
|
- if (obj[key] instanceof Savable){
|
|
|
- if (!(obj[key]._id)){
|
|
|
- await obj[key].save().catch(err => console.log('ERR', err))
|
|
|
+
|
|
|
+ async function recursiveSlicer(obj){
|
|
|
+ let result = obj instanceof Array ? [] : {}
|
|
|
+ for (const key in obj){
|
|
|
+
|
|
|
+ if (obj[key] && typeof obj[key] === 'object'){
|
|
|
+ if (obj[key] instanceof Savable){
|
|
|
+ if (!(obj[key]._id)){
|
|
|
+ await obj[key].save().catch(err => console.log('ERR', err))
|
|
|
+ }
|
|
|
+ result[key] = obj[key].shortData()
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ result[key] = await recursiveSlicer(obj[key])
|
|
|
}
|
|
|
- result[key] = obj[key].shortData()
|
|
|
}
|
|
|
else {
|
|
|
- result[key] = await recursiveSlicer(obj[key])
|
|
|
+ result[key] = obj[key]
|
|
|
}
|
|
|
}
|
|
|
- else {
|
|
|
- result[key] = obj[key]
|
|
|
- }
|
|
|
+ return result;
|
|
|
}
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- const {_id, _empty, _ref, _loadRelations, then, ...toSave} = await recursiveSlicer(this)
|
|
|
-
|
|
|
-
|
|
|
- if (!this._id){
|
|
|
- const { insertedId } = await this.collection.insertOne(toSave)
|
|
|
- this._id = insertedId
|
|
|
+
|
|
|
+ const {_id, _empty, _ref, _loadRelations, then, ...toSave} = await recursiveSlicer(this)
|
|
|
+
|
|
|
+
|
|
|
+ if (!this._id){
|
|
|
+ const { insertedId } = await this.collection.insertOne(toSave)
|
|
|
+ this._id = insertedId
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ await this.collection.updateOne({_id: this._id}, {$set: toSave}).catch(err => console.log('UPDATE ERR', err))
|
|
|
+ }
|
|
|
+
|
|
|
+ await syncRelations()
|
|
|
+ this.backupRelations()
|
|
|
+ return this
|
|
|
}
|
|
|
- else {
|
|
|
- await this.collection.updateOne({_id: this._id}, {$set: toSave}).catch(err => console.log('UPDATE ERR', err))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ shortData(){
|
|
|
+ const {_id, _class} = this
|
|
|
+ return { _id, _class }
|
|
|
}
|
|
|
-
|
|
|
- await syncRelations()
|
|
|
- this.backupRelations()
|
|
|
- return this
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- shortData(){
|
|
|
- const {_id, _class} = this
|
|
|
- return { _id, _class }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- async setRelation(ref, refRelationName){
|
|
|
- await this
|
|
|
- const ourRelation = ref.__proto__.constructor.relations[refRelationName]
|
|
|
- const ourArray = ourRelation instanceof Array
|
|
|
- const ourRelationName = ourArray ? ourRelation[0] : ourRelation
|
|
|
-
|
|
|
- let shortQuery = {[ourRelationName]: ref.shortData()}
|
|
|
- let query;
|
|
|
-
|
|
|
- if (ourArray || this[ourRelationName] instanceof Array){
|
|
|
- this[ourRelationName] = this[ourRelationName] || []
|
|
|
- this[ourRelationName] = this[ourRelationName] instanceof Array ? this[ourRelationName] : [this[ourRelationName]]
|
|
|
-
|
|
|
- if (!Savable.existsInArray(this[ourRelationName], ref)) {
|
|
|
- this[ourRelationName].push(ref)
|
|
|
- if (this._id && this._loadRelations[ourRelationName] instanceof Array){
|
|
|
- this._loadRelations[ourRelationName].push(ref)
|
|
|
+
|
|
|
+
|
|
|
+ async setRelation(ref, refRelationName){
|
|
|
+ await this
|
|
|
+ const ourRelation = ref.__proto__.constructor.relations[refRelationName]
|
|
|
+ const ourArray = ourRelation instanceof Array
|
|
|
+ const ourRelationName = ourArray ? ourRelation[0] : ourRelation
|
|
|
+
|
|
|
+ let shortQuery = {[ourRelationName]: ref.shortData()}
|
|
|
+ let query;
|
|
|
+
|
|
|
+ if (ourArray || this[ourRelationName] instanceof Array){
|
|
|
+ this[ourRelationName] = this[ourRelationName] || []
|
|
|
+ this[ourRelationName] = this[ourRelationName] instanceof Array ? this[ourRelationName] : [this[ourRelationName]]
|
|
|
+
|
|
|
+ if (!Savable.existsInArray(this[ourRelationName], ref)) {
|
|
|
+ this[ourRelationName].push(ref)
|
|
|
+ if (this._id && this._loadRelations[ourRelationName] instanceof Array){
|
|
|
+ this._loadRelations[ourRelationName].push(ref)
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ query = {$addToSet: shortQuery}
|
|
|
}
|
|
|
-
|
|
|
- query = {$addToSet: shortQuery}
|
|
|
- }
|
|
|
- else {
|
|
|
- this[ourRelationName] = ref
|
|
|
- this._id && (this._loadRelations[ourRelationName] = ref)
|
|
|
- query = {$set: shortQuery}
|
|
|
- }
|
|
|
-
|
|
|
- console.log('SET RELATION:', query)
|
|
|
-
|
|
|
- if (this._id){
|
|
|
+ else {
|
|
|
+ this[ourRelationName] = ref
|
|
|
+ this._id && (this._loadRelations[ourRelationName] = ref)
|
|
|
+ query = {$set: shortQuery}
|
|
|
+ }
|
|
|
+
|
|
|
console.log('SET RELATION:', query)
|
|
|
- await this.collection.updateOne({_id: this._id}, query).catch(err => console.log('UPDATE ERR', err))
|
|
|
+
|
|
|
+ if (this._id){
|
|
|
+ console.log('SET RELATION:', query)
|
|
|
+ await this.collection.updateOne({_id: this._id}, query).catch(err => console.log('UPDATE ERR', err))
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- async removeRelation(ref, refRelationName){
|
|
|
- await this
|
|
|
- const ourRelation = ref.__proto__.constructor.relations[refRelationName]
|
|
|
- const ourArray = ourRelation instanceof Array
|
|
|
- const ourRelationName = ourArray ? ourRelation[0] : ourRelation
|
|
|
-
|
|
|
-
|
|
|
- if (this._id){
|
|
|
- const query = ourArray ? {$pull: {[ourRelationName]: ref.shortData()}}
|
|
|
- : {$set: {[ourRelationName]: null}}
|
|
|
- console.log('REMOVE RELATION:', query)
|
|
|
- await this.collection.updateOne({_id: this._id}, query).catch(err => console.log('UPDATE ERR', err))
|
|
|
+
|
|
|
+ async removeRelation(ref, refRelationName){
|
|
|
+ await this
|
|
|
+ const ourRelation = ref.__proto__.constructor.relations[refRelationName]
|
|
|
+ const ourArray = ourRelation instanceof Array
|
|
|
+ const ourRelationName = ourArray ? ourRelation[0] : ourRelation
|
|
|
+
|
|
|
+
|
|
|
+ if (this._id){
|
|
|
+ const query = ourArray ? {$pull: {[ourRelationName]: ref.shortData()}}
|
|
|
+ : {$set: {[ourRelationName]: null}}
|
|
|
+ console.log('REMOVE RELATION:', query)
|
|
|
+ await this.collection.updateOne({_id: this._id}, query).catch(err => console.log('UPDATE ERR', err))
|
|
|
+ }
|
|
|
+
|
|
|
+ (this[ourRelationName] instanceof Array) ? this._loadRelations[ourRelationName] = this[ourRelationName] = this[ourRelationName].filter(ourRef => !ourRef.equals(ref))
|
|
|
+ : this[ourRelationName] = null;
|
|
|
}
|
|
|
-
|
|
|
- (this[ourRelationName] instanceof Array) ? this._loadRelations[ourRelationName] = this[ourRelationName] = this[ourRelationName].filter(ourRef => !ourRef.equals(ref))
|
|
|
- : this[ourRelationName] = null;
|
|
|
- }
|
|
|
-
|
|
|
- async delete(noRefs=false){
|
|
|
- if (!noRefs) for (const relation in this.__proto__.constructor.relations){
|
|
|
- const backRef = this.__proto__.constructor.relations[relation]
|
|
|
-
|
|
|
- const loadRelation = this._loadRelations && this._loadRelations[relation]
|
|
|
- const loadRelationAsArray = loadRelation instanceof Savable ? [loadRelation] : loadRelation
|
|
|
-
|
|
|
- if (loadRelationAsArray){
|
|
|
- for (const ref of loadRelationAsArray){
|
|
|
- try {
|
|
|
- await ref
|
|
|
+
|
|
|
+ async delete(noRefs=false){
|
|
|
+ if (!noRefs) for (const relation in this.__proto__.constructor.relations){
|
|
|
+ const backRef = this.__proto__.constructor.relations[relation]
|
|
|
+
|
|
|
+ const loadRelation = this._loadRelations && this._loadRelations[relation]
|
|
|
+ const loadRelationAsArray = loadRelation instanceof Savable ? [loadRelation] : loadRelation
|
|
|
+
|
|
|
+ if (loadRelationAsArray){
|
|
|
+ for (const ref of loadRelationAsArray){
|
|
|
+ try {
|
|
|
+ await ref
|
|
|
+ }
|
|
|
+ catch (e) {console.log('DELETE SYNC RELATIONS ERROR') }
|
|
|
+ if (ref instanceof Savable)
|
|
|
+ await ref.removeRelation(this, relation)
|
|
|
}
|
|
|
- catch (e) {console.log('DELETE SYNC RELATIONS ERROR') }
|
|
|
- if (ref instanceof Savable)
|
|
|
- await ref.removeRelation(this, relation)
|
|
|
}
|
|
|
}
|
|
|
+ const id = this._id
|
|
|
+ const col = this._class && this.collection
|
|
|
+
|
|
|
+ for (let key in this)
|
|
|
+ delete this[key]
|
|
|
+
|
|
|
+ delete this.__proto__
|
|
|
+
|
|
|
+ if (col)
|
|
|
+ return await col.deleteOne({_id: id})
|
|
|
}
|
|
|
- const id = this._id
|
|
|
- const col = this._class && this.collection
|
|
|
-
|
|
|
- for (let key in this)
|
|
|
- delete this[key]
|
|
|
-
|
|
|
- delete this.__proto__
|
|
|
-
|
|
|
- if (col)
|
|
|
- return await col.deleteOne({_id: id})
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- static arrize(value){
|
|
|
- if (Array.isArray(value)) return value
|
|
|
- if (value) return [value]
|
|
|
- return []
|
|
|
- }
|
|
|
-
|
|
|
- static equals(obj1, obj2){
|
|
|
- if (!obj1 || !obj2) return false
|
|
|
- if(!obj1._id) return obj1 === obj2
|
|
|
- if(!obj2._id) return obj1 === obj2
|
|
|
- return obj1._id.toString() === obj2._id.toString()
|
|
|
- }
|
|
|
-
|
|
|
- equals(obj){
|
|
|
- return Savable.equals(this, obj)
|
|
|
- }
|
|
|
-
|
|
|
- static existsInArray(arr, obj){
|
|
|
- if (!Array.isArray(arr)) return false
|
|
|
-
|
|
|
- let filtered = arr.filter(item => Savable.equals(item, obj))
|
|
|
- return filtered.length
|
|
|
- }
|
|
|
-
|
|
|
- static isSavable(obj){
|
|
|
- return obj && obj._id && obj._class
|
|
|
- }
|
|
|
-
|
|
|
- static newSavable(obj, ref, empty=true){
|
|
|
- let className = obj._class || "Savable"
|
|
|
- className = Savable.classes[className] ? className : "Savable"
|
|
|
- if (obj.__proto__.constructor === Savable.classes[className]){
|
|
|
- return obj
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ static arrize(value){
|
|
|
+ if (Array.isArray(value)) return value
|
|
|
+ if (value) return [value]
|
|
|
+ return []
|
|
|
}
|
|
|
-
|
|
|
- return new Savable.classes[className](obj, ref, empty)
|
|
|
- }
|
|
|
-
|
|
|
- static addClass(_class){
|
|
|
- (typeof _class == 'function') && (Savable.classes[_class.name] = _class)
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- static get m(){
|
|
|
- return Savable._m = (Savable._m || (new Proxy({}, {
|
|
|
- get(obj, _class){
|
|
|
- if (_class in obj){
|
|
|
- return obj[_class]
|
|
|
- }
|
|
|
-
|
|
|
- const applyCursorCalls = (cursor, calls) =>{
|
|
|
- if (!calls) return cursor;
|
|
|
- for (let [method, params] of Object.entries(calls)){
|
|
|
- if (typeof cursor[method] !== "function"){
|
|
|
- throw new SyntaxError(`Wrong cursor method ${method}`)
|
|
|
+
|
|
|
+ static equals(obj1, obj2){
|
|
|
+ if (!obj1 || !obj2) return false
|
|
|
+ if(!obj1._id) return obj1 === obj2
|
|
|
+ if(!obj2._id) return obj1 === obj2
|
|
|
+ return obj1._id.toString() === obj2._id.toString()
|
|
|
+ }
|
|
|
+
|
|
|
+ equals(obj){
|
|
|
+ return Savable.equals(this, obj)
|
|
|
+ }
|
|
|
+
|
|
|
+ static existsInArray(arr, obj){
|
|
|
+ if (!Array.isArray(arr)) return false
|
|
|
+
|
|
|
+ let filtered = arr.filter(item => Savable.equals(item, obj))
|
|
|
+ return filtered.length
|
|
|
+ }
|
|
|
+
|
|
|
+ static isSavable(obj){
|
|
|
+ return obj && obj._id && obj._class
|
|
|
+ }
|
|
|
+
|
|
|
+ static newSavable(obj, ref, empty=true){
|
|
|
+ let className = obj._class || "Savable"
|
|
|
+ className = Savable.classes[className] ? className : "Savable"
|
|
|
+ if (obj.__proto__.constructor === Savable.classes[className]){
|
|
|
+ return obj
|
|
|
+ }
|
|
|
+
|
|
|
+ return new Savable.classes[className](obj, ref, empty)
|
|
|
+ }
|
|
|
+
|
|
|
+ static addClass(_class){
|
|
|
+ (typeof _class == 'function') && (Savable.classes[_class.name] = _class)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ static get m(){
|
|
|
+ return Savable._m = (Savable._m || (new Proxy({}, {
|
|
|
+ get(obj, _class){
|
|
|
+ if (_class in obj){
|
|
|
+ return obj[_class]
|
|
|
+ }
|
|
|
+
|
|
|
+ const applyCursorCalls = (cursor, calls) =>{
|
|
|
+ if (!calls) return cursor;
|
|
|
+ for (let [method, params] of Object.entries(calls)){
|
|
|
+ if (typeof cursor[method] !== "function"){
|
|
|
+ throw new SyntaxError(`Wrong cursor method ${method}`)
|
|
|
+ }
|
|
|
+ cursor = cursor[method](...params)
|
|
|
}
|
|
|
- cursor = cursor[method](...params)
|
|
|
+ return cursor;
|
|
|
}
|
|
|
- return cursor;
|
|
|
- }
|
|
|
-
|
|
|
- return obj[_class] = {
|
|
|
- * find(query, cursorCalls={}){
|
|
|
-
|
|
|
- let cursor = applyCursorCalls(db.collection(_class).find(query), cursorCalls)
|
|
|
- let cursorGen = asynchronize({s: cursor.stream(),
|
|
|
- chunkEventName: 'data',
|
|
|
- endEventName: 'close',
|
|
|
- errEventName: 'error',
|
|
|
- countMethodName: 'count'})
|
|
|
-
|
|
|
- for (const pObj of cursorGen()){
|
|
|
- yield new Promise((ok, fail) =>
|
|
|
- pObj.then(obj => (ok(Savable.newSavable(obj, null, false))),
|
|
|
- err => fail(err)))
|
|
|
+
|
|
|
+ return obj[_class] = {
|
|
|
+ * find(query, cursorCalls={}){
|
|
|
+
|
|
|
+ let cursor = applyCursorCalls(db.collection(_class).find(query), cursorCalls)
|
|
|
+ let cursorGen = asynchronize({s: cursor.stream(),
|
|
|
+ chunkEventName: 'data',
|
|
|
+ endEventName: 'close',
|
|
|
+ errEventName: 'error',
|
|
|
+ countMethodName: 'count'})
|
|
|
+
|
|
|
+ for (const pObj of cursorGen()){
|
|
|
+ yield new Promise((ok, fail) =>
|
|
|
+ pObj.then(obj => (ok(Savable.newSavable(obj, null, false))),
|
|
|
+ err => fail(err)))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async count(query, cursorCalls={}){
|
|
|
+ let cursor = applyCursorCalls(db.collection(_class).find(query), cursorCalls)
|
|
|
+ return await cursor.count(true)
|
|
|
+ },
|
|
|
+ async findOne(query){
|
|
|
+ let result = await db.collection(_class).findOne(query)
|
|
|
+ if (result)
|
|
|
+ return Savable.newSavable(result, null, false)
|
|
|
+ return result
|
|
|
}
|
|
|
- },
|
|
|
- async count(query, cursorCalls={}){
|
|
|
- let cursor = applyCursorCalls(db.collection(_class).find(query), cursorCalls)
|
|
|
- return await cursor.count(true)
|
|
|
- },
|
|
|
- async findOne(query){
|
|
|
- let result = await db.collection(_class).findOne(query)
|
|
|
- if (result)
|
|
|
- return Savable.newSavable(result, null, false)
|
|
|
- return result
|
|
|
}
|
|
|
+ },
|
|
|
+
|
|
|
+ set(obj, propName, value){
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
- set(obj, propName, value){
|
|
|
- }
|
|
|
- })))
|
|
|
- }
|
|
|
-
|
|
|
- static get relations(){
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- return {}
|
|
|
+ })))
|
|
|
+ }
|
|
|
+
|
|
|
+ static get relations(){
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return {}
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- Savable.classes = {Savable}
|
|
|
-
|
|
|
+
|
|
|
+ Savable.classes = {Savable}
|
|
|
+
|
|
|
|
|
|
* sliceSavable - slice (limit) Savables for some permission
|
|
|
* Array userACL - array of objectIDs, words or savable refs - current user, group objectid, or `tags` or `role` (ACL)
|
|
|
*/
|
|
|
|
|
|
- function sliceSavable(userACL){
|
|
|
- userACL = userACL.map(tag => tag.toString())
|
|
|
-
|
|
|
class SlicedSavable extends Savable {
|
|
|
constructor(...params){
|
|
|
super (...params)
|