UNPKG

@webda/core

Version:

Expose API with Lambda

199 lines 7.57 kB
import { Service } from "../services/service.js"; /** * Mapper service * * Ensure each ModelMapped properties is in-sync with their respective model */ export class ModelMapper extends Service { constructor() { super(...arguments); this.mappers = {}; } resolve() { var _a, _b; super.resolve(); const app = this.getWebda().getApplication(); const graph = app.getGraph(); for (let i in graph) { if (!graph[i].maps) continue; this.log("INFO", i, graph[i].maps); const targetModel = app.getModel(i); for (let j in graph[i].maps) { const mapper = graph[i].maps[j]; (_a = this.mappers)[_b = mapper.model] ?? (_a[_b] = []); // Search for links this.mappers[mapper.model].push({ attribute: mapper.targetLink, attributes: mapper.targetAttributes, model: targetModel, modelAttribute: mapper.attribute, type: graph[mapper.model].links?.find(p => p.attribute === mapper.targetLink)?.type || "LINK" }); } } this.log("INFO", "Add listeners for", this.mappers); for (let modelName in this.mappers) { const model = app.getModel(modelName); model.on("Store.Deleted", async (evt) => { return this.handleEvent(modelName, evt, "Store.Deleted"); }); model.on("Store.Saved", async (evt) => { return this.handleEvent(modelName, evt, "Store.Saved"); }); model.on("Store.Updated", async (evt) => { return this.handleEvent(modelName, evt, "Store.Updated"); }); model.on("Store.PatchUpdated", async (evt) => { return this.handlePartialEvent(modelName, evt, "Store.PatchUpdated"); }); model.on("Store.PartialUpdated", async (evt) => { return this.handlePartialEvent(modelName, evt, "Store.PartialUpdated"); }); } return this; } /** * Get uuids from a ModelLinker * @param object * @param mapper * @returns */ getUuidsFromMapper(object, mapper) { const targetUuids = []; if (mapper.type === "LINK") { targetUuids.push(object[mapper.attribute]?.getUuid()); } else if (mapper.type === "LINKS_MAP") { targetUuids.push(...Object.values(object[mapper.attribute]).map((p) => p.getUuid())); } else { targetUuids.push(...object[mapper.attribute].map(p => (typeof p === "string" ? p : p.getUuid()))); } return targetUuids.filter(p => p); } /** * Handle event * @param modelName * @param evt * @param type * @returns */ async handleEvent(modelName, evt, type) { let p = []; for (let mapper of this.mappers[modelName]) { // For each mapper we identify uuid to add mapper to, uuid to remove mapper from and uuid to update const toAdds = []; const toRemoves = []; const toUpdates = []; if (type === "Store.Saved") { toAdds.push(...this.getUuidsFromMapper(evt.object, mapper)); } else if (type === "Store.Deleted") { toRemoves.push(...this.getUuidsFromMapper(evt.object, mapper)); } else if (type === "Store.Updated") { const oldUuids = this.getUuidsFromMapper(evt.previous, mapper); const newUuids = this.getUuidsFromMapper(evt.object, mapper); oldUuids.forEach(u => { if (newUuids.includes(u)) { toUpdates.push(u); } else { toRemoves.push(u); } }); newUuids.forEach(u => { if (!oldUuids.includes(u)) { toAdds.push(u); } }); } const mapped = this.getMapped(evt.object, mapper); // Add all promise toAdds.forEach(u => { p.push(mapper.model.ref(u).upsertItemToCollection(mapper.modelAttribute, mapped)); }); // Remove all promise toRemoves.forEach(u => { p.push((async () => { const targetModel = await mapper.model.ref(u).get(); if (!targetModel) return; const ind = targetModel[mapper.modelAttribute].findIndex(p => p.uuid === evt.object.getUuid()); if (ind >= 0) { await mapper.model.ref(u).deleteItemFromCollection(mapper.modelAttribute, ind, evt.object.getUuid()); } })()); }); // Update all promise toUpdates.forEach(u => { p.push((async () => { const targetModel = await mapper.model.ref(u).get(); if (!targetModel) return; const ind = targetModel[mapper.modelAttribute].findIndex(p => p.uuid === evt.object.getUuid()); if (ind >= 0) { await mapper.model .ref(u) .upsertItemToCollection(mapper.modelAttribute, mapped, ind, evt.object.getUuid()); } })()); }); } return Promise.all(p); } /** * Handle partial event to update a mapper * * @param modelName * @param evt * @param type * @returns */ async handlePartialEvent(modelName, evt, type) { let uuid = evt.object?.getUuid(); // Only use in Store.PartialUpdated const attributes = []; if (type === "Store.PartialUpdated") { const partial = evt; if (partial.partial_update.increments) { attributes.push(...partial.partial_update.increments.map(p => p.property)); } if (partial.partial_update.deleteAttribute) { attributes.push(partial.partial_update.deleteAttribute); } uuid = partial.object_id; } else if (type === "Store.PatchUpdated") { attributes.push(...Object.keys(evt.object)); } for (let mapper of this.mappers[modelName]) { this.log("TRACE", "Should update a mapper based on Store.PartialUpdated", evt); const partial = evt; // Search if one property is mapped then redirect to Store.Updated if (mapper.attributes.find(p => attributes.find(i => i === p))) { const object = await partial.store.get(uuid); return await this.handleEvent(modelName, { object, previous: object }, "Store.Updated"); } } } /** * Get a mapped object based on Mapper * * Copy attributes that are supposed to be copied * * @param model * @param mapper * @returns */ getMapped(model, mapper) { let obj = {}; for (let attr of mapper.attributes) { obj[attr] = model[attr]; } obj["uuid"] = model.getUuid(); return obj; } } //# sourceMappingURL=modelmapper.js.map