Skip to content

Commit

Permalink
Parse extension points
Browse files Browse the repository at this point in the history
  • Loading branch information
teyc committed Dec 21, 2024
1 parent 4cfb4c4 commit cff19e6
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 35 deletions.
3 changes: 3 additions & 0 deletions demos/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ <h2><a href="./zenuml.html">ZenUML</a></h2>
<li>
<h2><a href="./sankey.html">Sankey</a></h2>
</li>
<li>
<h2><a href="./usecase.html">Use Case</a></h2>
</li>
<li>
<h2><a href="./packet.html">Packet</a></h2>
</li>
Expand Down
48 changes: 24 additions & 24 deletions packages/mermaid/src/diagrams/usecase/parser/usecase.jison
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@
%% /* language grammar */

start
: 'DECLARATION' optional_sections relationships
: 'DECLARATION' optional_sections
| 'DECLARATION' title_definition optional_sections
;

optional_sections
: title_definition participant_definitions systemboundary_definitions
| participant_definitions systemboundary_definitions
| title_definition participant_definitions
| title_definition systemboundary_definitions
| participant_definitions
: participant_definitions
| systemboundary_definitions
| title_definition
| relationships
| participant_definitions systemboundary_definitions relationships
| participant_definitions relationships
| systemboundary_definitions relationships
| /* empty */
;

Expand All @@ -85,8 +85,8 @@ participant_definition
;

systemboundary_definitions
: 'SYSTEMBOUNDARY' systemboundary_elements 'END' { yy.addSystemBoundary($2); }
| 'SYSTEMBOUNDARY' systemboundary_title_definition systemboundary_elements 'END' { yy.addSystemBoundary($3, $2); }
: 'SYSTEMBOUNDARY' use_cases 'END' { yy.addSystemBoundary($2); }
| 'SYSTEMBOUNDARY' systemboundary_title_definition use_cases 'END' { yy.addSystemBoundary($3, $2); }
;

systemboundary_title_definition
Expand All @@ -97,24 +97,27 @@ title_definition
: 'TITLE' {var title = $1.substring(6).trim(); yy.setDiagramTitle(title);$$=title;}
;

systemboundary_elements
: systemboundary_elements systemboundary_element { $$ = $1.concat($2); }
| systemboundary_element { $$ = [$1] }
use_cases
: use_cases use_case { $$ = $1.concat($2); }
| use_case { $$ = [$1] }
;

systemboundary_element
: 'USECASE' '{' systemboundary_tasks '}'
| 'USECASE'
| 'USECASE_AS' '{' systemboundary_tasks '}'
| 'USECASE_AS' { $$ = yy.addAlias($1)}
use_casexx
: 'USECASE';

use_case
: 'USECASE' '{' extension_points '}' { $$ = { type: 'USECASE', id: $1.trim(), extensionPoints: $3 }; }
| 'USECASE' { $$ = { type: 'USECASE', id: $1.trim(), extensionPoints: [] }; }
| 'USECASE_AS' '{' extension_points '}' { $$ = { type: 'USECASE', id: yy.addAlias($1), extensionPoints: $3 }; }
| 'USECASE_AS' { $$ = { type: 'USECASE', id: yy.addAlias($1), extensionPoints: [] }; }
;

systemboundary_tasks
: systemboundary_tasks systemboundary_task { $$ = $1.concat($2) }
| systemboundary_task { $$ = [$1] }
extension_points
: extension_points extension_point { $$ = $1.concat($2.replace(/^- +/, '')); }
| extension_point { $$ = [$1.replace(/^- +/, '')]; }
;

systemboundary_task
extension_point
: TASK
;

Expand Down Expand Up @@ -144,6 +147,3 @@ note_statement
: 'NOTE' TEXT NEWLINE { $$ = { type: 'note', text: $2 }; }
;

alias_statement
: IDENTIFIER 'AS' IDENTIFIER NEWLINE { $$ = { type: 'alias', original: $1, alias: $3 }; }
;
51 changes: 46 additions & 5 deletions packages/mermaid/src/diagrams/usecase/parser/usecase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import usecase from './usecase.jison';
import db, { type UsecaseDB, UsecaseLink, UsecaseNode } from '../usecaseDB.js';
import { prepareTextForParsing } from '../usecaseUtils.js';
import { setConfig } from '../../../config.js';
// @ts-ignore unused variable
import { log, setLogLevel } from '../../../logger.js';

