Update requirementDb to class to encapsulate data

This commit is contained in:
yari-dewalt
2025-01-27 09:21:34 -08:00
parent 97788df7e3
commit 9609aced14
2 changed files with 298 additions and 288 deletions

View File

@@ -1,4 +1,5 @@
import { getConfig } from '../../diagram-api/diagramAPI.js'; import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import type { Node, Edge } from '../../rendering-util/types.js'; import type { Node, Edge } from '../../rendering-util/types.js';
@@ -22,29 +23,38 @@ import type {
VerifyType, VerifyType,
} from './types.js'; } from './types.js';
const RequirementType = { export class RequirementDB implements DiagramDB {
private relations: Relation[] = [];
private latestRequirement: Requirement = this.getInitialRequirement();
private requirements = new Map<string, Requirement>();
private latestElement: Element = this.getInitialElement();
private elements = new Map<string, Element>();
private classes = new Map<string, RequirementClass>();
private direction = 'TB';
private RequirementType = {
REQUIREMENT: 'Requirement', REQUIREMENT: 'Requirement',
FUNCTIONAL_REQUIREMENT: 'Functional Requirement', FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
INTERFACE_REQUIREMENT: 'Interface Requirement', INTERFACE_REQUIREMENT: 'Interface Requirement',
PERFORMANCE_REQUIREMENT: 'Performance Requirement', PERFORMANCE_REQUIREMENT: 'Performance Requirement',
PHYSICAL_REQUIREMENT: 'Physical Requirement', PHYSICAL_REQUIREMENT: 'Physical Requirement',
DESIGN_CONSTRAINT: 'Design Constraint', DESIGN_CONSTRAINT: 'Design Constraint',
}; };
const RiskLevel = { private RiskLevel = {
LOW_RISK: 'Low', LOW_RISK: 'Low',
MED_RISK: 'Medium', MED_RISK: 'Medium',
HIGH_RISK: 'High', HIGH_RISK: 'High',
}; };
const VerifyType = { private VerifyType = {
VERIFY_ANALYSIS: 'Analysis', VERIFY_ANALYSIS: 'Analysis',
VERIFY_DEMONSTRATION: 'Demonstration', VERIFY_DEMONSTRATION: 'Demonstration',
VERIFY_INSPECTION: 'Inspection', VERIFY_INSPECTION: 'Inspection',
VERIFY_TEST: 'Test', VERIFY_TEST: 'Test',
}; };
const Relationships = { private Relationships = {
CONTAINS: 'contains', CONTAINS: 'contains',
COPIES: 'copies', COPIES: 'copies',
DERIVES: 'derives', DERIVES: 'derives',
@@ -52,15 +62,46 @@ const Relationships = {
VERIFIES: 'verifies', VERIFIES: 'verifies',
REFINES: 'refines', REFINES: 'refines',
TRACES: 'traces', TRACES: 'traces',
}; };
let direction = 'TB'; constructor() {
const getDirection = () => direction; this.clear();
const setDirection = (dir: string) => {
direction = dir;
};
const getInitialRequirement = (): Requirement => ({ // Needed for JISON since it only supports direct properties
this.setDirection = this.setDirection.bind(this);
this.addRequirement = this.addRequirement.bind(this);
this.setNewReqId = this.setNewReqId.bind(this);
this.setNewReqRisk = this.setNewReqRisk.bind(this);
this.setNewReqText = this.setNewReqText.bind(this);
this.setNewReqVerifyMethod = this.setNewReqVerifyMethod.bind(this);
this.addElement = this.addElement.bind(this);
this.setNewElementType = this.setNewElementType.bind(this);
this.setNewElementDocRef = this.setNewElementDocRef.bind(this);
this.addRelationship = this.addRelationship.bind(this);
this.setCssStyle = this.setCssStyle.bind(this);
this.setClass = this.setClass.bind(this);
this.defineClass = this.defineClass.bind(this);
this.setAccTitle = this.setAccTitle.bind(this);
this.setAccDescription = this.setAccDescription.bind(this);
}
public getDirection() {
return this.direction;
}
public setDirection(dir: string) {
this.direction = dir;
}
private resetLatestRequirement() {
this.latestRequirement = this.getInitialRequirement();
}
private resetLatestElement() {
this.latestElement = this.getInitialElement();
}
private getInitialRequirement(): Requirement {
return {
requirementId: '', requirementId: '',
text: '', text: '',
risk: '' as RiskLevel, risk: '' as RiskLevel,
@@ -69,130 +110,122 @@ const getInitialRequirement = (): Requirement => ({
type: '' as RequirementType, type: '' as RequirementType,
cssStyles: [], cssStyles: [],
classes: ['default'], classes: ['default'],
}); };
}
const getInitialElement = (): Element => ({ private getInitialElement(): Element {
return {
name: '', name: '',
type: '', type: '',
docRef: '', docRef: '',
cssStyles: [], cssStyles: [],
classes: ['default'], classes: ['default'],
}); };
}
// Update initial declarations public addRequirement(name: string, type: RequirementType) {
let relations: Relation[] = []; if (!this.requirements.has(name)) {
let latestRequirement: Requirement = getInitialRequirement(); this.requirements.set(name, {
let requirements = new Map<string, Requirement>();
let latestElement: Element = getInitialElement();
let elements = new Map<string, Element>();
let classes = new Map<string, RequirementClass>();
// Add reset functions
const resetLatestRequirement = () => {
latestRequirement = getInitialRequirement();
};
const resetLatestElement = () => {
latestElement = getInitialElement();
};
const addRequirement = (name: string, type: RequirementType) => {
if (!requirements.has(name)) {
requirements.set(name, {
name, name,
type, type,
requirementId: latestRequirement.requirementId, requirementId: this.latestRequirement.requirementId,
text: latestRequirement.text, text: this.latestRequirement.text,
risk: latestRequirement.risk, risk: this.latestRequirement.risk,
verifyMethod: latestRequirement.verifyMethod, verifyMethod: this.latestRequirement.verifyMethod,
cssStyles: [], cssStyles: [],
classes: ['default'], classes: ['default'],
}); });
} }
resetLatestRequirement(); this.resetLatestRequirement();
return requirements.get(name); return this.requirements.get(name);
};
const getRequirements = () => requirements;
const setNewReqId = (id: string) => {
if (latestRequirement !== undefined) {
latestRequirement.requirementId = id;
} }
};
const setNewReqText = (text: string) => { public getRequirements() {
if (latestRequirement !== undefined) { return this.requirements;
latestRequirement.text = text;
} }
};
const setNewReqRisk = (risk: RiskLevel) => { public setNewReqId(id: string) {
if (latestRequirement !== undefined) { if (this.latestRequirement !== undefined) {
latestRequirement.risk = risk; this.latestRequirement.requirementId = id;
} }
};
const setNewReqVerifyMethod = (verifyMethod: VerifyType) => {
if (latestRequirement !== undefined) {
latestRequirement.verifyMethod = verifyMethod;
} }
};
const addElement = (name: string) => { public setNewReqText(text: string) {
if (!elements.has(name)) { if (this.latestRequirement !== undefined) {
elements.set(name, { this.latestRequirement.text = text;
}
}
public setNewReqRisk(risk: RiskLevel) {
if (this.latestRequirement !== undefined) {
this.latestRequirement.risk = risk;
}
}
public setNewReqVerifyMethod(verifyMethod: VerifyType) {
if (this.latestRequirement !== undefined) {
this.latestRequirement.verifyMethod = verifyMethod;
}
}
public addElement(name: string) {
if (!this.elements.has(name)) {
this.elements.set(name, {
name, name,
type: latestElement.type, type: this.latestElement.type,
docRef: latestElement.docRef, docRef: this.latestElement.docRef,
cssStyles: [], cssStyles: [],
classes: ['default'], classes: ['default'],
}); });
log.info('Added new element: ', name); log.info('Added new element: ', name);
} }
resetLatestElement(); this.resetLatestElement();
return elements.get(name); return this.elements.get(name);
};
const getElements = () => elements;
const setNewElementType = (type: string) => {
if (latestElement !== undefined) {
latestElement.type = type;
} }
};
const setNewElementDocRef = (docRef: string) => { public getElements() {
if (latestElement !== undefined) { return this.elements;
latestElement.docRef = docRef;
} }
};
const addRelationship = (type: RelationshipType, src: string, dst: string) => { public setNewElementType(type: string) {
relations.push({ if (this.latestElement !== undefined) {
this.latestElement.type = type;
}
}
public setNewElementDocRef(docRef: string) {
if (this.latestElement !== undefined) {
this.latestElement.docRef = docRef;
}
}
public addRelationship(type: RelationshipType, src: string, dst: string) {
this.relations.push({
type, type,
src, src,
dst, dst,
}); });
}; }
const getRelationships = () => relations; public getRelationships() {
return this.relations;
}
const clear = () => { public clear() {
relations = []; this.relations = [];
resetLatestRequirement(); this.resetLatestRequirement();
requirements = new Map(); this.requirements = new Map();
resetLatestElement(); this.resetLatestElement();
elements = new Map(); this.elements = new Map();
classes = new Map(); this.classes = new Map();
commonClear(); commonClear();
}; }
export const setCssStyle = function (ids: string[], styles: string[]) { public setCssStyle(ids: string[], styles: string[]) {
for (const id of ids) { for (const id of ids) {
const node = requirements.get(id) ?? elements.get(id); const node = this.requirements.get(id) ?? this.elements.get(id);
if (!styles || !node) { if (!styles || !node) {
return; return;
} }
@@ -204,29 +237,29 @@ export const setCssStyle = function (ids: string[], styles: string[]) {
} }
} }
} }
}; }
export const setClass = function (ids: string[], classNames: string[]) { public setClass(ids: string[], classNames: string[]) {
for (const id of ids) { for (const id of ids) {
const node = requirements.get(id) ?? elements.get(id); const node = this.requirements.get(id) ?? this.elements.get(id);
if (node) { if (node) {
for (const _class of classNames) { for (const _class of classNames) {
node.classes.push(_class); node.classes.push(_class);
const styles = classes.get(_class)?.styles; const styles = this.classes.get(_class)?.styles;
if (styles) { if (styles) {
node.cssStyles.push(...styles); node.cssStyles.push(...styles);
} }
} }
} }
} }
}; }
export const defineClass = function (ids: string[], style: string[]) { public defineClass(ids: string[], style: string[]) {
for (const id of ids) { for (const id of ids) {
let styleClass = classes.get(id); let styleClass = this.classes.get(id);
if (styleClass === undefined) { if (styleClass === undefined) {
styleClass = { id, styles: [], textStyles: [] }; styleClass = { id, styles: [], textStyles: [] };
classes.set(id, styleClass); this.classes.set(id, styleClass);
} }
if (style) { if (style) {
@@ -239,28 +272,28 @@ export const defineClass = function (ids: string[], style: string[]) {
}); });
} }
requirements.forEach((value) => { this.requirements.forEach((value) => {
if (value.classes.includes(id)) { if (value.classes.includes(id)) {
value.cssStyles.push(...style.flatMap((s) => s.split(','))); value.cssStyles.push(...style.flatMap((s) => s.split(',')));
} }
}); });
elements.forEach((value) => { this.elements.forEach((value) => {
if (value.classes.includes(id)) { if (value.classes.includes(id)) {
value.cssStyles.push(...style.flatMap((s) => s.split(','))); value.cssStyles.push(...style.flatMap((s) => s.split(',')));
} }
}); });
} }
}; }
export const getClasses = () => { public getClasses() {
return classes; return this.classes;
}; }
const getData = () => { public getData() {
const config = getConfig(); const config = getConfig();
const nodes: Node[] = []; const nodes: Node[] = [];
const edges: Edge[] = []; const edges: Edge[] = [];
for (const requirement of requirements.values()) { for (const requirement of this.requirements.values()) {
const node = requirement as unknown as Node; const node = requirement as unknown as Node;
node.id = requirement.name; node.id = requirement.name;
node.cssStyles = requirement.cssStyles; node.cssStyles = requirement.cssStyles;
@@ -270,7 +303,7 @@ const getData = () => {
nodes.push(node); nodes.push(node);
} }
for (const element of elements.values()) { for (const element of this.elements.values()) {
const node = element as unknown as Node; const node = element as unknown as Node;
node.shape = 'requirementBox'; node.shape = 'requirementBox';
node.look = config.look; node.look = config.look;
@@ -281,13 +314,13 @@ const getData = () => {
nodes.push(node); nodes.push(node);
} }
for (const relation of relations) { for (const relation of this.relations) {
let counter = 0; let counter = 0;
const isContains = relation.type === Relationships.CONTAINS; const isContains = relation.type === this.Relationships.CONTAINS;
const edge: Edge = { const edge: Edge = {
id: `${relation.src}-${relation.dst}-${counter}`, id: `${relation.src}-${relation.dst}-${counter}`,
start: requirements.get(relation.src)?.name ?? elements.get(relation.src)?.name, start: this.requirements.get(relation.src)?.name ?? this.elements.get(relation.src)?.name,
end: requirements.get(relation.dst)?.name ?? elements.get(relation.dst)?.name, end: this.requirements.get(relation.dst)?.name ?? this.elements.get(relation.dst)?.name,
label: `&lt;&lt;${relation.type}&gt;&gt;`, label: `&lt;&lt;${relation.type}&gt;&gt;`,
classes: 'relationshipLine', classes: 'relationshipLine',
style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'], style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
@@ -303,39 +336,14 @@ const getData = () => {
counter++; counter++;
} }
return { nodes, edges, other: {}, config, direction: getDirection() }; return { nodes, edges, other: {}, config, direction: this.getDirection() };
}; }
export default { public setAccTitle = setAccTitle;
Relationships, public getAccTitle = getAccTitle;
RequirementType, public setAccDescription = setAccDescription;
RiskLevel, public getAccDescription = getAccDescription;
VerifyType, public setDiagramTitle = setDiagramTitle;
getConfig: () => getConfig().requirement, public getDiagramTitle = getDiagramTitle;
addRequirement, public getConfig = () => getConfig().class;
getRequirements, }
setNewReqId,
setNewReqText,
setNewReqRisk,
setNewReqVerifyMethod,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
setDiagramTitle,
getDiagramTitle,
getDirection,
setDirection,
addElement,
getElements,
setNewElementType,
setNewElementDocRef,
addRelationship,
getRelationships,
clear,
setCssStyle,
setClass,
defineClass,
getClasses,
getData,
};

View File

@@ -1,13 +1,15 @@
import type { DiagramDefinition } from '../../diagram-api/types.js'; import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types // @ts-ignore: JISON doesn't support types
import parser from './parser/requirementDiagram.jison'; import parser from './parser/requirementDiagram.jison';
import db from './requirementDb.js'; import { RequirementDB } from './requirementDb.js';
import styles from './styles.js'; import styles from './styles.js';
import renderer from './requirementRenderer.js'; import renderer from './requirementRenderer.js';
export const diagram: DiagramDefinition = { export const diagram: DiagramDefinition = {
parser, parser,
db, get db() {
return new RequirementDB();
},
renderer, renderer,
styles, styles,
}; };