UNPKG

@typeheim/orm-on-fire

Version:
298 lines 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MutationTracker = exports.EntityManager = void 0; const Reference_1 = require("../Model/Reference"); const DocInitializer_1 = require("./DocInitializer"); const DocPersistenceManager_1 = require("./DocPersistenceManager"); const DocReference_1 = require("./DocReference"); const singletons_1 = require("../singletons"); // import FieldValue = types.FieldValue - somehow fails TS class EntityManager { constructor(metadata, entityConstructor, collectionReference) { this.metadata = metadata; this.entityConstructor = entityConstructor; this.collectionReference = collectionReference; } fromSnapshot(docSnapshot) { if (!docSnapshot.exists) { return null; } let data = docSnapshot.data(); let entity = new this.entityConstructor(); const fields = this.metadata.fields; entity['id'] = docSnapshot.id; this.metadata.fields.forEach(field => { var _a, _b, _c, _d, _e, _f; if (data[field.name] === undefined) { return; } else if ((((_a = data[field.name]) === null || _a === void 0 ? void 0 : _a.toDate) !== undefined && typeof ((_b = data[field.name]) === null || _b === void 0 ? void 0 : _b.toDate) === 'function') || (typeof data[field.name] === 'object' && ((_d = (_c = data[field.name]) === null || _c === void 0 ? void 0 : _c.constructor) === null || _d === void 0 ? void 0 : _d.name) === 'Timestamp')) { entity[field.name] = (_e = data[field.name]) === null || _e === void 0 ? void 0 : _e.toDate(); } else if ((field === null || field === void 0 ? void 0 : field.isDate) && data[field.name] && ((_f = data[field.name]) === null || _f === void 0 ? void 0 : _f.length) > 0) { try { entity[field.name] = new Date(data[field.name]); } catch (error) { console.error(error); } } else if (typeof data[field.name] === 'object' && (field === null || field === void 0 ? void 0 : field.isMap) && (typeof (field === null || field === void 0 ? void 0 : field.constructor) === 'object' || typeof (field === null || field === void 0 ? void 0 : field.constructor) === 'function')) { if (Array.isArray(data[field.name])) { entity[field.name] = data[field.name].map(obj => Object.assign(new field.constructor(), obj)); } else { entity[field.name] = Object.assign(new field.constructor(), data[field.name]); } } else { entity[field.name] = data[field.name]; } }); if (!entity['toJSON']) { //@todo need to add support for passing additional properties entity['toJSON'] = () => { let jsonData = {}; fields.forEach(field => { if (entity[field.name] !== undefined) { jsonData[field.name] = entity[field.name]; } }); return jsonData; }; } this.attachOrmMetadataToEntity(entity, docSnapshot.ref); this.attachSubCollectionsToEntity(entity, docSnapshot.ref); this.attachRefsToEntity(entity, data); if (typeof entity.init === 'function') { // @todo handle promises entity.init(); } return entity; } createTextIndex(text) { const arrName = []; let curName = ''; text.split('').forEach(letter => { curName += letter.toLowerCase(); arrName.push(curName); }); return arrName; } createReverseTextIndex(text) { const arrName = []; let curName = ''; text.split('').reverse().forEach(letter => { curName += letter.toLowerCase(); arrName.push(curName); }); return arrName; } createEntity() { let entity = new this.entityConstructor(); this.attachMetadataToNewEntity(entity); return entity; } attachSubCollectionsToEntity(entity, docReference) { let subCollectionsMetadata = this.metadata.collectionRefs; if (subCollectionsMetadata.length == 0) { return; } subCollectionsMetadata.forEach(subCollection => { entity[subCollection.fieldName] = singletons_1.CollectionFactory.createWithRef(subCollection.entity, docReference); }); } attachRefsToEntity(entity, data) { let docRefs = this.metadata.docRefs; for (let fieldName in docRefs) { entity[fieldName] = new Reference_1.Reference(docRefs[fieldName].entity, entity); if (data[fieldName] !== undefined) { entity[fieldName].___attachDockRef(DocReference_1.DocReference.fromNativeRef(data[fieldName])); } } } extractDataFromEntity(entity) { var _a, _b, _c, _d; const fields = this.metadata.fields; let dataToSave = {}; let changes = null; if (!(((_a = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _a === void 0 ? void 0 : _a.isNew) || entity.__ormOnFire === undefined)) { // not new entities require mutation check changes = (_c = (_b = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _b === void 0 ? void 0 : _b.mutation) === null || _c === void 0 ? void 0 : _c.getChanges(entity); } fields.forEach(field => { var _a, _b; if ((field === null || field === void 0 ? void 0 : field.isDate) && (field === null || field === void 0 ? void 0 : field.updateOnSave)) { let date = new Date(); entity[field.name] = date; dataToSave[field.name] = date; } else if ((field === null || field === void 0 ? void 0 : field.isDate) && (field === null || field === void 0 ? void 0 : field.generateOnCreate) && (((_a = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _a === void 0 ? void 0 : _a.isNew) || entity.__ormOnFire === undefined)) { let date = new Date(); entity[field.name] = date; dataToSave[field.name] = date; } if (changes && field.name in changes) { dataToSave[field.name] = changes[field.name]; } else if ((((_b = entity === null || entity === void 0 ? void 0 : entity.__ormOnFire) === null || _b === void 0 ? void 0 : _b.isNew) || entity.__ormOnFire === undefined) && entity[field.name] !== undefined) { // this condition required only for new entities if ((field === null || field === void 0 ? void 0 : field.isMap) && Array.isArray(entity[field.name])) { dataToSave[field.name] = entity[field.name].map(obj => { return Object.assign({}, obj); }); } else if (field === null || field === void 0 ? void 0 : field.isMap) { dataToSave[field.name] = Object.assign({}, entity[field.name]); } else { dataToSave[field.name] = entity[field.name]; } } }); const docRefs = this.metadata.docRefs; for (let fieldName in docRefs) { let docRef = (_d = entity[fieldName]) === null || _d === void 0 ? void 0 : _d.___docReference; if (docRef) { dataToSave[fieldName] = docRef.nativeRef; } } return dataToSave; } refreshNewEntity(entity, docReference) { this.attachOrmMetadataToEntity(entity, docReference); this.attachSubCollectionsToEntity(entity, docReference); } attachOrmMetadataToEntity(entity, docReference) { let persistenceManager = new DocPersistenceManager_1.DocPersistenceManager(docReference); entity['__ormOnFire'] = { isNew: false, docRef: DocReference_1.DocReference.fromNativeRef(docReference), mutation: this.createMutationTracker(entity), save: () => { return persistenceManager.update(this.extractDataFromEntity(entity), entity['__ormOnFire'].mutation); }, remove: () => { return persistenceManager.remove(); }, }; } attachMetadataToNewEntity(entity) { let docInitializer = new DocInitializer_1.DocInitializer(entity, this); entity['__ormOnFire'] = { isNew: true, docRef: null, mutation: this.createMutationTracker(entity), save: () => { return docInitializer.addTo(this.collectionReference); }, remove: () => { // @todo throw exceptions return false; }, }; } createMutationTracker(entity) { return new MutationTracker(entity, this.metadata.fields); } } exports.EntityManager = EntityManager; // @todo - change mutation tracker to use snapshot as source of original data class MutationTracker { constructor(entity, fields) { this.copy = {}; this.fields = fields; this.entity = entity; this.refreshEntity(); } refreshEntity() { let entity = this.entity; let fields = this.fields; this.copy = {}; fields.forEach(field => { if (entity[field.name] === undefined) { return; } if (Array.isArray(entity[field.name])) { // this.copy[field.name] = entity[field.name]?.slice() this.copy[field.name] = this.deepCopy(entity[field.name]); } else if (field.isMap) { this.copy[field.name] = Object.assign({}, entity[field.name]); } else if (typeof entity[field.name] === 'object' && entity[field.name] instanceof Date) { this.copy[field.name] = new Date(entity[field.name].getTime()); } else if (typeof entity[field.name] === 'object') { this.copy[field.name] = Object.assign({}, entity[field.name]); } else { this.copy[field.name] = entity[field.name]; } }); } getChanges(entity) { let changes = {}; this.fields.forEach(field => { var _a, _b; // @todo - half of checks disabled because of instability. Need further tetsing if (entity[field.name] === undefined) { if (this.copy[field.name] !== undefined) { // changes[field.name] = FieldValue.delete() - somehow fails TS changes[field.name] = undefined; } else { return; } } else if (Array.isArray(entity[field.name])) { // array should be checked explicitly changes[field.name] = entity[field.name]; // if (JSON.stringify(entity[field.name]) !== JSON.stringify(this.copy[field.name])) { // changes[field.name] = entity[field.name] // } } else if (typeof entity[field.name] === 'object' && entity[field.name] !== null) { // dates must be compared by time // if (entity[field.name] instanceof Date && entity[field.name]?.getTime() !== this.copy[field.name]?.getTime()) { // changes[field.name] = entity[field.name] // } else if (JSON.stringify(entity[field.name]) !== JSON.stringify(this.copy[field.name])) { // // objects need deep equality compare // changes[field.name] = { ...entity[field.name] } // } //dates must be compared by time if (entity[field.name] instanceof Date) { if (this.copy[field.name] && ((_a = entity[field.name]) === null || _a === void 0 ? void 0 : _a.getTime()) !== ((_b = this.copy[field.name]) === null || _b === void 0 ? void 0 : _b.getTime())) { changes[field.name] = entity[field.name]; } else if (!this.copy[field.name]) { changes[field.name] = entity[field.name]; } } else { // objects need deep equality compare changes[field.name] = Object.assign({}, entity[field.name]); } } else if (entity[field.name] !== this.copy[field.name]) { changes[field.name] = entity[field.name]; } }); return changes; } deepCopy(inObject) { let outObject, value, key; if (typeof inObject !== 'object' || inObject === null) { return inObject; // Return the value if inObject is not an object } // Create an array or object to hold the values outObject = Array.isArray(inObject) ? [] : {}; for (key in inObject) { value = inObject[key]; // Recursively (deep) copy for nested objects, including arrays outObject[key] = this.deepCopy(value); } return outObject; } } exports.MutationTracker = MutationTracker; //# sourceMappingURL=EntityManager.js.map