setConfig({
securityLevel: 'strict',
Expand Down Expand Up @@ -128,12 +130,13 @@ describe('Usecase diagram', function () {
expect(usecase.yy.getDiagramTitle()).toEqual('Arrows in Use Case diagrams');

const db = usecase.yy as UsecaseDB;
expect(
db.getRelationships().some((rel) => rel.source.id == 'Admin' && rel.target.id == '(Login)')
).toBeTruthy();

// console.log(usecase.yy.getRelationships());
// console.log(usecase.yy.getSystemBoundaries());
expect(db.getRelationships()).toContainEqual({
source: { id: 'Admin', nodeType: 'actor' },
target: { id: '(Login)', nodeType: 'usecase', extensionPoints: ['Authenticate'] },
arrow: '->',
label: '',
});
});

it('should handle aliases', function () {
Expand All @@ -157,5 +160,43 @@ describe('Usecase diagram', function () {
'Payment Management System'
);
});

it('should parse system boundary with no extension points', function () {
const input = `
usecase-beta
systemboundary
(Repair Car)
end
`;
const result = usecase.parse(prepareTextForParsing(input));
expect(result).toBeTruthy();
const db = usecase.yy as UsecaseDB;
expect(db.getSystemBoundaries()[0].useCases).toStrictEqual(['(Repair Car)']);
expect(db.getUseCases()).toStrictEqual(['(Repair Car)']);
});

it('should parse system boundary with extension points', function () {
const input = `
usecase-beta
systemboundary
(Repair Device) {
- Login with Secure Token
- Run Diagnostics
- Logout
- Get Help
}
end
`;
const result = usecase.parse(prepareTextForParsing(input));
expect(result).toBeTruthy();
const db = usecase.yy as UsecaseDB;
expect(db.getSystemBoundaries()[0].useCases).toStrictEqual(['(Repair Device)']);
expect(db.getUseCaseExtensionPoints('(Repair Device)')).toStrictEqual([
'Login with Secure Token',
'Run Diagnostics',
'Logout',
'Get Help',
]);
});
});
});
31 changes: 25 additions & 6 deletions packages/mermaid/src/diagrams/usecase/usecaseDB.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { BaseDiagramConfig, MermaidConfig } from '../../config.type.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { log } from '../../logger.js';

import { log, setLogLevel } from '../../logger.js';
import type { LayoutData } from '../../mermaid.js';
import type { Edge, Node } from '../../rendering-util/types.js';
import common from '../common/common.js';
Expand Down Expand Up @@ -41,15 +42,14 @@ export class UsecaseDB {
commonClear();
}

/** parses an "foo as bar" and returns "foo" */
addAlias(token: string): string {
const [source, target] = token.split('as').map((_) => _.trim());
this.aliases.set(source, target);
return source;
}

addParticipant(participant: { service: string; as?: string } | { actor: string; as?: string }) {
setLogLevel('info');
log.info('addParticipants', participant);
const nodeType = 'actor' in participant ? 'actor' : 'service';
const id = 'actor' in participant ? participant.actor.trim() : participant.service.trim();
const _ = this.getOrCreateNode(id, nodeType);
Expand All @@ -74,14 +74,23 @@ export class UsecaseDB {
this.links.push(new UsecaseLink(sourceNode, targetNode, arrow, label));
}

addSystemBoundary(useCases: string[], title?: string) {
addSystemBoundary(useCases: { id: string; extensionPoints?: string[] }[], title?: string) {
if (title) {
title = common.sanitizeText(title.trim(), getConfig());
if (title.startsWith('title')) {
title = title.slice(5).trim();
}
}
this.systemBoundaries.push({ id: 'boundary-' + this.systemBoundaries.length, useCases, title });
const useCaseIds = useCases.map((useCase) => useCase.id);
this.systemBoundaries.push({
id: 'boundary-' + this.systemBoundaries.length,
useCases: useCaseIds,
title,
});
useCases.forEach((useCase) => {
const node = this.getOrCreateNode(useCase.id, 'usecase');
node.extensionPoints = useCase.extensionPoints;
});
}

getActors() {
Expand Down Expand Up @@ -193,6 +202,12 @@ export class UsecaseDB {
.map((node) => node.id);
}

getUseCaseExtensionPoints(useCaseId: string): string[] | undefined {
return [...this.nodesMap.values()].find(
(node) => node.nodeType === 'usecase' && node.id === useCaseId
)?.extensionPoints;
}

getAccDescription = getAccDescription;
getAccTitle = getAccTitle;
getDiagramTitle = getDiagramTitle;
Expand Down Expand Up @@ -233,7 +248,10 @@ export class UsecaseLink {
export class UsecaseNode {
constructor(
public id: string,
public nodeType: UsecaseNodeType | undefined
public nodeType: UsecaseNodeType | undefined,

/** used for 'usecase' nodeType only */
public extensionPoints?: string[]
) {}
}

Expand All @@ -258,6 +276,7 @@ export default {
getServices: db.getServices.bind(db),
getSystemBoundaries: db.getSystemBoundaries.bind(db),
getUseCases: db.getUseCases.bind(db),
getUseCaseExtensionPoints: db.getUseCaseExtensionPoints.bind(db),
setAccDescription,
setAccTitle,
setDiagramTitle: db.setDiagramTitle.bind(db),
Expand Down

0 comments on commit cff19e6

Please sign in to comment.