UNPKG

breeze-client

Version:

Breeze data management for JavaScript clients

255 lines (251 loc) 9.51 kB
import { EntityManager, EntityQuery, EntityState, ExpandClause } from 'breeze-client'; //#region Copyright, Version, and Description function mixinEntityGraph(emclass) { const proto = EntityManager.prototype; if (!proto.getEntityGraph) { proto.getEntityGraph = getEntityGraph; } } mixinEntityGraph(EntityManager); function getEntityGraph(roots, expand) { if (roots instanceof EntityQuery) { let newRoots = this.executeQueryLocally(roots); return getEntityGraphCore(newRoots, expand || roots.expandClause); } else { return getEntityGraphCore(roots, expand); } } function getEntityGraphCore(root, expand) { let entityGroupMap; let graph = []; let rootType; let roots = Array.isArray(root) ? root : [root]; addToGraph(roots); // removes dups & nulls roots = graph.slice(); // copy of de-duped roots if (roots.length) { getRootInfo(); getExpand(); buildGraph(); } return graph; function addToGraph(entities) { entities.forEach(function (entity) { if (entity && graph.indexOf(entity) < 0) { graph.push(entity); } }); } function getRootInfo() { let compatTypes; roots.forEach(function (root, ix) { let aspect; if (!root || !(aspect = root.entityAspect)) { throw getRootErr(ix, 'is not an entity'); } if (aspect.entityState === EntityState.Detached) { throw getRootErr(ix, 'is a detached entity'); } let em = aspect.entityManager; if (entityGroupMap) { if (entityGroupMap !== em._entityGroupMap) { throw getRootErr(ix, "has a different 'EntityManager' than other roots"); } } else { entityGroupMap = em._entityGroupMap; } getRootType(root, ix); }); function getRootErr(ix, msg) { return new Error("'getEntityGraph' root[" + ix + "] " + msg); } function getRootType(root, ix) { let thisType = root.entityType; if (!rootType) { rootType = thisType; return; } else if (rootType === thisType) { return; } // Types differs. Look for closest common base type // does thisType derive from current rootType? let baseType = rootType; do { compatTypes = compatTypes || baseType.getSelfAndSubtypes(); if (compatTypes.indexOf(thisType) > -1) { rootType = baseType; return; } baseType = baseType.baseEntityType; compatTypes = null; } while (baseType); // does current rootType derives from thisType? baseType = thisType; do { compatTypes = baseType.getSelfAndSubtypes(); if (compatTypes.indexOf(rootType) > -1) { rootType = baseType; return; } baseType = baseType.baseEntityType; } while (baseType); throw getRootErr(ix, "is not EntityType-compatible with other roots"); } } function getExpand() { try { if (!expand) { expand = []; } else if (typeof expand === 'string') { // tricky because Breeze expandClause not exposed publically expand = new EntityQuery().expand(expand).expandClause; } if (expand instanceof ExpandClause && expand.propertyPaths) { // expand clause expand = expand.propertyPaths; } else if (Array.isArray(expand)) { if (!expand.every(function (elem) { return typeof elem === 'string'; })) { throw ''; } } else { throw ''; } } catch (_) { throw new Error("expand must be an expand string, array of string paths, or a query expand clause"); } } function buildGraph() { if (expand && expand instanceof Array && expand.length) { let fns = expand.map(makePathFn); fns.forEach(function (fn) { fn(roots); }); } } // Make function to get entities along a single expand path // such as 'Orders.OrderDetails.Product' function makePathFn(path) { let fns = [], segments = path.split('.'), type = rootType; for (let i = 0, slen = segments.length; i < slen; i++) { let f = makePathSegmentFn(type, segments[i]); type = f.navType; fns.push(f); } return function pathFn(entities) { for (let j = 0, flen = fns.length; j < flen; j++) { let elen = entities.length; if (elen === 0) { return; } // nothing left to explore // fn to get related entities for this path segment let fn = fns[j]; // get entities related by this path segment let related = []; for (let k = 0; k < elen; k++) { related = related.concat(fn(entities[k])); } addToGraph(related); if (j >= flen - 1) { return; } // no more path segments // reset entities to deduped related entities entities = []; for (let l = 0, rlen = related.length; l < rlen; l++) { let r = related[l]; if (entities.indexOf(r) < 0) { entities.push(r); } } } }; } // Make function to get entities along a single expand path segment // such as the 'OrderDetails' in the 'Orders.OrderDetails.Product' path function makePathSegmentFn(baseType, segment) { let baseTypeName, fn = undefined, navType; try { baseTypeName = baseType.name; let nav = baseType.getNavigationProperty(segment); let fkName = nav.foreignKeyNames[0]; if (!nav) { throw new Error(segment + " is not a navigation property of " + baseTypeName); } navType = nav.entityType; // add derived types let navTypes = navType.getSelfAndSubtypes(); let grps = []; // non-empty groups for these types navTypes.forEach(function (t) { let grp = entityGroupMap[t.name]; if (grp && grp._entities.length > 0) { grps.push(grp); } }); let grpCount = grps.length; if (grpCount === 0) { // no related entities in cache fn = function () { return []; }; } else if (fkName) { fn = function (entity) { let val = null; try { let keyValue = entity.getProperty(fkName); for (let i = 0; i < grpCount; i += 1) { val = grps[i]._entities[grps[i]._indexMap[keyValue]]; if (val) { break; } } } catch (e) { rethrow(e); } return val; }; } else { fkName = nav.inverse ? nav.inverse.foreignKeyNames[0] : nav.invForeignKeyNames[0]; if (!fkName) { throw new Error("No inverse keys"); } fn = function (entity) { let vals = []; try { let keyValue = entity.entityAspect.getKey().values[0]; grps.forEach(function (grp) { vals = vals.concat(grp._entities.filter(function (en) { return en && en.getProperty(fkName) === keyValue; })); }); } catch (e) { rethrow(e); } return vals; }; } fn.navType = navType; fn.path = segment; } catch (err) { rethrow(err); } return fn; function rethrow(e) { let typeName = baseTypeName || baseType; let error = new Error("'getEntityGraph' can't expand '" + segment + "' for " + typeName); error.innerError = e; throw error; } } } /** * Generated bundle index. Do not edit. */ export { mixinEntityGraph }; //# sourceMappingURL=breeze-client-mixin-get-entity-graph.mjs.map