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(
`
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/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');
+ });
+});
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'; }
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';