Files
supabase/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.utils.ts
T
Joshen LimandJonathan Summers-Muir 3620ca8ebe RR Scaffolding Work (#18658)
* Init demo world map for read replicas

* Update package.json

* Scaffold and mock data for flow view of project settings infrastructure configuration

* Shift map stuff and install deck.gl to experiment

* Flesh out map view a bit more

* Scaffold deploy new replica panel

* Add restart replica option

* Add animations, shift node generation into utils, attempt to show region nodes

* Experiments

* General improvement to chart and map UI

* Improve some copy writing for restart and drop replica modals

* Simplify loading, error, success states in DatabaseSettings

* Scaffold DB selection in connection string section

* Link replica node to connection string

* Scaffold database selection for SQL editor

* Make DatabaseSelector into a component

* Add database selector to infra activity

* Update

* Hide read replicas UI behind a feature flag

* Update flags

* Update package-lock

---------

Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2023-11-27 12:50:19 +08:00

135 lines
4.4 KiB
TypeScript

import dagre from '@dagrejs/dagre'
import { Edge, Node, Position } from 'reactflow'
import {
AVAILABLE_REPLICA_REGIONS,
DatabaseConfiguration,
NODE_ROW_HEIGHT,
NODE_SEP,
NODE_WIDTH,
} from './InstanceConfiguration.constants'
import { groupBy } from 'lodash'
export const generateNodes = (
databases: DatabaseConfiguration[],
{
onSelectRestartReplica,
onSelectResizeReplica,
onSelectDropReplica,
}: {
onSelectRestartReplica: (database: DatabaseConfiguration) => void
onSelectResizeReplica: (database: DatabaseConfiguration) => void
onSelectDropReplica: (database: DatabaseConfiguration) => void
}
): Node[] => {
const position = { x: 0, y: 0 }
const replicas = databases.filter((d) => d.type === 'READ_REPLICA')
const regions = groupBy(replicas, (d) => {
const region = AVAILABLE_REPLICA_REGIONS.find((region) => d.region.includes(region.region))
return region?.key
})
const databaseNodes: Node[] = databases
.sort((a, b) => (a.region > b.region ? 1 : -1))
.map((database) => {
const region = AVAILABLE_REPLICA_REGIONS.find((region) =>
database.region.includes(region.region)
)
return {
position,
id: `database-${database.id}`,
type: database.type,
data: {
id: database.id,
region,
label: database.type === 'PRIMARY' ? 'Primary Database' : 'Read Replica',
provider: database.cloud_provider,
inserted_at: database.inserted_at,
computeSize: database.size,
...(database.type === 'READ_REPLICA'
? {
onSelectRestartReplica: () => onSelectRestartReplica(database),
onSelectResizeReplica: () => onSelectResizeReplica(database),
onSelectDropReplica: () => onSelectDropReplica(database),
}
: {}),
...(database.type === 'PRIMARY'
? {
numReplicas: replicas.length,
numRegions: Object.keys(regions).length,
}
: {}),
},
}
})
return [...databaseNodes]
}
export const getDagreGraphLayout = (nodes: Node[], edges: Edge[]) => {
const dagreGraph = new dagre.graphlib.Graph()
dagreGraph.setDefaultEdgeLabel(() => ({}))
dagreGraph.setGraph({ rankdir: 'TB', ranksep: 200, nodesep: NODE_SEP })
nodes.forEach((node) =>
dagreGraph.setNode(node.id, { width: NODE_WIDTH / 2, height: NODE_ROW_HEIGHT / 2 })
)
edges.forEach((edge) => dagreGraph.setEdge(edge.source, edge.target))
dagre.layout(dagreGraph)
nodes.forEach((node) => {
const nodeWithPosition = dagreGraph.node(node.id)
node.targetPosition = Position.Top
node.sourcePosition = Position.Bottom
// We are shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
node.position = {
x: nodeWithPosition.x - nodeWithPosition.width / 2,
y: nodeWithPosition.y - nodeWithPosition.height / 2,
}
return node
})
return { nodes, edges }
}
/**
* [Joshen] This is some customized logic to add region nodes as "subflow" as dagre doesn't support
* subflows, and I didn't want to go down a rabbit hole with the other layout libraries that react-flow
* supports. Definitely some things to improve in the future
* - Allow setting max number of nodes per row, so that the chart does not become too horizontally sparse
* when many many replicas created
* - Nodes are a bit too spaced out between each other within a region
*/
export const addRegionNodes = (nodes: Node[], edges: Edge[]) => {
const regionNodes: Node[] = []
const replicaNodes = nodes.filter((node) => node.type === 'READ_REPLICA')
const nodesByRegion = groupBy(replicaNodes, (node) => node.data.region.key)
Object.entries(nodesByRegion).map(([key, value]) => {
const region = AVAILABLE_REPLICA_REGIONS.find((r) => r.key === key)
const nodeXPositions = value.map((x) => x.position.x)
const nodeYPositions = value.map((x) => x.position.y)
const minX = Math.min(...nodeXPositions)
const maxX = Math.max(...nodeXPositions)
const minY = Math.max(...nodeYPositions)
const regionNode: Node = {
id: key,
position: { x: minX - 10, y: minY - 10 },
width: maxX - minX,
type: 'REGION',
data: { region, numReplicas: value.length },
}
regionNodes.push(regionNode)
})
return { nodes: [...regionNodes, ...nodes], edges }
}