diff --git a/.eslintignore b/.eslintignore index 1d59e9a1d..c4aa1be52 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,8 +11,6 @@ src/Common/CosmosClient.test.ts src/Common/CosmosClient.ts src/Common/DataAccessUtilityBase.test.ts src/Common/DataAccessUtilityBase.ts -src/Common/DeleteFeedback.ts -src/Common/DocumentClientUtilityBase.ts src/Common/EditableUtility.ts src/Common/HashMap.test.ts src/Common/HashMap.ts @@ -55,8 +53,6 @@ src/Explorer/ComponentRegisterer.test.ts src/Explorer/ComponentRegisterer.ts src/Explorer/ContextMenuButtonFactory.ts src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts -src/Explorer/Controls/CommandButton/CommandButton.test.ts -src/Explorer/Controls/CommandButton/CommandButton.ts src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts src/Explorer/Controls/DynamicList/DynamicList.test.ts src/Explorer/Controls/DynamicList/DynamicListComponent.ts @@ -92,8 +88,6 @@ src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.ts src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts src/Explorer/Graph/GraphExplorerComponent/GraphData.ts -src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts -src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts src/Explorer/Graph/GraphExplorerComponent/GremlinClient.test.ts src/Explorer/Graph/GraphExplorerComponent/GremlinClient.ts src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts diff --git a/src/Explorer/Graph/GraphExplorerComponent/EditorNeighborsComponent.tsx b/src/Explorer/Graph/GraphExplorerComponent/EditorNeighborsComponent.tsx index 58ba8def1..5b4470054 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/EditorNeighborsComponent.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/EditorNeighborsComponent.tsx @@ -5,7 +5,7 @@ import * as React from "react"; import { NeighborVertexBasicInfo, EditedEdges, GraphNewEdgeData, PossibleVertex } from "./GraphExplorer"; -import { GraphUtil } from "./GraphUtil"; +import * as GraphUtil from "./GraphUtil"; import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent"; import DeleteIcon from "../../../../images/delete.svg"; import AddPropertyIcon from "../../../../images/Add-property.svg"; diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx index cda7370ba..ba35d2fd8 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx @@ -9,7 +9,7 @@ import { GraphVizComponentProps } from "./GraphVizComponent"; import * as GraphData from "./GraphData"; import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent"; import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; -import { GraphUtil } from "./GraphUtil"; +import * as GraphUtil from "./GraphUtil"; import * as DataModels from "../../../Contracts/DataModels"; import * as ViewModels from "../../../Contracts/ViewModels"; import * as GremlinClient from "./GremlinClient"; diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts index 1e06263bd..754f6217c 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts @@ -1,4 +1,4 @@ -import { GraphUtil } from "./GraphUtil"; +import * as GraphUtil from "./GraphUtil"; import { GraphData, GremlinVertex, GremlinEdge } from "./GraphData"; import * as sinon from "sinon"; import { GraphExplorer } from "./GraphExplorer"; @@ -69,7 +69,7 @@ describe("Process Gremlin vertex", () => { describe("getLimitedArrayString()", () => { const expectedEmptyResult = { result: "", consumedCount: 0 }; it("should handle null array", () => { - expect(GraphUtil.getLimitedArrayString(null, 10)).toEqual(expectedEmptyResult); + expect(GraphUtil.getLimitedArrayString(undefined, 10)).toEqual(expectedEmptyResult); }); it("should handle empty array", () => { diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts index 7467e11bc..2faf25c67 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts @@ -7,180 +7,184 @@ interface JoinArrayMaxCharOutput { consumedCount: number; // Number of items consumed } -export class GraphUtil { - public static getNeighborTitle(neighbor: NeighborVertexBasicInfo): string { - return `edge id: ${neighbor.edgeId}, vertex id: ${neighbor.id}`; - } +interface EdgePropertyType { + id: string; + outV?: string; + inV?: string; +} - /** - * Collect all edges from this node - * @param vertex - * @param graphData - * @param newNodes (optional) object describing new nodes encountered - */ - public static createEdgesfromNode( - vertex: GraphData.GremlinVertex, - graphData: GraphData.GraphData, - newNodes?: { [id: string]: boolean } - ): void { - if (vertex.hasOwnProperty("outE")) { - let outE = vertex.outE; - for (var label in outE) { - $.each(outE[label], (index: number, edge: any) => { - // We create our own edge. No need to fetch - let e = { - id: edge.id, - label: label, - inV: edge.inV, - outV: vertex.id, - }; +export function getNeighborTitle(neighbor: NeighborVertexBasicInfo): string { + return `edge id: ${neighbor.edgeId}, vertex id: ${neighbor.id}`; +} - graphData.addEdge(e); - if (newNodes) { - newNodes[edge.inV] = true; - } - }); - } - } - if (vertex.hasOwnProperty("inE")) { - let inE = vertex.inE; - for (var label in inE) { - $.each(inE[label], (index: number, edge: any) => { - // We create our own edge. No need to fetch - let e = { - id: edge.id, - label: label, - inV: vertex.id, - outV: edge.outV, - }; +/** + * Collect all edges from this node + * @param vertex + * @param graphData + * @param newNodes (optional) object describing new nodes encountered + */ +export function createEdgesfromNode( + vertex: GraphData.GremlinVertex, + graphData: GraphData.GraphData, + newNodes?: { [id: string]: boolean } +): void { + if (Object.prototype.hasOwnProperty.call(vertex, "outE")) { + const outE = vertex.outE; + for (const label in outE) { + $.each(outE[label], (index: number, edge: EdgePropertyType) => { + // We create our own edge. No need to fetch + const e = { + id: edge.id, + label: label, + inV: edge.inV, + outV: vertex.id, + }; - graphData.addEdge(e); - if (newNodes) { - newNodes[edge.outV] = true; - } - }); - } + graphData.addEdge(e); + if (newNodes) { + newNodes[edge.inV] = true; + } + }); } } + if (Object.prototype.hasOwnProperty.call(vertex, "inE")) { + const inE = vertex.inE; + for (const label in inE) { + $.each(inE[label], (index: number, edge: EdgePropertyType) => { + // We create our own edge. No need to fetch + const e = { + id: edge.id, + label: label, + inV: vertex.id, + outV: edge.outV, + }; - /** - * From ['id1', 'id2', 'idn'] build the following string "'id1','id2','idn'". - * The string length cannot exceed maxSize. - * @param array - * @param maxSize - * @return - */ - public static getLimitedArrayString(array: string[], maxSize: number): JoinArrayMaxCharOutput { - if (!array || array.length === 0 || array[0].length + 2 > maxSize) { - return { result: "", consumedCount: 0 }; + graphData.addEdge(e); + if (newNodes) { + newNodes[edge.outV] = true; + } + }); } - - const end = array.length - 1; - let output = `'${array[0]}'`; - let i = 0; - for (; i < end; i++) { - const candidate = `${output},'${array[i + 1]}'`; - if (candidate.length <= maxSize) { - output = candidate; - } else { - break; - } - } - - return { - result: output, - consumedCount: i + 1, - }; - } - - public static createFetchEdgePairQuery( - outE: boolean, - pkid: string, - excludedEdgeIds: string[], - startIndex: number, - pageSize: number, - withoutStepArgMaxLenght: number - ): string { - let gremlinQuery: string; - if (excludedEdgeIds.length > 0) { - // build a string up to max char - const joined = GraphUtil.getLimitedArrayString(excludedEdgeIds, withoutStepArgMaxLenght); - const hasWithoutStep = !!joined.result ? `.has(id, without(${joined.result}))` : ""; - - if (joined.consumedCount === excludedEdgeIds.length) { - gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.limit(${pageSize}).as('e').${ - outE ? "inV" : "outV" - }().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')`; - } - } else { - gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}().limit(${pageSize}).as('e').${ - outE ? "inV" : "outV" - }().as('v').select('e', 'v')`; - } - return gremlinQuery; - } - - /** - * Trim graph - */ - public static trimGraph( - currentRoot: GraphData.GremlinVertex, - graphData: GraphData.GraphData - ) { - const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId); - graphData.unloadAllVertices(importantNodes); - - // Keep only ancestors node in fixed position - $.each(graphData.ids, (index: number, id: string) => { - graphData.getVertexById(id)._isFixedPosition = importantNodes.indexOf(id) !== -1; - }); - } - - public static addRootChildToGraph( - root: GraphData.GremlinVertex, - child: GraphData.GremlinVertex, - graphData: GraphData.GraphData - ) { - child._ancestorsId = (root._ancestorsId || []).concat([root.id]); - graphData.addVertex(child); - GraphUtil.createEdgesfromNode(child, graphData); - graphData.addNeighborInfo(child); - } - - /** - * TODO Perform minimal substitution to prevent breaking gremlin query and allow \"" for now. - * @param value - */ - public static escapeDoubleQuotes(value: string): string { - return value == null ? value : value.replace(/"/g, '\\"'); - } - - /** - * Surround with double-quotes if val is a string. - * @param val - */ - public static getQuotedPropValue(ip: ViewModels.InputPropertyValue): string { - switch (ip.type) { - case "number": - case "boolean": - return `${ip.value}`; - case "null": - return null; - default: - return `"${GraphUtil.escapeDoubleQuotes(ip.value as string)}"`; - } - } - - /** - * TODO Perform minimal substitution to prevent breaking gremlin query and allow \' for now. - * @param value - */ - public static escapeSingleQuotes(value: string): string { - return value == null ? value : value.replace(/'/g, "\\'"); } } + +/** + * From ['id1', 'id2', 'idn'] build the following string "'id1','id2','idn'". + * The string length cannot exceed maxSize. + * @param array + * @param maxSize + * @return + */ +export function getLimitedArrayString(array: string[], maxSize: number): JoinArrayMaxCharOutput { + if (!array || array.length === 0 || array[0].length + 2 > maxSize) { + return { result: "", consumedCount: 0 }; + } + + const end = array.length - 1; + let output = `'${array[0]}'`; + let i = 0; + for (; i < end; i++) { + const candidate = `${output},'${array[i + 1]}'`; + if (candidate.length <= maxSize) { + output = candidate; + } else { + break; + } + } + + return { + result: output, + consumedCount: i + 1, + }; +} + +export function createFetchEdgePairQuery( + outE: boolean, + pkid: string, + excludedEdgeIds: string[], + startIndex: number, + pageSize: number, + withoutStepArgMaxLenght: number +): string { + let gremlinQuery: string; + if (excludedEdgeIds.length > 0) { + // build a string up to max char + const joined = getLimitedArrayString(excludedEdgeIds, withoutStepArgMaxLenght); + const hasWithoutStep = joined.result ? `.has(id, without(${joined.result}))` : ""; + + if (joined.consumedCount === excludedEdgeIds.length) { + gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.limit(${pageSize}).as('e').${ + outE ? "inV" : "outV" + }().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')`; + } + } else { + gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}().limit(${pageSize}).as('e').${ + outE ? "inV" : "outV" + }().as('v').select('e', 'v')`; + } + return gremlinQuery; +} + +/** + * Trim graph + */ +export function trimGraph( + currentRoot: GraphData.GremlinVertex, + graphData: GraphData.GraphData +) { + const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId); + graphData.unloadAllVertices(importantNodes); + + // Keep only ancestors node in fixed position + $.each(graphData.ids, (index: number, id: string) => { + graphData.getVertexById(id)._isFixedPosition = importantNodes.indexOf(id) !== -1; + }); +} + +export function addRootChildToGraph( + root: GraphData.GremlinVertex, + child: GraphData.GremlinVertex, + graphData: GraphData.GraphData +) { + child._ancestorsId = (root._ancestorsId || []).concat([root.id]); + graphData.addVertex(child); + createEdgesfromNode(child, graphData); + graphData.addNeighborInfo(child); +} + +/** + * TODO Perform minimal substitution to prevent breaking gremlin query and allow \"" for now. + * @param value + */ +export function escapeDoubleQuotes(value: string): string { + return value === undefined ? value : value.replace(/"/g, '\\"'); +} + +/** + * Surround with double-quotes if val is a string. + * @param val + */ +export function getQuotedPropValue(ip: ViewModels.InputPropertyValue): string { + switch (ip.type) { + case "number": + case "boolean": + return `${ip.value}`; + case "null": + return undefined; + default: + return `"${escapeDoubleQuotes(ip.value as string)}"`; + } +} + +/** + * TODO Perform minimal substitution to prevent breaking gremlin query and allow \' for now. + * @param value + */ +export function escapeSingleQuotes(value: string): string { + return value === undefined ? value : value.replace(/'/g, "\\'"); +} diff --git a/src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx b/src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx index 69cc2c4b9..8873d998e 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx @@ -5,7 +5,7 @@ import * as React from "react"; import { GraphHighlightedNodeData, NeighborVertexBasicInfo } from "./GraphExplorer"; -import { GraphUtil } from "./GraphUtil"; +import * as GraphUtil from "./GraphUtil"; import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement"; export interface ReadOnlyNeighborsComponentProps {