mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 20:01:45 +00:00
Compare commits
3 Commits
resolve_De
...
users/srna
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55837db65b | ||
|
|
9f27cb95b9 | ||
|
|
271256bffb |
@@ -81,18 +81,15 @@ src/Explorer/Tables/DataTable/DataTableBindingManager.ts
|
|||||||
src/Explorer/Tables/DataTable/DataTableBuilder.ts
|
src/Explorer/Tables/DataTable/DataTableBuilder.ts
|
||||||
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
|
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
|
||||||
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
|
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
|
||||||
# src/Explorer/Tables/DataTable/DataTableOperations.ts
|
|
||||||
src/Explorer/Tables/DataTable/DataTableViewModel.ts
|
src/Explorer/Tables/DataTable/DataTableViewModel.ts
|
||||||
# src/Explorer/Tables/DataTable/TableCommands.ts
|
|
||||||
# src/Explorer/Tables/DataTable/TableEntityCache.ts
|
|
||||||
src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
|
src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
|
||||||
# src/Explorer/Tables/Entities.ts
|
|
||||||
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
|
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
|
||||||
src/Explorer/Tables/TableDataClient.ts
|
src/Explorer/Tables/TableDataClient.ts
|
||||||
src/Explorer/Tables/TableEntityProcessor.ts
|
src/Explorer/Tables/TableEntityProcessor.ts
|
||||||
src/Explorer/Tables/Utilities.ts
|
src/Explorer/Tables/Utilities.ts
|
||||||
src/Explorer/Tabs/ConflictsTab.ts
|
src/Explorer/Tabs/ConflictsTab.ts
|
||||||
src/Explorer/Tabs/DatabaseSettingsTab.ts
|
src/Explorer/Tabs/DatabaseSettingsTab.ts
|
||||||
|
src/Explorer/Tabs/DocumentsTab.test.ts
|
||||||
src/Explorer/Tabs/DocumentsTab.ts
|
src/Explorer/Tabs/DocumentsTab.ts
|
||||||
src/Explorer/Tabs/GraphTab.ts
|
src/Explorer/Tabs/GraphTab.ts
|
||||||
src/Explorer/Tabs/MongoDocumentsTab.ts
|
src/Explorer/Tabs/MongoDocumentsTab.ts
|
||||||
@@ -117,6 +114,8 @@ src/Index.ts
|
|||||||
src/Platform/Hosted/Authorization.ts
|
src/Platform/Hosted/Authorization.ts
|
||||||
src/ReactDevTools.ts
|
src/ReactDevTools.ts
|
||||||
src/Shared/Constants.ts
|
src/Shared/Constants.ts
|
||||||
|
src/Shared/DefaultExperienceUtility.test.ts
|
||||||
|
src/Shared/DefaultExperienceUtility.ts
|
||||||
src/Shared/appInsights.ts
|
src/Shared/appInsights.ts
|
||||||
src/SparkClusterManager/ArcadiaResourceManager.ts
|
src/SparkClusterManager/ArcadiaResourceManager.ts
|
||||||
src/SparkClusterManager/SparkClusterManager.ts
|
src/SparkClusterManager/SparkClusterManager.ts
|
||||||
@@ -131,20 +130,13 @@ src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
|||||||
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
|
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
|
||||||
src/Explorer/Controls/TreeComponent/TreeComponent.tsx
|
src/Explorer/Controls/TreeComponent/TreeComponent.tsx
|
||||||
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx
|
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx
|
||||||
; src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx
|
|
||||||
src/Explorer/Graph/GraphExplorerComponent/GraphVizComponent.tsx
|
|
||||||
src/Explorer/Graph/GraphExplorerComponent/LeftPaneComponent.tsx
|
|
||||||
src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
|
|
||||||
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.test.tsx
|
|
||||||
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
|
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
|
||||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
|
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
|
||||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
|
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
|
||||||
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
|
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
|
||||||
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
|
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
|
||||||
; src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx
|
|
||||||
src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx
|
src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx
|
||||||
src/Explorer/Notebook/NotebookComponent/contents/index.tsx
|
src/Explorer/Notebook/NotebookComponent/contents/index.tsx
|
||||||
; src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx
|
|
||||||
src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx
|
src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx
|
||||||
src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx
|
src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx
|
||||||
src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx
|
src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
|
|||||||
className={className}
|
className={className}
|
||||||
as="tr"
|
as="tr"
|
||||||
aria-label={node.caption}
|
aria-label={node.caption}
|
||||||
onActivated={(e) => this.props.onRootNodeSelected(node.id)}
|
onActivated={() => this.props.onRootNodeSelected(node.id)}
|
||||||
key={node.id}
|
key={node.id}
|
||||||
>
|
>
|
||||||
<td className="resultItem">
|
<td className="resultItem">
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
|
||||||
import { mount, ReactWrapper } from "enzyme";
|
import { mount, ReactWrapper } from "enzyme";
|
||||||
import * as Q from "q";
|
import * as Q from "q";
|
||||||
import { NodePropertiesComponent, NodePropertiesComponentProps, Mode } from "./NodePropertiesComponent";
|
import React from "react";
|
||||||
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer";
|
import { GraphHighlightedNodeData, PossibleVertex } from "./GraphExplorer";
|
||||||
|
import { Mode, NodePropertiesComponent, NodePropertiesComponentProps } from "./NodePropertiesComponent";
|
||||||
|
|
||||||
describe("Property pane", () => {
|
describe("Property pane", () => {
|
||||||
const title = "My Title";
|
const title = "My Title";
|
||||||
@@ -37,17 +37,18 @@ describe("Property pane", () => {
|
|||||||
return {
|
return {
|
||||||
expandedTitle: title,
|
expandedTitle: title,
|
||||||
isCollapsed: false,
|
isCollapsed: false,
|
||||||
onCollapsedChanged: (newValue: boolean): void => {},
|
onCollapsedChanged: jest.fn(),
|
||||||
node: highlightedNode,
|
node: highlightedNode,
|
||||||
getPkIdFromNodeData: (v: GraphHighlightedNodeData): string => null,
|
getPkIdFromNodeData: (): string => undefined,
|
||||||
collectionPartitionKeyProperty: null,
|
collectionPartitionKeyProperty: undefined,
|
||||||
updateVertexProperties: (editedProperties: EditedProperties): Q.Promise<void> => Q.resolve(),
|
updateVertexProperties: (): Q.Promise<void> => Q.resolve(),
|
||||||
selectNode: (id: string): void => {},
|
selectNode: jest.fn(),
|
||||||
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(null),
|
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(undefined),
|
||||||
possibleEdgeLabels: null,
|
possibleEdgeLabels: undefined,
|
||||||
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
|
//eslint-disable-next-line
|
||||||
deleteHighlightedNode: (): void => {},
|
editGraphEdges: (): Q.Promise<any> => Q.resolve(),
|
||||||
onModeChanged: (newMode: Mode): void => {},
|
deleteHighlightedNode: jest.fn(),
|
||||||
|
onModeChanged: jest.fn(),
|
||||||
viewMode: Mode.READONLY_PROP,
|
viewMode: Mode.READONLY_PROP,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
editedProperties: {
|
editedProperties: {
|
||||||
pkId: null,
|
pkId: undefined,
|
||||||
readOnlyProperties: [],
|
readOnlyProperties: [],
|
||||||
existingProperties: [],
|
existingProperties: [],
|
||||||
addedProperties: [],
|
addedProperties: [],
|
||||||
@@ -98,15 +98,12 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDerivedStateFromProps(
|
public static getDerivedStateFromProps(props: NodePropertiesComponentProps): Partial<NodePropertiesComponentState> {
|
||||||
props: NodePropertiesComponentProps,
|
|
||||||
state: NodePropertiesComponentState
|
|
||||||
): Partial<NodePropertiesComponentState> {
|
|
||||||
if (props.viewMode !== Mode.READONLY_PROP) {
|
if (props.viewMode !== Mode.READONLY_PROP) {
|
||||||
return { isDeleteConfirm: false };
|
return { isDeleteConfirm: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
@@ -138,10 +135,10 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString {
|
private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString {
|
||||||
if (value == null) {
|
if (value === undefined) {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
let type = typeof value;
|
const type = typeof value;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "number":
|
case "number":
|
||||||
case "boolean":
|
case "boolean":
|
||||||
@@ -172,10 +169,9 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
];
|
];
|
||||||
|
|
||||||
const existingProps: ViewModels.InputProperty[] = [];
|
const existingProps: ViewModels.InputProperty[] = [];
|
||||||
|
|
||||||
if (this.props.node.hasOwnProperty("properties")) {
|
if (this.props.node.hasOwnProperty("properties")) {
|
||||||
const hProps = this.props.node["properties"];
|
const hProps = this.props.node["properties"];
|
||||||
for (let p in hProps) {
|
for (const p in hProps) {
|
||||||
const propValues = hProps[p];
|
const propValues = hProps[p];
|
||||||
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
|
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
|
||||||
key: p,
|
key: p,
|
||||||
@@ -437,7 +433,7 @@ export class NodePropertiesComponent extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,18 +307,11 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
|||||||
|
|
||||||
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "New " + getDatabaseName();
|
const label = "New " + getDatabaseName();
|
||||||
const newDatabaseButton = document.activeElement as HTMLElement;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: AddDatabaseIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () =>
|
onCommandClick: () =>
|
||||||
useSidePanel
|
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />),
|
||||||
.getState()
|
|
||||||
.openSidePanel(
|
|
||||||
"New " + getDatabaseName(),
|
|
||||||
<AddDatabasePanel explorer={container} buttonElement={newDatabaseButton} />
|
|
||||||
),
|
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import * as React from "react";
|
|||||||
import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
|
import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
|
||||||
|
|
||||||
export const OpenFullScreen: React.FunctionComponent = () => {
|
export const OpenFullScreen: React.FunctionComponent = () => {
|
||||||
|
const [isReadUrlCopy, setIsReadUrlCopy] = React.useState<boolean>(false);
|
||||||
|
const [isReadWriteUrlCopy, setIsReadWriteUrlCopy] = React.useState<boolean>(false);
|
||||||
const result = useFullScreenURLs();
|
const result = useFullScreenURLs();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />;
|
return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />;
|
||||||
@@ -25,8 +27,9 @@ export const OpenFullScreen: React.FunctionComponent = () => {
|
|||||||
<DefaultButton
|
<DefaultButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyToClipboard(readWriteUrl);
|
copyToClipboard(readWriteUrl);
|
||||||
|
setIsReadWriteUrlCopy(true);
|
||||||
}}
|
}}
|
||||||
text="Copy"
|
text={isReadWriteUrlCopy ? "Copied" : "Copy"}
|
||||||
iconProps={{ iconName: "Copy" }}
|
iconProps={{ iconName: "Copy" }}
|
||||||
/>
|
/>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
@@ -41,9 +44,10 @@ export const OpenFullScreen: React.FunctionComponent = () => {
|
|||||||
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
||||||
<DefaultButton
|
<DefaultButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setIsReadUrlCopy(true);
|
||||||
copyToClipboard(readUrl);
|
copyToClipboard(readUrl);
|
||||||
}}
|
}}
|
||||||
text="Copy"
|
text={isReadUrlCopy ? "Copied" : "Copy"}
|
||||||
iconProps={{ iconName: "Copy" }}
|
iconProps={{ iconName: "Copy" }}
|
||||||
/>
|
/>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
|
|||||||
|
|
||||||
export interface AddDatabasePaneProps {
|
export interface AddDatabasePaneProps {
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
buttonElement?: HTMLElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||||
explorer: container,
|
explorer: container,
|
||||||
buttonElement,
|
|
||||||
}: AddDatabasePaneProps) => {
|
}: AddDatabasePaneProps) => {
|
||||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||||
let throughput: number;
|
let throughput: number;
|
||||||
@@ -79,7 +77,6 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
|||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
};
|
};
|
||||||
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
|
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
|
||||||
buttonElement.focus();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Collection } from "Contracts/ViewModels";
|
|||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Collection, Database } from "Contracts/ViewModels";
|
|||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
|
|||||||
@@ -307,23 +307,16 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: AddDatabaseIcon,
|
||||||
title: "New " + getDatabaseName(),
|
title: "New " + getDatabaseName(),
|
||||||
description: undefined,
|
description: undefined,
|
||||||
onClick: () => this.openAddDatabasePanel(),
|
onClick: () =>
|
||||||
|
useSidePanel
|
||||||
|
.getState()
|
||||||
|
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private openAddDatabasePanel() {
|
|
||||||
const newDatabaseButton = document.activeElement as HTMLElement;
|
|
||||||
useSidePanel
|
|
||||||
.getState()
|
|
||||||
.openSidePanel(
|
|
||||||
"New " + getDatabaseName(),
|
|
||||||
<AddDatabasePanel explorer={this.container} buttonElement={newDatabaseButton} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
|
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
|
||||||
return {
|
return {
|
||||||
iconSrc: NotebookIcon,
|
iconSrc: NotebookIcon,
|
||||||
|
|||||||
@@ -202,14 +202,21 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
|
|
||||||
let updateQuery = `UPDATE ${collection.databaseId}.${collection.id()}`;
|
let updateQuery = `UPDATE ${collection.databaseId}.${collection.id()}`;
|
||||||
let isPropertyUpdated = false;
|
let isPropertyUpdated = false;
|
||||||
|
let isFirstPropertyToUpdate = true;
|
||||||
for (let property in newEntity) {
|
for (let property in newEntity) {
|
||||||
if (
|
if (
|
||||||
!originalDocument[property] ||
|
!originalDocument[property] ||
|
||||||
newEntity[property]._.toString() !== originalDocument[property]._.toString()
|
newEntity[property]._.toString() !== originalDocument[property]._.toString()
|
||||||
) {
|
) {
|
||||||
updateQuery += this.isStringType(newEntity[property].$)
|
let propertyQuerySegment = this.isStringType(newEntity[property].$)
|
||||||
? ` SET ${property} = '${newEntity[property]._}',`
|
? `${property} = '${newEntity[property]._}',`
|
||||||
: ` SET ${property} = ${newEntity[property]._},`;
|
: `${property} = ${newEntity[property]._},`;
|
||||||
|
// Only add the "SET" keyword once
|
||||||
|
if (isFirstPropertyToUpdate) {
|
||||||
|
propertyQuerySegment = " SET " + propertyQuerySegment;
|
||||||
|
isFirstPropertyToUpdate = false;
|
||||||
|
}
|
||||||
|
updateQuery += propertyQuerySegment;
|
||||||
isPropertyUpdated = true;
|
isPropertyUpdated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ describe("Documents tab", () => {
|
|||||||
describe("buildQuery", () => {
|
describe("buildQuery", () => {
|
||||||
it("should generate the right select query for SQL API", () => {
|
it("should generate the right select query for SQL API", () => {
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
@@ -82,9 +82,9 @@ describe("Documents tab", () => {
|
|||||||
container: mongoExplorer,
|
container: mongoExplorer,
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false for undefined or undefined collection", () => {
|
it("should be false for null or undefined collection", () => {
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
@@ -94,10 +94,10 @@ describe("Documents tab", () => {
|
|||||||
expect(documentsTab.showPartitionKey).toBe(false);
|
expect(documentsTab.showPartitionKey).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false for undefined or undefined partitionKey", () => {
|
it("should be false for null or undefined partitionKey", () => {
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
collection: collectionWithoutPartitionKey,
|
collection: collectionWithoutPartitionKey,
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
@@ -110,7 +110,7 @@ describe("Documents tab", () => {
|
|||||||
it("should be true for non-Mongo accounts with system partitionKey", () => {
|
it("should be true for non-Mongo accounts with system partitionKey", () => {
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
collection: collectionWithSystemPartitionKey,
|
collection: collectionWithSystemPartitionKey,
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
@@ -126,7 +126,7 @@ describe("Documents tab", () => {
|
|||||||
});
|
});
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
collection: mongoCollectionWithSystemPartitionKey,
|
collection: mongoCollectionWithSystemPartitionKey,
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
@@ -139,7 +139,7 @@ describe("Documents tab", () => {
|
|||||||
it("should be true for non-system partitionKey", () => {
|
it("should be true for non-system partitionKey", () => {
|
||||||
const documentsTab = new DocumentsTab({
|
const documentsTab = new DocumentsTab({
|
||||||
collection: collectionWithNonSystemPartitionKey,
|
collection: collectionWithNonSystemPartitionKey,
|
||||||
partitionKey: undefined,
|
partitionKey: null,
|
||||||
documentIds: ko.observableArray<DocumentId>(),
|
documentIds: ko.observableArray<DocumentId>(),
|
||||||
tabKind: ViewModels.CollectionTabKind.Documents,
|
tabKind: ViewModels.CollectionTabKind.Documents,
|
||||||
title: "",
|
title: "",
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
|
|||||||
const saveButtonChildren = [];
|
const saveButtonChildren = [];
|
||||||
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
|
||||||
saveButtonChildren.push({
|
saveButtonChildren.push({
|
||||||
iconName: "Copy",
|
iconName: copyToLabel,
|
||||||
onCommandClick: () => this.copyNotebook(),
|
onCommandClick: () => this.copyNotebook(),
|
||||||
commandButtonLabel: copyToLabel,
|
commandButtonLabel: copyToLabel,
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as Constants from "../Common/Constants";
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
|
import * as Constants from "../Common/Constants";
|
||||||
|
|
||||||
export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTemplate {
|
export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTemplate {
|
||||||
public isTemplateReady: ko.Observable<boolean>;
|
public isTemplateReady: ko.Observable<boolean>;
|
||||||
@@ -14,11 +14,11 @@ export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTe
|
|||||||
callback(value);
|
callback(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("keydown", (e: KeyboardEvent) => {
|
document.addEventListener("keydown", function (e: KeyboardEvent) {
|
||||||
// To trap keyboard focus in AddCollection pane
|
// To trap keyboard focus in AddCollection pane
|
||||||
const firstFocusableElement = document.getElementById("closeBtnAddCollection");
|
let firstFocusableElement = document.getElementById("closeBtnAddCollection");
|
||||||
const lastFocusableElement = document.getElementById("submitBtnAddCollection");
|
let lastFocusableElement = document.getElementById("submitBtnAddCollection");
|
||||||
const isTabPressed = e.keyCode === Constants.KeyCodes.Tab;
|
var isTabPressed = e.keyCode === Constants.KeyCodes.Tab;
|
||||||
if (isTabPressed) {
|
if (isTabPressed) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
/* shift + tab */ if (document.activeElement === firstFocusableElement) {
|
/* shift + tab */ if (document.activeElement === firstFocusableElement) {
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
export const _generateResourceUrl = (): string => {
|
export default class AuthHeadersUtil {
|
||||||
|
public static async generateEncryptedToken(readOnly: boolean = false): Promise<DataModels.GenerateTokenResponse> {
|
||||||
|
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + AuthHeadersUtil._generateResourceUrl();
|
||||||
|
const headers: any = { authorization: userContext.authorizationToken };
|
||||||
|
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
|
||||||
|
|
||||||
|
const response = await fetch(url, { method: "POST", headers });
|
||||||
|
const result = await response.json();
|
||||||
|
// This API has a quirk where the response must be parsed to JSON twice
|
||||||
|
return JSON.parse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _generateResourceUrl(): string {
|
||||||
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
|
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
|
||||||
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
|
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
|
||||||
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
|
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
|
||||||
@@ -15,15 +27,5 @@ export const _generateResourceUrl = (): string => {
|
|||||||
const rid = "";
|
const rid = "";
|
||||||
const rtype = "";
|
const rtype = "";
|
||||||
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
|
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
export const generateEncryptedToken = async (readOnly = false): Promise<DataModels.GenerateTokenResponse> => {
|
|
||||||
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + _generateResourceUrl();
|
|
||||||
const headers: any = { authorization: userContext.authorizationToken };
|
|
||||||
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
|
|
||||||
|
|
||||||
const response = await fetch(url, { method: "POST", headers });
|
|
||||||
const result = await response.json();
|
|
||||||
// This API has a quirk where the response must be parsed to JSON twice
|
|
||||||
return JSON.parse(result);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AccountKind, CapabilityNames } from "../../Common/Constants";
|
import { AccountKind, CapabilityNames } from "../../Common/Constants";
|
||||||
import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels";
|
import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels";
|
||||||
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {
|
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import * as DefaultExperienceUtility from "./DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "./DefaultExperienceUtility";
|
||||||
|
|
||||||
describe("Default Experience Utility", () => {
|
describe("Default Experience Utility", () => {
|
||||||
describe("getDefaultExperienceFromApiKind()", () => {
|
describe("getDefaultExperienceFromApiKind()", () => {
|
||||||
const runScenario = (apiKind: number, expectedExperience: typeof userContext.apiType): void => {
|
function runScenario(apiKind: number, expectedExperience: typeof userContext.apiType): void {
|
||||||
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
|
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
|
||||||
expect(resolvedExperience).toEqual(expectedExperience);
|
expect(resolvedExperience).toEqual(expectedExperience);
|
||||||
};
|
}
|
||||||
|
|
||||||
describe("On SQL", () => {
|
describe("On SQL", () => {
|
||||||
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL"));
|
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL"));
|
||||||
@@ -35,10 +35,10 @@ describe("Default Experience Utility", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("getApiKindFromDefaultExperience()", () => {
|
describe("getApiKindFromDefaultExperience()", () => {
|
||||||
const runScenario = (defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void => {
|
function runScenario(defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void {
|
||||||
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
|
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
|
||||||
expect(resolvedApiKind).toEqual(expectedApiKind);
|
expect(resolvedApiKind).toEqual(expectedApiKind);
|
||||||
};
|
}
|
||||||
|
|
||||||
describe("On SQL", () => {
|
describe("On SQL", () => {
|
||||||
it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL));
|
it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL));
|
||||||
@@ -60,8 +60,8 @@ describe("Default Experience Utility", () => {
|
|||||||
it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph));
|
it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("On undefined", () => {
|
describe("On null", () => {
|
||||||
it("should return SQL", () => runScenario(undefined, DataModels.ApiKind.SQL));
|
it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
export const getApiKindFromDefaultExperience = (defaultExperience: typeof userContext.apiType): DataModels.ApiKind => {
|
export class DefaultExperienceUtility {
|
||||||
|
public static getApiKindFromDefaultExperience(defaultExperience: typeof userContext.apiType): DataModels.ApiKind {
|
||||||
if (!defaultExperience) {
|
if (!defaultExperience) {
|
||||||
return DataModels.ApiKind.SQL;
|
return DataModels.ApiKind.SQL;
|
||||||
}
|
}
|
||||||
@@ -20,10 +21,10 @@ export const getApiKindFromDefaultExperience = (defaultExperience: typeof userCo
|
|||||||
default:
|
default:
|
||||||
return DataModels.ApiKind.SQL;
|
return DataModels.ApiKind.SQL;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getDefaultExperienceFromApiKind = (apiKind: DataModels.ApiKind): typeof userContext.apiType => {
|
public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): typeof userContext.apiType {
|
||||||
if (apiKind === undefined) {
|
if (apiKind == null) {
|
||||||
return "SQL";
|
return "SQL";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,4 +43,5 @@ export const getDefaultExperienceFromApiKind = (apiKind: DataModels.ApiKind): ty
|
|||||||
default:
|
default:
|
||||||
return "SQL";
|
return "SQL";
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { ServerConnection, TerminalManager } from "@jupyterlab/services";
|
|||||||
import { Terminal } from "@jupyterlab/terminal";
|
import { Terminal } from "@jupyterlab/terminal";
|
||||||
import { Panel, Widget } from "@phosphor/widgets";
|
import { Panel, Widget } from "@phosphor/widgets";
|
||||||
|
|
||||||
export const createTerminalApp = async (serverSettings: ServerConnection.ISettings) => {
|
export class JupyterLabAppFactory {
|
||||||
|
public static async createTerminalApp(serverSettings: ServerConnection.ISettings) {
|
||||||
const manager = new TerminalManager({
|
const manager = new TerminalManager({
|
||||||
serverSettings: serverSettings,
|
serverSettings: serverSettings,
|
||||||
});
|
});
|
||||||
@@ -20,7 +21,7 @@ export const createTerminalApp = async (serverSettings: ServerConnection.ISettin
|
|||||||
term.title.closable = false;
|
term.title.closable = false;
|
||||||
term.addClass("terminalWidget");
|
term.addClass("terminalWidget");
|
||||||
|
|
||||||
const panel = new Panel();
|
let panel = new Panel();
|
||||||
panel.addWidget(term as any);
|
panel.addWidget(term as any);
|
||||||
panel.id = "main";
|
panel.id = "main";
|
||||||
|
|
||||||
@@ -36,4 +37,5 @@ export const createTerminalApp = async (serverSettings: ServerConnection.ISettin
|
|||||||
window.addEventListener("unload", () => {
|
window.addEventListener("unload", () => {
|
||||||
panel.dispose();
|
panel.dispose();
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Action } from "../Shared/Telemetry/TelemetryConstants";
|
|||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { updateUserContext } from "../UserContext";
|
import { updateUserContext } from "../UserContext";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { createTerminalApp } from "./JupyterLabAppFactory";
|
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
|
||||||
import { TerminalProps } from "./TerminalProps";
|
import { TerminalProps } from "./TerminalProps";
|
||||||
|
|
||||||
const createServerSettings = (props: TerminalProps): ServerConnection.ISettings => {
|
const createServerSettings = (props: TerminalProps): ServerConnection.ISettings => {
|
||||||
@@ -54,7 +54,7 @@ const initTerminal = async (props: TerminalProps) => {
|
|||||||
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
|
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createTerminalApp(serverSettings);
|
await JupyterLabAppFactory.createTerminalApp(serverSettings);
|
||||||
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
|
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
|
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { GenerateTokenResponse } from "../Contracts/DataModels";
|
import { GenerateTokenResponse } from "../Contracts/DataModels";
|
||||||
import * as AuthHeadersUtil from "../Platform/Hosted/Authorization";
|
import AuthHeadersUtil from "../Platform/Hosted/Authorization";
|
||||||
|
|
||||||
export function useFullScreenURLs(): GenerateTokenResponse | undefined {
|
export function useFullScreenURLs(): GenerateTokenResponse | undefined {
|
||||||
const [state, setState] = useState<GenerateTokenResponse>();
|
const [state, setState] = useState<GenerateTokenResponse>();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
getDatabaseAccountPropertiesFromMetadata,
|
getDatabaseAccountPropertiesFromMetadata,
|
||||||
} from "../Platform/Hosted/HostedUtils";
|
} from "../Platform/Hosted/HostedUtils";
|
||||||
import { CollectionCreation } from "../Shared/Constants";
|
import { CollectionCreation } from "../Shared/Constants";
|
||||||
import * as DefaultExperienceUtility from "../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
|
||||||
import { PortalEnv, updateUserContext, userContext } from "../UserContext";
|
import { PortalEnv, updateUserContext, userContext } from "../UserContext";
|
||||||
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
||||||
|
|||||||
Reference in New Issue
Block a user