From ea3573c9898592a42f2d5890b234d9fd4888c809 Mon Sep 17 00:00:00 2001 From: FlorianWoelki Date: Sat, 20 Aug 2022 13:42:51 +0200 Subject: [PATCH 1/4] feat: add array and generic symbols for erDiagram --- src/diagrams/class/svgDraw.js | 24 +----------------------- src/diagrams/common/common.js | 23 +++++++++++++++++++++++ src/diagrams/er/erRenderer.js | 5 ++++- src/diagrams/er/parser/erDiagram.jison | 3 ++- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/diagrams/class/svgDraw.js b/src/diagrams/class/svgDraw.js index a6b11cd95..b9d31772a 100644 --- a/src/diagrams/class/svgDraw.js +++ b/src/diagrams/class/svgDraw.js @@ -1,6 +1,7 @@ import { line, curveBasis } from 'd3'; import utils from '../../utils'; import { log } from '../../logger'; +import { parseGenericTypes } from '../common/common'; let edgeCount = 0; export const drawEdge = function (elem, path, relation, conf, diagObj) { @@ -412,29 +413,6 @@ const addTspan = function (textEl, txt, isFirst, conf) { } }; -/** - * Makes generics in typescript syntax - * - * @example Array of array of strings in typescript syntax - * // returns "Array>" - * parseGenericTypes('Array~Array~string~~'); - * - * @param {string} text The text to convert - * @returns {string} The converted string - */ -const parseGenericTypes = function (text) { - let cleanedText = text; - - if (text.indexOf('~') != -1) { - cleanedText = cleanedText.replace('~', '<'); - cleanedText = cleanedText.replace('~', '>'); - - return parseGenericTypes(cleanedText); - } else { - return cleanedText; - } -}; - /** * Gives the styles for a classifier * diff --git a/src/diagrams/common/common.js b/src/diagrams/common/common.js index b4a341169..73ccf1cce 100644 --- a/src/diagrams/common/common.js +++ b/src/diagrams/common/common.js @@ -182,6 +182,29 @@ const getUrl = (useAbsolute) => { */ export const evaluate = (val) => (val === 'false' || val === false ? false : true); +/** + * Makes generics in typescript syntax + * + * @example Array of array of strings in typescript syntax + * // returns "Array>" + * parseGenericTypes('Array~Array~string~~'); + * + * @param {string} text The text to convert + * @returns {string} The converted string + */ +export const parseGenericTypes = function (text) { + let cleanedText = text; + + if (text.indexOf('~') != -1) { + cleanedText = cleanedText.replace('~', '<'); + cleanedText = cleanedText.replace('~', '>'); + + return parseGenericTypes(cleanedText); + } else { + return cleanedText; + } +}; + export default { getRows, sanitizeText, diff --git a/src/diagrams/er/erRenderer.js b/src/diagrams/er/erRenderer.js index 6c2de26db..5b5e4de80 100644 --- a/src/diagrams/er/erRenderer.js +++ b/src/diagrams/er/erRenderer.js @@ -8,6 +8,7 @@ import { log } from '../../logger'; import erMarkers from './erMarkers'; import { configureSvgSize } from '../../utils'; import addSVGAccessibilityFields from '../../accessibility'; +import { parseGenericTypes } from '../common/common'; let conf = {}; @@ -63,6 +64,8 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { const attrPrefix = `${entityTextNode.node().id}-attr-${attrNum}`; let nodeHeight = 0; + const attributeType = parseGenericTypes(item.attributeType); + // Add a text node for the attribute type const typeNode = groupNode .append('text') @@ -76,7 +79,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => { 'style', 'font-family: ' + getConfig().fontFamily + '; font-size: ' + attrFontSize + 'px' ) - .text(item.attributeType); + .text(attributeType); // Add a text node for the attribute name const nameNode = groupNode diff --git a/src/diagrams/er/parser/erDiagram.jison b/src/diagrams/er/parser/erDiagram.jison index 64d395aed..ceae4f18c 100644 --- a/src/diagrams/er/parser/erDiagram.jison +++ b/src/diagrams/er/parser/erDiagram.jison @@ -29,7 +29,8 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili "{" { this.begin("block"); return 'BLOCK_START'; } \s+ /* skip whitespace in block */ \b((?:PK)|(?:FK))\b return 'ATTRIBUTE_KEY' -[A-Za-z][A-Za-z0-9\-_]* return 'ATTRIBUTE_WORD' +(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD'; +[A-Za-z][A-Za-z0-9\-_\[\]]* return 'ATTRIBUTE_WORD' \"[^"]*\" return 'COMMENT'; [\n]+ /* nothing */ "}" { this.popState(); return 'BLOCK_STOP'; } From ecf62e3b7ad31a4c33bc92a7aa9ef3db8faceb8b Mon Sep 17 00:00:00 2001 From: FlorianWoelki Date: Sat, 20 Aug 2022 13:56:39 +0200 Subject: [PATCH 2/4] test(parser): add tests for generics and arrays in erDiagram --- src/diagrams/er/parser/erDiagram.spec.js | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/diagrams/er/parser/erDiagram.spec.js b/src/diagrams/er/parser/erDiagram.spec.js index 1e0a40104..409176f78 100644 --- a/src/diagrams/er/parser/erDiagram.spec.js +++ b/src/diagrams/er/parser/erDiagram.spec.js @@ -85,6 +85,31 @@ describe('when parsing ER diagram it...', function () { expect(entities[entity].attributes.length).toBe(3); }); + it('should allow an entity with attribute that has a generic type', function () { + const entity = 'BOOK'; + const attribute1 = 'type~T~ type'; + const attribute2 = 'option~T~ readable "comment"'; + const attribute3 = 'string id PK'; + + erDiagram.parser.parse( + `erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n}` + ); + const entities = erDb.getEntities(); + expect(Object.keys(entities).length).toBe(1); + expect(entities[entity].attributes.length).toBe(3); + }); + + it('should allow an entity with attribute that is an array', function () { + const entity = 'BOOK'; + const attribute1 = 'string[] readers FK "comment"'; + const attribute2 = 'string[] authors FK'; + + erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n}`); + const entities = erDb.getEntities(); + expect(Object.keys(entities).length).toBe(1); + expect(entities[entity].attributes.length).toBe(2); + }); + it('should allow an entity with multiple attributes to be defined', function () { const entity = 'BOOK'; const attribute1 = 'string title'; From e19581b540928a8a191563076985c4d068494c70 Mon Sep 17 00:00:00 2001 From: FlorianWoelki Date: Sat, 20 Aug 2022 14:08:53 +0200 Subject: [PATCH 3/4] test(common): add generic parser test --- src/diagrams/common/common.spec.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/diagrams/common/common.spec.js b/src/diagrams/common/common.spec.js index ac7309829..01033d3ce 100644 --- a/src/diagrams/common/common.spec.js +++ b/src/diagrams/common/common.spec.js @@ -1,4 +1,4 @@ -import { sanitizeText, removeScript, removeEscapes } from './common'; +import { sanitizeText, removeScript, removeEscapes, parseGenericTypes } from './common'; describe('when securityLevel is antiscript, all script must be removed', function () { /** @@ -103,3 +103,10 @@ describe('Sanitize text', function () { expect(result).not.toContain('javascript:alert(1)'); }); }); + +describe('generic parser', function () { + it('should parse generic types', function () { + const result = parseGenericTypes('test~T~'); + expect(result).toEqual('test'); + }); +}); From 19a9f90186624935f9a0f294dde992fc1edd3555 Mon Sep 17 00:00:00 2001 From: FlorianWoelki Date: Sat, 20 Aug 2022 15:15:05 +0200 Subject: [PATCH 4/4] test(e2e): add array and generic attributes erDiagram test --- cypress/integration/rendering/erDiagram.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index 579a1808d..781a8ca25 100644 --- a/cypress/integration/rendering/erDiagram.spec.js +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -167,6 +167,21 @@ describe('Entity Relationship Diagram', () => { cy.get('svg'); }); + it.only('should render entities with generic and array attributes', () => { + renderGraph( + ` + erDiagram + BOOK { + string title + string[] authors + type~T~ type + } + `, + { logLevel: 1 } + ); + cy.get('svg'); + }); + it('should render entities and attributes with big and small entity names', () => { renderGraph( `