More rigid typing of node information, does not cover all properties (#8)

This commit is contained in:
fj-msft 2020-07-28 16:15:41 -05:00 коммит произвёл GitHub
Родитель b6f41cf608
Коммит 935b1c306a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 100 добавлений и 51 удалений

Просмотреть файл

@ -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;
}