161 lines
4.2 KiB
JavaScript
161 lines
4.2 KiB
JavaScript
import dagre from "@dagrejs/dagre"
|
|
import { NodeWidth, NodeHeight } from "./constants"
|
|
import { Position } from "@xyflow/svelte"
|
|
import { roles } from "stores/builder"
|
|
import { Roles } from "constants/backend"
|
|
import { get } from "svelte/store"
|
|
import { Helpers } from "@budibase/bbui"
|
|
|
|
// Generates a flow compatible structure of nodes and edges from the current roles
|
|
export const defaultLayout = () => {
|
|
const ignoredRoles = [Roles.PUBLIC]
|
|
const $roles = get(roles)
|
|
const descriptions = {
|
|
[Roles.BASIC]: "Basic user",
|
|
[Roles.POWER]: "Power user",
|
|
[Roles.ADMIN]: "Can do everything",
|
|
}
|
|
|
|
let nodes = []
|
|
let edges = []
|
|
|
|
for (let role of $roles) {
|
|
if (ignoredRoles.includes(role._id)) {
|
|
continue
|
|
}
|
|
nodes.push({
|
|
id: role._id,
|
|
sourcePosition: Position.Right,
|
|
targetPosition: Position.Left,
|
|
type: "role",
|
|
data: {
|
|
displayName: role.displayName || role.name || "",
|
|
description: descriptions[role._id] || "Custom role",
|
|
color: role.color,
|
|
custom: !role._id.match(/[A-Z]+/),
|
|
},
|
|
})
|
|
|
|
let inherits = []
|
|
if (role.inherits) {
|
|
inherits = Array.isArray(role.inherits) ? role.inherits : [role.inherits]
|
|
}
|
|
for (let sourceRole of inherits) {
|
|
// Ensure source role exists
|
|
if (!$roles.some(x => x._id === sourceRole)) {
|
|
continue
|
|
}
|
|
edges.push({
|
|
id: `${sourceRole}-${role._id}`,
|
|
source: sourceRole,
|
|
target: role._id,
|
|
animated: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
nodes,
|
|
edges,
|
|
}
|
|
}
|
|
|
|
// Converts the flow structure of ndes and edges back into an array of roles
|
|
export const layoutToRoles = ({ nodes, edges }) => {
|
|
// Clone and wipe existing inheritance
|
|
let newRoles = Helpers.cloneDeep(get(roles)).map(role => {
|
|
return { ...role, inherits: [] }
|
|
})
|
|
|
|
// Copy over names and colours
|
|
for (let node of nodes) {
|
|
let role = newRoles.find(x => x._id === node.id)
|
|
if (role) {
|
|
role.name = node.data.label
|
|
role.color = node.data.color
|
|
} else {
|
|
// New role
|
|
}
|
|
}
|
|
|
|
// Build inheritance
|
|
for (let edge of edges) {
|
|
let role = newRoles.find(x => x._id === edge.target)
|
|
if (role) {
|
|
role.inherits.push(edge.source)
|
|
} else {
|
|
// New role
|
|
}
|
|
}
|
|
|
|
// Ensure basic is correct
|
|
newRoles.find(x => x._id === Roles.BASIC).inherits = [Roles.BASIC]
|
|
|
|
return newRoles
|
|
}
|
|
|
|
// Updates positions of nodes and edges into a nice graph structure
|
|
const dagreLayout = ({ nodes, edges }) => {
|
|
const dagreGraph = new dagre.graphlib.Graph()
|
|
dagreGraph.setDefaultEdgeLabel(() => ({}))
|
|
dagreGraph.setGraph({
|
|
rankdir: "LR",
|
|
ranksep: 100,
|
|
nodesep: 100,
|
|
})
|
|
nodes.forEach(node => {
|
|
dagreGraph.setNode(node.id, { width: NodeWidth, height: NodeHeight })
|
|
})
|
|
edges.forEach(edge => {
|
|
dagreGraph.setEdge(edge.source, edge.target)
|
|
})
|
|
dagre.layout(dagreGraph)
|
|
nodes.forEach(node => {
|
|
const nodeWithPosition = dagreGraph.node(node.id)
|
|
node.targetPosition = Position.Left
|
|
node.sourcePosition = Position.Right
|
|
node.position = {
|
|
x: nodeWithPosition.x - NodeWidth / 2,
|
|
y: nodeWithPosition.y - NodeHeight / 2,
|
|
}
|
|
})
|
|
return { nodes, edges }
|
|
}
|
|
|
|
// Adds additional edges as needed to the flow structure to ensure compatibility with BB role logic
|
|
const sanitiseLayout = ({ nodes, edges }) => {
|
|
let additions = []
|
|
|
|
for (let node of nodes) {
|
|
// If a node does not inherit anything, let it inherit basic
|
|
if (!edges.some(x => x.target === node.id) && node.id !== Roles.BASIC) {
|
|
additions.push({
|
|
id: Helpers.uuid(),
|
|
source: Roles.BASIC,
|
|
target: node.id,
|
|
animated: true,
|
|
})
|
|
}
|
|
|
|
// If a node is not inherited by anything, let it be inherited by admin
|
|
if (!edges.some(x => x.source === node.id) && node.id !== Roles.ADMIN) {
|
|
additions.push({
|
|
id: Helpers.uuid(),
|
|
source: node.id,
|
|
target: Roles.ADMIN,
|
|
animated: true,
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
nodes,
|
|
edges: [...edges, ...additions],
|
|
}
|
|
}
|
|
|
|
// Automatically lays out the graph, sanitising and enriching the structure
|
|
export const autoLayout = ({ nodes, edges }) => {
|
|
return dagreLayout(sanitiseLayout({ nodes, edges }))
|
|
}
|