Skip to content

Commit

Permalink
Merge pull request #252 from PrefectHQ/sub-nodes
Browse files Browse the repository at this point in the history
Render sub flow runs
  • Loading branch information
pleek91 authored Oct 19, 2023
2 parents 9da2bfb + b4cde62 commit 0edbdce
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 48 deletions.
21 changes: 20 additions & 1 deletion src/factories/flowRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,33 @@ import { BitmapText, Container, Graphics } from 'pixi.js'
import { DEFAULT_NODE_CONTAINER_NAME } from '@/consts'
import { nodeBoxFactory } from '@/factories/box'
import { nodeLabelFactory } from '@/factories/label'
import { nodesContainerFactory } from '@/factories/nodes'
import { Pixels } from '@/models/layout'
import { RunGraphNode } from '@/models/RunGraph'
import { waitForConfig } from '@/objects/config'

export type FlowRunContainer = Awaited<ReturnType<typeof flowRunContainerFactory>>

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export async function flowRunContainerFactory() {
export async function flowRunContainerFactory(node: RunGraphNode) {
const container = new Container()
const { label, render: renderLabel } = await nodeLabelFactory()
const { box, render: renderBox } = await nodeBoxFactory()
const { container: nodesContainer, render: renderNodes } = await nodesContainerFactory(node.id)

let open = false

container.addChild(box)
container.addChild(label)
container.addChild(nodesContainer)

container.name = DEFAULT_NODE_CONTAINER_NAME
container.eventMode = 'static'
container.cursor = 'pointer'

container.on('click', toggle)
nodesContainer.on('resized', () => container.emit('resized'))

async function render(node: RunGraphNode): Promise<Container> {
const label = await renderLabel(node)
const box = await renderBox(node)
Expand All @@ -28,6 +38,14 @@ export async function flowRunContainerFactory() {
return container
}

function toggle(): void {
open = !open

if (open) {
renderNodes()
}
}

async function getLabelPosition(label: BitmapText, box: Graphics): Promise<Pixels> {
const config = await waitForConfig()

Expand All @@ -50,6 +68,7 @@ export async function flowRunContainerFactory() {
}

return {
kind: 'flow-run' as const,
container,
render,
}
Expand Down
9 changes: 4 additions & 5 deletions src/factories/node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Container, Ticker } from 'pixi.js'
import { flowRunContainerFactory } from '@/factories/flowRun'
import { taskRunContainerFactory } from '@/factories/taskRun'
import { FlowRunContainer, flowRunContainerFactory } from '@/factories/flowRun'
import { TaskRunContainer, taskRunContainerFactory } from '@/factories/taskRun'
import { RunGraphNode } from '@/models/RunGraph'

export type NodeContainerFactory = Awaited<ReturnType<typeof nodeContainerFactory>>
Expand All @@ -26,15 +26,14 @@ export async function nodeContainerFactory(node: RunGraphNode) {
return container
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
async function getNodeFactory(node: RunGraphNode) {
async function getNodeFactory(node: RunGraphNode): Promise<TaskRunContainer | FlowRunContainer> {
const { kind } = node

switch (kind) {
case 'task-run':
return await taskRunContainerFactory()
case 'flow-run':
return await flowRunContainerFactory()
return await flowRunContainerFactory(node)
default:
const exhaustive: never = kind
throw new Error(`switch does not have case for value: ${exhaustive}`)
Expand Down
47 changes: 29 additions & 18 deletions src/factories/nodes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Container } from 'pixi.js'
import { DEFAULT_NODES_CONTAINER_NAME, DEFAULT_POLL_INTERVAL } from '@/consts'
import { eventsFactory } from '@/factories/events'
import { NodeContainerFactory, nodeContainerFactory } from '@/factories/node'
import { offsetsFactory } from '@/factories/offsets'
import { HorizontalPositionSettings } from '@/factories/position'
Expand All @@ -13,39 +12,32 @@ import { WorkerLayoutMessage, WorkerMessage, layoutWorkerFactory } from '@/worke

export type NodesContainer = Awaited<ReturnType<typeof nodesContainerFactory>>

type Events = {
rendered: void,
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export async function nodesContainerFactory(runId: string) {
const worker = layoutWorkerFactory(onmessage)
const nodes = new Map<string, NodeContainerFactory>()
const container = new Container()
const config = await waitForConfig()
const offsets = offsetsFactory()
const events = eventsFactory<Events>()
const rows = await offsetsFactory()

let settings: HorizontalPositionSettings
let layout: NodeLayoutResponse = new Map()

container.name = DEFAULT_NODES_CONTAINER_NAME

fetch()

async function fetch(): Promise<void> {
async function render(): Promise<void> {
const data = await config.fetch(runId)

settings = horizontalSettingsFactory(data.start_time)

await render(data.nodes)
await renderNodes(data.nodes)

if (!data.end_time) {
setTimeout(() => fetch(), DEFAULT_POLL_INTERVAL)
setTimeout(() => render(), DEFAULT_POLL_INTERVAL)
}
}

async function render(nodes: RunGraphNodes): Promise<void> {
async function renderNodes(nodes: RunGraphNodes): Promise<void> {
const request: NodeLayoutRequest = new Map()

for (const [nodeId, node] of nodes) {
Expand All @@ -71,7 +63,7 @@ export async function nodesContainerFactory(runId: string) {
return await render(node)
}

function updateLayout(): void {
function setPositions(): void {
layout.forEach((position, nodeId) => {
const node = nodes.get(nodeId)

Expand All @@ -83,7 +75,8 @@ export async function nodesContainerFactory(runId: string) {
node.container.position = getActualPosition(position)
})

events.emit('rendered')
container.emit('resized')
container.emit('rendered')
}

async function getNodeContainerService(node: RunGraphNode): Promise<NodeContainerFactory> {
Expand All @@ -95,14 +88,32 @@ export async function nodesContainerFactory(runId: string) {

const response = await nodeContainerFactory(node)

response.container.on('resized', () => resizeNode(node.id))

nodes.set(node.id, response)
container.addChild(response.container)

return response
}

function resizeNode(nodeId: string): void {
const node = nodes.get(nodeId)
const nodeLayout = layout.get(nodeId)

if (!node || !nodeLayout) {
return
}

const axis = nodeLayout.y
const offset = node.container.height

rows.setOffset({ axis, nodeId, offset })

setPositions()
}

function getActualPosition(position: Pixels): Pixels {
const y = offsets.getTotalOffset(position.y) + position.y * config.styles.nodeHeight
const y = rows.getTotalOffset(position.y)
const { x } = position

return {
Expand All @@ -127,11 +138,11 @@ export async function nodesContainerFactory(runId: string) {
// eslint-disable-next-line prefer-destructuring
layout = data.layout

updateLayout()
setPositions()
}

return {
container,
events,
render,
}
}
20 changes: 9 additions & 11 deletions src/factories/offsets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Map<YAxis, Map<NodeId, offset>>
type Offsets = Map<number, Map<string, number> | undefined>
import { waitForConfig } from '@/objects/config'

type SetOffsetParameters = {
axis: number,
Expand All @@ -12,24 +11,23 @@ type RemoveOffsetParameters = {
nodeId: string,
}

export type Offsets = Awaited<ReturnType<typeof offsetsFactory>>

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function offsetsFactory() {
const offsets: Offsets = new Map()
export async function offsetsFactory() {
const config = await waitForConfig()
const offsets: Map<number, Map<string, number> | undefined> = new Map()

function getOffset(axis: number): number {
const values = offsets.get(axis)

if (!values) {
return 0
}
const values = offsets.get(axis) ?? []

return Math.max(...values.values(), 0)
return Math.max(...values.values(), config.styles.nodeHeight)
}

function getTotalOffset(axis: number): number {
let value = 0

for (let index = 1; index <= axis; index++) {
for (let index = 0; index < axis; index++) {
value += getOffset(index)
}

Expand Down
3 changes: 3 additions & 0 deletions src/factories/taskRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Pixels } from '@/models/layout'
import { RunGraphNode } from '@/models/RunGraph'
import { waitForConfig } from '@/objects/config'

export type TaskRunContainer = Awaited<ReturnType<typeof taskRunContainerFactory>>

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export async function taskRunContainerFactory() {
const container = new Container()
Expand Down Expand Up @@ -49,6 +51,7 @@ export async function taskRunContainerFactory() {
}

return {
kind: 'task-run' as const,
render,
container,
}
Expand Down
2 changes: 0 additions & 2 deletions src/objects/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Viewport } from 'pixi-viewport'
import { Application, Container } from 'pixi.js'
import { EffectScope } from 'vue'
import { eventsFactory } from '@/factories/events'
import { NodesContainer } from '@/factories/nodes'
import { HorizontalScale } from '@/factories/position'
import { LayoutMode } from '@/models/layout'
import { RequiredGraphConfig } from '@/models/RunGraph'
Expand All @@ -24,7 +23,6 @@ type Events = {
fontsLoaded: Fonts,
containerCreated: Container,
layoutUpdated: LayoutMode,
nodesCreated: NodesContainer,
}

export type EventKey = keyof Events
Expand Down
13 changes: 2 additions & 11 deletions src/objects/nodes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Ticker } from 'pixi.js'
import { NodesContainer, nodesContainerFactory } from '@/factories/nodes'
import { waitForConfig } from '@/objects/config'
import { emitter, waitForEvent } from '@/objects/events'
import { centerViewport, waitForViewport } from '@/objects/viewport'

let nodes: NodesContainer | null = null
Expand All @@ -16,23 +15,15 @@ export async function startNodes(): Promise<void> {

nodes.container.alpha = 0

nodes.events.once('rendered', center)
nodes.render()

emitter.emit('nodesCreated', nodes)
nodes.container.once('rendered', center)
}

export function stopNodes(): void {
nodes = null
}

export async function waitForNodes(): Promise<NodesContainer> {
if (nodes) {
return nodes
}

return await waitForEvent('nodesCreated')
}

function center(): void {
centerViewport()

Expand Down
6 changes: 6 additions & 0 deletions src/pixi.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare namespace GlobalMixins {
interface DisplayObjectEvents {
resized: [],
rendered: [],
}
}

0 comments on commit 0edbdce

Please sign in to comment.