@webda/core
Version:
Expose API with Lambda
293 lines • 10.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { ModelRef } from "../models/coremodel.js";
import { Inject, Service, ServiceParameters } from "../services/service.js";
/**
* Mapper configuration
*/
export class MapperParameters extends ServiceParameters {
constructor(params) {
super(params);
if (typeof params.fields === "string") {
this.fields = params.fields.split(",");
}
this.cascade ?? (this.cascade = false);
this.fields ?? (this.fields = []);
}
}
/**
* Map object to another object
* @WebdaModda Mapper
*/
export default class MapperService extends Service {
/**
* @override
*/
loadParameters(params) {
return new MapperParameters(params);
}
/**
* @override
*/
resolve() {
super.resolve();
this.targetStore.addReverseMap(this.parameters.targetAttribute, this.sourceService);
const method = this.parameters.async ? "onAsync" : "on";
this.sourceService[method]("Store.PartialUpdated", async (evt) => {
let prop;
if (evt.partial_update.increments) {
prop = evt.partial_update.increments.map(c => c.property);
}
else if (evt.partial_update.addItem) {
prop = evt.partial_update.addItem.property;
}
else if (evt.partial_update.deleteItem) {
prop = evt.partial_update.deleteItem.property;
}
await this._handleMapFromPartial(evt.object_id, evt.updateDate, prop);
});
this.sourceService[method]("Store.Saved", async (evt) => {
await this.handleMap(evt.object, "created");
});
this.sourceService[method]("Store.Update", async (evt) => {
await this.handleMap(evt.object, evt.update);
});
this.sourceService[method]("Store.PatchUpdate", async (evt) => {
await this.handleMap(evt.object, evt.update);
});
this.sourceService[method]("Store.Delete", async (evt) => {
await this.handleMap(evt.object, "deleted");
});
// Cascade delete when target is destroyed
if (this.parameters.cascade) {
this.targetStore[method]("Store.Deleted", async (evt) => {
let maps = evt.object[this.parameters.targetAttribute];
if (!maps) {
return;
}
await Promise.all(maps.map(mapper => this.sourceService.cascadeDelete(mapper, evt.object.getUuid())));
});
}
return this;
}
/**
* Get index of the mapper for an object
* @param map
* @param uuid
* @returns
*/
getMapper(map, uuid) {
for (let i = 0; i < map.length; i++) {
if (map[i].uuid === uuid) {
return i;
}
}
return -1;
}
/**
* Create an object mapper
*
* @param map map to create mapper for
* @param object for the mapper to represent
* @param updates to the object being made
* @returns mapper object and found = true if updates will impact the mapper
*/
createMapper(object, updates) {
let mapper = {
// TODO Move to getFullUuid
uuid: object.getUuid()
};
let found = false;
for (let mapperfield of this.parameters.fields) {
// Create the mapper object
if (updates[mapperfield] !== undefined) {
mapper[mapperfield] = updates[mapperfield];
found = true;
}
else if (object[mapperfield] !== undefined) {
mapper[mapperfield] = object[mapperfield];
}
}
return [mapper, found];
}
/**
* Update a mapper after source object was modified
*
* @param source
* @param target
* @param mapper
* @returns
*/
async _handleUpdatedMap(source, target, updates) {
let [mapper, found] = this.createMapper(source, updates);
// Linked object has not changed, check if map has changed and require updates
if (!found) {
// None of the mapped keys has been modified -> return
return;
}
return this._handleUpdatedMapMapper(source, target, mapper);
}
/**
* Update a mapper after source object was modified without change in target
*
* @param source
* @param target
* @param mapper
* @returns
*/
_handleUpdatedMapMapper(source, target, mapper) {
// Remove old reference
let i = this.getMapper(target[this.parameters.targetAttribute], source.getUuid());
// If not found just add it to the collection
if (i < 0) {
return this.targetStore.upsertItemToCollection(target.getUuid(), this.parameters.targetAttribute, mapper);
}
// Else update with a check on the uuid
return this.targetStore.upsertItemToCollection(target.getUuid(), this.parameters.targetAttribute, mapper, i, source.getUuid(), "uuid");
}
/**
* Remove the mapper for a deleted object
*
* @param source
* @param target
* @returns
*/
async _handleDeletedMap(source, target) {
// Remove from the collection
if (target[this.parameters.targetAttribute] === undefined) {
return;
}
let i = this.getMapper(target[this.parameters.targetAttribute], source.getUuid());
if (i >= 0) {
return this.targetStore.deleteItemFromCollection(target.getUuid(), this.parameters.targetAttribute, i, source.getUuid(), "uuid");
}
}
/**
* Add the mapper for a newly created object
*
* @param source
* @param target
* @returns
*/
async _handleCreatedMap(source, target) {
// Add to the object
let [mapper] = this.createMapper(source, {});
return this.targetStore.upsertItemToCollection(target.getUuid(), this.parameters.targetAttribute, mapper);
}
/**
* Return true if property belongs to the mapped properties
*
* @param property
* @returns
*/
isMapped(property) {
return this.parameters.fields.includes(property);
}
/**
* Handle the map from a partial update
*
* @param uid
* @param updateDate
* @param prop
*/
async _handleMapFromPartial(uid, updateDate, property = undefined) {
const props = Array.isArray(property) ? property : [property];
if (props.find(p => this.isMapped(p)) || this.isMapped("_lastUpdate")) {
// Not optimal need to reload the object
let source = await this.sourceService.getObject(uid);
let updates = {};
if (this.isMapped("_lastUpdate")) {
updates["_lastUpdate"] = updateDate;
}
props
.filter(prop => this.isMapped(prop))
.forEach(prop => {
if (this.isMapped(prop)) {
updates[prop] = source[prop];
}
});
await this.handleMap(source, updates);
}
}
/**
* Manage one mapping update
*
* @param store
* @param object
* @param map
* @param updates
* @returns
*/
async handleMap(object, updates) {
let attribute = object[this.parameters.attribute] || updates[this.parameters.attribute];
let mappeds = [];
let toDelete = [];
let toAdd = [];
let ids = [];
// Manage the update of linked object
if (typeof attribute === "string" || attribute instanceof ModelRef) {
attribute = attribute.toString();
if (updates[this.parameters.attribute] !== undefined && updates[this.parameters.attribute] !== attribute) {
toAdd.push(updates[this.parameters.attribute].toString());
toDelete.push(attribute);
}
else {
ids.push(attribute);
}
}
else {
if (Array.isArray(attribute)) {
ids = attribute;
if (typeof updates == "object" && updates[this.parameters.attribute]) {
let refs = updates[this.parameters.attribute];
toAdd = refs.filter(id => !ids.includes(id));
toDelete = attribute.filter(id => !refs.includes(id));
}
}
else if (typeof attribute === "object") {
ids = Object.keys(attribute);
if (typeof updates == "object" && updates[this.parameters.attribute]) {
let refs = Object.keys(updates[this.parameters.attribute]);
toAdd = refs.filter(id => !ids.includes(id));
toDelete = ids.filter(id => !refs.includes(id));
}
}
}
mappeds = await Promise.all([
...ids.map(i => this.targetStore.get(i)),
...toDelete.map(i => this.targetStore.get(i)),
...toAdd.map(i => this.targetStore.get(i))
]);
await Promise.all(mappeds
.filter(a => a !== undefined)
.map(mapped => {
if (updates === "created" || toAdd.includes(mapped.getUuid())) {
return this._handleCreatedMap(object, mapped);
}
else if (updates == "deleted" || toDelete.includes(mapped.getUuid())) {
return this._handleDeletedMap(object, mapped);
}
else if (typeof updates == "object") {
return this._handleUpdatedMap(object, mapped, updates);
}
}));
}
/**
* Recompute the whole mappers
*/
async recompute() {
// TODO Implement
}
}
__decorate([
Inject("params:target")
], MapperService.prototype, "targetStore", void 0);
__decorate([
Inject("params:source")
], MapperService.prototype, "sourceService", void 0);
export { MapperService };
//# sourceMappingURL=mapper.js.map