mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 08:51:24 +00:00
Prettier 2.0 (#393)
This commit is contained in:
@@ -1,158 +1,156 @@
|
||||
import * as sinon from "sinon";
|
||||
import { D3ForceGraph, LoadMoreDataAction, D3GraphNodeData } from "./D3ForceGraph";
|
||||
import { D3Node, D3Link, GraphData } from "../GraphExplorerComponent/GraphData";
|
||||
import GraphTab from "../../Tabs/GraphTab";
|
||||
|
||||
describe("D3ForceGraph", () => {
|
||||
const v1Id = "v1";
|
||||
const l1: D3Link = {
|
||||
id: "id1",
|
||||
inV: v1Id,
|
||||
outV: "v2",
|
||||
label: "l1",
|
||||
source: null,
|
||||
target: null
|
||||
};
|
||||
|
||||
it("should count neighbors", () => {
|
||||
const l2: D3Link = {
|
||||
id: "id1",
|
||||
inV: "v2",
|
||||
outV: v1Id,
|
||||
label: "l2",
|
||||
source: null,
|
||||
target: null
|
||||
};
|
||||
|
||||
const l3: D3Link = {
|
||||
id: "id1",
|
||||
inV: v1Id,
|
||||
outV: "v3",
|
||||
label: "l3",
|
||||
source: null,
|
||||
target: null
|
||||
};
|
||||
|
||||
const links = [l1, l2, l3];
|
||||
const count = D3ForceGraph.countEdges(links);
|
||||
|
||||
expect(count.get(v1Id)).toBe(3);
|
||||
expect(count.get("v2")).toBe(2);
|
||||
expect(count.get("v3")).toBe(1);
|
||||
});
|
||||
|
||||
describe("Behavior", () => {
|
||||
let forceGraph: D3ForceGraph;
|
||||
let rootNode: SVGSVGElement;
|
||||
|
||||
const newGraph: GraphData<D3Node, D3Link> = new GraphData();
|
||||
newGraph.addVertex({
|
||||
id: v1Id,
|
||||
label: "vlabel1",
|
||||
_isRoot: true
|
||||
});
|
||||
newGraph.addVertex({
|
||||
id: "v2",
|
||||
label: "vlabel2"
|
||||
});
|
||||
newGraph.addEdge(l1);
|
||||
|
||||
beforeAll(() => {
|
||||
rootNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
rootNode.setAttribute("class", "maingraph");
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
rootNode.remove();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
forceGraph = new D3ForceGraph({
|
||||
graphConfig: GraphTab.createGraphConfig(),
|
||||
onHighlightedNode: sinon.spy(),
|
||||
onLoadMoreData: (action: LoadMoreDataAction): void => {},
|
||||
|
||||
// parent to graph
|
||||
onInitialized: sinon.spy(),
|
||||
|
||||
// For unit testing purposes
|
||||
onGraphUpdated: null
|
||||
});
|
||||
|
||||
forceGraph.init(rootNode);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
forceGraph.destroy();
|
||||
});
|
||||
|
||||
it("should render graph d3 nodes and edges", done => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".nodes").length).toBe(1);
|
||||
expect($(rootNode).find(".links").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render vertices (as circle)", done => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".node circle").length).toBe(2);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render vertex label", done => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(`text:contains(${v1Id})`).length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render root vertex", done => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".node.root").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render edge", done => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find("path.link").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should call onInitialized callback", () => {
|
||||
expect((forceGraph.params.onInitialized as sinon.SinonSpy).calledOnce).toBe(true);
|
||||
});
|
||||
|
||||
it("should call onHighlightedNode callback when mouse hovering over node", () => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
const mouseoverEvent = document.createEvent("Events");
|
||||
mouseoverEvent.initEvent("mouseover", true, false);
|
||||
$(rootNode)
|
||||
.find(".node")[0]
|
||||
.dispatchEvent(mouseoverEvent); // [0] is v1 vertex
|
||||
|
||||
// onHighlightedNode is always called once to clear the selection
|
||||
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
|
||||
|
||||
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData;
|
||||
expect(onHighlightedNode).not.toBe(null);
|
||||
expect(onHighlightedNode.id).toEqual(v1Id);
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
});
|
||||
});
|
||||
import * as sinon from "sinon";
|
||||
import { D3ForceGraph, LoadMoreDataAction, D3GraphNodeData } from "./D3ForceGraph";
|
||||
import { D3Node, D3Link, GraphData } from "../GraphExplorerComponent/GraphData";
|
||||
import GraphTab from "../../Tabs/GraphTab";
|
||||
|
||||
describe("D3ForceGraph", () => {
|
||||
const v1Id = "v1";
|
||||
const l1: D3Link = {
|
||||
id: "id1",
|
||||
inV: v1Id,
|
||||
outV: "v2",
|
||||
label: "l1",
|
||||
source: null,
|
||||
target: null,
|
||||
};
|
||||
|
||||
it("should count neighbors", () => {
|
||||
const l2: D3Link = {
|
||||
id: "id1",
|
||||
inV: "v2",
|
||||
outV: v1Id,
|
||||
label: "l2",
|
||||
source: null,
|
||||
target: null,
|
||||
};
|
||||
|
||||
const l3: D3Link = {
|
||||
id: "id1",
|
||||
inV: v1Id,
|
||||
outV: "v3",
|
||||
label: "l3",
|
||||
source: null,
|
||||
target: null,
|
||||
};
|
||||
|
||||
const links = [l1, l2, l3];
|
||||
const count = D3ForceGraph.countEdges(links);
|
||||
|
||||
expect(count.get(v1Id)).toBe(3);
|
||||
expect(count.get("v2")).toBe(2);
|
||||
expect(count.get("v3")).toBe(1);
|
||||
});
|
||||
|
||||
describe("Behavior", () => {
|
||||
let forceGraph: D3ForceGraph;
|
||||
let rootNode: SVGSVGElement;
|
||||
|
||||
const newGraph: GraphData<D3Node, D3Link> = new GraphData();
|
||||
newGraph.addVertex({
|
||||
id: v1Id,
|
||||
label: "vlabel1",
|
||||
_isRoot: true,
|
||||
});
|
||||
newGraph.addVertex({
|
||||
id: "v2",
|
||||
label: "vlabel2",
|
||||
});
|
||||
newGraph.addEdge(l1);
|
||||
|
||||
beforeAll(() => {
|
||||
rootNode = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
rootNode.setAttribute("class", "maingraph");
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
rootNode.remove();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
forceGraph = new D3ForceGraph({
|
||||
graphConfig: GraphTab.createGraphConfig(),
|
||||
onHighlightedNode: sinon.spy(),
|
||||
onLoadMoreData: (action: LoadMoreDataAction): void => {},
|
||||
|
||||
// parent to graph
|
||||
onInitialized: sinon.spy(),
|
||||
|
||||
// For unit testing purposes
|
||||
onGraphUpdated: null,
|
||||
});
|
||||
|
||||
forceGraph.init(rootNode);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
forceGraph.destroy();
|
||||
});
|
||||
|
||||
it("should render graph d3 nodes and edges", (done) => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".nodes").length).toBe(1);
|
||||
expect($(rootNode).find(".links").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render vertices (as circle)", (done) => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".node circle").length).toBe(2);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render vertex label", (done) => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(`text:contains(${v1Id})`).length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render root vertex", (done) => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find(".node.root").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should render edge", (done) => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
expect($(rootNode).find("path.link").length).toBe(1);
|
||||
done();
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
|
||||
it("should call onInitialized callback", () => {
|
||||
expect((forceGraph.params.onInitialized as sinon.SinonSpy).calledOnce).toBe(true);
|
||||
});
|
||||
|
||||
it("should call onHighlightedNode callback when mouse hovering over node", () => {
|
||||
forceGraph.params.onGraphUpdated = () => {
|
||||
const mouseoverEvent = document.createEvent("Events");
|
||||
mouseoverEvent.initEvent("mouseover", true, false);
|
||||
$(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex
|
||||
|
||||
// onHighlightedNode is always called once to clear the selection
|
||||
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
|
||||
|
||||
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData;
|
||||
expect(onHighlightedNode).not.toBe(null);
|
||||
expect(onHighlightedNode.id).toEqual(v1Id);
|
||||
};
|
||||
|
||||
forceGraph.updateGraph(newGraph);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,7 +101,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
|
||||
this.props.editedNeighbors.addedEdges.push({
|
||||
inputInV: inV,
|
||||
inputOutV: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
|
||||
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE
|
||||
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
|
||||
});
|
||||
this.onUpdateEdges();
|
||||
}
|
||||
@@ -110,7 +110,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
|
||||
this.props.editedNeighbors.addedEdges.push({
|
||||
inputInV: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
|
||||
inputOutV: outV,
|
||||
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE
|
||||
label: EditorNeighborsComponent.DEFAULT_BLANK_VALUE,
|
||||
});
|
||||
this.onUpdateEdges();
|
||||
}
|
||||
@@ -215,7 +215,7 @@ export class EditorNeighborsComponent extends React.Component<EditorNeighborsCom
|
||||
</td>
|
||||
<td className="actionCol">
|
||||
<span className="rightPaneTrashIcon rightPaneBtns">
|
||||
<img src={DeleteIcon} alt="Delete" onClick={e => this.removeAddedEdgeToNeighbor(index)} />
|
||||
<img src={DeleteIcon} alt="Delete" onClick={(e) => this.removeAddedEdgeToNeighbor(index)} />
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("<EditorNodePropertiesComponent />", () => {
|
||||
readOnlyProperties: [
|
||||
{
|
||||
key: "singlevalueprop",
|
||||
values: [{ value: "abcd", type: "string" }]
|
||||
values: [{ value: "abcd", type: "string" }],
|
||||
},
|
||||
{
|
||||
key: "multivaluesprop",
|
||||
@@ -24,14 +24,14 @@ describe("<EditorNodePropertiesComponent />", () => {
|
||||
{ value: true, type: "boolean" },
|
||||
{ value: false, type: "boolean" },
|
||||
{ value: undefined, type: "null" },
|
||||
{ value: null, type: "null" }
|
||||
]
|
||||
}
|
||||
{ value: null, type: "null" },
|
||||
],
|
||||
},
|
||||
],
|
||||
existingProperties: [
|
||||
{
|
||||
key: "singlevalueprop2",
|
||||
values: [{ value: "ijkl", type: "string" }]
|
||||
values: [{ value: "ijkl", type: "string" }],
|
||||
},
|
||||
{
|
||||
key: "multivaluesprop2",
|
||||
@@ -41,14 +41,14 @@ describe("<EditorNodePropertiesComponent />", () => {
|
||||
{ value: true, type: "boolean" },
|
||||
{ value: false, type: "boolean" },
|
||||
{ value: undefined, type: "null" },
|
||||
{ value: null, type: "null" }
|
||||
]
|
||||
}
|
||||
{ value: null, type: "null" },
|
||||
],
|
||||
},
|
||||
],
|
||||
addedProperties: [],
|
||||
droppedKeys: []
|
||||
droppedKeys: [],
|
||||
},
|
||||
onUpdateProperties: (editedProperties: EditedProperties): void => {}
|
||||
onUpdateProperties: (editedProperties: EditedProperties): void => {},
|
||||
};
|
||||
const wrapper = shallow(<EditorNodePropertiesComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
@@ -61,27 +61,27 @@ describe("<EditorNodePropertiesComponent />", () => {
|
||||
readOnlyProperties: [
|
||||
{
|
||||
key: "unicode1",
|
||||
values: [{ value: "Véronique", type: "string" }]
|
||||
values: [{ value: "Véronique", type: "string" }],
|
||||
},
|
||||
{
|
||||
key: "unicode2",
|
||||
values: [{ value: "亜妃子", type: "string" }]
|
||||
}
|
||||
values: [{ value: "亜妃子", type: "string" }],
|
||||
},
|
||||
],
|
||||
existingProperties: [
|
||||
{
|
||||
key: "unicode1",
|
||||
values: [{ value: "André", type: "string" }]
|
||||
values: [{ value: "André", type: "string" }],
|
||||
},
|
||||
{
|
||||
key: "unicode2",
|
||||
values: [{ value: "あきら, アキラ,安喜良", type: "string" }]
|
||||
}
|
||||
values: [{ value: "あきら, アキラ,安喜良", type: "string" }],
|
||||
},
|
||||
],
|
||||
addedProperties: [],
|
||||
droppedKeys: []
|
||||
droppedKeys: [],
|
||||
},
|
||||
onUpdateProperties: (editedProperties: EditedProperties): void => {}
|
||||
onUpdateProperties: (editedProperties: EditedProperties): void => {},
|
||||
};
|
||||
const wrapper = shallow(<EditorNodePropertiesComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
@@ -79,7 +79,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
{this.props.editedProperties.readOnlyProperties.map((nodeProp: ViewModels.InputProperty) =>
|
||||
ReadOnlyNodePropertiesComponent.renderReadOnlyPropertyKeyPair(
|
||||
nodeProp.key,
|
||||
nodeProp.values.map(val => val.value)
|
||||
nodeProp.values.map((val) => val.value)
|
||||
)
|
||||
)}
|
||||
</React.Fragment>
|
||||
@@ -112,7 +112,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
type="text"
|
||||
value={singleValue.value.toString()}
|
||||
placeholder="Value"
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
singleValue.value = e.target.value;
|
||||
this.props.onUpdateProperties(this.props.editedProperties);
|
||||
}}
|
||||
@@ -123,7 +123,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
<select
|
||||
className="typeSelect"
|
||||
value={singleValue.type}
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
singleValue.type = e.target.value as ViewModels.InputPropertyValueTypeString;
|
||||
if (singleValue.type === "null") {
|
||||
singleValue.value = null;
|
||||
@@ -144,7 +144,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
className="rightPaneTrashIcon rightPaneBtns"
|
||||
as="span"
|
||||
aria-label="Delete property"
|
||||
onActivated={e => this.removeExistingProperty(key)}
|
||||
onActivated={(e) => this.removeExistingProperty(key)}
|
||||
>
|
||||
<img src={DeleteIcon} alt="Delete" />
|
||||
</AccessibleElement>
|
||||
@@ -157,14 +157,16 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
return (
|
||||
<tr key={nodeProp.key}>
|
||||
<td className="labelCol propertyId">{nodeProp.key}</td>
|
||||
<td>{nodeProp.values.map(value => ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value.value))}</td>
|
||||
<td>
|
||||
{nodeProp.values.map((value) => ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value.value))}
|
||||
</td>
|
||||
<td />
|
||||
<td className="actionCol">
|
||||
<AccessibleElement
|
||||
className="rightPaneTrashIcon rightPaneBtns"
|
||||
as="span"
|
||||
aria-label="Remove existing property"
|
||||
onActivated={e => this.removeExistingProperty(nodeProp.key)}
|
||||
onActivated={(e) => this.removeExistingProperty(nodeProp.key)}
|
||||
>
|
||||
<img src={DeleteIcon} alt="Delete" />
|
||||
</AccessibleElement>
|
||||
@@ -188,7 +190,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
type="text"
|
||||
value={addedProperty.key}
|
||||
placeholder="Key"
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
addedProperty.key = e.target.value;
|
||||
this.props.onUpdateProperties(this.props.editedProperties);
|
||||
}}
|
||||
@@ -201,7 +203,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
type="text"
|
||||
value={firstValue.value.toString()}
|
||||
placeholder="Value"
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
firstValue.value = e.target.value;
|
||||
if (firstValue.type === "null") {
|
||||
firstValue.value = null;
|
||||
@@ -215,7 +217,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
<select
|
||||
className="typeSelect"
|
||||
value={firstValue.type}
|
||||
onChange={e => {
|
||||
onChange={(e) => {
|
||||
firstValue.type = e.target.value as ViewModels.InputPropertyValueTypeString;
|
||||
this.props.onUpdateProperties(this.props.editedProperties);
|
||||
}}
|
||||
@@ -233,7 +235,7 @@ export class EditorNodePropertiesComponent extends React.Component<EditorNodePro
|
||||
className="rightPaneTrashIcon rightPaneBtns"
|
||||
as="span"
|
||||
aria-label="Remove property"
|
||||
onActivated={e => this.removeAddedProperty(index)}
|
||||
onActivated={(e) => this.removeAddedProperty(index)}
|
||||
>
|
||||
<img src={DeleteIcon} alt="Delete" />
|
||||
</AccessibleElement>
|
||||
|
||||
@@ -69,8 +69,8 @@ describe("Graph Data", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
properties: {
|
||||
testString: [{ id: "123", value: stringValue }]
|
||||
}
|
||||
testString: [{ id: "123", value: stringValue }],
|
||||
},
|
||||
},
|
||||
"testString"
|
||||
);
|
||||
@@ -85,8 +85,8 @@ describe("Graph Data", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
properties: {
|
||||
testString: [{ id: "123", value: numberValue }]
|
||||
}
|
||||
testString: [{ id: "123", value: numberValue }],
|
||||
},
|
||||
},
|
||||
"testString"
|
||||
);
|
||||
@@ -101,8 +101,8 @@ describe("Graph Data", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
properties: {
|
||||
testString: [{ id: "123", value: booleanValue }]
|
||||
}
|
||||
testString: [{ id: "123", value: booleanValue }],
|
||||
},
|
||||
},
|
||||
"testString"
|
||||
);
|
||||
|
||||
@@ -152,7 +152,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
||||
const v = this.getVertexById(e.outV);
|
||||
GraphData.addOutE(v, p, {
|
||||
id: e.id,
|
||||
inV: e.outV
|
||||
inV: e.outV,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -165,7 +165,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
||||
const v = this.getVertexById(e.inV);
|
||||
GraphData.addInE(v, p, {
|
||||
id: e.id,
|
||||
outV: e.inV
|
||||
outV: e.inV,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -52,8 +52,8 @@ describe("Check whether query result is edge-vertex array", () => {
|
||||
GraphExplorer.isEdgeVertexPairArray([
|
||||
{
|
||||
e: { id: "ide", type: "edge" },
|
||||
v: { id: "idv", type: "vertex" }
|
||||
}
|
||||
v: { id: "idv", type: "vertex" },
|
||||
},
|
||||
])
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -75,7 +75,7 @@ describe("getPkIdFromDocumentId", () => {
|
||||
_self: "_self",
|
||||
_etag: "_etag",
|
||||
_ts: 1234,
|
||||
...override
|
||||
...override,
|
||||
});
|
||||
|
||||
it("should create pkid pair from non-partitioned graph", () => {
|
||||
@@ -171,7 +171,7 @@ describe("GraphExplorer", () => {
|
||||
|
||||
/* TODO Figure out how to make this Knockout-free */
|
||||
graphConfigUiData: graphConfigUi,
|
||||
graphConfig: graphConfig
|
||||
graphConfig: graphConfig,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -244,7 +244,7 @@ describe("GraphExplorer", () => {
|
||||
selectNode: sinon.spy(),
|
||||
resetZoom: sinon.spy(),
|
||||
updateGraph: sinon.stub().callsFake(() => complete()),
|
||||
enableHighlight: sinon.spy()
|
||||
enableHighlight: sinon.spy(),
|
||||
};
|
||||
graphExplorer.d3ForceGraph = mockGraphRenderer;
|
||||
});
|
||||
@@ -268,7 +268,7 @@ describe("GraphExplorer", () => {
|
||||
client.params.successCallback({
|
||||
requestId: requestId,
|
||||
data: backendResponse.response,
|
||||
requestCharge: gremlinRU
|
||||
requestCharge: gremlinRU,
|
||||
});
|
||||
|
||||
if (backendResponse.isLast) {
|
||||
@@ -305,7 +305,7 @@ describe("GraphExplorer", () => {
|
||||
_query: query,
|
||||
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
|
||||
hasMoreResults: () => false,
|
||||
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
|
||||
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {},
|
||||
};
|
||||
});
|
||||
(queryDocumentsPage as jest.Mock).mockImplementation(
|
||||
@@ -318,7 +318,7 @@ describe("GraphExplorer", () => {
|
||||
documents: docDBResponse.response,
|
||||
activityId: "",
|
||||
headers: [] as any[],
|
||||
requestCharge: gVRU
|
||||
requestCharge: gVRU,
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -343,11 +343,11 @@ describe("GraphExplorer", () => {
|
||||
});
|
||||
|
||||
describe("Load Graph button", () => {
|
||||
beforeEach(async done => {
|
||||
beforeEach(async (done) => {
|
||||
const backendResponses: BackendResponses = {};
|
||||
backendResponses["g.V()"] = backendResponses["g.V('1')"] = {
|
||||
response: [{ id: "1", type: "vertex" }],
|
||||
isLast: false
|
||||
isLast: false,
|
||||
};
|
||||
backendResponses[createFetchOutEQuery("1", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: false };
|
||||
backendResponses[createFetchInEQuery("1", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: true };
|
||||
@@ -370,7 +370,7 @@ describe("GraphExplorer", () => {
|
||||
it("should submit g.V() as docdb query with proper parameters", () => {
|
||||
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
|
||||
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||
enableCrossPartitionQuery: true
|
||||
enableCrossPartitionQuery: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -380,11 +380,11 @@ describe("GraphExplorer", () => {
|
||||
});
|
||||
|
||||
describe("Execute Gremlin Query button", () => {
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
const backendResponses: BackendResponses = {};
|
||||
backendResponses["g.V()"] = backendResponses["g.V('2')"] = {
|
||||
response: [{ id: "2", type: "vertex" }],
|
||||
isLast: false
|
||||
isLast: false,
|
||||
};
|
||||
backendResponses[createFetchOutEQuery("2", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: false };
|
||||
backendResponses[createFetchInEQuery("2", GraphExplorer.LOAD_PAGE_SIZE + 1)] = { response: [], isLast: true };
|
||||
@@ -407,7 +407,7 @@ describe("GraphExplorer", () => {
|
||||
it("should submit g.V() as docdb query with proper parameters", () => {
|
||||
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
|
||||
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||
enableCrossPartitionQuery: true
|
||||
enableCrossPartitionQuery: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -435,10 +435,10 @@ describe("GraphExplorer", () => {
|
||||
inV: node2Id,
|
||||
outV: node1Id,
|
||||
label: linkLabel,
|
||||
type: "edge"
|
||||
type: "edge",
|
||||
};
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
const backendResponses: BackendResponses = {};
|
||||
// TODO Make this less dependent on spaces, order and quotes
|
||||
backendResponses["g.V()"] = backendResponses[`g.V('${node1Id}','${node2Id}')`] = {
|
||||
@@ -447,15 +447,15 @@ describe("GraphExplorer", () => {
|
||||
id: node1Id,
|
||||
label: label1,
|
||||
type: "vertex",
|
||||
properties: { prop1Id: [{ id: "id123", value: prop1Val1 }] }
|
||||
properties: { prop1Id: [{ id: "id123", value: prop1Val1 }] },
|
||||
},
|
||||
{
|
||||
id: node2Id,
|
||||
label: label2,
|
||||
type: "vertex"
|
||||
}
|
||||
type: "vertex",
|
||||
},
|
||||
],
|
||||
isLast: false
|
||||
isLast: false,
|
||||
};
|
||||
|
||||
backendResponses[createFetchOutEQuery(node1Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
|
||||
@@ -465,17 +465,17 @@ describe("GraphExplorer", () => {
|
||||
v: {
|
||||
id: node2Id,
|
||||
label: label2,
|
||||
type: "vertex"
|
||||
}
|
||||
}
|
||||
type: "vertex",
|
||||
},
|
||||
},
|
||||
],
|
||||
isLast: false
|
||||
isLast: false,
|
||||
};
|
||||
backendResponses[createFetchInEQuery(node1Id, GraphExplorer.LOAD_PAGE_SIZE)] = { response: [], isLast: true };
|
||||
|
||||
backendResponses[createFetchOutEQuery(node2Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
|
||||
response: [],
|
||||
isLast: false
|
||||
isLast: false,
|
||||
};
|
||||
backendResponses[createFetchInEQuery(node2Id, GraphExplorer.LOAD_PAGE_SIZE + 1)] = {
|
||||
response: [
|
||||
@@ -485,16 +485,16 @@ describe("GraphExplorer", () => {
|
||||
inV: node2Id,
|
||||
outV: node1Id,
|
||||
label: linkLabel,
|
||||
type: "edge"
|
||||
type: "edge",
|
||||
},
|
||||
v: {
|
||||
id: node1Id,
|
||||
label: label1,
|
||||
type: "vertex"
|
||||
}
|
||||
}
|
||||
type: "vertex",
|
||||
},
|
||||
},
|
||||
],
|
||||
isLast: true
|
||||
isLast: true,
|
||||
};
|
||||
|
||||
const docDBResponse: AjaxResponse = { response: [{ id: node1Id }, { id: node2Id }], isLast: false };
|
||||
@@ -581,7 +581,7 @@ describe("GraphExplorer", () => {
|
||||
describe("Select root node", () => {
|
||||
let loadNeighborsPageStub: sinon.SinonSpy;
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
loadNeighborsPageStub = sinon.stub(graphExplorerInstance, "loadNeighborsPage").callsFake(() => {
|
||||
return Q.resolve();
|
||||
});
|
||||
@@ -660,12 +660,12 @@ describe("GraphExplorer", () => {
|
||||
let processGremlinQueryResultsStub: sinon.SinonSpy;
|
||||
let graphExplorerInstance: GraphExplorer;
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
const backendResponses: BackendResponses = {};
|
||||
// TODO Make this less dependent on spaces, order and quotes
|
||||
backendResponses["g.V()"] = {
|
||||
response: "invalid response",
|
||||
isLast: true
|
||||
isLast: true,
|
||||
};
|
||||
|
||||
const docDBResponse: AjaxResponse = { response: [], isLast: false };
|
||||
@@ -695,11 +695,11 @@ describe("GraphExplorer", () => {
|
||||
});
|
||||
|
||||
describe("when isGraphAutoVizDisabled setting is true (autoviz disabled)", () => {
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
const backendResponses: BackendResponses = {};
|
||||
backendResponses["g.V()"] = backendResponses["g.V('3')"] = {
|
||||
response: [{ id: "3", type: "vertex" }],
|
||||
isLast: true
|
||||
isLast: true,
|
||||
};
|
||||
const docDBResponse: AjaxResponse = { response: [{ id: "3" }], isLast: false };
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ enum FilterQueryStatus {
|
||||
GraphResult,
|
||||
Loading,
|
||||
NonGraphResult,
|
||||
ErrorResult
|
||||
ErrorResult,
|
||||
}
|
||||
|
||||
interface GraphExplorerState {
|
||||
@@ -159,7 +159,7 @@ enum ResultDisplay {
|
||||
None,
|
||||
Graph,
|
||||
Json,
|
||||
Stats
|
||||
Stats,
|
||||
}
|
||||
|
||||
interface UserQueryResult {
|
||||
@@ -237,7 +237,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
resultDisplay: ResultDisplay.None,
|
||||
filterQueryError: null,
|
||||
filterQueryWarning: null,
|
||||
filterQueryStatus: FilterQueryStatus.NoResult
|
||||
filterQueryStatus: FilterQueryStatus.NoResult,
|
||||
};
|
||||
|
||||
// Not part of React state
|
||||
@@ -252,26 +252,26 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
title: "JSON",
|
||||
content: {
|
||||
className: "graphJsonEditor graphTabContent",
|
||||
render: () => this.renderResultAsJson()
|
||||
render: () => this.renderResultAsJson(),
|
||||
},
|
||||
isVisible: () => true
|
||||
isVisible: () => true,
|
||||
},
|
||||
{
|
||||
title: "Graph",
|
||||
content: {
|
||||
className: "graphTabContent",
|
||||
render: () => this.renderResultAsGraph()
|
||||
render: () => this.renderResultAsGraph(),
|
||||
},
|
||||
isVisible: () => this.state.filterQueryStatus === FilterQueryStatus.GraphResult
|
||||
isVisible: () => this.state.filterQueryStatus === FilterQueryStatus.GraphResult,
|
||||
},
|
||||
{
|
||||
title: GraphExplorer.QUERY_STATS_BUTTON_LABEL,
|
||||
content: {
|
||||
className: "graphTabContent",
|
||||
render: () => this.renderResultStats()
|
||||
render: () => this.renderResultStats(),
|
||||
},
|
||||
isVisible: () => true
|
||||
}
|
||||
isVisible: () => true,
|
||||
},
|
||||
];
|
||||
|
||||
this.queryRawData = null;
|
||||
@@ -286,7 +286,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
|
||||
/* TODO Make this Knockout-free ! */
|
||||
this.props.graphConfigUiData.nodeCaptionChoice.subscribe(key => {
|
||||
this.props.graphConfigUiData.nodeCaptionChoice.subscribe((key) => {
|
||||
this.props.graphConfig.nodeCaption(key);
|
||||
const selectedNode = this.state.highlightedNode;
|
||||
if (selectedNode) {
|
||||
@@ -295,20 +295,20 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
|
||||
this.render();
|
||||
});
|
||||
this.props.graphConfigUiData.nodeColorKeyChoice.subscribe(val => {
|
||||
this.props.graphConfigUiData.nodeColorKeyChoice.subscribe((val) => {
|
||||
this.props.graphConfig.nodeColorKey(val === GraphExplorer.NONE_CHOICE ? null : val);
|
||||
this.render();
|
||||
});
|
||||
this.props.graphConfigUiData.showNeighborType.subscribe(val => {
|
||||
this.props.graphConfigUiData.showNeighborType.subscribe((val) => {
|
||||
this.props.graphConfig.showNeighborType(val);
|
||||
this.render();
|
||||
});
|
||||
|
||||
this.props.graphConfigUiData.nodeIconChoice.subscribe(val => {
|
||||
this.props.graphConfigUiData.nodeIconChoice.subscribe((val) => {
|
||||
this.updateNodeIcons(val, this.props.graphConfigUiData.nodeIconSet());
|
||||
this.render();
|
||||
});
|
||||
this.props.graphConfigUiData.nodeIconSet.subscribe(val => {
|
||||
this.props.graphConfigUiData.nodeIconSet.subscribe((val) => {
|
||||
this.updateNodeIcons(this.props.graphConfigUiData.nodeIconChoice(), val);
|
||||
this.render();
|
||||
});
|
||||
@@ -316,7 +316,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
|
||||
props.onGraphAccessorCreated({
|
||||
applyFilter: this.submitQuery.bind(this),
|
||||
addVertex: this.addVertex.bind(this)
|
||||
addVertex: this.addVertex.bind(this),
|
||||
});
|
||||
} // constructor
|
||||
|
||||
@@ -341,7 +341,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
let pkId = editedProperties.pkId;
|
||||
let updateQueryFragment = "";
|
||||
|
||||
finalProperties.forEach(p => {
|
||||
finalProperties.forEach((p) => {
|
||||
// Partition key cannot be updated
|
||||
if (p.key === partitionKeyProperty) {
|
||||
return;
|
||||
@@ -370,7 +370,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.setNodePropertiesViewMode(NodeProperties.Mode.READONLY_PROP);
|
||||
return Q.resolve({
|
||||
data: [],
|
||||
isIncomplete: false
|
||||
isIncomplete: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
} else {
|
||||
promise = Q.resolve({
|
||||
data: [],
|
||||
isIncomplete: false
|
||||
isIncomplete: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -683,8 +683,8 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
: null),
|
||||
currentPage: {
|
||||
start: offsetIndex,
|
||||
end: offsetIndex + addedEdgesNb
|
||||
}
|
||||
end: offsetIndex + addedEdgesNb,
|
||||
},
|
||||
};
|
||||
}
|
||||
updateGraphData();
|
||||
@@ -739,7 +739,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
enableCrossPartitionQuery:
|
||||
StorageUtility.LocalStorageUtility.getEntryString(
|
||||
StorageUtility.StorageKey.IsCrossPartitionQueryEnabled
|
||||
) === "true"
|
||||
) === "true",
|
||||
} as FeedOptions
|
||||
);
|
||||
const response = await iterator.fetchNext();
|
||||
@@ -878,7 +878,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.setState({
|
||||
rootMap: {},
|
||||
hasMoreRoots: false,
|
||||
selectedRootId: null
|
||||
selectedRootId: null,
|
||||
});
|
||||
this.setFilterQueryStatus(FilterQueryStatus.Loading);
|
||||
|
||||
@@ -903,7 +903,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const errorMsg = `Failure in submitting query: ${query}: ${getErrorMessage(error)}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||
this.setState({
|
||||
filterQueryError: errorMsg
|
||||
filterQueryError: errorMsg,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1036,7 +1036,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
collectionName: this.props.collectionId,
|
||||
defaultExperience: Constants.DefaultAccountExperience.Graph,
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: "Graph"
|
||||
tabTitle: "Graph",
|
||||
},
|
||||
this.props.onLoadStartKey
|
||||
);
|
||||
@@ -1148,7 +1148,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
$.each(documents, (index: number, doc: any) => {
|
||||
newIconsMap[doc["_graph_icon_property_value"]] = {
|
||||
data: doc["icon"],
|
||||
format: doc["format"]
|
||||
format: doc["format"],
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1172,7 +1172,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
this.setResultDisplay(ResultDisplay.None);
|
||||
this.setState({
|
||||
filterQueryError: null,
|
||||
filterQueryWarning: null
|
||||
filterQueryWarning: null,
|
||||
});
|
||||
break;
|
||||
case FilterQueryStatus.NonGraphResult:
|
||||
@@ -1206,7 +1206,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
let result = GraphData.GraphData.getNodePropValue(value, key);
|
||||
return {
|
||||
caption: result !== undefined ? result : value.id,
|
||||
id: value.id
|
||||
id: value.id,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -1273,7 +1273,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
databaseId: this.props.databaseId,
|
||||
collectionId: this.props.collectionId,
|
||||
masterKey: this.props.masterKey,
|
||||
maxResultSize: GraphExplorer.MAX_RESULT_SIZE
|
||||
maxResultSize: GraphExplorer.MAX_RESULT_SIZE,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1298,7 +1298,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const isTabsContentExpanded = !this.state.isTabsContentExpanded;
|
||||
this.setState({
|
||||
isTabsContentExpanded: isTabsContentExpanded,
|
||||
isPropertiesCollapsed: isTabsContentExpanded
|
||||
isPropertiesCollapsed: isTabsContentExpanded,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1399,8 +1399,9 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
private updatePossibleVertices(): Promise<PossibleVertex[]> {
|
||||
const highlightedNodeId = this.state.highlightedNode ? this.state.highlightedNode.id : null;
|
||||
|
||||
const q = `SELECT c.id, c["${this.props.graphConfigUiData.nodeCaptionChoice() ||
|
||||
"id"}"] AS p FROM c WHERE NOT IS_DEFINED(c._isEdge)`;
|
||||
const q = `SELECT c.id, c["${
|
||||
this.props.graphConfigUiData.nodeCaptionChoice() || "id"
|
||||
}"] AS p FROM c WHERE NOT IS_DEFINED(c._isEdge)`;
|
||||
return this.executeNonPagedDocDbQuery(q).then(
|
||||
(documents: DataModels.DocumentId[]) => {
|
||||
let possibleVertices = [] as PossibleVertex[];
|
||||
@@ -1414,13 +1415,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
if (typeof item.p === "string" || item.p instanceof String) {
|
||||
possibleVertices.push({
|
||||
value: item.id,
|
||||
caption: item.p
|
||||
caption: item.p,
|
||||
});
|
||||
} else {
|
||||
if (item.hasOwnProperty("p")) {
|
||||
possibleVertices.push({
|
||||
value: item.id,
|
||||
caption: item.p[0]["_value"]
|
||||
caption: item.p[0]["_value"],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1548,7 +1549,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
// A bit of translation to make it easier to display
|
||||
let props: { [id: string]: ViewModels.GremlinPropertyValueType[] } = {};
|
||||
for (let p in data.properties) {
|
||||
props[p] = data.properties[p].map(gremlinProperty => gremlinProperty.value);
|
||||
props[p] = data.properties[p].map((gremlinProperty) => gremlinProperty.value);
|
||||
}
|
||||
|
||||
// update neighbors
|
||||
@@ -1564,7 +1565,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
properties: props,
|
||||
areNeighborsUnknown: !data._inEdgeIds || !data._outEdgeIds,
|
||||
sources: sources, //<VertexBasicInfo[]>[],
|
||||
targets: targets //<VertexBasicInfo[]>[]
|
||||
targets: targets, //<VertexBasicInfo[]>[]
|
||||
};
|
||||
|
||||
// Update KO
|
||||
@@ -1627,7 +1628,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
(value: string, index: number, array: string[]): InputTypeaheadComponent.Item => {
|
||||
return { caption: value, value: value };
|
||||
}
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1687,11 +1688,11 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
onHighlightedNode: this.onHighlightedNode.bind(this),
|
||||
onLoadMoreData: this.onLoadMoreData.bind(this),
|
||||
onInitialized: (instance: D3ForceGraph.GraphRenderer): void => this.onMiddlePaneInitialized(instance),
|
||||
onGraphUpdated: this.onGraphUpdated.bind(this)
|
||||
onGraphUpdated: this.onGraphUpdated.bind(this),
|
||||
};
|
||||
|
||||
const graphVizProp: GraphVizComponentProps = {
|
||||
forceGraphParams: forceGraphParams
|
||||
forceGraphParams: forceGraphParams,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -1741,13 +1742,13 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
{
|
||||
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||
enableCrossPartitionQuery:
|
||||
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
||||
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true",
|
||||
} as FeedOptions
|
||||
);
|
||||
this.currentDocDBQueryInfo = {
|
||||
iterator: iterator,
|
||||
index: 0,
|
||||
query: query
|
||||
query: query,
|
||||
};
|
||||
return await this.loadMoreRootNodes();
|
||||
} catch (error) {
|
||||
@@ -1765,8 +1766,9 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
}
|
||||
|
||||
let RU: string = GraphExplorer.REQUEST_CHARGE_UNKNOWN_MSG;
|
||||
const queryInfoStr = `${this.currentDocDBQueryInfo.query} (${this.currentDocDBQueryInfo.index + 1}-${this
|
||||
.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE})`;
|
||||
const queryInfoStr = `${this.currentDocDBQueryInfo.query} (${this.currentDocDBQueryInfo.index + 1}-${
|
||||
this.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE
|
||||
})`;
|
||||
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
|
||||
|
||||
try {
|
||||
@@ -1797,7 +1799,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const errorMsg = `Failed to query: ${this.currentDocDBQueryInfo.query}. Reason:${getErrorMessage(error)}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||
this.setState({
|
||||
filterQueryError: errorMsg
|
||||
filterQueryError: errorMsg,
|
||||
});
|
||||
this.setFilterQueryStatus(FilterQueryStatus.ErrorResult);
|
||||
throw error;
|
||||
@@ -1819,7 +1821,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const errorMsg = `Failed to execute query: ${query}: ${error}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||
this.setState({
|
||||
filterQueryError: errorMsg
|
||||
filterQueryError: errorMsg,
|
||||
});
|
||||
this.setFilterQueryStatus(FilterQueryStatus.ErrorResult);
|
||||
throw error;
|
||||
@@ -1832,7 +1834,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
const errorMsg = `Failed to process query result: ${getErrorMessage(error)}`;
|
||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||
this.setState({
|
||||
filterQueryError: errorMsg
|
||||
filterQueryError: errorMsg,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ describe("Process Gremlin vertex", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
inE: {
|
||||
inEdge: [{ id: "id1", outV: "outV1" }]
|
||||
}
|
||||
inEdge: [{ id: "id1", outV: "outV1" }],
|
||||
},
|
||||
};
|
||||
GraphUtil.createEdgesfromNode(v, graphData);
|
||||
const expectedEdge: GremlinEdge = { id: "id1", inV: "id", outV: "outV1", label: "inEdge" };
|
||||
@@ -33,8 +33,8 @@ describe("Process Gremlin vertex", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
outE: {
|
||||
outEdge: [{ id: "id2", inV: "inV2" }]
|
||||
}
|
||||
outEdge: [{ id: "id2", inV: "inV2" }],
|
||||
},
|
||||
};
|
||||
GraphUtil.createEdgesfromNode(v, graphData);
|
||||
const expectedEdge: GremlinEdge = { id: "id2", inV: "inV2", outV: "id", label: "outEdge" };
|
||||
@@ -47,14 +47,14 @@ describe("Process Gremlin vertex", () => {
|
||||
id: "id",
|
||||
label: "label",
|
||||
inE: {
|
||||
inEdge: [{ id: "id1", outV: "outV1" }]
|
||||
inEdge: [{ id: "id1", outV: "outV1" }],
|
||||
},
|
||||
outE: {
|
||||
outEdge: [
|
||||
{ id: "id2", inV: "inV2" },
|
||||
{ id: "id3", inV: "inV3" }
|
||||
]
|
||||
}
|
||||
{ id: "id3", inV: "inV3" },
|
||||
],
|
||||
},
|
||||
};
|
||||
const newNodes = {};
|
||||
GraphUtil.createEdgesfromNode(v, graphData, newNodes);
|
||||
@@ -83,7 +83,7 @@ describe("getLimitedArrayString()", () => {
|
||||
it("should handle nth element makes it exceed max limit", () => {
|
||||
const expected = {
|
||||
result: "'1','2'",
|
||||
consumedCount: 2
|
||||
consumedCount: 2,
|
||||
};
|
||||
expect(GraphUtil.getLimitedArrayString(["1", "2", "12345", "4", "5"], 10)).toEqual(expected);
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe("getLimitedArrayString()", () => {
|
||||
it("should consume all elements if limit never exceeding limit", () => {
|
||||
const expected = {
|
||||
result: "'1','22','3'",
|
||||
consumedCount: 3
|
||||
consumedCount: 3,
|
||||
};
|
||||
expect(GraphUtil.getLimitedArrayString(["1", "22", "3"], 12)).toEqual(expected);
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export class GraphUtil {
|
||||
id: edge.id,
|
||||
label: label,
|
||||
inV: edge.inV,
|
||||
outV: vertex.id
|
||||
outV: vertex.id,
|
||||
};
|
||||
|
||||
graphData.addEdge(e);
|
||||
@@ -51,7 +51,7 @@ export class GraphUtil {
|
||||
id: edge.id,
|
||||
label: label,
|
||||
inV: vertex.id,
|
||||
outV: edge.outV
|
||||
outV: edge.outV,
|
||||
};
|
||||
|
||||
graphData.addEdge(e);
|
||||
@@ -89,7 +89,7 @@ export class GraphUtil {
|
||||
|
||||
return {
|
||||
result: output,
|
||||
consumedCount: i + 1
|
||||
consumedCount: i + 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,8 +113,9 @@ export class GraphUtil {
|
||||
}().as('v').select('e', 'v')`;
|
||||
} else {
|
||||
const start = startIndex - joined.consumedCount;
|
||||
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.range(${start},${start +
|
||||
pageSize}).as('e').${outE ? "inV" : "outV"}().as('v').select('e', 'v')`;
|
||||
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.range(${start},${
|
||||
start + pageSize
|
||||
}).as('e').${outE ? "inV" : "outV"}().as('v').select('e', 'v')`;
|
||||
}
|
||||
} else {
|
||||
gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}().limit(${pageSize}).as('e').${
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Gremlin Client", () => {
|
||||
collectionId: null,
|
||||
databaseId: null,
|
||||
masterKey: null,
|
||||
maxResultSize: 10000
|
||||
maxResultSize: 10000,
|
||||
};
|
||||
|
||||
it("should use databaseId, collectionId and masterKey to authenticate", () => {
|
||||
@@ -23,7 +23,7 @@ describe("Gremlin Client", () => {
|
||||
collectionId,
|
||||
databaseId,
|
||||
masterKey,
|
||||
maxResultSize: 0
|
||||
maxResultSize: 0,
|
||||
});
|
||||
|
||||
// User must includes these values
|
||||
@@ -32,7 +32,7 @@ describe("Gremlin Client", () => {
|
||||
expect(gremlinClient.client.params.password).toEqual(masterKey);
|
||||
});
|
||||
|
||||
it("should aggregate RU charges across multiple responses", done => {
|
||||
it("should aggregate RU charges across multiple responses", (done) => {
|
||||
const gremlinClient = new GremlinClient();
|
||||
const ru1 = 1;
|
||||
const ru2 = 2;
|
||||
@@ -42,23 +42,23 @@ describe("Gremlin Client", () => {
|
||||
sinon.stub(gremlinClient.client, "executeGremlinQuery").callsFake((query: string): string => requestId);
|
||||
gremlinClient
|
||||
.execute("fake query")
|
||||
.then(result => expect(result.totalRequestCharge).toBe(ru1 + ru2 + ru3))
|
||||
.then((result) => expect(result.totalRequestCharge).toBe(ru1 + ru2 + ru3))
|
||||
.finally(done);
|
||||
|
||||
gremlinClient.client.params.progressCallback({
|
||||
data: ["data1"],
|
||||
requestCharge: ru1,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
gremlinClient.client.params.progressCallback({
|
||||
data: ["data2"],
|
||||
requestCharge: ru2,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
gremlinClient.client.params.successCallback({
|
||||
data: ["data3"],
|
||||
requestCharge: ru3,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient.client.params.successCallback({
|
||||
data: ["data1"],
|
||||
requestCharge: ru1,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
}, 0);
|
||||
return requestId;
|
||||
@@ -103,7 +103,7 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient.client.params.successCallback({
|
||||
data: ["data1"],
|
||||
requestCharge: 1,
|
||||
requestId: "unknownId"
|
||||
requestId: "unknownId",
|
||||
});
|
||||
|
||||
expect(logConsoleSpy.called).toBe(true);
|
||||
@@ -121,7 +121,7 @@ describe("Gremlin Client", () => {
|
||||
expect(GremlinClient.getRequestChargeString("123")).not.toEqual(emptyResult);
|
||||
});
|
||||
|
||||
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", done => {
|
||||
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", (done) => {
|
||||
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
|
||||
const logErrorSpy = sinon.spy(Logger, "logError");
|
||||
|
||||
@@ -135,7 +135,7 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient
|
||||
.execute("fake query")
|
||||
.then(
|
||||
result => {
|
||||
(result) => {
|
||||
try {
|
||||
expect(result.totalRequestCharge).toBe(undefined);
|
||||
expect(logConsoleSpy.called).toBe(true);
|
||||
@@ -145,7 +145,7 @@ describe("Gremlin Client", () => {
|
||||
done(e);
|
||||
}
|
||||
},
|
||||
error => done.fail(error)
|
||||
(error) => done.fail(error)
|
||||
)
|
||||
.finally(() => {
|
||||
logConsoleSpy.restore();
|
||||
@@ -155,16 +155,16 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient.client.params.progressCallback({
|
||||
data: ["data1"],
|
||||
requestCharge: ru1,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
gremlinClient.client.params.successCallback({
|
||||
data: ["data2"],
|
||||
requestCharge: ru2 as any,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
});
|
||||
|
||||
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", done => {
|
||||
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", (done) => {
|
||||
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
|
||||
const logErrorSpy = sinon.spy(Logger, "logError");
|
||||
|
||||
@@ -178,7 +178,7 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient
|
||||
.execute("fake query")
|
||||
.then(
|
||||
result => {
|
||||
(result) => {
|
||||
try {
|
||||
expect(result.totalRequestCharge).toBe(undefined);
|
||||
expect(logConsoleSpy.called).toBe(true);
|
||||
@@ -188,7 +188,7 @@ describe("Gremlin Client", () => {
|
||||
done(e);
|
||||
}
|
||||
},
|
||||
error => done.fail(error)
|
||||
(error) => done.fail(error)
|
||||
)
|
||||
.finally(() => {
|
||||
logConsoleSpy.restore();
|
||||
@@ -198,16 +198,16 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient.client.params.progressCallback({
|
||||
data: ["data1"],
|
||||
requestCharge: ru1,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
gremlinClient.client.params.successCallback({
|
||||
data: ["data2"],
|
||||
requestCharge: ru2,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
});
|
||||
});
|
||||
|
||||
it("should track RUs even on failure", done => {
|
||||
it("should track RUs even on failure", (done) => {
|
||||
const gremlinClient = new GremlinClient();
|
||||
const requestId = "id";
|
||||
const RU = 1234;
|
||||
@@ -217,8 +217,8 @@ describe("Gremlin Client", () => {
|
||||
sinon.stub(gremlinClient.client, "executeGremlinQuery").callsFake((query: string): string => requestId);
|
||||
const abortPendingRequestSpy = sinon.spy(gremlinClient, "abortPendingRequest");
|
||||
gremlinClient.execute("fake query").then(
|
||||
result => done.fail(`Unexpectedly succeeded with ${result}`),
|
||||
error => {
|
||||
(result) => done.fail(`Unexpectedly succeeded with ${result}`),
|
||||
(error) => {
|
||||
try {
|
||||
expect(abortPendingRequestSpy.calledWith(requestId, error, RU)).toBe(true);
|
||||
done();
|
||||
@@ -232,13 +232,13 @@ describe("Gremlin Client", () => {
|
||||
{
|
||||
data: null,
|
||||
requestCharge: RU,
|
||||
requestId: requestId
|
||||
requestId: requestId,
|
||||
},
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it("should abort all pending requests if requestId from failure response", done => {
|
||||
it("should abort all pending requests if requestId from failure response", (done) => {
|
||||
const gremlinClient = new GremlinClient();
|
||||
const requestId = "id";
|
||||
const error = "Some error";
|
||||
@@ -258,7 +258,7 @@ describe("Gremlin Client", () => {
|
||||
{
|
||||
data: null,
|
||||
requestCharge: undefined,
|
||||
requestId: undefined
|
||||
requestId: undefined,
|
||||
},
|
||||
error
|
||||
);
|
||||
|
||||
@@ -79,7 +79,7 @@ export class GremlinClient {
|
||||
},
|
||||
infoCallback: (msg: string) => {
|
||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, msg);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,13 +89,13 @@ export class GremlinClient {
|
||||
this.pendingResults.set(requestId, {
|
||||
result: {
|
||||
data: [] as any[],
|
||||
isIncomplete: false
|
||||
isIncomplete: false,
|
||||
},
|
||||
deferred: deferred,
|
||||
timeoutId: window.setTimeout(
|
||||
() => this.abortPendingRequest(requestId, GremlinClient.TIMEOUT_ERROR_MSG, null),
|
||||
GremlinClient.PENDING_REQUEST_TIMEOUT_MS
|
||||
)
|
||||
),
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
GremlinSimpleClientParameters,
|
||||
Result,
|
||||
GremlinRequestMessage,
|
||||
GremlinResponseMessage
|
||||
GremlinResponseMessage,
|
||||
} from "./GremlinSimpleClient";
|
||||
|
||||
describe("Gremlin Simple Client", () => {
|
||||
@@ -24,7 +24,7 @@ describe("Gremlin Simple Client", () => {
|
||||
successCallback: (result: Result) => {},
|
||||
progressCallback: (result: Result) => {},
|
||||
failureCallback: (result: Result, error: string) => {},
|
||||
infoCallback: (msg: string) => {}
|
||||
infoCallback: (msg: string) => {},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -41,10 +41,10 @@ describe("Gremlin Simple Client", () => {
|
||||
} => ({
|
||||
attributes: {
|
||||
"x-ms-request-charge": requestCharge,
|
||||
"x-ms-total-request-charge": -123
|
||||
"x-ms-total-request-charge": -123,
|
||||
},
|
||||
code: code,
|
||||
message: null
|
||||
message: null,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -60,7 +60,7 @@ describe("Gremlin Simple Client", () => {
|
||||
fakeSocket.onmessage(fakeSocket.fakeResponse);
|
||||
}
|
||||
},
|
||||
close: () => {}
|
||||
close: () => {},
|
||||
};
|
||||
sandbox.stub(GremlinSimpleClient, "createWebSocket").returns(fakeSocket);
|
||||
});
|
||||
@@ -89,9 +89,9 @@ describe("Gremlin Simple Client", () => {
|
||||
status: {
|
||||
code: 200,
|
||||
attributes: { graphExecutionStatus: 200, StorageRU: 2.29, ComputeRU: 1.07, PerPartitionComputeCharges: {} },
|
||||
message: ""
|
||||
message: "",
|
||||
},
|
||||
result: { data: ["é"], meta: {} }
|
||||
result: { data: ["é"], meta: {} },
|
||||
};
|
||||
const expectedDecodedUint8ArrayValues = [
|
||||
123,
|
||||
@@ -323,7 +323,7 @@ describe("Gremlin Simple Client", () => {
|
||||
123,
|
||||
125,
|
||||
125,
|
||||
125
|
||||
125,
|
||||
];
|
||||
// We do our best here to emulate what the server should return
|
||||
const gremlinResponseData = new Uint8Array(<any>expectedDecodedUint8ArrayValues).buffer;
|
||||
@@ -352,7 +352,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(200, null),
|
||||
requestId: "id",
|
||||
result: { data: "mydata" }
|
||||
result: { data: "mydata" },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
const onMessageSpy = sandbox.spy(client, "onMessage");
|
||||
@@ -368,7 +368,7 @@ describe("Gremlin Simple Client", () => {
|
||||
fakeSocket.fakeResponse = {
|
||||
status: fakeStatus(200, null),
|
||||
requestId: Object.keys(client.pendingRequests)[0],
|
||||
data: new Uint8Array([1, 1, 1, 1]).buffer
|
||||
data: new Uint8Array([1, 1, 1, 1]).buffer,
|
||||
};
|
||||
client.executeGremlinQuery("test");
|
||||
fakeSocket.onopen();
|
||||
@@ -386,7 +386,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(200, RU),
|
||||
requestId: Object.keys(client.pendingRequests)[0],
|
||||
result: { data: "mydata" }
|
||||
result: { data: "mydata" },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(new MessageEvent("test2"));
|
||||
@@ -394,7 +394,7 @@ describe("Gremlin Simple Client", () => {
|
||||
onSuccessSpy.calledWith({
|
||||
requestId: fakeResponse.requestId,
|
||||
data: fakeResponse.result.data,
|
||||
requestCharge: RU
|
||||
requestCharge: RU,
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -410,7 +410,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(204, RU),
|
||||
requestId: Object.keys(client.pendingRequests)[0],
|
||||
result: { data: "THIS SHOULD BE IGNORED" }
|
||||
result: { data: "THIS SHOULD BE IGNORED" },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(new MessageEvent("test2"));
|
||||
@@ -418,7 +418,7 @@ describe("Gremlin Simple Client", () => {
|
||||
onSuccessSpy.calledWith({
|
||||
requestId: fakeResponse.requestId,
|
||||
data: null,
|
||||
requestCharge: RU
|
||||
requestCharge: RU,
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -435,7 +435,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(206, RU),
|
||||
requestId: Object.keys(client.pendingRequests)[0],
|
||||
result: { data: [1, 2, 3] }
|
||||
result: { data: [1, 2, 3] },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(new MessageEvent("test2"));
|
||||
@@ -443,7 +443,7 @@ describe("Gremlin Simple Client", () => {
|
||||
onProgressSpy.calledWith({
|
||||
requestId: fakeResponse.requestId,
|
||||
data: fakeResponse.result.data,
|
||||
requestCharge: RU
|
||||
requestCharge: RU,
|
||||
})
|
||||
).toBe(true);
|
||||
expect(onSuccessSpy.notCalled).toBe(true);
|
||||
@@ -460,7 +460,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(407, null),
|
||||
requestId: Object.keys(client.pendingRequests)[0],
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(new MessageEvent("test2"));
|
||||
@@ -489,7 +489,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(401, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -501,7 +501,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(401, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -513,7 +513,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(498, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -525,7 +525,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(500, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -537,7 +537,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(597, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -549,7 +549,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(598, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -561,7 +561,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(599, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -573,7 +573,7 @@ describe("Gremlin Simple Client", () => {
|
||||
const fakeResponse: GremlinResponseMessage = {
|
||||
status: fakeStatus(123123123, null),
|
||||
requestId: "id",
|
||||
result: { data: <any>null }
|
||||
result: { data: <any>null },
|
||||
};
|
||||
sandbox.stub(client, "decodeMessage").returns(fakeResponse);
|
||||
client.onMessage(null);
|
||||
@@ -595,16 +595,16 @@ describe("Gremlin Simple Client", () => {
|
||||
args: {
|
||||
gremlin: "gremlin",
|
||||
bindings: {},
|
||||
language: "language"
|
||||
}
|
||||
language: "language",
|
||||
},
|
||||
};
|
||||
const expectedResult: GremlinRequestMessage = {
|
||||
requestId: request.requestId,
|
||||
processor: request.processor,
|
||||
op: "authentication",
|
||||
args: {
|
||||
SASL: expectedSASLResult
|
||||
}
|
||||
SASL: expectedSASLResult,
|
||||
},
|
||||
};
|
||||
const actual = client.buildChallengeResponse(request);
|
||||
expect(actual).toEqual(expectedResult);
|
||||
|
||||
@@ -165,7 +165,7 @@ export class GremlinSimpleClient {
|
||||
const result: Result = {
|
||||
requestId: requestId,
|
||||
data: rawMessage.result ? rawMessage.result.data : null,
|
||||
requestCharge: rawMessage.status.attributes[GremlinSimpleClient.requestChargeHeader]
|
||||
requestCharge: rawMessage.status.attributes[GremlinSimpleClient.requestChargeHeader],
|
||||
};
|
||||
|
||||
if (!this.pendingRequests[requestId]) {
|
||||
@@ -262,8 +262,8 @@ export class GremlinSimpleClient {
|
||||
args: {
|
||||
gremlin: query,
|
||||
bindings: {},
|
||||
language: "gremlin-groovy"
|
||||
}
|
||||
language: "gremlin-groovy",
|
||||
},
|
||||
};
|
||||
this.connect();
|
||||
return requestId;
|
||||
@@ -271,19 +271,19 @@ export class GremlinSimpleClient {
|
||||
|
||||
public buildChallengeResponse(request: GremlinRequestMessage): GremlinRequestMessage {
|
||||
var args = {
|
||||
SASL: GremlinSimpleClient.utf8ToB64("\0" + this.params.user + "\0" + this.params.password)
|
||||
SASL: GremlinSimpleClient.utf8ToB64("\0" + this.params.user + "\0" + this.params.password),
|
||||
};
|
||||
return {
|
||||
requestId: request.requestId,
|
||||
processor: request.processor,
|
||||
op: "authentication",
|
||||
args
|
||||
args,
|
||||
};
|
||||
}
|
||||
|
||||
public static utf8ToB64(utf8Str: string) {
|
||||
return btoa(
|
||||
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
||||
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||
return String.fromCharCode(parseInt(p1, 16));
|
||||
})
|
||||
);
|
||||
@@ -342,7 +342,7 @@ export class GremlinSimpleClient {
|
||||
* RFC4122 version 4 compliant UUID
|
||||
*/
|
||||
private static uuidv4() {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
|
||||
@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
|
||||
className={className}
|
||||
as="tr"
|
||||
aria-label={node.caption}
|
||||
onActivated={e => this.props.onRootNodeSelected(node.id)}
|
||||
onActivated={(e) => this.props.onRootNodeSelected(node.id)}
|
||||
key={node.id}
|
||||
>
|
||||
<td className="resultItem">
|
||||
|
||||
@@ -20,17 +20,17 @@ describe("Property pane", () => {
|
||||
name: "sourceName",
|
||||
id: "sourceId",
|
||||
edgeId: "edgeId",
|
||||
edgeLabel: "sourceEdgeLabel"
|
||||
}
|
||||
edgeLabel: "sourceEdgeLabel",
|
||||
},
|
||||
],
|
||||
targets: [
|
||||
{
|
||||
name: "targetName",
|
||||
id: "targetId",
|
||||
edgeId: "edgeId",
|
||||
edgeLabel: "targetEdgeLabel"
|
||||
}
|
||||
]
|
||||
edgeLabel: "targetEdgeLabel",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const createMockProps = (): NodePropertiesComponentProps => {
|
||||
@@ -48,7 +48,7 @@ describe("Property pane", () => {
|
||||
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
|
||||
deleteHighlightedNode: (): void => {},
|
||||
onModeChanged: (newMode: Mode): void => {},
|
||||
viewMode: Mode.READONLY_PROP
|
||||
viewMode: Mode.READONLY_PROP,
|
||||
};
|
||||
};
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
@@ -25,7 +25,7 @@ export enum Mode {
|
||||
READONLY_PROP,
|
||||
PROPERTY_EDITOR,
|
||||
EDIT_SOURCES,
|
||||
EDIT_TARGETS
|
||||
EDIT_TARGETS,
|
||||
}
|
||||
|
||||
export interface NodePropertiesComponentProps {
|
||||
@@ -71,25 +71,25 @@ export class NodePropertiesComponent extends React.Component<
|
||||
readOnlyProperties: [],
|
||||
existingProperties: [],
|
||||
addedProperties: [],
|
||||
droppedKeys: []
|
||||
droppedKeys: [],
|
||||
},
|
||||
editedSources: {
|
||||
vertexId: undefined,
|
||||
currentNeighbors: [],
|
||||
droppedIds: [],
|
||||
addedEdges: []
|
||||
addedEdges: [],
|
||||
},
|
||||
editedTargets: {
|
||||
vertexId: undefined,
|
||||
currentNeighbors: [],
|
||||
droppedIds: [],
|
||||
addedEdges: []
|
||||
addedEdges: [],
|
||||
},
|
||||
possibleVertices: [],
|
||||
isDeleteConfirm: false,
|
||||
isPropertiesExpanded: true,
|
||||
isSourcesExpanded: true,
|
||||
isTargetsExpanded: true
|
||||
isTargetsExpanded: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ export class NodePropertiesComponent extends React.Component<
|
||||
const readOnlyProps: ViewModels.InputProperty[] = [
|
||||
{
|
||||
key: "label",
|
||||
values: [{ value: this.props.node.label, type: "string" }]
|
||||
}
|
||||
values: [{ value: this.props.node.label, type: "string" }],
|
||||
},
|
||||
];
|
||||
|
||||
const existingProps: ViewModels.InputProperty[] = [];
|
||||
@@ -174,7 +174,10 @@ export class NodePropertiesComponent extends React.Component<
|
||||
const propValues = hProps[p];
|
||||
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
|
||||
key: p,
|
||||
values: propValues.map(val => ({ value: val.toString(), type: NodePropertiesComponent.getTypeOption(val) }))
|
||||
values: propValues.map((val) => ({
|
||||
value: val.toString(),
|
||||
type: NodePropertiesComponent.getTypeOption(val),
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -186,8 +189,8 @@ export class NodePropertiesComponent extends React.Component<
|
||||
readOnlyProperties: readOnlyProps,
|
||||
existingProperties: existingProps,
|
||||
addedProperties: [],
|
||||
droppedKeys: []
|
||||
}
|
||||
droppedKeys: [],
|
||||
},
|
||||
});
|
||||
this.props.onModeChanged(newMode);
|
||||
}
|
||||
@@ -195,20 +198,20 @@ export class NodePropertiesComponent extends React.Component<
|
||||
private showSourcesEditor(): void {
|
||||
this.props.updatePossibleVertices().then((possibleVertices: PossibleVertex[]) => {
|
||||
this.setState({
|
||||
possibleVertices: possibleVertices
|
||||
possibleVertices: possibleVertices,
|
||||
});
|
||||
|
||||
const editedSources: EditedEdges = {
|
||||
vertexId: this.props.node.id,
|
||||
currentNeighbors: this.props.node.sources.slice(),
|
||||
droppedIds: [],
|
||||
addedEdges: []
|
||||
addedEdges: [],
|
||||
};
|
||||
|
||||
const newMode = Mode.EDIT_SOURCES;
|
||||
this.setState({
|
||||
editedProperties: this.state.editedProperties,
|
||||
editedSources: editedSources
|
||||
editedSources: editedSources,
|
||||
});
|
||||
this.props.onModeChanged(newMode);
|
||||
});
|
||||
@@ -217,20 +220,20 @@ export class NodePropertiesComponent extends React.Component<
|
||||
private showTargetsEditor(): void {
|
||||
this.props.updatePossibleVertices().then((possibleVertices: PossibleVertex[]) => {
|
||||
this.setState({
|
||||
possibleVertices: possibleVertices
|
||||
possibleVertices: possibleVertices,
|
||||
});
|
||||
|
||||
const editedTargets: EditedEdges = {
|
||||
vertexId: this.props.node.id,
|
||||
currentNeighbors: this.props.node.targets.slice(),
|
||||
droppedIds: [],
|
||||
addedEdges: []
|
||||
addedEdges: [],
|
||||
};
|
||||
|
||||
const newMode = Mode.EDIT_TARGETS;
|
||||
this.setState({
|
||||
editedProperties: this.state.editedProperties,
|
||||
editedTargets: editedTargets
|
||||
editedTargets: editedTargets,
|
||||
});
|
||||
this.props.onModeChanged(newMode);
|
||||
});
|
||||
@@ -250,7 +253,7 @@ export class NodePropertiesComponent extends React.Component<
|
||||
|
||||
private onUpdateProperties(editedProperties: EditedProperties): void {
|
||||
this.setState({
|
||||
editedProperties: editedProperties
|
||||
editedProperties: editedProperties,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -264,7 +267,7 @@ export class NodePropertiesComponent extends React.Component<
|
||||
|
||||
private setIsDeleteConfirm(state: boolean): void {
|
||||
this.setState({
|
||||
isDeleteConfirm: state
|
||||
isDeleteConfirm: state,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export class QueryContainerComponent extends React.Component<
|
||||
public constructor(props: QueryContainerComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
query: this.props.initialQuery
|
||||
query: this.props.initialQuery,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export class QueryContainerComponent extends React.Component<
|
||||
<button
|
||||
type="button"
|
||||
className="filterbtnstyle queryButton"
|
||||
onClick={e => this.props.onExecuteClick(this.state.query)}
|
||||
onClick={(e) => this.props.onExecuteClick(this.state.query)}
|
||||
disabled={this.props.isLoading || !QueryContainerComponent.isQueryValid(this.state.query)}
|
||||
>
|
||||
Execute Gremlin Query
|
||||
|
||||
@@ -48,7 +48,7 @@ export class ReadOnlyNeighborsComponent extends React.Component<ReadOnlyNeighbor
|
||||
className="clickableLink"
|
||||
as="a"
|
||||
aria-label={_neighbor.name}
|
||||
onActivated={e => this.props.selectNode(_neighbor.id)}
|
||||
onActivated={(e) => this.props.selectNode(_neighbor.id)}
|
||||
title={GraphUtil.getNeighborTitle(_neighbor)}
|
||||
>
|
||||
{_neighbor.name}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GraphHighlightedNodeData } from "./GraphExplorer";
|
||||
|
||||
import {
|
||||
ReadOnlyNodePropertiesComponent,
|
||||
ReadOnlyNodePropertiesComponentProps
|
||||
ReadOnlyNodePropertiesComponentProps,
|
||||
} from "./ReadOnlyNodePropertiesComponent";
|
||||
|
||||
describe("<ReadOnlyNodePropertiesComponent />", () => {
|
||||
@@ -15,11 +15,11 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
|
||||
label: label,
|
||||
properties: {
|
||||
key1: ["value1"],
|
||||
key2: ["value2"]
|
||||
key2: ["value2"],
|
||||
},
|
||||
areNeighborsUnknown: false,
|
||||
sources: [],
|
||||
targets: []
|
||||
targets: [],
|
||||
};
|
||||
|
||||
it("renders id", () => {
|
||||
@@ -44,8 +44,8 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
|
||||
const mockNode2 = {
|
||||
...mockNode,
|
||||
properties: {
|
||||
key3: ["abcd", 1234, true, false, undefined, null]
|
||||
}
|
||||
key3: ["abcd", 1234, true, false, undefined, null],
|
||||
},
|
||||
};
|
||||
const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 };
|
||||
const wrapper = shallow(<ReadOnlyNodePropertiesComponent {...props} />);
|
||||
@@ -57,8 +57,8 @@ describe("<ReadOnlyNodePropertiesComponent />", () => {
|
||||
...mockNode,
|
||||
properties: {
|
||||
key4: ["あきら, アキラ,安喜良"],
|
||||
key5: ["Véronique"]
|
||||
}
|
||||
key5: ["Véronique"],
|
||||
},
|
||||
};
|
||||
const props: ReadOnlyNodePropertiesComponentProps = { node: mockNode2 };
|
||||
const wrapper = shallow(<ReadOnlyNodePropertiesComponent {...props} />);
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
|
||||
<span className="vertexLabel">{this.props.node.label}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{Object.keys(this.props.node.properties).map(_propkey => {
|
||||
{Object.keys(this.props.node.properties).map((_propkey) => {
|
||||
const gremlinValues = this.props.node.properties[_propkey];
|
||||
return ReadOnlyNodePropertiesComponent.renderReadOnlyPropertyKeyPair(_propkey, gremlinValues);
|
||||
})}
|
||||
@@ -41,11 +41,11 @@ export class ReadOnlyNodePropertiesComponent extends React.Component<ReadOnlyNod
|
||||
key: string,
|
||||
propertyValues: ViewModels.GremlinPropertyValueType[]
|
||||
): JSX.Element {
|
||||
const renderedValues = propertyValues.map(value =>
|
||||
const renderedValues = propertyValues.map((value) =>
|
||||
ReadOnlyNodePropertiesComponent.renderSinglePropertyValue(value)
|
||||
);
|
||||
const stringifiedValues = propertyValues
|
||||
.map(value => ReadOnlyNodePropertiesComponent.singlePropertyValueToString(value))
|
||||
.map((value) => ReadOnlyNodePropertiesComponent.singlePropertyValueToString(value))
|
||||
.join(", ");
|
||||
return (
|
||||
<tr key={key}>
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import * as ko from "knockout";
|
||||
import { GraphStyleComponent, GraphStyleParams } from "./GraphStyleComponent";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
|
||||
function buildComponent(buttonOptions: any) {
|
||||
document.body.innerHTML = GraphStyleComponent.template as any;
|
||||
const vm = new GraphStyleComponent.viewModel(buttonOptions);
|
||||
ko.applyBindings(vm);
|
||||
}
|
||||
|
||||
describe("Graph Style Component", () => {
|
||||
let buildParams = (config: ViewModels.GraphConfigUiData): GraphStyleParams => {
|
||||
return {
|
||||
config: config
|
||||
};
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
ko.cleanNode(document);
|
||||
});
|
||||
|
||||
describe("Rendering", () => {
|
||||
it("should display proper list of choices passed in component parameters", () => {
|
||||
const PROP2 = "prop2";
|
||||
const PROPC = "prop3";
|
||||
const params = buildParams({
|
||||
nodeCaptionChoice: ko.observable(null),
|
||||
nodeIconChoice: ko.observable(null),
|
||||
nodeColorKeyChoice: ko.observable(null),
|
||||
nodeIconSet: ko.observable(null),
|
||||
nodeProperties: ko.observableArray(["prop1", PROP2]),
|
||||
nodePropertiesWithNone: ko.observableArray(["propa", "propb", PROPC]),
|
||||
showNeighborType: ko.observable(null)
|
||||
});
|
||||
|
||||
buildComponent(params);
|
||||
|
||||
var e: any = document.querySelector(".graphStyle #nodeCaptionChoices");
|
||||
expect(e.options.length).toBe(2);
|
||||
expect(e.options[1].value).toBe(PROP2);
|
||||
|
||||
e = document.querySelector(".graphStyle #nodeColorKeyChoices");
|
||||
expect(e.options.length).toBe(3);
|
||||
expect(e.options[2].value).toBe(PROPC);
|
||||
|
||||
e = document.querySelector(".graphStyle #nodeIconChoices");
|
||||
expect(e.options.length).toBe(3);
|
||||
expect(e.options[2].value).toBe(PROPC);
|
||||
});
|
||||
});
|
||||
});
|
||||
import * as ko from "knockout";
|
||||
import { GraphStyleComponent, GraphStyleParams } from "./GraphStyleComponent";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
|
||||
function buildComponent(buttonOptions: any) {
|
||||
document.body.innerHTML = GraphStyleComponent.template as any;
|
||||
const vm = new GraphStyleComponent.viewModel(buttonOptions);
|
||||
ko.applyBindings(vm);
|
||||
}
|
||||
|
||||
describe("Graph Style Component", () => {
|
||||
let buildParams = (config: ViewModels.GraphConfigUiData): GraphStyleParams => {
|
||||
return {
|
||||
config: config,
|
||||
};
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
ko.cleanNode(document);
|
||||
});
|
||||
|
||||
describe("Rendering", () => {
|
||||
it("should display proper list of choices passed in component parameters", () => {
|
||||
const PROP2 = "prop2";
|
||||
const PROPC = "prop3";
|
||||
const params = buildParams({
|
||||
nodeCaptionChoice: ko.observable(null),
|
||||
nodeIconChoice: ko.observable(null),
|
||||
nodeColorKeyChoice: ko.observable(null),
|
||||
nodeIconSet: ko.observable(null),
|
||||
nodeProperties: ko.observableArray(["prop1", PROP2]),
|
||||
nodePropertiesWithNone: ko.observableArray(["propa", "propb", PROPC]),
|
||||
showNeighborType: ko.observable(null),
|
||||
});
|
||||
|
||||
buildComponent(params);
|
||||
|
||||
var e: any = document.querySelector(".graphStyle #nodeCaptionChoices");
|
||||
expect(e.options.length).toBe(2);
|
||||
expect(e.options[1].value).toBe(PROP2);
|
||||
|
||||
e = document.querySelector(".graphStyle #nodeColorKeyChoices");
|
||||
expect(e.options.length).toBe(3);
|
||||
expect(e.options[2].value).toBe(PROPC);
|
||||
|
||||
e = document.querySelector(".graphStyle #nodeIconChoices");
|
||||
expect(e.options.length).toBe(3);
|
||||
expect(e.options[2].value).toBe(PROPC);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||
|
||||
/**
|
||||
* Parameters for this component
|
||||
*/
|
||||
export interface GraphStyleParams {
|
||||
config: ViewModels.GraphConfigUiData;
|
||||
firstFieldHasFocus?: ko.Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Callback triggered when the template is bound to the component (for testing purposes)
|
||||
*/
|
||||
onTemplateReady?: () => void;
|
||||
}
|
||||
|
||||
class GraphStyleViewModel extends WaitsForTemplateViewModel {
|
||||
private params: GraphStyleParams;
|
||||
|
||||
public constructor(params: GraphStyleParams) {
|
||||
super();
|
||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
||||
if (isTemplateReady && params.onTemplateReady) {
|
||||
params.onTemplateReady();
|
||||
}
|
||||
});
|
||||
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public onAllNeighborsKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.BOTH);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
public onSourcesKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.SOURCES_ONLY);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
public onTargetsKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.TARGETS_ONLY);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const template = `
|
||||
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
|
||||
<div class="seconddivpadding">
|
||||
<p>Show vertex (node) as</p>
|
||||
<select id="nodeCaptionChoices" class="formTree paneselect" required data-bind="options:nodeProperties,
|
||||
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node color</p>
|
||||
<select id="nodeColorKeyChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeColorKeyChoice"></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node icon</p>
|
||||
<select id="nodeIconChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeIconChoice"></select>
|
||||
<input type="text" data-bind="value:nodeIconSet" placeholder="Icon set: blank for collection id" class="nodeIconSet" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<p class="seconddivpadding">Show</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab11" name="graphneighbortype" class="radio" data-bind="checkedValue:2, checked:showNeighborType" />
|
||||
<label for="tab11" tabindex="0" data-bind="event: { keypress: $parent.onAllNeighborsKeyPress }">All neighbors</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab12" name="graphneighbortype" class="radio" data-bind="checkedValue:0, checked:showNeighborType" />
|
||||
<label for="tab12" tabindex="0" data-bind="event: { keypress: $parent.onSourcesKeyPress }">Sources</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab13" name="graphneighbortype" class="radio" data-bind="checkedValue:1, checked:showNeighborType" />
|
||||
<label for="tab13" tabindex="0" data-bind="event: { keypress: $parent.onTargetsKeyPress }">Targets</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
export const GraphStyleComponent = {
|
||||
viewModel: GraphStyleViewModel,
|
||||
template
|
||||
};
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||
|
||||
/**
|
||||
* Parameters for this component
|
||||
*/
|
||||
export interface GraphStyleParams {
|
||||
config: ViewModels.GraphConfigUiData;
|
||||
firstFieldHasFocus?: ko.Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Callback triggered when the template is bound to the component (for testing purposes)
|
||||
*/
|
||||
onTemplateReady?: () => void;
|
||||
}
|
||||
|
||||
class GraphStyleViewModel extends WaitsForTemplateViewModel {
|
||||
private params: GraphStyleParams;
|
||||
|
||||
public constructor(params: GraphStyleParams) {
|
||||
super();
|
||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
||||
if (isTemplateReady && params.onTemplateReady) {
|
||||
params.onTemplateReady();
|
||||
}
|
||||
});
|
||||
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public onAllNeighborsKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.BOTH);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
public onSourcesKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.SOURCES_ONLY);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
public onTargetsKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Space || event.keyCode === Constants.KeyCodes.Enter) {
|
||||
this.params.config.showNeighborType(ViewModels.NeighborType.TARGETS_ONLY);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const template = `
|
||||
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
|
||||
<div class="seconddivpadding">
|
||||
<p>Show vertex (node) as</p>
|
||||
<select id="nodeCaptionChoices" class="formTree paneselect" required data-bind="options:nodeProperties,
|
||||
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node color</p>
|
||||
<select id="nodeColorKeyChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeColorKeyChoice"></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node icon</p>
|
||||
<select id="nodeIconChoices" class="formTree paneselect" required data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeIconChoice"></select>
|
||||
<input type="text" data-bind="value:nodeIconSet" placeholder="Icon set: blank for collection id" class="nodeIconSet" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<p class="seconddivpadding">Show</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab11" name="graphneighbortype" class="radio" data-bind="checkedValue:2, checked:showNeighborType" />
|
||||
<label for="tab11" tabindex="0" data-bind="event: { keypress: $parent.onAllNeighborsKeyPress }">All neighbors</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab12" name="graphneighbortype" class="radio" data-bind="checkedValue:0, checked:showNeighborType" />
|
||||
<label for="tab12" tabindex="0" data-bind="event: { keypress: $parent.onSourcesKeyPress }">Sources</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab13" name="graphneighbortype" class="radio" data-bind="checkedValue:1, checked:showNeighborType" />
|
||||
<label for="tab13" tabindex="0" data-bind="event: { keypress: $parent.onTargetsKeyPress }">Targets</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
export const GraphStyleComponent = {
|
||||
viewModel: GraphStyleViewModel,
|
||||
template,
|
||||
};
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
|
||||
<div class="seconddivpadding">
|
||||
<p>Show vertex (node) as</p>
|
||||
<select
|
||||
id="nodeCaptionChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodeProperties,
|
||||
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"
|
||||
></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node color</p>
|
||||
<select
|
||||
id="nodeColorKeyChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeColorKeyChoice"
|
||||
></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node icon</p>
|
||||
<select
|
||||
id="nodeIconChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeIconChoice"
|
||||
></select>
|
||||
<input
|
||||
type="text"
|
||||
data-bind="value:nodeIconSet"
|
||||
placeholder="Icon set: blank for collection id"
|
||||
class="nodeIconSet"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="seconddivpadding">Show</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab11"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:2, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab11">All neighbors</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab12"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:0, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab12">Sources</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab13"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:1, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab13">Targets</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="graphStyle" class="graphStyle" data-bind="setTemplateReady: true, with:params.config">
|
||||
<div class="seconddivpadding">
|
||||
<p>Show vertex (node) as</p>
|
||||
<select
|
||||
id="nodeCaptionChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodeProperties,
|
||||
value:nodeCaptionChoice, hasFocus: $parent.params.firstFieldHasFocus"
|
||||
></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node color</p>
|
||||
<select
|
||||
id="nodeColorKeyChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeColorKeyChoice"
|
||||
></select>
|
||||
</div>
|
||||
<div class="seconddivpadding">
|
||||
<p>Map this property to node icon</p>
|
||||
<select
|
||||
id="nodeIconChoices"
|
||||
class="formTree paneselect"
|
||||
required
|
||||
data-bind="options:nodePropertiesWithNone,
|
||||
value:nodeIconChoice"
|
||||
></select>
|
||||
<input
|
||||
type="text"
|
||||
data-bind="value:nodeIconSet"
|
||||
placeholder="Icon set: blank for collection id"
|
||||
class="nodeIconSet"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="seconddivpadding">Show</p>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab11"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:2, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab11">All neighbors</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab12"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:0, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab12">Sources</label>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
id="tab13"
|
||||
name="graphneighbortype"
|
||||
class="radio"
|
||||
data-bind="checkedValue:1, checked:showNeighborType"
|
||||
/>
|
||||
<label for="tab13">Targets</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
import * as ko from "knockout";
|
||||
import { NewVertexComponent, NewVertexViewModel } from "./NewVertexComponent";
|
||||
|
||||
const component = NewVertexComponent;
|
||||
|
||||
describe("New Vertex Component", () => {
|
||||
let vm: NewVertexViewModel;
|
||||
let partitionKeyProperty: ko.Observable<string>;
|
||||
|
||||
beforeEach(async () => {
|
||||
document.body.innerHTML = component.template as any;
|
||||
partitionKeyProperty = ko.observable(null);
|
||||
vm = new component.viewModel({
|
||||
newVertexData: null,
|
||||
partitionKeyProperty
|
||||
});
|
||||
ko.applyBindings(vm);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ko.cleanNode(document);
|
||||
});
|
||||
|
||||
describe("Rendering", () => {
|
||||
it("should display property list with input and +Add Property", () => {
|
||||
expect(document.querySelector(".newVertexComponent .newVertexForm")).not.toBeNull();
|
||||
expect(document.querySelector(".newVertexComponent .edgeInput")).not.toBeNull();
|
||||
expect(document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should display partition key property if set", () => {
|
||||
partitionKeyProperty("testKey");
|
||||
expect(
|
||||
(document.querySelector(".newVertexComponent .newVertexForm .labelCol input") as HTMLInputElement).value
|
||||
).toEqual("testKey");
|
||||
});
|
||||
|
||||
it("should NOT display partition key property if NOT set", () => {
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Behavior", () => {
|
||||
let clickSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
clickSpy = jasmine.createSpy("Command button click spy");
|
||||
});
|
||||
|
||||
it("should add new property row when +Add property button is pressed", () => {
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(3);
|
||||
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(3);
|
||||
});
|
||||
|
||||
it("should remove property row when trash button is pressed", () => {
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
|
||||
// Mark this one to delete
|
||||
const elts = document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg");
|
||||
elts[elts.length - 1].className += " deleteme";
|
||||
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document
|
||||
.querySelector(".newVertexComponent .rightPaneTrashIconImg.deleteme")
|
||||
.parentElement.dispatchEvent(new Event("click"));
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(2);
|
||||
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(2);
|
||||
expect(document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg.deleteme").length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
import * as ko from "knockout";
|
||||
import { NewVertexComponent, NewVertexViewModel } from "./NewVertexComponent";
|
||||
|
||||
const component = NewVertexComponent;
|
||||
|
||||
describe("New Vertex Component", () => {
|
||||
let vm: NewVertexViewModel;
|
||||
let partitionKeyProperty: ko.Observable<string>;
|
||||
|
||||
beforeEach(async () => {
|
||||
document.body.innerHTML = component.template as any;
|
||||
partitionKeyProperty = ko.observable(null);
|
||||
vm = new component.viewModel({
|
||||
newVertexData: null,
|
||||
partitionKeyProperty,
|
||||
});
|
||||
ko.applyBindings(vm);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ko.cleanNode(document);
|
||||
});
|
||||
|
||||
describe("Rendering", () => {
|
||||
it("should display property list with input and +Add Property", () => {
|
||||
expect(document.querySelector(".newVertexComponent .newVertexForm")).not.toBeNull();
|
||||
expect(document.querySelector(".newVertexComponent .edgeInput")).not.toBeNull();
|
||||
expect(document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should display partition key property if set", () => {
|
||||
partitionKeyProperty("testKey");
|
||||
expect(
|
||||
(document.querySelector(".newVertexComponent .newVertexForm .labelCol input") as HTMLInputElement).value
|
||||
).toEqual("testKey");
|
||||
});
|
||||
|
||||
it("should NOT display partition key property if NOT set", () => {
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Behavior", () => {
|
||||
let clickSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
clickSpy = jasmine.createSpy("Command button click spy");
|
||||
});
|
||||
|
||||
it("should add new property row when +Add property button is pressed", () => {
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(3);
|
||||
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(3);
|
||||
});
|
||||
|
||||
it("should remove property row when trash button is pressed", () => {
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
|
||||
// Mark this one to delete
|
||||
const elts = document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg");
|
||||
elts[elts.length - 1].className += " deleteme";
|
||||
|
||||
document.querySelector(".newVertexComponent .rightPaneAddPropertyBtn").dispatchEvent(new Event("click"));
|
||||
document
|
||||
.querySelector(".newVertexComponent .rightPaneTrashIconImg.deleteme")
|
||||
.parentElement.dispatchEvent(new Event("click"));
|
||||
expect(document.getElementsByClassName("valueCol").length).toBe(2);
|
||||
expect(document.getElementsByClassName("rightPaneTrashIcon").length).toBe(2);
|
||||
expect(document.querySelectorAll(".newVertexComponent .rightPaneTrashIconImg.deleteme").length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
import * as ko from "knockout";
|
||||
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";
|
||||
import { NewVertexData, InputProperty } from "../../../Contracts/ViewModels";
|
||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import template from "./NewVertexComponent.html";
|
||||
|
||||
/**
|
||||
* Parameters for this component
|
||||
*/
|
||||
export interface NewVertexParams {
|
||||
// Data to be edited by the component
|
||||
newVertexData: ko.Observable<NewVertexData>;
|
||||
partitionKeyProperty: ko.Observable<string>;
|
||||
firstFieldHasFocus?: ko.Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Callback triggered when the template is bound to the component (for testing purposes)
|
||||
*/
|
||||
onTemplateReady?: () => void;
|
||||
}
|
||||
|
||||
export class NewVertexViewModel extends WaitsForTemplateViewModel {
|
||||
private static readonly DEFAULT_PROPERTY_TYPE = "string";
|
||||
|
||||
private newVertexData: ko.Observable<NewVertexData>;
|
||||
private firstFieldHasFocus: ko.Observable<boolean>;
|
||||
private propertyTypes: string[];
|
||||
|
||||
public constructor(params: NewVertexParams) {
|
||||
super();
|
||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
||||
if (isTemplateReady && params.onTemplateReady) {
|
||||
params.onTemplateReady();
|
||||
}
|
||||
});
|
||||
|
||||
this.newVertexData =
|
||||
params.newVertexData ||
|
||||
ko.observable({
|
||||
label: "",
|
||||
properties: <InputProperty[]>[]
|
||||
});
|
||||
this.firstFieldHasFocus = params.firstFieldHasFocus || ko.observable(false);
|
||||
this.propertyTypes = EditorNodePropertiesComponent.VERTEX_PROPERTY_TYPES;
|
||||
if (params.partitionKeyProperty) {
|
||||
params.partitionKeyProperty.subscribe((newKeyProp: string) => {
|
||||
if (!newKeyProp) {
|
||||
return;
|
||||
}
|
||||
this.addNewVertexProperty(newKeyProp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onAddNewProperty() {
|
||||
this.addNewVertexProperty();
|
||||
document.getElementById("propertyKeyNewVertexPane").focus();
|
||||
}
|
||||
|
||||
public onAddNewPropertyKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
|
||||
this.onAddNewProperty();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
public addNewVertexProperty(key?: string) {
|
||||
let ap = this.newVertexData().properties;
|
||||
ap.push({ key: key || "", values: [{ value: "", type: NewVertexViewModel.DEFAULT_PROPERTY_TYPE }] });
|
||||
this.newVertexData.valueHasMutated();
|
||||
}
|
||||
|
||||
public removeNewVertexProperty(index: number) {
|
||||
let ap = this.newVertexData().properties;
|
||||
ap.splice(index, 1);
|
||||
this.newVertexData.valueHasMutated();
|
||||
document.getElementById("addProperyNewVertexBtn").focus();
|
||||
}
|
||||
|
||||
public removeNewVertexPropertyKeyPress = (index: number, source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
|
||||
this.removeNewVertexProperty(index);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for ko component registration
|
||||
*/
|
||||
export const NewVertexComponent = {
|
||||
viewModel: NewVertexViewModel,
|
||||
template
|
||||
};
|
||||
import * as ko from "knockout";
|
||||
import { EditorNodePropertiesComponent } from "../GraphExplorerComponent/EditorNodePropertiesComponent";
|
||||
import { NewVertexData, InputProperty } from "../../../Contracts/ViewModels";
|
||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import template from "./NewVertexComponent.html";
|
||||
|
||||
/**
|
||||
* Parameters for this component
|
||||
*/
|
||||
export interface NewVertexParams {
|
||||
// Data to be edited by the component
|
||||
newVertexData: ko.Observable<NewVertexData>;
|
||||
partitionKeyProperty: ko.Observable<string>;
|
||||
firstFieldHasFocus?: ko.Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Callback triggered when the template is bound to the component (for testing purposes)
|
||||
*/
|
||||
onTemplateReady?: () => void;
|
||||
}
|
||||
|
||||
export class NewVertexViewModel extends WaitsForTemplateViewModel {
|
||||
private static readonly DEFAULT_PROPERTY_TYPE = "string";
|
||||
|
||||
private newVertexData: ko.Observable<NewVertexData>;
|
||||
private firstFieldHasFocus: ko.Observable<boolean>;
|
||||
private propertyTypes: string[];
|
||||
|
||||
public constructor(params: NewVertexParams) {
|
||||
super();
|
||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
||||
if (isTemplateReady && params.onTemplateReady) {
|
||||
params.onTemplateReady();
|
||||
}
|
||||
});
|
||||
|
||||
this.newVertexData =
|
||||
params.newVertexData ||
|
||||
ko.observable({
|
||||
label: "",
|
||||
properties: <InputProperty[]>[],
|
||||
});
|
||||
this.firstFieldHasFocus = params.firstFieldHasFocus || ko.observable(false);
|
||||
this.propertyTypes = EditorNodePropertiesComponent.VERTEX_PROPERTY_TYPES;
|
||||
if (params.partitionKeyProperty) {
|
||||
params.partitionKeyProperty.subscribe((newKeyProp: string) => {
|
||||
if (!newKeyProp) {
|
||||
return;
|
||||
}
|
||||
this.addNewVertexProperty(newKeyProp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onAddNewProperty() {
|
||||
this.addNewVertexProperty();
|
||||
document.getElementById("propertyKeyNewVertexPane").focus();
|
||||
}
|
||||
|
||||
public onAddNewPropertyKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
|
||||
this.onAddNewProperty();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
public addNewVertexProperty(key?: string) {
|
||||
let ap = this.newVertexData().properties;
|
||||
ap.push({ key: key || "", values: [{ value: "", type: NewVertexViewModel.DEFAULT_PROPERTY_TYPE }] });
|
||||
this.newVertexData.valueHasMutated();
|
||||
}
|
||||
|
||||
public removeNewVertexProperty(index: number) {
|
||||
let ap = this.newVertexData().properties;
|
||||
ap.splice(index, 1);
|
||||
this.newVertexData.valueHasMutated();
|
||||
document.getElementById("addProperyNewVertexBtn").focus();
|
||||
}
|
||||
|
||||
public removeNewVertexPropertyKeyPress = (index: number, source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
|
||||
this.removeNewVertexProperty(index);
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for ko component registration
|
||||
*/
|
||||
export const NewVertexComponent = {
|
||||
viewModel: NewVertexViewModel,
|
||||
template,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user