More rigid typing of node information, does not cover all properties (#8)
This commit is contained in:
Родитель
b6f41cf608
Коммит
935b1c306a
|
@ -1,14 +1,16 @@
|
|||
import { ITreeNode } from "react-accessible-tree";
|
||||
import { NodeDefinition } from "../types/graphTypes";
|
||||
import { NodeDefinition, CanvasNodeProperties } from "../types/graphTypes";
|
||||
import * as storedNodes from "./v1.0/nodes.json";
|
||||
|
||||
const availableNodes: NodeDefinition[] = storedNodes.availableNodes;
|
||||
const itemPanelNodes: ITreeNode[] = storedNodes.itemPanelNodes;
|
||||
|
||||
export default class Definitions {
|
||||
public static getNodeDefinition(nodeProperties: any) {
|
||||
public static getNodeDefinition(
|
||||
nodeProperties: CanvasNodeProperties
|
||||
): NodeDefinition {
|
||||
return availableNodes.filter(
|
||||
(x: any) =>
|
||||
(x) =>
|
||||
nodeProperties["@type"] &&
|
||||
x.name === nodeProperties["@type"].replace("#Microsoft.Media.", "")
|
||||
)[0];
|
||||
|
|
|
@ -22,6 +22,8 @@ const PropertyEditor: React.FunctionComponent<IPropertyEditorProps> = (
|
|||
for (const name in definition.properties) {
|
||||
const property = definition.properties[name];
|
||||
|
||||
if (!property) continue;
|
||||
|
||||
// skip the type field (already shown as dropdown by PropertyNestedObject)
|
||||
if (name === "@type") continue;
|
||||
|
||||
|
@ -35,7 +37,7 @@ const PropertyEditor: React.FunctionComponent<IPropertyEditorProps> = (
|
|||
>
|
||||
<label htmlFor={name}>
|
||||
<strong>{name}</strong> ({property.type}
|
||||
{property.type === "object" && (
|
||||
{property.type === "object" && property.parsedRef && (
|
||||
<>
|
||||
{" "}
|
||||
{Localizer.l("conforming to {type}").format(
|
||||
|
|
|
@ -14,8 +14,14 @@ import {
|
|||
MediaGraphSinkUnion,
|
||||
MediaGraphSourceUnion,
|
||||
MediaGraphTopology,
|
||||
MediaGraphNodeInput,
|
||||
} from "../lva-sdk/lvaSDKtypes";
|
||||
import { GraphInfo, MediaGraphNodeType } from "../types/graphTypes";
|
||||
import {
|
||||
GraphInfo,
|
||||
MediaGraphNodeType,
|
||||
CanvasNodeData,
|
||||
CanvasNodeProperties,
|
||||
} from "../types/graphTypes";
|
||||
import Localizer from "../localization";
|
||||
|
||||
export default class Graph {
|
||||
|
@ -34,7 +40,7 @@ export default class Graph {
|
|||
private sinks: MediaGraphSinkUnion[] = [];
|
||||
|
||||
// output is a list of nodes, edges, and some metadata
|
||||
private nodes: ICanvasNode[] = [];
|
||||
private nodes: ICanvasNode<CanvasNodeData>[] = [];
|
||||
private edges: ICanvasEdge[] = [];
|
||||
|
||||
public setGraphData(graphInfo: GraphInfo) {
|
||||
|
@ -73,7 +79,7 @@ export default class Graph {
|
|||
...NodeHelpers.getNodeProperties(nodeType),
|
||||
nodeProperties: node,
|
||||
nodeType: nodeType,
|
||||
},
|
||||
} as CanvasNodeData,
|
||||
ports: ports,
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -81,23 +87,25 @@ export default class Graph {
|
|||
}
|
||||
}
|
||||
|
||||
this.forEachNodeInput((node: any, input: any) => {
|
||||
const sourceNode = this.getNode(input.nodeName);
|
||||
const sourcePort = this.getPort(input.nodeName, false);
|
||||
const targetNode = this.getNode(node.name);
|
||||
const targetPort = this.getPort(node.name, true);
|
||||
this.forEachNodeInput(
|
||||
(node: CanvasNodeProperties, input: MediaGraphNodeInput) => {
|
||||
const sourceNode = this.getNode(input.nodeName || "");
|
||||
const sourcePort = this.getPort(input.nodeName || "", false);
|
||||
const targetNode = this.getNode(node.name);
|
||||
const targetPort = this.getPort(node.name, true);
|
||||
|
||||
// since we know all of the inputs for node, we can form edges (input, node)
|
||||
if (sourceNode && sourcePort && targetNode && targetPort) {
|
||||
this.edges.push({
|
||||
source: sourceNode.id,
|
||||
target: targetNode.id,
|
||||
sourcePortId: sourcePort.id,
|
||||
targetPortId: targetPort.id,
|
||||
id: uuid(),
|
||||
});
|
||||
// since we know all of the inputs for node, we can form edges (input, node)
|
||||
if (sourceNode && sourcePort && targetNode && targetPort) {
|
||||
this.edges.push({
|
||||
source: sourceNode.id,
|
||||
target: targetNode.id,
|
||||
sourcePortId: sourcePort.id,
|
||||
targetPortId: targetPort.id,
|
||||
id: uuid(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
this.layoutGraph();
|
||||
}
|
||||
|
@ -113,7 +121,7 @@ export default class Graph {
|
|||
if (nodeData) {
|
||||
// only save used node properties i.e. those that match the selected types
|
||||
const properties = this.getTrimmedNodeProperties(
|
||||
nodeData.nodeProperties
|
||||
nodeData.nodeProperties as CanvasNodeProperties
|
||||
);
|
||||
properties.name = nodeData.nodeProperties.name;
|
||||
|
||||
|
@ -126,13 +134,13 @@ export default class Graph {
|
|||
// filter into three categories
|
||||
switch (nodeData.nodeType) {
|
||||
case MediaGraphNodeType.Source:
|
||||
this.sources.push(properties);
|
||||
this.sources.push(properties as any);
|
||||
break;
|
||||
case MediaGraphNodeType.Processor:
|
||||
this.processors.push(properties);
|
||||
this.processors.push(properties as any);
|
||||
break;
|
||||
case MediaGraphNodeType.Sink:
|
||||
this.sinks.push(properties);
|
||||
this.sinks.push(properties as any);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -246,12 +254,14 @@ export default class Graph {
|
|||
}
|
||||
|
||||
// helper to loop through all inputs for all nodes
|
||||
private forEachNodeInput(callback: (node: any, input: any) => void) {
|
||||
private forEachNodeInput(
|
||||
callback: (node: CanvasNodeProperties, input: MediaGraphNodeInput) => void
|
||||
) {
|
||||
if (this.graphInformation && this.graphInformation.properties) {
|
||||
for (const nodeType of Graph.nodeTypeList) {
|
||||
for (const node of (this.graphInformation.properties as Record<
|
||||
string,
|
||||
any[]
|
||||
CanvasNodeProperties[]
|
||||
>)[NodeHelpers.getNodeTypeKey(nodeType)]) {
|
||||
if (node.inputs) {
|
||||
for (const input of node.inputs) {
|
||||
|
@ -266,21 +276,26 @@ export default class Graph {
|
|||
/* To be able to switch between multiple different types of properties without
|
||||
loosing the values or properties not needed for the selected type, properties
|
||||
that might not be needed are retained. We can remove these when exporting. */
|
||||
private getTrimmedNodeProperties(nodeProperties: any): any {
|
||||
private getTrimmedNodeProperties(
|
||||
nodeProperties: CanvasNodeProperties
|
||||
): CanvasNodeProperties {
|
||||
const definition = Definitions.getNodeDefinition(nodeProperties);
|
||||
const neededProperties: any = {};
|
||||
|
||||
if (!definition) {
|
||||
return {};
|
||||
return {
|
||||
"@type": nodeProperties["@type"],
|
||||
name: nodeProperties.name,
|
||||
};
|
||||
}
|
||||
|
||||
// copy over only properties as needed (determined by definition)
|
||||
for (const name in definition.properties) {
|
||||
const property = definition.properties[name];
|
||||
const nestedProperties = nodeProperties[name];
|
||||
const nestedProperties = (nodeProperties as any)[name];
|
||||
|
||||
if (nestedProperties) {
|
||||
if (property.type === "object") {
|
||||
if (property && property.type === "object") {
|
||||
if (!Helpers.isEmptyObject(nestedProperties)) {
|
||||
neededProperties[name] = this.getTrimmedNodeProperties(
|
||||
nestedProperties
|
||||
|
@ -324,8 +339,11 @@ export default class Graph {
|
|||
// converts from edges (u, v) to an array of nodes [u] pointing to v
|
||||
private getNodeInputs(nodeId: string) {
|
||||
const inboundEdges = this.edges.filter((edge) => edge.target === nodeId);
|
||||
return inboundEdges.map((edge) => ({
|
||||
nodeName: this.getNodeName(edge.source),
|
||||
}));
|
||||
return inboundEdges.map(
|
||||
(edge) =>
|
||||
({
|
||||
nodeName: this.getNodeName(edge.source),
|
||||
} as MediaGraphNodeInput)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { v4 as uuid } from "uuid";
|
||||
import { ICanvasNode, ICanvasPort } from "@vienna/react-dag-editor";
|
||||
import { MediaGraphNodeType } from "../types/graphTypes";
|
||||
import { MediaGraphNodeType, NodeDefinition } from "../types/graphTypes";
|
||||
|
||||
export default class NodeHelpers {
|
||||
// maps a MediaGraphNodeType to a string to index into the topology JSON
|
||||
|
@ -18,7 +18,10 @@ export default class NodeHelpers {
|
|||
}
|
||||
|
||||
// returns the appropriate ports for a node (proper input and input according to type)
|
||||
static getPorts(node: any, type?: MediaGraphNodeType): ICanvasPort[] {
|
||||
static getPorts(
|
||||
node: NodeDefinition,
|
||||
type?: MediaGraphNodeType
|
||||
): ICanvasPort[] {
|
||||
const ports = [];
|
||||
// type might be a value of MediaGraphNodeType that maps to 0, which is falsy
|
||||
const nodeType = typeof type === "undefined" ? node.nodeType : type;
|
||||
|
|
|
@ -2,9 +2,8 @@ import * as fs from "fs";
|
|||
import * as path from "path";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import Helpers from "../../helpers/helpers";
|
||||
import HodeHelpers from "../../helpers/nodeHelpers";
|
||||
import { MediaGraphNodeType } from "../../types/graphTypes";
|
||||
import NodeHelpers from "../../helpers/nodeHelpers";
|
||||
import { MediaGraphNodeType, NodeDefinition } from "../../types/graphTypes";
|
||||
|
||||
export default class DefinitionGenerator {
|
||||
private apiDefinition: any;
|
||||
|
@ -13,7 +12,7 @@ export default class DefinitionGenerator {
|
|||
private definitions: any;
|
||||
|
||||
private localizable: Record<string, string> = {};
|
||||
private availableNodes: any[] = [];
|
||||
private availableNodes: NodeDefinition[] = [];
|
||||
private itemPanelNodes: any[] = [];
|
||||
private usableNodes: Record<string, string[]> = {};
|
||||
|
||||
|
@ -111,9 +110,9 @@ export default class DefinitionGenerator {
|
|||
private generateItemPanelNodeList() {
|
||||
// generate nodes shown in the drag-and-droppable item panel on the left
|
||||
this.itemPanelNodes = DefinitionGenerator.nodeTypeList.map((nodeType) => ({
|
||||
title: HodeHelpers.getNodeTypeKey(nodeType),
|
||||
id: HodeHelpers.getNodeTypeKey(nodeType),
|
||||
searchKeys: [HodeHelpers.getNodeTypeKey(nodeType)],
|
||||
title: NodeHelpers.getNodeTypeKey(nodeType),
|
||||
id: NodeHelpers.getNodeTypeKey(nodeType),
|
||||
searchKeys: [NodeHelpers.getNodeTypeKey(nodeType)],
|
||||
children: this.availableNodes
|
||||
.filter((node) => node.nodeType === nodeType)
|
||||
.map((node) => {
|
||||
|
@ -121,9 +120,9 @@ export default class DefinitionGenerator {
|
|||
id: uuid(),
|
||||
name: Helpers.lowercaseFirstCharacter(node.name),
|
||||
shape: "module",
|
||||
ports: HodeHelpers.getPorts(node),
|
||||
ports: NodeHelpers.getPorts(node),
|
||||
data: {
|
||||
...HodeHelpers.getNodeProperties(node.nodeType),
|
||||
...NodeHelpers.getNodeProperties(node.nodeType),
|
||||
nodeProperties: {
|
||||
"@type": node["x-ms-discriminator-value"],
|
||||
name: node.name,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { ICanvasEdge, ICanvasNode } from "@vienna/react-dag-editor";
|
||||
import { MediaGraphTopology } from "../lva-sdk/lvaSDKtypes";
|
||||
import {
|
||||
MediaGraphTopology,
|
||||
MediaGraphNodeInput,
|
||||
} from "../lva-sdk/lvaSDKtypes";
|
||||
|
||||
export enum MediaGraphNodeType {
|
||||
Source,
|
||||
|
@ -14,12 +17,34 @@ export interface GraphInfo {
|
|||
edges: ICanvasEdge[];
|
||||
}
|
||||
|
||||
export interface NodeDefinition {
|
||||
description: string;
|
||||
export interface NodeDefinitionProperty {
|
||||
type?: string;
|
||||
parsedRef?: string;
|
||||
format?: string;
|
||||
properties?: Record<string, NodeDefinitionProperty | undefined>;
|
||||
description?: string;
|
||||
required?: string[];
|
||||
enum?: string[];
|
||||
example?: string;
|
||||
"x-ms-discriminator-value"?: string;
|
||||
}
|
||||
|
||||
export interface NodeDefinition extends NodeDefinitionProperty {
|
||||
name: string;
|
||||
nodeType: MediaGraphNodeType;
|
||||
properties: any;
|
||||
type?: string;
|
||||
required?: string[];
|
||||
parsedAllOf?: string[];
|
||||
}
|
||||
|
||||
export interface CanvasNodeProperties {
|
||||
"@type": string;
|
||||
inputs?: MediaGraphNodeInput[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface CanvasNodeData {
|
||||
color: string;
|
||||
colorAlt: string;
|
||||
iconName: string;
|
||||
nodeProperties: CanvasNodeProperties | Record<string, any>;
|
||||
nodeType: MediaGraphNodeType;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче