store and reference entity by unique id, not name (elements, graph, etc.)

This commit is contained in:
Ashley Engelund (weedySeaDragon @ github)
2022-09-24 16:05:20 -07:00
parent 481b5cde0f
commit 91091c614f

View File

@@ -7,9 +7,17 @@ import erMarkers from './erMarkers';
import { configureSvgSize } from '../../setupGraphViewbox'; import { configureSvgSize } from '../../setupGraphViewbox';
import addSVGAccessibilityFields from '../../accessibility'; import addSVGAccessibilityFields from '../../accessibility';
import { parseGenericTypes } from '../common/common'; import { parseGenericTypes } from '../common/common';
import { v4 as uuid4 } from 'uuid';
/** Regex used to remove chars from the entity name so the result can be used in an id */
const BAD_ID_CHARS_REGEXP = /[^A-Za-z0-9]([\W])*/g;
// Configuration
let conf = {}; let conf = {};
// Map so we can look up the id of an entity based on the name
let entityNameIds = new Map();
/** /**
* Allows the top-level API module to inject config specific to this renderer, storing it in the * Allows the top-level API module to inject config specific to this renderer, storing it in the
* local conf object. Note that generic config still needs to be retrieved using getConfig() * local conf object. Note that generic config still needs to be retrieved using getConfig()
@@ -313,15 +321,18 @@ const drawEntities = function (svgNode, entities, graph) {
const keys = Object.keys(entities); const keys = Object.keys(entities);
let firstOne; let firstOne;
keys.forEach(function (id) { keys.forEach(function (entityName) {
// Create a group for each entity const entityId = generateId(entityName, 'entity');
const groupNode = svgNode.append('g').attr('id', id); entityNameIds.set(entityName, entityId);
firstOne = firstOne === undefined ? id : firstOne; // Create a group for each entity
const groupNode = svgNode.append('g').attr('id', entityId);
firstOne = firstOne === undefined ? entityId : firstOne;
// Label the entity - this is done first so that we can get the bounding box // Label the entity - this is done first so that we can get the bounding box
// which then determines the size of the rectangle // which then determines the size of the rectangle
const textId = 'entity-' + id; const textId = 'text-' + entityId;
const textNode = groupNode const textNode = groupNode
.append('text') .append('text')
.attr('class', 'er entityLabel') .attr('class', 'er entityLabel')
@@ -334,12 +345,12 @@ const drawEntities = function (svgNode, entities, graph) {
'style', 'style',
'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px' 'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
) )
.text(id); .text(entityName);
const { width: entityWidth, height: entityHeight } = drawAttributes( const { width: entityWidth, height: entityHeight } = drawAttributes(
groupNode, groupNode,
textNode, textNode,
entities[id].attributes entities[entityName].attributes
); );
// Draw the rectangle - insert it before the text so that the text is not obscured // Draw the rectangle - insert it before the text so that the text is not obscured
@@ -356,12 +367,12 @@ const drawEntities = function (svgNode, entities, graph) {
const rectBBox = rectNode.node().getBBox(); const rectBBox = rectNode.node().getBBox();
// Add the entity to the graph // Add the entity to the graph using the entityId
graph.setNode(id, { graph.setNode(entityId, {
width: rectBBox.width, width: rectBBox.width,
height: rectBBox.height, height: rectBBox.height,
shape: 'rect', shape: 'rect',
id: id, id: entityId,
}); });
}); });
return firstOne; return firstOne;
@@ -405,7 +416,12 @@ const getEdgeName = function (rel) {
*/ */
const addRelationships = function (relationships, g) { const addRelationships = function (relationships, g) {
relationships.forEach(function (r) { relationships.forEach(function (r) {
g.setEdge(r.entityA, r.entityB, { relationship: r }, getEdgeName(r)); g.setEdge(
entityNameIds.get(r.entityA),
entityNameIds.get(r.entityB),
{ relationship: r },
getEdgeName(r)
);
}); });
return relationships; return relationships;
}; // addRelationships }; // addRelationships
@@ -425,7 +441,11 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
relCnt++; relCnt++;
// Find the edge relating to this relationship // Find the edge relating to this relationship
const edge = g.edge(rel.entityA, rel.entityB, getEdgeName(rel)); const edge = g.edge(
entityNameIds.get(rel.entityA),
entityNameIds.get(rel.entityB),
getEdgeName(rel)
);
// Get a function that will generate the line path // Get a function that will generate the line path
const lineFunction = line() const lineFunction = line()
@@ -652,6 +672,35 @@ export const draw = function (text, id, _version, diagObj) {
addSVGAccessibilityFields(diagObj.db, svg, id); addSVGAccessibilityFields(diagObj.db, svg, id);
}; // draw }; // draw
/**
* Return a unique id based on the given string. Start with the prefix, then a hyphen, then the
* simplified str, then a hyphen, then a unique uuid. (Hyphens are only included if needed.)
* Although the official XML standard for ids says that many more characters are valid in the id,
* this keeps things simple by accepting only A-Za-z0-9.
*
* @param {string} [str?=''] Given string to use as the basis for the id. Default is `''`
* @param {string} [prefix?=''] String to put at the start, followed by '-'. Default is `''`
* @param str
* @param prefix
* @returns {string}
* @see https://www.w3.org/TR/xml/#NT-Name
*/
export function generateId(str = '', prefix = '') {
const simplifiedStr = str.replace(BAD_ID_CHARS_REGEXP, '');
return `${strWithHyphen(prefix)}${strWithHyphen(simplifiedStr)}${uuid4()}`;
}
/**
* Append a hyphen to a string only if the string isn't empty
*
* @param {string} str
* @returns {string}
* @todo This could be moved into a string utility file/class.
*/
function strWithHyphen(str = '') {
return str.length > 0 ? `${str}-` : '';
}
export default { export default {
setConf, setConf,
draw, draw,