@ping-identity/dvlint-base-rule-pack
Version:
Collection of base rules used to lint DaVinci flows.
199 lines (178 loc) • 6.54 kB
JavaScript
const { LintRule } = require("@ping-identity/dvlint");
class AnnotationRules extends LintRule {
constructor() {
super({
id: "dv-rule-annotations-001",
description:
"Verify several best practices for annotations nodes include checks on color, font and positions on the canvas",
cleans: false,
reference: "https://library.pingidentity.com/page/davinci-color-palette",
});
this.addCode("dv-bp-annotation-001", {
description:
"Found color not included in best practices list for annotation node",
message: "Annotation color is not in palette [%] (%)",
type: "best-practice",
recommendation: "The annotation color is not part of the recommended palette. To ensure optimal results, use the suggested colors.",
});
this.addCode("dv-bp-annotation-002", {
description: "Found non-standard fontFamily used for Annotation",
message: "Annotation font is not in sans-serif [%] (%)",
type: "best-practice",
recommendation: "The current annotation font is not Sans-Serif. For best clarity and consistency, it is recommended to use a Sans-Serif font",
});
this.addCode("dv-bp-missing-title-annotation-001", {
description: "Title annotation node not found",
message:
"Each flow should contain a title annotation node with a background color of #4462ed",
type: "best-practice",
recommendation: "This flow is missing a title annotation node with a proper description. To improve flow organization, add a title annotation node and set the background color to #4462ed.",
});
this.addCode("dv-bp-title-not-on-top-001", {
description: "Title annotation not on top of flow",
message: "The topmost node in your flow should be a title annotation (%)",
type: "best-practice",
recommendation: "The title annotation node is not positioned at the top of the flow. For better readability, move the title annotation node to the top.",
});
this.addCode("dv-bp-annotation-003", {
description:
"It is a best practice to include annotations to document your flow",
message: "Flow contains no annotations",
type: "best-practice",
recommendation: "This flow lacks annotations. For improved clarity and structure, add annotations to the flow.",
});
}
runRule() {
try {
const annotationNodes = this.dvUtil.getNodesByType("annotationConnector");
this.testAnnotationColors(annotationNodes);
this.testAnnotationFonts(annotationNodes);
this.testAnnotationTitle(annotationNodes);
this.testTitleAtTop();
this.testMissingAnnotations(annotationNodes);
} catch (err) {
this.addError(undefined, { messageArgs: [`${err}`] });
}
}
/**
* Test annotation color based on set of expected colors
* @param {*} nodes
*/
testAnnotationColors(nodes) {
const colors = [
"#4462edff",
"#5d00d6ff",
"#f2f3f4ff",
"#fffaa0ff",
"#fffaa0ff",
"#50e3c2ff",
"#ffffffff",
"#f7f7adff",
];
nodes.forEach((node) => {
const { data } = node;
const backgroundColor =
data.properties?.backgroundColor?.value?.toLowerCase();
if (!colors.includes(backgroundColor)) {
this.addError("dv-bp-annotation-001", {
messageArgs: [data.properties.backgroundColor.value, data.id],
nodeId: data.id,
});
}
});
}
/**
* Test annotation fonts based on expected font family.
*
* Additionally, this will clean the flow replacing any invalid
* font families with the expected font family.
* @param {*} nodes
*/
testAnnotationFonts(nodes) {
const fontFamily = "sans-serif";
nodes.forEach((node) => {
const { data } = node;
if (
data.properties?.fontFamily?.value?.toLowerCase() !==
fontFamily.toLowerCase()
) {
this.addError("dv-bp-annotation-002", {
messageArgs: [data.properties.fontFamily.value, data.id],
nodeId: data.id,
});
if (this.cleanFlow) {
data.properties.fontFamily.value = fontFamily;
this.addCleanResult(
"Set annotation font to sans-serif",
data.id,
"fontFamily"
);
}
}
});
}
/**
* Verify that a title annotation is present in the flow by looking for
* a flow with background color.
* @param {*} nodes
*/
testAnnotationTitle(nodes) {
const titleAnnotationColor = "#4462edff";
let titleNodePresent;
nodes.forEach((node) => {
const { data } = node;
if (data.properties?.backgroundColor?.value === titleAnnotationColor) {
titleNodePresent = true;
}
});
if (!titleNodePresent) {
this.addError("dv-bp-missing-title-annotation-001", {});
}
}
testTitleAtTop() {
const annotationType = "annotationConnector";
const annotationColor = "#4462edff";
const flowNodeTypes = this.dvUtil.getFlowNodeTypes();
let nodeTopYCoord = Number.MAX_SAFE_INTEGER;
// Ensure annotationConnector is in list of nodes
if (flowNodeTypes && flowNodeTypes.includes(annotationType)) {
flowNodeTypes.forEach((nodeType) => {
if (nodeType === annotationType) {
return; // Skip annotation nodes
}
const nodesByType = this.dvUtil.getNodesByType(nodeType);
if (nodesByType) {
nodesByType.forEach((node) => {
const { position } = node;
// Capture the node with the smallest y coord
if (position.y < nodeTopYCoord) {
nodeTopYCoord = position.y;
}
});
}
});
const annotationNodes = this.dvUtil.getNodesByType(annotationType);
const titleAnnotation = annotationNodes.filter(
(obj) =>
obj.data?.properties?.backgroundColor?.value === annotationColor
);
if (titleAnnotation.length > 0) {
const { position, data } = titleAnnotation[0];
if (position?.y > nodeTopYCoord) {
this.addError("dv-bp-title-not-on-top-001", {
messageArgs: [data.id],
nodeId: data.id,
});
}
}
}
}
testMissingAnnotations(nodes) {
const annotationType = "annotationConnector";
const flowNodeTypes = this.dvUtil.getFlowNodeTypes();
if (!flowNodeTypes.includes(annotationType)) {
this.addError("dv-bp-annotation-003", {});
}
}
}
module.exports = AnnotationRules;