Skip to content

Commit

Permalink
Picking away
Browse files Browse the repository at this point in the history
  • Loading branch information
ajthinking committed Mar 12, 2023
1 parent fec981a commit 445d747
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 19 deletions.
1 change: 1 addition & 0 deletions server/Computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Param } from "./Param"
export type RunArgs = {
input: InputDeviceInterface,
output: OutputDeviceInterface,
params: Record<string, Param>
}

export interface Computer {
Expand Down
69 changes: 57 additions & 12 deletions server/DiagramBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,69 @@ describe('get', () => {
})

describe('add', () => {
it('adds a node to the diagram', () => {
it('adds a node to the diagram and ensures unique ids', () => {
const diagram = new DiagramBuilder()
.add(CreateJson)
.add(Pass)
.add(Pass)
.add(Accept)
.get()

/*
### Expect nodes
* ids: CreateJson.1, Pass.1, Accept.1
* types: CreateJson, Pass, Accept
* inputs: [], ['input'], ['input']
* outputs: ['output'], ['output'], []
### Expect links
* CreateJson.1.output-->Pass.1.input
* Pass.1.output-->Accept.1.input
*/
const nodeIds = diagram.nodes.map(node => node.id)
const nodeTypes = diagram.nodes.map(node => node.type)
const nodeInputs = diagram.nodes.map(node => node.inputs)
const nodeOutputs = diagram.nodes.map(node => node.outputs)

expect(nodeIds).toMatchObject([
'CreateJson.1',
'Pass.1',
'Pass.2',
'Accept.1'
])

expect(nodeTypes).toMatchObject([
'CreateJson',
'Pass',
'Pass',
'Accept'
])

expect(nodeInputs).toMatchObject([
[],
[{id: 'Pass.1.input', name: 'input'}],
[{id: 'Pass.2.input', name: 'input'}],
[{id: 'Accept.1.input', name: 'input'}]
])

expect(nodeOutputs).toMatchObject([
[{id: 'CreateJson.1.output', name: 'output'}],
[{id: 'Pass.1.output', name: 'output'}],
[{id: 'Pass.2.output', name: 'output'}],
[]
])
})

it('links nodes together if possible', () => {
const diagram = new DiagramBuilder()
.add(CreateJson)
.add(Pass)
.get()

expect(diagram.links).toMatchObject([
{
id: 'CreateJson.1.output-->Pass.1.input',
sourcePortId: 'CreateJson.1.output',
targetPortId: 'Pass.1.input'
}
])
})

it('does not link nodes together if not possible', () => {
const diagram = new DiagramBuilder()
.add(CreateJson)
.add(CreateJson)
.get()

expect(diagram.links).toMatchObject([])
})
})
44 changes: 39 additions & 5 deletions server/DiagramBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Computer } from "./Computer";
import { Diagram } from "./Diagram";
import { Link } from "./Link";
import { Node } from "./Node";
import { Port } from "./Port";

export class DiagramBuilder {
diagram: Diagram
Expand All @@ -11,16 +13,24 @@ export class DiagramBuilder {
}

add(computer: Computer) {
const nodeId = `${computer.name}.${this.getScopedId(computer.name)}`

const node = new Node({
id: `${computer.name}.1`,
id: nodeId,
type: computer.name,
inputs: [],
outputs: [],
inputs: (computer.inputs ?? []).map(name => {
return new Port(`${nodeId}.${name}`, name)
}),
outputs: (computer.outputs ?? []).map(name => {
return new Port(`${nodeId}.${name}`, name)
}),
})

this.diagram.nodes.push(node)

if (this.previousNode) this.linkToPrevious(node)

if (this.previousNode) this.attemptLink(this.previousNode, node)
this.previousNode = node

return this
}
Expand All @@ -29,7 +39,31 @@ export class DiagramBuilder {
return this.diagram
}

protected attemptLink(previous: Node, next: Node) {
protected getScopedId(computerName: string) {
const max = this.diagram.nodes
.filter(node => node.type === computerName)
.map(node => node.id)
.map(id => id.split('.')[1])
.map(id => parseInt(id))
.reduce((max, id) => Math.max(max, id), 0)

return max + 1
}

protected linkToPrevious(newNode: Node) {
const previousNode = this.previousNode!

const previousNodePort: Port | undefined = previousNode.outputs[0]
const newNodePort: Port | undefined = newNode.inputs[0]

if(!previousNodePort || !newNodePort) return

const link = new Link(
`${previousNodePort.id}-->${newNodePort.id}`,
previousNodePort.id,
newNodePort.id,
)

this.diagram.links.push(link)
}
}
7 changes: 6 additions & 1 deletion server/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class Executor {
while(!this.isComplete()) {
// Start execution of all nodes that can run
const runnables = this.getRunnableNodes()

const promises = runnables.map(node => {
// Put node in busy state
this.nodeStatuses.set(node.id, 'BUSY')
Expand Down Expand Up @@ -84,6 +84,11 @@ export class Executor {
computer.run({
input: this.makeInputDevice(node),
output: this.makeOutputDevice(node),
// TODO: WHO OWNS THE PARAMS??
// I think, while they originating from React Flow NODES configuration,
// they should be considered as settings given to the computer.
// The computer is initialized/hydrated with the params
params: {},
})
)
}
Expand Down
3 changes: 2 additions & 1 deletion server/computers/CreateJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ export const CreateJson: Computer = {
name: 'CreateJson',
outputs: ['output'],

async *run({ output }: RunArgs) {
async *run({ output, params }: RunArgs) {
while(true) {
const json = '[{"id": 1}, {"id": 2}, {"id": 3}]'
// const json = params.json.value // TODO return to this
output.push(JSON.parse(json))
yield;
}
Expand Down
38 changes: 38 additions & 0 deletions utils/JsonLike.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { JsonLike } from "./JsonLike";

describe('parse', () => {
it('should parse regular JSON', () => {
const json = '{"name": "John", "age": 30}';
const parsed = JsonLike.parse(json);
expect(parsed).toEqual({ name: 'John', age: 30 });
});

it('should parse indented JSON-like string with unquoted keys', () => {
const json = `{
name: "John",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
state: "CA"
}
}`;
const parsed = JsonLike.parse(json);
expect(parsed).toEqual({
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA'
}
});
});

it('cant parse unquoted keys without indentation', () => {
const json = `{name: "John", age: 30}`

expect(() => JsonLike.parse(json)).toThrow()
})
});
18 changes: 18 additions & 0 deletions utils/JsonLike.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const JsonLike = {
parse(json: string) {
// Check if the string is already valid JSON
try {
return JSON.parse(json);
} catch (e) {}

// Handle JSON-like string where keys may not have quotes
const matchKey = /\s+(\w+)\s*:/g;
const fixed = json.replace(matchKey, '"$1":');

try {
return JSON.parse(fixed);
} catch (e) {
throw new Error('Could not parse JSON-like string.');
}
},
};

0 comments on commit 445d747

Please sign in to comment.