mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-05 18:47:41 +00:00
marge master
This commit is contained in:
@@ -73,7 +73,13 @@ describe("Add Collection Pane", () => {
|
||||
});
|
||||
|
||||
it("should be true for any non-graph API with /id or /label partition key", () => {
|
||||
updateUserContext({});
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableCassandra" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||
|
||||
addCollectionPane.partitionKey("/id");
|
||||
|
||||
@@ -127,13 +127,13 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
});
|
||||
this.partitionKey.extend({ rateLimit: 100 });
|
||||
this.partitionKeyPattern = ko.pureComputed(() => {
|
||||
if (this.container && this.container.isPreferredApiGraph()) {
|
||||
if (userContext.apiType === "Gremlin") {
|
||||
return "^/[^/]*";
|
||||
}
|
||||
return ".*";
|
||||
});
|
||||
this.partitionKeyTitle = ko.pureComputed(() => {
|
||||
if (this.container && this.container.isPreferredApiGraph()) {
|
||||
if (userContext.apiType === "Gremlin") {
|
||||
return "May not use composite partition key";
|
||||
}
|
||||
return "";
|
||||
@@ -331,7 +331,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
|
||||
if (currentCollections >= maxCollections) {
|
||||
let typeOfContainer = "collection";
|
||||
if (this.container.isPreferredApiGraph() || this.container.isPreferredApiTable()) {
|
||||
if (userContext.apiType === "Gremlin" || userContext.apiType === "Tables") {
|
||||
typeOfContainer = "container";
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return "e.g., address.zipCode";
|
||||
}
|
||||
|
||||
if (this.container && !!this.container.isPreferredApiGraph()) {
|
||||
if (userContext.apiType === "Gremlin") {
|
||||
return "e.g., /address";
|
||||
}
|
||||
|
||||
@@ -384,21 +384,15 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
});
|
||||
|
||||
this.uniqueKeysVisible = ko.pureComputed<boolean>(() => {
|
||||
if (
|
||||
this.container == null ||
|
||||
!!this.container.isPreferredApiMongoDB() ||
|
||||
!!this.container.isPreferredApiTable() ||
|
||||
!!this.container.isPreferredApiCassandra() ||
|
||||
!!this.container.isPreferredApiGraph()
|
||||
) {
|
||||
return false;
|
||||
if (userContext.apiType === "SQL") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
this.partitionKeyVisible = ko.computed<boolean>(() => {
|
||||
if (this.container == null || !!this.container.isPreferredApiTable()) {
|
||||
if (this.container == null || userContext.apiType === "Tables") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -599,7 +593,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.container.isPreferredApiCassandra() && this.container.hasStorageAnalyticsAfecFeature()) {
|
||||
if (userContext.apiType === "Cassandra" && this.container.hasStorageAnalyticsAfecFeature()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -763,7 +757,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!this.container.isPreferredApiTable()) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
// Table require fixed Database: TablesDB, and fixed Partition Key: /'$pk'
|
||||
this.databaseId(SharedConstants.CollectionCreation.TablesAPIDefaultDatabase);
|
||||
this.partitionKey("/'$pk'");
|
||||
@@ -923,8 +917,10 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
this.databaseId("");
|
||||
this.partitionKey("");
|
||||
this.throughputSpendAck(false);
|
||||
this.isAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
||||
this.isSharedAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
||||
if (!this.container.isServerlessEnabled()) {
|
||||
this.isAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
||||
this.isSharedAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
||||
}
|
||||
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||
|
||||
@@ -958,7 +954,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
}
|
||||
|
||||
public isNonTableApi = (): boolean => {
|
||||
return !this.container.isPreferredApiTable();
|
||||
return userContext.apiType !== "Tables";
|
||||
};
|
||||
|
||||
public isUnlimitedStorageSelected = (): boolean => {
|
||||
@@ -1011,7 +1007,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.container.isPreferredApiGraph() && (this.partitionKey() === "/id" || this.partitionKey() === "/label")) {
|
||||
if (userContext.apiType === "Gremlin" && (this.partitionKey() === "/id" || this.partitionKey() === "/label")) {
|
||||
this.formErrors("/id and /label as partition keys are not allowed for graph.");
|
||||
return false;
|
||||
}
|
||||
@@ -1032,7 +1028,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
|
||||
private _setFocus() {
|
||||
// Autofocus is enabled on AddCollectionPane based on the preferred API
|
||||
if (this.container.isPreferredApiTable()) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
const focusTableId = document.getElementById("containerId");
|
||||
focusTableId && focusTableId.focus();
|
||||
return;
|
||||
|
||||
@@ -62,21 +62,21 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
||||
this.databaseCreateNewShared = ko.observable<boolean>(this.getSharedThroughputDefault());
|
||||
|
||||
this.databaseIdLabel = ko.computed<string>(() =>
|
||||
this.container.isPreferredApiCassandra() ? "Keyspace id" : "Database id"
|
||||
userContext.apiType === "Cassandra" ? "Keyspace id" : "Database id"
|
||||
);
|
||||
|
||||
this.databaseIdPlaceHolder = ko.computed<string>(() =>
|
||||
this.container.isPreferredApiCassandra() ? "Type a new keyspace id" : "Type a new database id"
|
||||
userContext.apiType === "Cassandra" ? "Type a new keyspace id" : "Type a new database id"
|
||||
);
|
||||
|
||||
this.databaseIdTooltipText = ko.computed<string>(() => {
|
||||
const isCassandraAccount: boolean = this.container.isPreferredApiCassandra();
|
||||
const isCassandraAccount: boolean = userContext.apiType === "Cassandra";
|
||||
return `A ${isCassandraAccount ? "keyspace" : "database"} is a logical container of one or more ${
|
||||
isCassandraAccount ? "tables" : "collections"
|
||||
}`;
|
||||
});
|
||||
this.databaseLevelThroughputTooltipText = ko.computed<string>(() => {
|
||||
const isCassandraAccount: boolean = this.container.isPreferredApiCassandra();
|
||||
const isCassandraAccount: boolean = userContext.apiType === "Cassandra";
|
||||
const databaseLabel: string = isCassandraAccount ? "keyspace" : "database";
|
||||
const collectionsLabel: string = isCassandraAccount ? "tables" : "collections";
|
||||
return `Provisioned throughput at the ${databaseLabel} level will be shared across all ${collectionsLabel} within the ${databaseLabel}.`;
|
||||
|
||||
@@ -4,13 +4,13 @@ import React from "react";
|
||||
import { QueriesClient } from "../../../Common/QueriesClient";
|
||||
import { Query } from "../../../Contracts/DataModels";
|
||||
import Explorer from "../../Explorer";
|
||||
import { BrowseQueriesPanel } from "./index";
|
||||
import { BrowseQueriesPane } from "./BrowseQueriesPane";
|
||||
|
||||
describe("Browse queries panel", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
fakeExplorer.canSaveQueries = ko.computed<boolean>(() => true);
|
||||
const fakeClientQuery = {} as QueriesClient;
|
||||
const fakeQueryData = {} as Query[];
|
||||
const fakeQueryData = [] as Query[];
|
||||
fakeClientQuery.getQueries = async () => fakeQueryData;
|
||||
fakeExplorer.queriesClient = fakeClientQuery;
|
||||
const props = {
|
||||
@@ -19,12 +19,12 @@ describe("Browse queries panel", () => {
|
||||
};
|
||||
|
||||
it("Should render Default properly", () => {
|
||||
const wrapper = mount(<BrowseQueriesPanel {...props} />);
|
||||
const wrapper = mount(<BrowseQueriesPane {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("Should show empty view when query is empty []", () => {
|
||||
const wrapper = mount(<BrowseQueriesPanel {...props} />);
|
||||
const wrapper = mount(<BrowseQueriesPane {...props} />);
|
||||
expect(wrapper.exists("#emptyQueryBanner")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -13,15 +13,15 @@ import {
|
||||
import Explorer from "../../Explorer";
|
||||
import QueryTab from "../../Tabs/QueryTab";
|
||||
|
||||
interface BrowseQueriesPanelProps {
|
||||
interface BrowseQueriesPaneProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const BrowseQueriesPanel: FunctionComponent<BrowseQueriesPanelProps> = ({
|
||||
export const BrowseQueriesPane: FunctionComponent<BrowseQueriesPaneProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
}: BrowseQueriesPanelProps): JSX.Element => {
|
||||
}: BrowseQueriesPaneProps): JSX.Element => {
|
||||
const loadSavedQuery = (savedQuery: Query): void => {
|
||||
const selectedCollection: Collection = explorer && explorer.findSelectedCollection();
|
||||
if (!selectedCollection) {
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Browse queries panel Should render Default properly 1`] = `
|
||||
<BrowseQueriesPanel
|
||||
<BrowseQueriesPane
|
||||
closePanel={[Function]}
|
||||
explorer={
|
||||
Object {
|
||||
@@ -54,5 +54,5 @@ exports[`Browse queries panel Should render Default properly 1`] = `
|
||||
</QueriesGridComponent>
|
||||
</div>
|
||||
</div>
|
||||
</BrowseQueriesPanel>
|
||||
</BrowseQueriesPane>
|
||||
`;
|
||||
@@ -1,194 +0,0 @@
|
||||
import ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||
import { JunoClient, IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../Explorer";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "./GenericRightPaneComponent";
|
||||
import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent";
|
||||
import { IDropdownOption } from "office-ui-fabric-react";
|
||||
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
||||
import { HttpStatusCodes } from "../../Common/Constants";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { NotebookContentItemType, NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||
import { handleError, getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
|
||||
interface Location {
|
||||
type: "MyNotebooks" | "GitHub";
|
||||
|
||||
// GitHub
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
}
|
||||
|
||||
export class CopyNotebookPaneAdapter implements ReactAdapter {
|
||||
private static readonly BranchNameWhiteSpace = " ";
|
||||
|
||||
parameters: ko.Observable<number>;
|
||||
private isOpened: boolean;
|
||||
private isExecuting: boolean;
|
||||
private formError: string;
|
||||
private formErrorDetail: string;
|
||||
private name: string;
|
||||
private content: string;
|
||||
private pinnedRepos: IPinnedRepo[];
|
||||
private selectedLocation: Location;
|
||||
|
||||
constructor(
|
||||
private container: Explorer,
|
||||
private junoClient: JunoClient,
|
||||
private gitHubOAuthService: GitHubOAuthService
|
||||
) {
|
||||
this.parameters = ko.observable(Date.now());
|
||||
this.reset();
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
if (!this.isOpened) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: this.container,
|
||||
formError: this.formError,
|
||||
formErrorDetail: this.formErrorDetail,
|
||||
id: "copynotebookpane",
|
||||
isExecuting: this.isExecuting,
|
||||
title: "Copy notebook",
|
||||
submitButtonText: "OK",
|
||||
onClose: () => this.close(),
|
||||
onSubmit: () => this.submit(),
|
||||
};
|
||||
|
||||
const copyNotebookPaneProps: CopyNotebookPaneProps = {
|
||||
name: this.name,
|
||||
pinnedRepos: this.pinnedRepos,
|
||||
onDropDownChange: this.onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<GenericRightPaneComponent {...genericPaneProps}>
|
||||
<CopyNotebookPaneComponent {...copyNotebookPaneProps} />
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
}
|
||||
|
||||
public triggerRender(): void {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
||||
public async open(name: string, content: string): Promise<void> {
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
|
||||
this.isOpened = true;
|
||||
this.triggerRender();
|
||||
|
||||
if (this.gitHubOAuthService.isLoggedIn()) {
|
||||
const response = await this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit");
|
||||
}
|
||||
|
||||
if (response.data?.length > 0) {
|
||||
this.pinnedRepos = response.data;
|
||||
this.triggerRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.reset();
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public async submit(): Promise<void> {
|
||||
let destination: string = this.selectedLocation?.type;
|
||||
let clearMessage: () => void;
|
||||
this.isExecuting = true;
|
||||
this.triggerRender();
|
||||
|
||||
try {
|
||||
if (!this.selectedLocation) {
|
||||
throw new Error(`No location selected`);
|
||||
}
|
||||
|
||||
if (this.selectedLocation.type === "GitHub") {
|
||||
destination = `${destination} - ${GitHubUtils.toRepoFullName(
|
||||
this.selectedLocation.owner,
|
||||
this.selectedLocation.repo
|
||||
)} - ${this.selectedLocation.branch}`;
|
||||
}
|
||||
|
||||
clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${this.name} to ${destination}`);
|
||||
|
||||
const notebookContentItem = await this.copyNotebook(this.selectedLocation);
|
||||
if (!notebookContentItem) {
|
||||
throw new Error(`Failed to upload ${this.name}`);
|
||||
}
|
||||
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${this.name} to ${destination}`);
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
this.formError = `Failed to copy ${this.name} to ${destination}`;
|
||||
this.formErrorDetail = `${errorMessage}`;
|
||||
handleError(errorMessage, "CopyNotebookPaneAdapter/submit", this.formError);
|
||||
return;
|
||||
} finally {
|
||||
clearMessage && clearMessage();
|
||||
this.isExecuting = false;
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
private copyNotebook = async (location: Location): Promise<NotebookContentItem> => {
|
||||
let parent: NotebookContentItem;
|
||||
switch (location.type) {
|
||||
case "MyNotebooks":
|
||||
parent = {
|
||||
name: ResourceTreeAdapter.MyNotebooksTitle,
|
||||
path: this.container.getNotebookBasePath(),
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
break;
|
||||
|
||||
case "GitHub":
|
||||
parent = {
|
||||
name: ResourceTreeAdapter.GitHubReposTitle,
|
||||
path: GitHubUtils.toContentUri(
|
||||
this.selectedLocation.owner,
|
||||
this.selectedLocation.repo,
|
||||
this.selectedLocation.branch,
|
||||
""
|
||||
),
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported location type ${location.type}`);
|
||||
}
|
||||
|
||||
return this.container.uploadFile(this.name, this.content, parent);
|
||||
};
|
||||
|
||||
private onDropDownChange = (_: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
|
||||
this.selectedLocation = option?.data;
|
||||
};
|
||||
|
||||
private reset = (): void => {
|
||||
this.isOpened = false;
|
||||
this.isExecuting = false;
|
||||
this.formError = undefined;
|
||||
this.formErrorDetail = undefined;
|
||||
this.name = undefined;
|
||||
this.content = undefined;
|
||||
this.pinnedRepos = undefined;
|
||||
this.selectedLocation = undefined;
|
||||
};
|
||||
}
|
||||
156
src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx
Normal file
156
src/Explorer/Panes/CopyNotebookPane/CopyNotebookPane.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import { IDropdownOption } from "office-ui-fabric-react";
|
||||
import React, { FormEvent, FunctionComponent, useEffect, useState } from "react";
|
||||
import { HttpStatusCodes } from "../../../Common/Constants";
|
||||
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "../../Notebook/NotebookContentItem";
|
||||
import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
import { CopyNotebookPaneComponent, CopyNotebookPaneProps } from "./CopyNotebookPaneComponent";
|
||||
|
||||
interface Location {
|
||||
type: "MyNotebooks" | "GitHub";
|
||||
|
||||
// GitHub
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
}
|
||||
export interface CopyNotebookPanelProps {
|
||||
name: string;
|
||||
content: string;
|
||||
container: Explorer;
|
||||
junoClient: JunoClient;
|
||||
gitHubOAuthService: GitHubOAuthService;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||
name,
|
||||
content,
|
||||
container,
|
||||
junoClient,
|
||||
gitHubOAuthService,
|
||||
closePanel,
|
||||
}: CopyNotebookPanelProps) => {
|
||||
const [isExecuting, setIsExecuting] = useState<boolean>();
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorDetail, setFormErrorDetail] = useState<string>("");
|
||||
const [pinnedRepos, setPinnedRepos] = useState<IPinnedRepo[]>();
|
||||
const [selectedLocation, setSelectedLocation] = useState<Location>();
|
||||
|
||||
useEffect(() => {
|
||||
open();
|
||||
}, []);
|
||||
|
||||
const open = async (): Promise<void> => {
|
||||
if (gitHubOAuthService.isLoggedIn()) {
|
||||
const response = await junoClient.getPinnedRepos(gitHubOAuthService.getTokenObservable()()?.scope);
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit");
|
||||
}
|
||||
|
||||
if (response.data?.length > 0) {
|
||||
setPinnedRepos(response.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
let destination: string = selectedLocation?.type;
|
||||
let clearMessage: () => void;
|
||||
setIsExecuting(true);
|
||||
|
||||
try {
|
||||
if (!selectedLocation) {
|
||||
throw new Error(`No location selected`);
|
||||
}
|
||||
|
||||
if (selectedLocation.type === "GitHub") {
|
||||
destination = `${destination} - ${GitHubUtils.toRepoFullName(
|
||||
selectedLocation.owner,
|
||||
selectedLocation.repo
|
||||
)} - ${selectedLocation.branch}`;
|
||||
}
|
||||
|
||||
clearMessage = NotificationConsoleUtils.logConsoleProgress(`Copying ${name} to ${destination}`);
|
||||
|
||||
const notebookContentItem = await copyNotebook(selectedLocation);
|
||||
if (!notebookContentItem) {
|
||||
throw new Error(`Failed to upload ${name}`);
|
||||
}
|
||||
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${name} to ${destination}`);
|
||||
closePanel();
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
setFormError(`Failed to copy ${name} to ${destination}`);
|
||||
setFormErrorDetail(`${errorMessage}`);
|
||||
handleError(errorMessage, "CopyNotebookPaneAdapter/submit", formError);
|
||||
} finally {
|
||||
clearMessage && clearMessage();
|
||||
setIsExecuting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const copyNotebook = async (location: Location): Promise<NotebookContentItem> => {
|
||||
let parent: NotebookContentItem;
|
||||
switch (location.type) {
|
||||
case "MyNotebooks":
|
||||
parent = {
|
||||
name: ResourceTreeAdapter.MyNotebooksTitle,
|
||||
path: container.getNotebookBasePath(),
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
break;
|
||||
|
||||
case "GitHub":
|
||||
parent = {
|
||||
name: ResourceTreeAdapter.GitHubReposTitle,
|
||||
path: GitHubUtils.toContentUri(selectedLocation.owner, selectedLocation.repo, selectedLocation.branch, ""),
|
||||
type: NotebookContentItemType.Directory,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported location type ${location.type}`);
|
||||
}
|
||||
|
||||
return container.uploadFile(name, content, parent);
|
||||
};
|
||||
|
||||
const onDropDownChange = (_: FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
|
||||
setSelectedLocation(option?.data);
|
||||
};
|
||||
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container,
|
||||
formError,
|
||||
formErrorDetail,
|
||||
id: "copynotebookpane",
|
||||
isExecuting: isExecuting,
|
||||
title: "Copy notebook",
|
||||
submitButtonText: "OK",
|
||||
onClose: closePanel,
|
||||
onSubmit: () => submit(),
|
||||
};
|
||||
|
||||
const copyNotebookPaneProps: CopyNotebookPaneProps = {
|
||||
name,
|
||||
pinnedRepos,
|
||||
onDropDownChange: onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<GenericRightPaneComponent {...genericPaneProps}>
|
||||
<CopyNotebookPaneComponent {...copyNotebookPaneProps} />
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +1,18 @@
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import * as React from "react";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||
import {
|
||||
Stack,
|
||||
Label,
|
||||
Text,
|
||||
Dropdown,
|
||||
IDropdownProps,
|
||||
IDropdownOption,
|
||||
SelectableOptionMenuItemType,
|
||||
IDropdownProps,
|
||||
IRenderFunction,
|
||||
ISelectableOption,
|
||||
Label,
|
||||
SelectableOptionMenuItemType,
|
||||
Stack,
|
||||
Text,
|
||||
} from "office-ui-fabric-react";
|
||||
import React, { FormEvent, FunctionComponent } from "react";
|
||||
import { IPinnedRepo } from "../../../Juno/JunoClient";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import { ResourceTreeAdapter } from "../../Tree/ResourceTreeAdapter";
|
||||
|
||||
interface Location {
|
||||
type: "MyNotebooks" | "GitHub";
|
||||
@@ -26,46 +26,25 @@ interface Location {
|
||||
export interface CopyNotebookPaneProps {
|
||||
name: string;
|
||||
pinnedRepos: IPinnedRepo[];
|
||||
onDropDownChange: (_: React.FormEvent<HTMLDivElement>, option?: IDropdownOption) => void;
|
||||
onDropDownChange: (_: FormEvent<HTMLDivElement>, option?: IDropdownOption) => void;
|
||||
}
|
||||
|
||||
export class CopyNotebookPaneComponent extends React.Component<CopyNotebookPaneProps> {
|
||||
private static readonly BranchNameWhiteSpace = " ";
|
||||
export const CopyNotebookPaneComponent: FunctionComponent<CopyNotebookPaneProps> = ({
|
||||
name,
|
||||
pinnedRepos,
|
||||
onDropDownChange,
|
||||
}: CopyNotebookPaneProps) => {
|
||||
const BranchNameWhiteSpace = " ";
|
||||
|
||||
public render(): JSX.Element {
|
||||
const dropDownProps: IDropdownProps = {
|
||||
label: "Location",
|
||||
ariaLabel: "Location",
|
||||
placeholder: "Select an option",
|
||||
onRenderTitle: this.onRenderDropDownTitle,
|
||||
onRenderOption: this.onRenderDropDownOption,
|
||||
options: this.getDropDownOptions(),
|
||||
onChange: this.props.onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="paneMainContent">
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Stack.Item>
|
||||
<Label htmlFor="notebookName">Name</Label>
|
||||
<Text id="notebookName">{this.props.name}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Dropdown {...dropDownProps} />
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onRenderDropDownTitle: IRenderFunction<IDropdownOption[]> = (options: IDropdownOption[]): JSX.Element => {
|
||||
const onRenderDropDownTitle: IRenderFunction<IDropdownOption[]> = (options: IDropdownOption[]): JSX.Element => {
|
||||
return <span>{options.length && options[0].title}</span>;
|
||||
};
|
||||
|
||||
private onRenderDropDownOption: IRenderFunction<ISelectableOption> = (option: ISelectableOption): JSX.Element => {
|
||||
const onRenderDropDownOption: IRenderFunction<ISelectableOption> = (option: ISelectableOption): JSX.Element => {
|
||||
return <span style={{ whiteSpace: "pre-wrap" }}>{option.text}</span>;
|
||||
};
|
||||
|
||||
private getDropDownOptions = (): IDropdownOption[] => {
|
||||
const getDropDownOptions = (): IDropdownOption[] => {
|
||||
const options: IDropdownOption[] = [];
|
||||
|
||||
options.push({
|
||||
@@ -77,7 +56,7 @@ export class CopyNotebookPaneComponent extends React.Component<CopyNotebookPaneP
|
||||
} as Location,
|
||||
});
|
||||
|
||||
if (this.props.pinnedRepos && this.props.pinnedRepos.length > 0) {
|
||||
if (pinnedRepos && pinnedRepos.length > 0) {
|
||||
options.push({
|
||||
key: "GitHub-Header-Divider",
|
||||
text: undefined,
|
||||
@@ -90,7 +69,7 @@ export class CopyNotebookPaneComponent extends React.Component<CopyNotebookPaneP
|
||||
itemType: SelectableOptionMenuItemType.Header,
|
||||
});
|
||||
|
||||
this.props.pinnedRepos.forEach((pinnedRepo) => {
|
||||
pinnedRepos.forEach((pinnedRepo) => {
|
||||
const repoFullName = GitHubUtils.toRepoFullName(pinnedRepo.owner, pinnedRepo.name);
|
||||
options.push({
|
||||
key: `GitHub-Repo-${repoFullName}`,
|
||||
@@ -101,7 +80,7 @@ export class CopyNotebookPaneComponent extends React.Component<CopyNotebookPaneP
|
||||
pinnedRepo.branches.forEach((branch) =>
|
||||
options.push({
|
||||
key: `GitHub-Repo-${repoFullName}-${branch.name}`,
|
||||
text: `${CopyNotebookPaneComponent.BranchNameWhiteSpace}${branch.name}`,
|
||||
text: `${BranchNameWhiteSpace}${branch.name}`,
|
||||
title: `${repoFullName} - ${branch.name}`,
|
||||
data: {
|
||||
type: "GitHub",
|
||||
@@ -116,4 +95,26 @@ export class CopyNotebookPaneComponent extends React.Component<CopyNotebookPaneP
|
||||
|
||||
return options;
|
||||
};
|
||||
}
|
||||
const dropDownProps: IDropdownProps = {
|
||||
label: "Location",
|
||||
ariaLabel: "Location",
|
||||
placeholder: "Select an option",
|
||||
onRenderTitle: onRenderDropDownTitle,
|
||||
onRenderOption: onRenderDropDownOption,
|
||||
options: getDropDownOptions(),
|
||||
onChange: onDropDownChange,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="paneMainContent">
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Stack.Item>
|
||||
<Label htmlFor="notebookName">Name</Label>
|
||||
<Text id="notebookName">{name}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Dropdown {...dropDownProps} />
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,6 @@ jest.mock("../../../Shared/Telemetry/TelemetryProcessor");
|
||||
import { mount, ReactWrapper, shallow } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import { DeleteCollectionConfirmationPanel } from ".";
|
||||
import { deleteCollection } from "../../../Common/dataAccess/deleteCollection";
|
||||
import DeleteFeedback from "../../../Common/DeleteFeedback";
|
||||
import { ApiKind, DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
@@ -13,6 +12,7 @@ import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryCons
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import { DeleteCollectionConfirmationPane } from "./DeleteCollectionConfirmationPane";
|
||||
|
||||
describe("Delete Collection Confirmation Pane", () => {
|
||||
describe("Explorer.isLastCollection()", () => {
|
||||
@@ -65,7 +65,7 @@ describe("Delete Collection Confirmation Pane", () => {
|
||||
closePanel: (): void => undefined,
|
||||
collectionName: "container",
|
||||
};
|
||||
const wrapper = shallow(<DeleteCollectionConfirmationPanel {...props} />);
|
||||
const wrapper = shallow(<DeleteCollectionConfirmationPane {...props} />);
|
||||
expect(wrapper.exists(".deleteCollectionFeedback")).toBe(true);
|
||||
|
||||
props.explorer.isLastCollection = () => true;
|
||||
@@ -119,7 +119,7 @@ describe("Delete Collection Confirmation Pane", () => {
|
||||
closePanel: (): void => undefined,
|
||||
collectionName: "container",
|
||||
};
|
||||
wrapper = mount(<DeleteCollectionConfirmationPanel {...props} />);
|
||||
wrapper = mount(<DeleteCollectionConfirmationPane {...props} />);
|
||||
});
|
||||
|
||||
it("should call delete collection", () => {
|
||||
@@ -11,18 +11,21 @@ import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcesso
|
||||
import { userContext } from "../../../UserContext";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
export interface DeleteCollectionConfirmationPanelProps {
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
export interface DeleteCollectionConfirmationPaneProps {
|
||||
explorer: Explorer;
|
||||
collectionName: string;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const DeleteCollectionConfirmationPanel: FunctionComponent<DeleteCollectionConfirmationPanelProps> = ({
|
||||
export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectionConfirmationPaneProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
collectionName,
|
||||
}: DeleteCollectionConfirmationPanelProps) => {
|
||||
}: DeleteCollectionConfirmationPaneProps) => {
|
||||
const [deleteCollectionFeedback, setDeleteCollectionFeedback] = useState<string>("");
|
||||
const [inputCollectionName, setInputCollectionName] = useState<string>("");
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Delete Collection Confirmation Pane submit() should call delete collection 1`] = `
|
||||
<DeleteCollectionConfirmationPanel
|
||||
<DeleteCollectionConfirmationPane
|
||||
closePanel={[Function]}
|
||||
collectionName="container"
|
||||
explorer={
|
||||
@@ -3627,5 +3627,5 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
||||
</div>
|
||||
</div>
|
||||
</GenericRightPaneComponent>
|
||||
</DeleteCollectionConfirmationPanel>
|
||||
</DeleteCollectionConfirmationPane>
|
||||
`;
|
||||
@@ -2,7 +2,7 @@ import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { ExecuteSprocParamsPanel } from "./index";
|
||||
import { ExecuteSprocParamsPane } from "./ExecuteSprocParamsPane";
|
||||
|
||||
describe("Excute Sproc Param Pane", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
@@ -14,23 +14,23 @@ describe("Excute Sproc Param Pane", () => {
|
||||
};
|
||||
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPanel {...props} />);
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("initially display 2 input field, 1 partition and 1 parameter", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPanel {...props} />);
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("add a new parameter field", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPanel {...props} />);
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
wrapper.find("#addparam").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("remove a parameter field", () => {
|
||||
const wrapper = mount(<ExecuteSprocParamsPanel {...props} />);
|
||||
const wrapper = mount(<ExecuteSprocParamsPane {...props} />);
|
||||
wrapper.find("#deleteparam").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(1);
|
||||
});
|
||||
@@ -4,7 +4,10 @@ import React, { FunctionComponent, useState } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
import { InputParameter } from "./InputParameter";
|
||||
|
||||
interface ExecuteSprocParamsPaneProps {
|
||||
@@ -23,14 +26,14 @@ interface UnwrappedExecuteSprocParam {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const ExecuteSprocParamsPanel: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
|
||||
export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
|
||||
explorer,
|
||||
storedProcedure,
|
||||
closePanel,
|
||||
}: ExecuteSprocParamsPaneProps): JSX.Element => {
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [paramKeyValues, setParamKeyValues] = useState<UnwrappedExecuteSprocParam[]>([{ key: "string", text: "" }]);
|
||||
const [partitionValue, setPartitionValue] = useState<string>("");
|
||||
const [partitionValue, setPartitionValue] = useState<string>(); // Defaulting to undefined here is important. It is not the same partition key as ""
|
||||
const [selectedKey, setSelectedKey] = React.useState<IDropdownOption>({ key: "string", text: "" });
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorsDetails, setFormErrorsDetails] = useState<string>("");
|
||||
@@ -79,8 +82,15 @@ export const ExecuteSprocParamsPanel: FunctionComponent<ExecuteSprocParamsPanePr
|
||||
return;
|
||||
}
|
||||
setLoadingTrue();
|
||||
const sprocParams = wrappedSprocParams && wrappedSprocParams.map((sprocParam) => sprocParam.text);
|
||||
storedProcedure.execute(sprocParams, partitionValue);
|
||||
const sprocParams =
|
||||
wrappedSprocParams &&
|
||||
wrappedSprocParams.map((sprocParam) => {
|
||||
if (sprocParam.key === "custom") {
|
||||
return JSON.parse(sprocParam.text);
|
||||
}
|
||||
return sprocParam.text;
|
||||
});
|
||||
storedProcedure.execute(sprocParams, partitionKey === "custom" ? JSON.parse(partitionValue) : partitionValue);
|
||||
setLoadingFalse();
|
||||
closePanel();
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
<ExecuteSprocParamsPanel
|
||||
<ExecuteSprocParamsPane
|
||||
closePanel={[Function]}
|
||||
explorer={Object {}}
|
||||
storedProcedure={Object {}}
|
||||
@@ -1149,7 +1149,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
isAddRemoveVisible={false}
|
||||
onParamKeyChange={[Function]}
|
||||
onParamValueChange={[Function]}
|
||||
paramValue=""
|
||||
selectedKey="string"
|
||||
>
|
||||
<StyledLabelBase>
|
||||
@@ -2684,7 +2683,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
key=".0:$.1"
|
||||
label="Value"
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
>
|
||||
<TextFieldBase
|
||||
autoFocus={true}
|
||||
@@ -2969,7 +2967,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
}
|
||||
}
|
||||
validateOnLoad={true}
|
||||
value=""
|
||||
>
|
||||
<div
|
||||
className="ms-TextField root-70"
|
||||
@@ -8179,5 +8176,5 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</GenericRightPaneComponent>
|
||||
</ExecuteSprocParamsPanel>
|
||||
</ExecuteSprocParamsPane>
|
||||
`;
|
||||
@@ -1,60 +0,0 @@
|
||||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
||||
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
||||
<div class="contextual-pane" data-bind="attr: { id: id }">
|
||||
<!-- New Vertex form - Start -->
|
||||
<div class="contextual-pane-in">
|
||||
<form class="paneContentContainer" data-bind="submit: submit">
|
||||
<!-- New Vertex header - Start -->
|
||||
<div class="firstdivbg headerline">
|
||||
<span role="heading" aria-level="2">New Vertex</span>
|
||||
<div
|
||||
class="closeImg"
|
||||
role="button"
|
||||
aria-label="Close pane"
|
||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
||||
tabindex="0"
|
||||
>
|
||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- New Vertex header - End -->
|
||||
|
||||
<!-- New Vertex errors - Start -->
|
||||
<div
|
||||
aria-live="assertive"
|
||||
class="warningErrorContainer"
|
||||
data-bind="visible: formErrors() && formErrors() !== ''"
|
||||
>
|
||||
<div class="warningErrorContent">
|
||||
<span><img class="paneErrorIcon" src="/error_red.svg" alt="Error" /></span>
|
||||
<span class="warningErrorDetailsLinkContainer">
|
||||
<span class="formErrors" data-bind="text: formErrors, attr: { title: formErrors }"></span>
|
||||
<a
|
||||
class="errorLink"
|
||||
role="link"
|
||||
data-bind="visible: formErrorsDetails() && formErrorsDetails() !== '' , click: showErrorDetails, event: { keypress: onMoreDetailsKeyPress }"
|
||||
tabindex="0"
|
||||
>
|
||||
More details
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- New Vertex errors - End -->
|
||||
|
||||
<!-- New Vertex inputs - Start -->
|
||||
<div class="paneMainContent">
|
||||
<new-vertex-form
|
||||
class="newvertexContainer"
|
||||
params="{ newVertexData: tempVertexData, firstFieldHasFocus: firstFieldHasFocus, partitionKeyProperty: partitionKeyProperty }"
|
||||
></new-vertex-form>
|
||||
</div>
|
||||
<div class="paneFooter">
|
||||
<div class="leftpanel-okbut"><input type="submit" value="OK" class="btncreatecoll1" /></div>
|
||||
</div>
|
||||
<!-- New Vertex inputs - End -->
|
||||
</form>
|
||||
</div>
|
||||
<!-- New Vertex form - End -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,10 +0,0 @@
|
||||
@import "../../../less/Common/Constants";
|
||||
|
||||
.newvertexContainer {
|
||||
height:100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { LoadQueryPanel } from "./index";
|
||||
import { LoadQueryPane } from "./LoadQueryPane";
|
||||
|
||||
describe("Load Query Pane", () => {
|
||||
it("should render Default properly", () => {
|
||||
@@ -11,7 +11,7 @@ describe("Load Query Pane", () => {
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<LoadQueryPanel {...props} />);
|
||||
const wrapper = shallow(<LoadQueryPane {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -3,22 +3,25 @@ import { IImageProps, Image, ImageFit, Stack, TextField } from "office-ui-fabric
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import folderIcon from "../../../../images/folder_16x16.svg";
|
||||
import { logError } from "../../../Common/Logger";
|
||||
import { Collection } from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import QueryTab from "../../Tabs/QueryTab";
|
||||
import { Collection } from "..//../../Contracts/ViewModels";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
interface LoadQueryPanelProps {
|
||||
interface LoadQueryPaneProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const LoadQueryPanel: FunctionComponent<LoadQueryPanelProps> = ({
|
||||
export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
}: LoadQueryPanelProps): JSX.Element => {
|
||||
}: LoadQueryPaneProps): JSX.Element => {
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorsDetails, setFormErrorsDetails] = useState<string>("");
|
||||
@@ -1,65 +0,0 @@
|
||||
import * as ko from "knockout";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||
import { KeyCodes } from "../../Common/Constants";
|
||||
import Explorer from "../Explorer";
|
||||
|
||||
export default class NewVertexPane extends ContextualPaneBase {
|
||||
public container: Explorer;
|
||||
public visible: ko.Observable<boolean>;
|
||||
public formErrors: ko.Observable<string>;
|
||||
public formErrorsDetails: ko.Observable<string>;
|
||||
|
||||
// Graph style stuff
|
||||
public tempVertexData: ko.Observable<ViewModels.NewVertexData>; // vertex data being edited
|
||||
private onSubmitCreateCallback: (newVertexData: ViewModels.NewVertexData) => void;
|
||||
private partitionKeyProperty: ko.Observable<string>;
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
this.tempVertexData = ko.observable<ViewModels.NewVertexData>(null);
|
||||
this.partitionKeyProperty = ko.observable(null);
|
||||
this.resetData();
|
||||
}
|
||||
|
||||
public submit() {
|
||||
// Commit edited changes
|
||||
if (this.onSubmitCreateCallback != null) {
|
||||
this.onSubmitCreateCallback(this.tempVertexData());
|
||||
}
|
||||
|
||||
// this.close();
|
||||
}
|
||||
|
||||
public resetData() {
|
||||
super.resetData();
|
||||
|
||||
this.onSubmitCreateCallback = null;
|
||||
|
||||
this.tempVertexData({
|
||||
label: "",
|
||||
properties: <ViewModels.InputProperty[]>[],
|
||||
});
|
||||
this.partitionKeyProperty(null);
|
||||
}
|
||||
|
||||
public subscribeOnSubmitCreate(callback: (newVertexData: ViewModels.NewVertexData) => void): void {
|
||||
this.onSubmitCreateCallback = callback;
|
||||
}
|
||||
|
||||
public setPartitionKeyProperty(pKeyProp: string): void {
|
||||
this.partitionKeyProperty(pKeyProp);
|
||||
}
|
||||
|
||||
public onMoreDetailsKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
||||
this.showErrorDetails();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
public buildString = (prefix: string, index: number): string => {
|
||||
return `${prefix}${index}`;
|
||||
};
|
||||
}
|
||||
10
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.less
Normal file
10
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.less
Normal file
@@ -0,0 +1,10 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
.newvertexContainer {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
}
|
||||
78
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.test.tsx
Normal file
78
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.test.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { shallow, ShallowWrapper } from "enzyme";
|
||||
import React from "react";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NewVertexPanel } from "./NewVertexPanel";
|
||||
|
||||
describe("New Vertex Panel", () => {
|
||||
let fakeExplorer: Explorer;
|
||||
let wrapper: ShallowWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeExplorer = new Explorer();
|
||||
});
|
||||
|
||||
it("should render default property", () => {
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
partitionKeyPropertyProp: "",
|
||||
onSubmit: (): void => undefined,
|
||||
openNotificationConsole: (): void => undefined,
|
||||
};
|
||||
wrapper = shallow(<NewVertexPanel {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render button in footer", () => {
|
||||
const button = wrapper.find("PrimaryButton").first();
|
||||
expect(button).toBeDefined();
|
||||
});
|
||||
|
||||
it("should render form", () => {
|
||||
const form = wrapper.find("form").first();
|
||||
expect(form).toBeDefined();
|
||||
});
|
||||
|
||||
it("should call form submit method", () => {
|
||||
const onSubmitSpy = jest.fn();
|
||||
|
||||
const newWrapper = shallow(
|
||||
<NewVertexPanel
|
||||
explorer={fakeExplorer}
|
||||
partitionKeyPropertyProp={undefined}
|
||||
openNotificationConsole={(): void => undefined}
|
||||
onSubmit={onSubmitSpy}
|
||||
/>
|
||||
);
|
||||
//eslint-disable-next-line
|
||||
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });
|
||||
|
||||
expect(onSubmitSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call error and success scenario method", () => {
|
||||
const onSubmitSpy = jest.fn();
|
||||
const onErrorSpy = jest.fn();
|
||||
const onSuccessSpy = jest.fn();
|
||||
const fakeNewVertexData: ViewModels.NewVertexData = {
|
||||
label: "",
|
||||
properties: [],
|
||||
};
|
||||
|
||||
const result = onSubmitSpy(fakeNewVertexData, onErrorSpy, onSuccessSpy);
|
||||
|
||||
const newWrapper = shallow(
|
||||
<NewVertexPanel
|
||||
explorer={fakeExplorer}
|
||||
partitionKeyPropertyProp={undefined}
|
||||
openNotificationConsole={(): void => undefined}
|
||||
onSubmit={onSubmitSpy}
|
||||
/>
|
||||
);
|
||||
//eslint-disable-next-line
|
||||
newWrapper.find("form").simulate("submit", { preventDefault: () => {} });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(onSubmitSpy).toHaveBeenCalledWith(fakeNewVertexData, onErrorSpy, onSuccessSpy);
|
||||
});
|
||||
});
|
||||
74
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx
Normal file
74
src/Explorer/Panes/NewVertexPanel/NewVertexPanel.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent";
|
||||
import { PanelFooterComponent } from "../PanelFooterComponent";
|
||||
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
||||
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
||||
export interface INewVertexPanelProps {
|
||||
explorer: Explorer;
|
||||
partitionKeyPropertyProp: string;
|
||||
onSubmit: (result: ViewModels.NewVertexData, onError: (errorMsg: string) => void, onSuccess: () => void) => void;
|
||||
openNotificationConsole: () => void;
|
||||
}
|
||||
|
||||
export const NewVertexPanel: FunctionComponent<INewVertexPanelProps> = ({
|
||||
explorer,
|
||||
partitionKeyPropertyProp,
|
||||
onSubmit,
|
||||
openNotificationConsole,
|
||||
}: INewVertexPanelProps): JSX.Element => {
|
||||
let newVertexDataValue: ViewModels.NewVertexData;
|
||||
const [errorMessage, setErrorMessage] = useState<string>("");
|
||||
const [showErrorDetails, setShowErrorDetails] = useState<boolean>(false);
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const buttonLabel = "OK";
|
||||
|
||||
const submit = (event: React.MouseEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
setErrorMessage(undefined);
|
||||
setShowErrorDetails(false);
|
||||
if (onSubmit !== undefined) {
|
||||
setLoadingTrue();
|
||||
onSubmit(newVertexDataValue, onError, onSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
const onError = (errorMsg: string) => {
|
||||
setErrorMessage(errorMsg);
|
||||
setShowErrorDetails(true);
|
||||
setLoadingFalse();
|
||||
};
|
||||
|
||||
const onSuccess = () => {
|
||||
setLoadingFalse();
|
||||
explorer.closeSidePanel();
|
||||
};
|
||||
|
||||
const onChange = (newVertexData: ViewModels.NewVertexData) => {
|
||||
newVertexDataValue = newVertexData;
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="panelFormWrapper" onSubmit={(event: React.MouseEvent<HTMLFormElement>) => submit(event)}>
|
||||
{errorMessage && (
|
||||
<PanelInfoErrorComponent
|
||||
message={errorMessage}
|
||||
messageType="error"
|
||||
showErrorDetails={showErrorDetails}
|
||||
openNotificationConsole={openNotificationConsole}
|
||||
/>
|
||||
)}
|
||||
<div className="panelMainContent">
|
||||
<NewVertexComponent
|
||||
newVertexDataProp={newVertexDataValue}
|
||||
partitionKeyPropertyProp={partitionKeyPropertyProp}
|
||||
onChangeProp={onChange}
|
||||
/>
|
||||
</div>
|
||||
<PanelFooterComponent buttonLabel={buttonLabel} />
|
||||
{isLoading && <PanelLoadingScreen />}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`New Vertex Panel should render default property 1`] = `
|
||||
<form
|
||||
className="panelFormWrapper"
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
<div
|
||||
className="panelMainContent"
|
||||
>
|
||||
<NewVertexComponent
|
||||
onChangeProp={[Function]}
|
||||
partitionKeyPropertyProp=""
|
||||
/>
|
||||
</div>
|
||||
<PanelFooterComponent
|
||||
buttonLabel="OK"
|
||||
/>
|
||||
</form>
|
||||
`;
|
||||
@@ -2,9 +2,7 @@ import AddCollectionPaneTemplate from "./AddCollectionPane.html";
|
||||
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
||||
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
||||
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
||||
import GraphNewVertexPaneTemplate from "./GraphNewVertexPane.html";
|
||||
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
||||
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
||||
import StringInputPaneTemplate from "./StringInputPane.html";
|
||||
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
|
||||
import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html";
|
||||
@@ -33,15 +31,6 @@ export class AddCollectionPaneComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export class GraphNewVertexPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
viewModel: PaneComponent,
|
||||
template: GraphNewVertexPaneTemplate,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class GraphStylingPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
@@ -86,15 +75,6 @@ export class StringInputPaneComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export class SetupNotebooksPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
viewModel: PaneComponent,
|
||||
template: SetupNotebooksPaneTemplate,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class GitHubReposPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
|
||||
@@ -152,6 +152,21 @@
|
||||
.removeIcon {
|
||||
color: @InfoIconColor;
|
||||
}
|
||||
.backImageIcon {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.entityValueTextField {
|
||||
margin: 24px;
|
||||
}
|
||||
.addEntityDatePicker {
|
||||
max-width: 145px;
|
||||
}
|
||||
.addEntityTextField {
|
||||
width: 237px;
|
||||
}
|
||||
.addButtonEntiy {
|
||||
width: 25%;
|
||||
}
|
||||
.column-select-view {
|
||||
margin: 20px 0px 0px 0px;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IPanelProps, IRenderFunction, Panel, PanelType } from "office-ui-fabric-react";
|
||||
import * as React from "react";
|
||||
import { Panel, PanelType } from "office-ui-fabric-react";
|
||||
|
||||
export interface PanelContainerProps {
|
||||
headerText: string;
|
||||
@@ -7,6 +7,8 @@ export interface PanelContainerProps {
|
||||
isConsoleExpanded: boolean;
|
||||
isOpen: boolean;
|
||||
closePanel: () => void;
|
||||
panelWidth?: string;
|
||||
onRenderNavigationContent?: IRenderFunction<IPanelProps>;
|
||||
}
|
||||
|
||||
export interface PanelContainerState {
|
||||
@@ -46,8 +48,9 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
||||
isLightDismiss
|
||||
type={PanelType.custom}
|
||||
closeButtonAriaLabel="Close"
|
||||
customWidth="440px"
|
||||
customWidth={this.props.panelWidth ? this.props.panelWidth : "440px"}
|
||||
headerClassName="panelHeader"
|
||||
onRenderNavigationContent={this.props.onRenderNavigationContent}
|
||||
styles={{
|
||||
navigation: { borderBottom: "1px solid #cccccc" },
|
||||
content: { padding: 0, height: "100%" },
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import { toJS } from "@nteract/commutable";
|
||||
import { ImmutableNotebook } from "@nteract/commutable/src";
|
||||
import ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||
import Explorer from "../Explorer";
|
||||
import { JunoClient } from "../../Juno/JunoClient";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "./GenericRightPaneComponent";
|
||||
import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./PublishNotebookPaneComponent";
|
||||
import { ImmutableNotebook } from "@nteract/commutable/src";
|
||||
import { toJS } from "@nteract/commutable";
|
||||
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
||||
import { HttpStatusCodes } from "../../Common/Constants";
|
||||
import { handleError, getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||
import { GalleryTab } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "../../Common/ErrorHandlingUtils";
|
||||
import { JunoClient } from "../../Juno/JunoClient";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
||||
import { GalleryTab } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import Explorer from "../Explorer";
|
||||
import * as FileSystemUtil from "../Notebook/FileSystemUtil";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "./GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./PublishNotebookPaneComponent";
|
||||
|
||||
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||
parameters: ko.Observable<number>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { shallow } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { SaveQueryPanel } from "./index";
|
||||
import { SaveQueryPane } from "./SaveQueryPane";
|
||||
|
||||
describe("Save Query Pane", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
@@ -13,7 +13,7 @@ describe("Save Query Pane", () => {
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<SaveQueryPanel {...props} />);
|
||||
const wrapper = shallow(<SaveQueryPane {...props} />);
|
||||
|
||||
it("should return true if can save Queries else false", () => {
|
||||
fakeExplorer.canSaveQueries = ko.computed<boolean>(() => true);
|
||||
@@ -26,7 +26,7 @@ describe("Save Query Pane", () => {
|
||||
});
|
||||
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = shallow(<SaveQueryPanel {...props} />);
|
||||
const wrapper = shallow(<SaveQueryPane {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -9,17 +9,20 @@ import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetr
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import QueryTab from "../../Tabs/QueryTab";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
interface SaveQueryPanelProps {
|
||||
interface SaveQueryPaneProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
export const SaveQueryPanel: FunctionComponent<SaveQueryPanelProps> = ({
|
||||
export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
}: SaveQueryPanelProps): JSX.Element => {
|
||||
}: SaveQueryPaneProps): JSX.Element => {
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorsDetails, setFormErrorsDetails] = useState<string>("");
|
||||
@@ -1,9 +1,9 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { SettingsPane } from ".";
|
||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import { SettingsPane } from "./SettingsPane";
|
||||
const props = {
|
||||
explorer: new Explorer(),
|
||||
closePanel: (): void => undefined,
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, MouseEvent, useState } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { Tooltip } from "../../../Common/Tooltip";
|
||||
import { Tooltip } from "../../../Common/Tooltip/Tooltip";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
||||
import * as StringUtility from "../../../Shared/StringUtility";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleInfo } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
export interface SettingsPaneProps {
|
||||
explorer: Explorer;
|
||||
@@ -153,42 +153,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
@@ -223,22 +187,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -300,20 +248,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -443,42 +377,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"visible": [Function],
|
||||
},
|
||||
"addDatabaseText": [Function],
|
||||
"addTableEntityPane": AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"arcadiaToken": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
@@ -614,7 +512,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"hasStorageAnalyticsAfecFeature": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isAutoscaleDefaultEnabled": [Function],
|
||||
"isCopyNotebookPaneEnabled": [Function],
|
||||
"isEnableMongoCapabilityPresent": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isGitHubPaneEnabled": [Function],
|
||||
@@ -623,10 +520,7 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiCassandra": [Function],
|
||||
"isPreferredApiGraph": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -637,22 +531,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
@@ -700,20 +578,6 @@ exports[`Settings Pane should render Default properly 1`] = `
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"setupNotebooksPane": SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"signInAad": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
@@ -1040,42 +904,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
@@ -1110,22 +938,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -1187,20 +999,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -1330,42 +1128,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"visible": [Function],
|
||||
},
|
||||
"addDatabaseText": [Function],
|
||||
"addTableEntityPane": AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"arcadiaToken": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
@@ -1501,7 +1263,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"hasStorageAnalyticsAfecFeature": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isAutoscaleDefaultEnabled": [Function],
|
||||
"isCopyNotebookPaneEnabled": [Function],
|
||||
"isEnableMongoCapabilityPresent": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isGitHubPaneEnabled": [Function],
|
||||
@@ -1510,10 +1271,7 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiCassandra": [Function],
|
||||
"isPreferredApiGraph": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -1524,22 +1282,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
@@ -1587,20 +1329,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"setupNotebooksPane": SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"signInAad": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
@@ -1,45 +0,0 @@
|
||||
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
||||
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
||||
<div class="contextual-pane" id="setupnotebookspane">
|
||||
<!-- Setup notebooks form -- Start -->
|
||||
<div class="contextual-pane-in">
|
||||
<div class="paneContentContainer">
|
||||
<!-- Setup notebooks header - Start -->
|
||||
<div class="firstdivbg headerline">
|
||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||
<div
|
||||
class="closeImg"
|
||||
role="button"
|
||||
aria-label="Close pane"
|
||||
tabindex="0"
|
||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
||||
>
|
||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Setup notebooks header - End -->
|
||||
|
||||
<div class="paneMainContent">
|
||||
<div class="pkPadding">
|
||||
<div data-bind="text: description"></div>
|
||||
<button
|
||||
id="completeSetupBtn"
|
||||
class="btncreatecoll1 btnSetupQueries"
|
||||
type="button"
|
||||
aria-label="Complete setup"
|
||||
data-bind="click: onCompleteSetupClick, event: { keypress: onCompleteSetupKeyPress }"
|
||||
>
|
||||
Complete setup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Setup notebooks form - Start -->
|
||||
<!-- Loader - Start -->
|
||||
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
||||
<img class="dataExplorerLoader" alt="loading indicator image" src="/LoadingIndicator_3Squares.gif" />
|
||||
</div>
|
||||
<!-- Loader - End -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,107 +0,0 @@
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import { Areas, KeyCodes } from "../../Common/Constants";
|
||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import * as ko from "knockout";
|
||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||
|
||||
export class SetupNotebooksPane extends ContextualPaneBase {
|
||||
private description: ko.Observable<string>;
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
|
||||
this.description = ko.observable<string>();
|
||||
this.resetData();
|
||||
}
|
||||
|
||||
public openWithTitleAndDescription(title: string, description: string) {
|
||||
this.title(title);
|
||||
this.description(description);
|
||||
|
||||
this.open();
|
||||
}
|
||||
|
||||
public open() {
|
||||
super.open();
|
||||
const completeSetupBtn = document.getElementById("completeSetupBtn");
|
||||
completeSetupBtn && completeSetupBtn.focus();
|
||||
}
|
||||
|
||||
public submit() {
|
||||
// override default behavior because this is not a form
|
||||
}
|
||||
|
||||
public onCompleteSetupClick = async (src: any, event: MouseEvent) => {
|
||||
await this.setupNotebookWorkspace();
|
||||
};
|
||||
|
||||
public onCompleteSetupKeyPress = async (src: any, event: KeyboardEvent) => {
|
||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
||||
await this.setupNotebookWorkspace();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
public async setupNotebookWorkspace(): Promise<void> {
|
||||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateNotebookWorkspace, {
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: this.title(),
|
||||
});
|
||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.InProgress,
|
||||
"Creating a new default notebook workspace"
|
||||
);
|
||||
try {
|
||||
this.isExecuting(true);
|
||||
await this.container.notebookWorkspaceManager.createNotebookWorkspaceAsync(
|
||||
this.container.databaseAccount() && this.container.databaseAccount().id,
|
||||
"default"
|
||||
);
|
||||
this.container.isAccountReady.valueHasMutated(); // re-trigger init notebooks
|
||||
this.close();
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.CreateNotebookWorkspace,
|
||||
{
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: this.title(),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Info,
|
||||
"Successfully created a default notebook workspace for the account"
|
||||
);
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateNotebookWorkspace,
|
||||
{
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: this.title(),
|
||||
error: errorMessage,
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
this.formErrors("Failed to setup a default notebook workspace");
|
||||
this.formErrorsDetails(`Failed to setup a default notebook workspace: ${errorMessage}`);
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`Failed to create a default notebook workspace: ${errorMessage}`
|
||||
);
|
||||
} finally {
|
||||
this.isExecuting(false);
|
||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { mount } from "enzyme";
|
||||
import { PrimaryButton } from "office-ui-fabric-react";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import { SetupNoteBooksPanel } from "./SetupNotebooksPanel";
|
||||
|
||||
describe("Setup Notebooks Panel", () => {
|
||||
it("should render Default properly", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
openNotificationConsole: (): void => undefined,
|
||||
panelTitle: "",
|
||||
panelDescription: "",
|
||||
};
|
||||
const wrapper = mount(<SetupNoteBooksPanel {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render button", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
openNotificationConsole: (): void => undefined,
|
||||
panelTitle: "",
|
||||
panelDescription: "",
|
||||
};
|
||||
const wrapper = mount(<SetupNoteBooksPanel {...props} />);
|
||||
const button = wrapper.find("PrimaryButton").first();
|
||||
expect(button).toBeDefined();
|
||||
});
|
||||
|
||||
it("Button onClick should call onCompleteSetup", () => {
|
||||
const onCompleteSetupClick = jest.fn();
|
||||
const wrapper = mount(<PrimaryButton onClick={onCompleteSetupClick} />);
|
||||
wrapper.find("button").simulate("click");
|
||||
|
||||
expect(onCompleteSetupClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Button onKeyPress should call onCompleteSetupKeyPress", () => {
|
||||
const onCompleteSetupKeyPress = jest.fn();
|
||||
const wrapper = mount(<PrimaryButton onKeyPress={onCompleteSetupKeyPress} />);
|
||||
wrapper.find("button").simulate("keypress");
|
||||
|
||||
expect(onCompleteSetupKeyPress).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
125
src/Explorer/Panes/SetupNotebooksPanel/SetupNotebooksPanel.tsx
Normal file
125
src/Explorer/Panes/SetupNotebooksPanel/SetupNotebooksPanel.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import { PrimaryButton } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, KeyboardEvent, useState } from "react";
|
||||
import { Areas, NormalizedEventKey } from "../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
||||
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
||||
interface SetupNoteBooksPanelProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
openNotificationConsole: () => void;
|
||||
panelTitle: string;
|
||||
panelDescription: string;
|
||||
}
|
||||
|
||||
export const SetupNoteBooksPanel: FunctionComponent<SetupNoteBooksPanelProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
openNotificationConsole,
|
||||
panelTitle,
|
||||
panelDescription,
|
||||
}: SetupNoteBooksPanelProps): JSX.Element => {
|
||||
const title = panelTitle;
|
||||
const description = panelDescription;
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string>("");
|
||||
const [showErrorDetails, setShowErrorDetails] = useState<boolean>(false);
|
||||
|
||||
const onCompleteSetupClick = async () => {
|
||||
await setupNotebookWorkspace();
|
||||
};
|
||||
|
||||
const onCompleteSetupKeyPress = async (event: KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (event.key === " " || event.key === NormalizedEventKey.Enter) {
|
||||
await setupNotebookWorkspace();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const setupNotebookWorkspace = async (): Promise<void> => {
|
||||
if (!explorer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateNotebookWorkspace, {
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: title,
|
||||
});
|
||||
|
||||
const clear = NotificationConsoleUtils.logConsoleProgress("Creating a new default notebook workspace");
|
||||
|
||||
try {
|
||||
setLoadingTrue();
|
||||
await explorer.notebookWorkspaceManager.createNotebookWorkspaceAsync(
|
||||
userContext.databaseAccount && userContext.databaseAccount.id,
|
||||
"default"
|
||||
);
|
||||
explorer.isAccountReady.valueHasMutated(); // re-trigger init notebooks
|
||||
|
||||
closePanel();
|
||||
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.CreateNotebookWorkspace,
|
||||
{
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: title,
|
||||
},
|
||||
startKey
|
||||
);
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully created a default notebook workspace for the account");
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateNotebookWorkspace,
|
||||
{
|
||||
dataExplorerArea: Areas.ContextualPane,
|
||||
paneTitle: title,
|
||||
error: errorMessage,
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
setErrorMessage(`Failed to setup a default notebook workspace: ${errorMessage}`);
|
||||
setShowErrorDetails(true);
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to create a default notebook workspace: ${errorMessage}`);
|
||||
} finally {
|
||||
setLoadingFalse();
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="panelFormWrapper">
|
||||
{errorMessage && (
|
||||
<PanelInfoErrorComponent
|
||||
message={errorMessage}
|
||||
messageType="error"
|
||||
showErrorDetails={showErrorDetails}
|
||||
openNotificationConsole={openNotificationConsole}
|
||||
/>
|
||||
)}
|
||||
<div className="panelMainContent">
|
||||
<div className="pkPadding">
|
||||
<div>{description}</div>
|
||||
<PrimaryButton
|
||||
id="completeSetupBtn"
|
||||
className="btncreatecoll1 btnSetupQueries"
|
||||
text="Complete Setup"
|
||||
onClick={onCompleteSetupClick}
|
||||
onKeyPress={onCompleteSetupKeyPress}
|
||||
aria-label="Complete setup"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{isLoading && <PanelLoadingScreen />}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,8 @@
|
||||
import * as ko from "knockout";
|
||||
import Q from "q";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||
|
||||
export interface StringInputPaneOpenOptions {
|
||||
paneTitle: string;
|
||||
@@ -39,19 +38,13 @@ export class StringInputPane extends ContextualPaneBase {
|
||||
this.formErrors("");
|
||||
this.formErrorsDetails("");
|
||||
|
||||
const id: string = NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.InProgress,
|
||||
`${this.openOptions.inProgressMessage} ${this.stringInput()}`
|
||||
);
|
||||
const clearInProgressMessage = logConsoleProgress(`${this.openOptions.inProgressMessage} ${this.stringInput()}`);
|
||||
this.isExecuting(true);
|
||||
this.openOptions
|
||||
.onSubmit(this.stringInput())
|
||||
.then(
|
||||
(value: any) => {
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Info,
|
||||
`${this.openOptions.successMessage}: ${this.stringInput()}`
|
||||
);
|
||||
logConsoleInfo(`${this.openOptions.successMessage}: ${this.stringInput()}`);
|
||||
this.close();
|
||||
this.paneDeferred.resolve(value);
|
||||
},
|
||||
@@ -70,16 +63,13 @@ export class StringInputPane extends ContextualPaneBase {
|
||||
|
||||
this.formErrors(this.openOptions.errorMessage);
|
||||
this.formErrorsDetails(`${this.openOptions.errorMessage}: ${error}`);
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`${this.openOptions.errorMessage} ${this.stringInput()}: ${error}`
|
||||
);
|
||||
logConsoleError(`${this.openOptions.errorMessage} ${this.stringInput()}: ${error}`);
|
||||
this.paneDeferred.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
this.isExecuting(false);
|
||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
||||
clearInProgressMessage();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
import * as ko from "knockout";
|
||||
import * as _ from "underscore";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { CassandraTableKey, CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import * as TableConstants from "../../Tables/Constants";
|
||||
import * as Utilities from "../../Tables/Utilities";
|
||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
||||
import TableEntityPane from "./TableEntityPane";
|
||||
|
||||
export default class AddTableEntityPane extends TableEntityPane {
|
||||
private static _excludedFields: string[] = [TableConstants.EntityKeyNames.Timestamp];
|
||||
|
||||
private static _readonlyFields: string[] = [
|
||||
TableConstants.EntityKeyNames.PartitionKey,
|
||||
TableConstants.EntityKeyNames.RowKey,
|
||||
TableConstants.EntityKeyNames.Timestamp,
|
||||
];
|
||||
|
||||
public enterRequiredValueLabel = "Enter identifier value."; // localize
|
||||
public enterValueLabel = "Enter value to keep property."; // localize
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
this.submitButtonText("Add Entity");
|
||||
this.container.isPreferredApiCassandra.subscribe((isCassandra) => {
|
||||
if (isCassandra) {
|
||||
this.submitButtonText("Add Row");
|
||||
}
|
||||
});
|
||||
this.scrollId = ko.observable<string>("addEntityScroll");
|
||||
}
|
||||
|
||||
public submit() {
|
||||
if (!this.canApply()) {
|
||||
return;
|
||||
}
|
||||
let entity: Entities.ITableEntity = this.entityFromAttributes(this.displayedAttributes());
|
||||
this.container.tableDataClient
|
||||
.createDocument(this.tableViewModel.queryTablesTab.collection, entity)
|
||||
.then((newEntity: Entities.ITableEntity) => {
|
||||
this.tableViewModel.addEntityToCache(newEntity).then(() => {
|
||||
if (!this.tryInsertNewHeaders(this.tableViewModel, newEntity)) {
|
||||
this.tableViewModel.redrawTableThrottled();
|
||||
}
|
||||
});
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
public open() {
|
||||
var headers = this.tableViewModel.headers;
|
||||
if (DataTableUtilities.checkForDefaultHeader(headers)) {
|
||||
headers = [];
|
||||
if (this.container.isPreferredApiTable()) {
|
||||
headers = [TableConstants.EntityKeyNames.PartitionKey, TableConstants.EntityKeyNames.RowKey];
|
||||
}
|
||||
}
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
(<CassandraAPIDataClient>this.container.tableDataClient)
|
||||
.getTableSchema(this.tableViewModel.queryTablesTab.collection)
|
||||
.then((columns: CassandraTableKey[]) => {
|
||||
this.displayedAttributes(
|
||||
this.constructDisplayedAttributes(
|
||||
columns.map((col) => col.property),
|
||||
Utilities.getDataTypesFromCassandraSchema(columns)
|
||||
)
|
||||
);
|
||||
this.updateIsActionEnabled();
|
||||
super.open();
|
||||
this.focusValueElement();
|
||||
});
|
||||
} else {
|
||||
this.displayedAttributes(
|
||||
this.constructDisplayedAttributes(
|
||||
headers,
|
||||
Utilities.getDataTypesFromEntities(headers, this.tableViewModel.items())
|
||||
)
|
||||
);
|
||||
this.updateIsActionEnabled();
|
||||
super.open();
|
||||
this.focusValueElement();
|
||||
}
|
||||
}
|
||||
|
||||
private focusValueElement() {
|
||||
const focusElement = document.getElementById("addTableEntityValue");
|
||||
focusElement && focusElement.focus();
|
||||
}
|
||||
|
||||
private constructDisplayedAttributes(headers: string[], dataTypes: any): EntityPropertyViewModel[] {
|
||||
var displayedAttributes: EntityPropertyViewModel[] = [];
|
||||
headers &&
|
||||
headers.forEach((key: string) => {
|
||||
if (!_.contains<string>(AddTableEntityPane._excludedFields, key)) {
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
const cassandraKeys = this.tableViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
|
||||
.concat(this.tableViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
|
||||
.map((key) => key.property);
|
||||
var isRequired: boolean = _.contains<string>(cassandraKeys, key);
|
||||
var editable: boolean = false;
|
||||
var placeholderLabel: string = isRequired ? this.enterRequiredValueLabel : this.enterValueLabel;
|
||||
var entityAttributeType: string = dataTypes[key] || TableConstants.CassandraType.Text; // Default to String if there is no type specified.
|
||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
||||
const nonEditableType: boolean =
|
||||
entityAttributeType === TableConstants.CassandraType.Blob ||
|
||||
entityAttributeType === TableConstants.CassandraType.Inet;
|
||||
var entity: EntityPropertyViewModel = new EntityPropertyViewModel(
|
||||
this,
|
||||
key,
|
||||
entityAttributeType,
|
||||
"", // default to empty string
|
||||
/* namePlaceholder */ undefined,
|
||||
nonEditableType ? "Type is not editable via DataExplorer." : placeholderLabel,
|
||||
editable,
|
||||
/* default valid name */ true,
|
||||
/* default valid value */ true,
|
||||
/* required value */ isRequired,
|
||||
/* removable */ false,
|
||||
/* valueEditable */ !nonEditableType,
|
||||
/* ignoreEmptyValue */ true
|
||||
);
|
||||
} else {
|
||||
var isRequired: boolean = _.contains<string>(AddTableEntityPane.requiredFieldsForTablesAPI, key);
|
||||
var editable: boolean = !_.contains<string>(AddTableEntityPane._readonlyFields, key);
|
||||
var placeholderLabel: string = isRequired ? this.enterRequiredValueLabel : this.enterValueLabel;
|
||||
var entityAttributeType: string = dataTypes[key] || TableConstants.TableType.String; // Default to String if there is no type specified.
|
||||
var entity: EntityPropertyViewModel = new EntityPropertyViewModel(
|
||||
this,
|
||||
key,
|
||||
entityAttributeType,
|
||||
"", // default to empty string
|
||||
/* namePlaceholder */ undefined,
|
||||
placeholderLabel,
|
||||
editable,
|
||||
/* default valid name */ true,
|
||||
/* default valid value */ true,
|
||||
/* required value */ isRequired,
|
||||
/* removable */ editable,
|
||||
/* valueEditable */ true,
|
||||
/* ignoreEmptyValue */ true
|
||||
);
|
||||
}
|
||||
displayedAttributes.push(entity);
|
||||
}
|
||||
});
|
||||
|
||||
return displayedAttributes;
|
||||
}
|
||||
}
|
||||
49
src/Explorer/Panes/Tables/AddTableEntityPanel.test.tsx
Normal file
49
src/Explorer/Panes/Tables/AddTableEntityPanel.test.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { mount } from "enzyme";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import TableListViewModal from "../../Tables/DataTable/TableEntityListViewModel";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import { CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
||||
import { AddTableEntityPanel } from "./AddTableEntityPanel";
|
||||
|
||||
describe("Excute Add Table Entity Pane", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const fakeQueryTablesTab = {} as QueryTablesTab;
|
||||
const fakeTableEntityListViewModel = {} as TableListViewModal;
|
||||
const fakeCassandraApiClient = {} as CassandraAPIDataClient;
|
||||
fakeTableEntityListViewModel.items = ko.observableArray<Entities.ITableEntity>();
|
||||
fakeTableEntityListViewModel.headers = [];
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
closePanel: (): void => undefined,
|
||||
queryTablesTab: fakeQueryTablesTab,
|
||||
tableEntityListViewModel: fakeTableEntityListViewModel,
|
||||
cassandraApiClient: fakeCassandraApiClient,
|
||||
};
|
||||
|
||||
it("should render Default properly", () => {
|
||||
const wrapper = mount(<AddTableEntityPanel {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("initially display 4 input field, 2 properties and 2 entity values", () => {
|
||||
const wrapper = mount(<AddTableEntityPanel {...props} />);
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("add a new entity row", () => {
|
||||
const wrapper = mount(<AddTableEntityPanel {...props} />);
|
||||
wrapper.find(".addButtonEntiy").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("remove a entity field", () => {
|
||||
const wrapper = mount(<AddTableEntityPanel {...props} />);
|
||||
// Since default entity row doesn't have delete option, so added row then delete for test cases.
|
||||
wrapper.find(".addButtonEntiy").last().simulate("click");
|
||||
wrapper.find("#deleteEntity").last().simulate("click");
|
||||
expect(wrapper.find("input[type='text']")).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
324
src/Explorer/Panes/Tables/AddTableEntityPanel.tsx
Normal file
324
src/Explorer/Panes/Tables/AddTableEntityPanel.tsx
Normal file
@@ -0,0 +1,324 @@
|
||||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import {
|
||||
IDropdownOption,
|
||||
Image,
|
||||
IPanelProps,
|
||||
IRenderFunction,
|
||||
Label,
|
||||
Stack,
|
||||
Text,
|
||||
TextField,
|
||||
} from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import * as _ from "underscore";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import RevertBackIcon from "../../../../images/RevertBack.svg";
|
||||
import { TableEntity } from "../../../Common/TableEntity";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import * as TableConstants from "../../Tables/Constants";
|
||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
||||
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import { CassandraAPIDataClient, CassandraTableKey } from "../../Tables/TableDataClient";
|
||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
||||
import * as Utilities from "../../Tables/Utilities";
|
||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
||||
import { PanelContainerComponent } from "../PanelContainerComponent";
|
||||
import {
|
||||
attributeNameLabel,
|
||||
attributeValueLabel,
|
||||
backImageProps,
|
||||
cassandraOptions,
|
||||
columnProps,
|
||||
dataTypeLabel,
|
||||
detailedHelp,
|
||||
entityFromAttributes,
|
||||
getAddButtonLabel,
|
||||
getButtonLabel,
|
||||
getCassandraDefaultEntities,
|
||||
getDefaultEntities,
|
||||
getEntityValuePlaceholder,
|
||||
getPanelTitle,
|
||||
imageProps,
|
||||
isValidEntities,
|
||||
options,
|
||||
} from "./Validators/EntityTableHelper";
|
||||
|
||||
interface AddTableEntityPanelProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
queryTablesTab: QueryTablesTab;
|
||||
tableEntityListViewModel: TableEntityListViewModel;
|
||||
cassandraApiClient: CassandraAPIDataClient;
|
||||
}
|
||||
|
||||
interface EntityRowType {
|
||||
property: string;
|
||||
type: string;
|
||||
value: string;
|
||||
isPropertyTypeDisable: boolean;
|
||||
isDeleteOptionVisible: boolean;
|
||||
id: number;
|
||||
entityValuePlaceholder: string;
|
||||
isEntityTypeDate: boolean;
|
||||
entityTimeValue?: string;
|
||||
}
|
||||
|
||||
export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> = ({
|
||||
explorer,
|
||||
closePanel,
|
||||
queryTablesTab,
|
||||
tableEntityListViewModel,
|
||||
cassandraApiClient,
|
||||
}: AddTableEntityPanelProps): JSX.Element => {
|
||||
const [entities, setEntities] = useState<EntityRowType[]>([]);
|
||||
const [selectedRow, setSelectedRow] = useState<number>(0);
|
||||
const [entityAttributeValue, setEntityAttributeValue] = useState<string>("");
|
||||
const [entityAttributeProperty, setEntityAttributeProperty] = useState<string>("");
|
||||
const [
|
||||
isEntityValuePanelOpen,
|
||||
{ setTrue: setIsEntityValuePanelTrue, setFalse: setIsEntityValuePanelFalse },
|
||||
] = useBoolean(false);
|
||||
|
||||
/* Get default and previous saved entity headers */
|
||||
useEffect(() => {
|
||||
getDefaultEntitiesAttribute();
|
||||
}, []);
|
||||
|
||||
const getDefaultEntitiesAttribute = async (): Promise<void> => {
|
||||
let headers = tableEntityListViewModel.headers;
|
||||
if (DataTableUtilities.checkForDefaultHeader(headers)) {
|
||||
headers = [];
|
||||
if (userContext.apiType === "Tables") {
|
||||
headers = [TableConstants.EntityKeyNames.PartitionKey, TableConstants.EntityKeyNames.RowKey];
|
||||
}
|
||||
}
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
const columns: CassandraTableKey[] = await cassandraApiClient.getTableSchema(queryTablesTab.collection);
|
||||
const cassandraEntities = Utilities.getDataTypesFromCassandraSchema(columns);
|
||||
const cassandraDefaultEntities: EntityRowType[] = getCassandraDefaultEntities(headers, cassandraEntities);
|
||||
setEntities(cassandraDefaultEntities);
|
||||
} else {
|
||||
const entityItems = tableEntityListViewModel.items();
|
||||
const entityTypes = Utilities.getDataTypesFromEntities(headers, entityItems);
|
||||
const defaultEntities: EntityRowType[] = getDefaultEntities(headers, entityTypes);
|
||||
setEntities(defaultEntities);
|
||||
}
|
||||
};
|
||||
|
||||
/* Add new entity attribute */
|
||||
const submit = async (event: React.FormEvent<HTMLInputElement>): Promise<void> => {
|
||||
if (!isValidEntities(entities)) {
|
||||
return undefined;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
const entity: Entities.ITableEntity = entityFromAttributes(entities);
|
||||
const newEntity: Entities.ITableEntity = await explorer.tableDataClient.createDocument(
|
||||
queryTablesTab.collection,
|
||||
entity
|
||||
);
|
||||
await tableEntityListViewModel.addEntityToCache(newEntity);
|
||||
if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) {
|
||||
tableEntityListViewModel.redrawTableThrottled();
|
||||
}
|
||||
closePanel();
|
||||
};
|
||||
|
||||
const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => {
|
||||
let newHeaders: string[] = [];
|
||||
const keys = Object.keys(newEntity);
|
||||
keys &&
|
||||
keys.forEach((key: string) => {
|
||||
if (
|
||||
!_.contains(viewModel.headers, key) &&
|
||||
key !== TableEntityProcessor.keyProperties.attachments &&
|
||||
key !== TableEntityProcessor.keyProperties.etag &&
|
||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
||||
key !== TableEntityProcessor.keyProperties.self &&
|
||||
(!(userContext.apiType === "Cassandra") || key !== TableConstants.EntityKeyNames.RowKey)
|
||||
) {
|
||||
newHeaders.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
let newHeadersInserted = false;
|
||||
if (newHeaders.length) {
|
||||
if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) {
|
||||
newHeaders = viewModel.headers.concat(newHeaders);
|
||||
}
|
||||
viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false);
|
||||
newHeadersInserted = true;
|
||||
}
|
||||
return newHeadersInserted;
|
||||
};
|
||||
|
||||
/* Add new entity row */
|
||||
const addNewEntity = (): void => {
|
||||
const cloneEntities: EntityRowType[] = [...entities];
|
||||
cloneEntities.splice(cloneEntities.length, 0, {
|
||||
property: "",
|
||||
type: "String",
|
||||
value: "",
|
||||
isPropertyTypeDisable: false,
|
||||
isDeleteOptionVisible: true,
|
||||
id: cloneEntities.length + 1,
|
||||
entityValuePlaceholder: "",
|
||||
isEntityTypeDate: false,
|
||||
});
|
||||
setEntities(cloneEntities);
|
||||
};
|
||||
|
||||
/* Delete entity row */
|
||||
const deleteEntityAtIndex = (indexToRemove: number): void => {
|
||||
const cloneEntities: EntityRowType[] = [...entities];
|
||||
cloneEntities.splice(indexToRemove, 1);
|
||||
setEntities(cloneEntities);
|
||||
};
|
||||
|
||||
/* handle Entity change */
|
||||
const entityChange = (value: string | Date, indexOfInput: number, key: string): void => {
|
||||
const cloneEntities: EntityRowType[] = [...entities];
|
||||
if (key === "property") {
|
||||
cloneEntities[indexOfInput].property = value.toString();
|
||||
} else if (key === "time") {
|
||||
cloneEntities[indexOfInput].entityTimeValue = value.toString();
|
||||
} else {
|
||||
cloneEntities[indexOfInput].value = value.toString();
|
||||
}
|
||||
setEntities(cloneEntities);
|
||||
};
|
||||
|
||||
/* handle Entity type */
|
||||
const entityTypeChange = (
|
||||
_event: React.FormEvent<HTMLDivElement>,
|
||||
selectedType: IDropdownOption,
|
||||
indexOfEntity: number
|
||||
): void => {
|
||||
const entityValuePlaceholder: string = getEntityValuePlaceholder(selectedType.key);
|
||||
const cloneEntities: EntityRowType[] = [...entities];
|
||||
cloneEntities[indexOfEntity].type = selectedType.key.toString();
|
||||
cloneEntities[indexOfEntity].entityValuePlaceholder = entityValuePlaceholder;
|
||||
cloneEntities[indexOfEntity].isEntityTypeDate = selectedType.key === "DateTime";
|
||||
setEntities(cloneEntities);
|
||||
};
|
||||
|
||||
/* Open edit entity value modal */
|
||||
const editEntity = (rowEndex: number): void => {
|
||||
const entityAttribute: EntityRowType = entities[rowEndex] && entities[rowEndex];
|
||||
setEntityAttributeValue(entityAttribute.value);
|
||||
setEntityAttributeProperty(entityAttribute.property);
|
||||
setSelectedRow(rowEndex);
|
||||
setIsEntityValuePanelTrue();
|
||||
};
|
||||
|
||||
const renderPanelContent = (): JSX.Element => {
|
||||
return (
|
||||
<form className="panelFormWrapper">
|
||||
<div className="panelFormWrapper">
|
||||
<div className="panelMainContent">
|
||||
{entities.map((entity, index) => {
|
||||
return (
|
||||
<TableEntity
|
||||
key={"" + entity.id + index}
|
||||
isDeleteOptionVisible={entity.isDeleteOptionVisible}
|
||||
entityTypeLabel={index === 0 && dataTypeLabel}
|
||||
entityPropertyLabel={index === 0 && attributeNameLabel}
|
||||
entityValueLabel={index === 0 && attributeValueLabel}
|
||||
options={userContext.apiType === "Cassandra" ? cassandraOptions : options}
|
||||
isPropertyTypeDisable={entity.isPropertyTypeDisable}
|
||||
entityProperty={entity.property}
|
||||
selectedKey={entity.type}
|
||||
entityPropertyPlaceHolder={detailedHelp}
|
||||
entityValuePlaceholder={entity.entityValuePlaceholder}
|
||||
entityValue={entity.value}
|
||||
isEntityTypeDate={entity.isEntityTypeDate}
|
||||
entityTimeValue={entity.entityTimeValue}
|
||||
onEditEntity={() => editEntity(index)}
|
||||
onSelectDate={(date: Date) => {
|
||||
entityChange(date, index, "value");
|
||||
}}
|
||||
onDeleteEntity={() => deleteEntityAtIndex(index)}
|
||||
onEntityPropertyChange={(event, newInput?: string) => {
|
||||
entityChange(newInput, index, "property");
|
||||
}}
|
||||
onEntityTypeChange={(event: React.FormEvent<HTMLDivElement>, selectedParam: IDropdownOption) => {
|
||||
entityTypeChange(event, selectedParam, index);
|
||||
}}
|
||||
onEntityValueChange={(event, newInput?: string) => {
|
||||
entityChange(newInput, index, "value");
|
||||
}}
|
||||
onEntityTimeValueChange={(event, newInput?: string) => {
|
||||
entityChange(newInput, index, "time");
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{userContext.apiType !== "Cassandra" && (
|
||||
<Stack horizontal onClick={addNewEntity} className="addButtonEntiy">
|
||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add Entity" />
|
||||
<Text className="addNewParamStyle">{getAddButtonLabel(userContext.apiType)}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
<div className="paneFooter">
|
||||
<div className="leftpanel-okbut">
|
||||
<input
|
||||
type="submit"
|
||||
onClick={submit}
|
||||
className="genericPaneSubmitBtn"
|
||||
value={getButtonLabel(userContext.apiType)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const onRenderNavigationContent: IRenderFunction<IPanelProps> = () => {
|
||||
return (
|
||||
<Stack horizontal {...columnProps}>
|
||||
<Image {...backImageProps} src={RevertBackIcon} alt="back" onClick={() => setIsEntityValuePanelFalse()} />
|
||||
<Label>{entityAttributeProperty}</Label>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
if (isEntityValuePanelOpen) {
|
||||
return (
|
||||
<PanelContainerComponent
|
||||
headerText=""
|
||||
onRenderNavigationContent={onRenderNavigationContent}
|
||||
panelWidth="700px"
|
||||
isOpen={true}
|
||||
panelContent={
|
||||
<TextField
|
||||
multiline
|
||||
rows={5}
|
||||
className="entityValueTextField"
|
||||
value={entityAttributeValue}
|
||||
onChange={(event, newInput?: string) => {
|
||||
entityChange(newInput, selectedRow, "value");
|
||||
setEntityAttributeValue(newInput);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
closePanel={() => closePanel()}
|
||||
isConsoleExpanded={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PanelContainerComponent
|
||||
headerText={getPanelTitle(userContext.apiType)}
|
||||
panelWidth="700px"
|
||||
isOpen={true}
|
||||
panelContent={renderPanelContent()}
|
||||
closePanel={() => closePanel()}
|
||||
isConsoleExpanded={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,15 @@
|
||||
import * as ko from "knockout";
|
||||
import _ from "underscore";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { CassandraTableKey, CassandraAPIDataClient } from "../../Tables/TableDataClient";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import TableEntityPane from "./TableEntityPane";
|
||||
import * as Utilities from "../../Tables/Utilities";
|
||||
import * as TableConstants from "../../Tables/Constants";
|
||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import * as TableConstants from "../../Tables/Constants";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import { CassandraAPIDataClient, CassandraTableKey } from "../../Tables/TableDataClient";
|
||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
||||
import * as Utilities from "../../Tables/Utilities";
|
||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
||||
import TableEntityPane from "./TableEntityPane";
|
||||
|
||||
export default class EditTableEntityPane extends TableEntityPane {
|
||||
container: Explorer;
|
||||
@@ -21,11 +22,9 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
this.submitButtonText("Update Entity");
|
||||
this.container.isPreferredApiCassandra.subscribe((isCassandra) => {
|
||||
if (isCassandra) {
|
||||
this.submitButtonText("Update Row");
|
||||
}
|
||||
});
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
this.submitButtonText("Update Row");
|
||||
}
|
||||
this.scrollId = ko.observable<string>("editEntityScroll");
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
property !== TableEntityProcessor.keyProperties.etag &&
|
||||
property !== TableEntityProcessor.keyProperties.resourceId &&
|
||||
property !== TableEntityProcessor.keyProperties.self &&
|
||||
(!this.container.isPreferredApiCassandra() || property !== TableConstants.EntityKeyNames.RowKey)
|
||||
(userContext.apiType !== "Cassandra" || property !== TableConstants.EntityKeyNames.RowKey)
|
||||
) {
|
||||
numberOfProperties++;
|
||||
}
|
||||
@@ -70,7 +69,7 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
|
||||
public open() {
|
||||
this.displayedAttributes(this.constructDisplayedAttributes(this.originEntity));
|
||||
if (this.container.isPreferredApiTable()) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
this.originalDocument = TableEntityProcessor.convertEntitiesToDocuments(
|
||||
[<Entities.ITableEntityForTablesAPI>this.originEntity],
|
||||
this.tableViewModel.queryTablesTab.collection
|
||||
@@ -93,9 +92,9 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
key !== TableEntityProcessor.keyProperties.etag &&
|
||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
||||
key !== TableEntityProcessor.keyProperties.self &&
|
||||
(!this.container.isPreferredApiCassandra() || key !== TableConstants.EntityKeyNames.RowKey)
|
||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
||||
) {
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
const cassandraKeys = this.tableViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
|
||||
.concat(this.tableViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
|
||||
.map((key) => key.property);
|
||||
@@ -150,7 +149,7 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
(<CassandraAPIDataClient>this.container.tableDataClient)
|
||||
.getTableSchema(this.tableViewModel.queryTablesTab.collection)
|
||||
.then((properties: CassandraTableKey[]) => {
|
||||
@@ -169,10 +168,7 @@ export default class EditTableEntityPane extends TableEntityPane {
|
||||
var updatedEntity: any = {};
|
||||
displayedAttributes &&
|
||||
displayedAttributes.forEach((attribute: EntityPropertyViewModel) => {
|
||||
if (
|
||||
attribute.name() &&
|
||||
(!this.tableViewModel.queryTablesTab.container.isPreferredApiCassandra() || attribute.value() !== "")
|
||||
) {
|
||||
if (attribute.name() && (userContext.apiType !== "Cassandra" || attribute.value() !== "")) {
|
||||
var value = attribute.getPropertyValue();
|
||||
var type = attribute.type();
|
||||
if (type === TableConstants.TableType.Int64) {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import * as ko from "knockout";
|
||||
import _ from "underscore";
|
||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
||||
import { KeyCodes } from "../../../Common/Constants";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import * as TableConstants from "../../Tables/Constants";
|
||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
||||
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
|
||||
import * as Entities from "../../Tables/Entities";
|
||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
||||
import * as Utilities from "../../Tables/Utilities";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { KeyCodes } from "../../../Common/Constants";
|
||||
import { ContextualPaneBase } from "../ContextualPaneBase";
|
||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
||||
|
||||
// Class with variables and functions that are common to both adding and editing entities
|
||||
export default abstract class TableEntityPane extends ContextualPaneBase {
|
||||
@@ -52,31 +53,29 @@ export default abstract class TableEntityPane extends ContextualPaneBase {
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
this.container.isPreferredApiCassandra.subscribe((isCassandra) => {
|
||||
if (isCassandra) {
|
||||
this.edmTypes([
|
||||
TableConstants.CassandraType.Text,
|
||||
TableConstants.CassandraType.Ascii,
|
||||
TableConstants.CassandraType.Bigint,
|
||||
TableConstants.CassandraType.Blob,
|
||||
TableConstants.CassandraType.Boolean,
|
||||
TableConstants.CassandraType.Decimal,
|
||||
TableConstants.CassandraType.Double,
|
||||
TableConstants.CassandraType.Float,
|
||||
TableConstants.CassandraType.Int,
|
||||
TableConstants.CassandraType.Uuid,
|
||||
TableConstants.CassandraType.Varchar,
|
||||
TableConstants.CassandraType.Varint,
|
||||
TableConstants.CassandraType.Inet,
|
||||
TableConstants.CassandraType.Smallint,
|
||||
TableConstants.CassandraType.Tinyint,
|
||||
]);
|
||||
}
|
||||
});
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
this.edmTypes([
|
||||
TableConstants.CassandraType.Text,
|
||||
TableConstants.CassandraType.Ascii,
|
||||
TableConstants.CassandraType.Bigint,
|
||||
TableConstants.CassandraType.Blob,
|
||||
TableConstants.CassandraType.Boolean,
|
||||
TableConstants.CassandraType.Decimal,
|
||||
TableConstants.CassandraType.Double,
|
||||
TableConstants.CassandraType.Float,
|
||||
TableConstants.CassandraType.Int,
|
||||
TableConstants.CassandraType.Uuid,
|
||||
TableConstants.CassandraType.Varchar,
|
||||
TableConstants.CassandraType.Varint,
|
||||
TableConstants.CassandraType.Inet,
|
||||
TableConstants.CassandraType.Smallint,
|
||||
TableConstants.CassandraType.Tinyint,
|
||||
]);
|
||||
}
|
||||
|
||||
this.canAdd = ko.computed<boolean>(() => {
|
||||
// Cassandra can't add since the schema can't be changed once created
|
||||
if (this.container.isPreferredApiCassandra()) {
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
return false;
|
||||
}
|
||||
// Adding '2' to the maximum to take into account PartitionKey and RowKey
|
||||
@@ -163,7 +162,7 @@ export default abstract class TableEntityPane extends ContextualPaneBase {
|
||||
|
||||
public insertAttribute = (name?: string, type?: string): void => {
|
||||
let entityProperty: EntityPropertyViewModel;
|
||||
if (!!name && !!type && this.container.isPreferredApiCassandra()) {
|
||||
if (!!name && !!type && userContext.apiType === "Cassandra") {
|
||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
||||
const nonEditableType: boolean =
|
||||
type === TableConstants.CassandraType.Blob || type === TableConstants.CassandraType.Inet;
|
||||
@@ -253,8 +252,7 @@ export default abstract class TableEntityPane extends ContextualPaneBase {
|
||||
key !== TableEntityProcessor.keyProperties.etag &&
|
||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
||||
key !== TableEntityProcessor.keyProperties.self &&
|
||||
(!viewModel.queryTablesTab.container.isPreferredApiCassandra() ||
|
||||
key !== TableConstants.EntityKeyNames.RowKey)
|
||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
||||
) {
|
||||
newHeaders.push(key);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import { userContext } from "../../../../UserContext";
|
||||
import Explorer from "../../../Explorer";
|
||||
import * as Constants from "../../../Tables/Constants";
|
||||
import QueryViewModel from "../../../Tables/QueryBuilder/QueryViewModel";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
interface TableQuerySelectPanelProps {
|
||||
explorer: Explorer;
|
||||
|
||||
247
src/Explorer/Panes/Tables/Validators/EntityTableHelper.tsx
Normal file
247
src/Explorer/Panes/Tables/Validators/EntityTableHelper.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import { IImageProps, IStackProps } from "office-ui-fabric-react";
|
||||
import * as _ from "underscore";
|
||||
import * as TableConstants from "../../../Tables/Constants";
|
||||
import * as Entities from "../../../Tables/Entities";
|
||||
import * as Utilities from "../../../Tables/Utilities";
|
||||
|
||||
export const defaultStringPlaceHolder = "Enter identifier value.";
|
||||
export const defaultEntities = [
|
||||
{
|
||||
property: "PartitionKey",
|
||||
type: "String",
|
||||
value: "",
|
||||
isPropertyTypeDisable: true,
|
||||
isDeleteOptionVisible: false,
|
||||
id: 1,
|
||||
entityValuePlaceholder: defaultStringPlaceHolder,
|
||||
isEntityTypeDate: false,
|
||||
},
|
||||
{
|
||||
property: "RowKey",
|
||||
type: "String",
|
||||
value: "",
|
||||
isPropertyTypeDisable: true,
|
||||
isDeleteOptionVisible: false,
|
||||
id: 2,
|
||||
entityValuePlaceholder: defaultStringPlaceHolder,
|
||||
isEntityTypeDate: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Dropdown options
|
||||
const { String, Boolean, Binary, DateTime, Double, Guid, Int32, Int64 } = TableConstants.TableType;
|
||||
export const options = [
|
||||
{ key: String, text: String },
|
||||
{ key: Boolean, text: Boolean },
|
||||
{ key: Binary, text: Binary, disabled: true },
|
||||
{ key: DateTime, text: DateTime },
|
||||
{ key: Double, text: Double },
|
||||
{ key: Guid, text: Guid },
|
||||
{ key: Int32, text: Int32 },
|
||||
{ key: Int64, text: Int64 },
|
||||
];
|
||||
|
||||
const {
|
||||
Text,
|
||||
Ascii,
|
||||
Bigint,
|
||||
Blob,
|
||||
Decimal,
|
||||
Float,
|
||||
Int,
|
||||
Uuid,
|
||||
Varchar,
|
||||
Varint,
|
||||
Inet,
|
||||
Smallint,
|
||||
Tinyint,
|
||||
} = TableConstants.CassandraType;
|
||||
export const cassandraOptions = [
|
||||
{ key: Text, text: Text },
|
||||
{ key: Ascii, text: Ascii },
|
||||
{ key: Bigint, text: Bigint },
|
||||
{ key: Blob, text: Blob },
|
||||
{ key: Boolean, text: Boolean },
|
||||
{ key: Decimal, text: Decimal },
|
||||
{ key: Double, text: Double },
|
||||
{ key: Float, text: Float },
|
||||
{ key: Int, text: Int },
|
||||
{ key: Uuid, text: Uuid },
|
||||
{ key: Varchar, text: Varchar },
|
||||
{ key: Varint, text: Varint },
|
||||
{ key: Inet, text: Inet },
|
||||
{ key: Smallint, text: Smallint },
|
||||
{ key: Tinyint, text: Tinyint },
|
||||
];
|
||||
|
||||
export const imageProps: IImageProps = {
|
||||
width: 16,
|
||||
height: 30,
|
||||
};
|
||||
|
||||
export const backImageProps: IImageProps = {
|
||||
width: 16,
|
||||
height: 16,
|
||||
className: "backImageIcon",
|
||||
};
|
||||
/* Labels */
|
||||
export const attributeNameLabel = "Property Name";
|
||||
export const dataTypeLabel = "Type";
|
||||
export const attributeValueLabel = "Value";
|
||||
export const addButtonLabel = "Add Property";
|
||||
|
||||
// add table entity placeholders
|
||||
export const detailedHelp = "Enter a name up to 255 characters in size. Most valid C# identifiers are allowed.";
|
||||
export const booleanPlaceHolder = "Enter true or false.";
|
||||
export const stringPlaceholder = "Enter a value up to 64 KB in size.";
|
||||
export const datePlaceholder = "Enter a date and time.";
|
||||
export const doublePlaceholder = "Enter a 64-bit floating point value.";
|
||||
export const guidPlaceholder = "Enter a 16-byte (128-bit) GUID value.";
|
||||
export const intPlaceholder = "Enter a signed 32-bit integer.";
|
||||
export const int64Placeholder = "Enter a signed 64-bit integer, in the range (-2^53 - 1, 2^53 - 1).";
|
||||
|
||||
export const columnProps: Partial<IStackProps> = {
|
||||
tokens: { childrenGap: 10 },
|
||||
styles: { root: { width: 680 } },
|
||||
};
|
||||
|
||||
// helper functions
|
||||
export const entityFromAttributes = (entities: EntityRowType[]): Entities.ITableEntity => {
|
||||
const entity: { [key: string]: { _: string; $: string } } = {};
|
||||
entities.forEach((entityRow: EntityRowType) => {
|
||||
if (entityRow) {
|
||||
let value = entityRow.value;
|
||||
if (entityRow.type === TableConstants.TableType.DateTime && entityRow.entityTimeValue) {
|
||||
// Add time in date as time has seperate textfield
|
||||
const [hours, minuntes] = entityRow.entityTimeValue.split(":");
|
||||
const entityDate = new Date(value);
|
||||
entityDate.setHours(+hours);
|
||||
entityDate.setMinutes(+minuntes);
|
||||
value = entityDate.toString();
|
||||
}
|
||||
if (entityRow.type === TableConstants.TableType.Int64) {
|
||||
value = Utilities.padLongWithZeros(value);
|
||||
}
|
||||
entity[entityRow.property] = {
|
||||
_: value,
|
||||
$: entityRow.type,
|
||||
};
|
||||
}
|
||||
});
|
||||
return entity;
|
||||
};
|
||||
|
||||
// GetPlaceholder according to entity type
|
||||
export const getEntityValuePlaceholder = (entityType: string | number): string => {
|
||||
switch (entityType) {
|
||||
case "String":
|
||||
return stringPlaceholder;
|
||||
case "Boolean":
|
||||
return booleanPlaceHolder;
|
||||
case "DateTime":
|
||||
return datePlaceholder;
|
||||
case "Double":
|
||||
return doublePlaceholder;
|
||||
case "Guid":
|
||||
return guidPlaceholder;
|
||||
case "Int32":
|
||||
return intPlaceholder;
|
||||
case "Int64":
|
||||
return int64Placeholder;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
export const isValidEntities = (entities: EntityRowType[]): boolean => {
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
const { property } = entities[i];
|
||||
if (property === "" || property === undefined) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const isEntityPropertyTypeDisable = (header: string): boolean => {
|
||||
if (header === "PartitionKey" || header === "RowKey") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const getDefaultEntities = (headers: string[], entityTypes: { [key: string]: string }): EntityRowType[] => {
|
||||
const defaultEntities: EntityRowType[] = [];
|
||||
headers.forEach((header: string) => {
|
||||
if (header !== "Timestamp") {
|
||||
const entityType = !_.isEmpty(entityTypes) ? entityTypes[header] : "String";
|
||||
const entityRow = {
|
||||
property: header,
|
||||
type: entityType,
|
||||
value: "",
|
||||
isPropertyTypeDisable: isEntityPropertyTypeDisable(header),
|
||||
isDeleteOptionVisible: !isEntityPropertyTypeDisable(header),
|
||||
id: 1,
|
||||
entityValuePlaceholder: defaultStringPlaceHolder,
|
||||
isEntityTypeDate: entityType === "DateTime",
|
||||
};
|
||||
defaultEntities.push(entityRow);
|
||||
}
|
||||
});
|
||||
return defaultEntities;
|
||||
};
|
||||
|
||||
export const getPanelTitle = (apiType: string): string => {
|
||||
if (apiType === "Cassandra") {
|
||||
return "Add Table Row";
|
||||
}
|
||||
return "Add Table Row";
|
||||
};
|
||||
|
||||
export const getAddButtonLabel = (apiType: string): string => {
|
||||
if (apiType === "Cassandra") {
|
||||
return "Add Row";
|
||||
}
|
||||
return addButtonLabel;
|
||||
};
|
||||
|
||||
export const getButtonLabel = (apiType: string): string => {
|
||||
if (apiType === "Cassandra") {
|
||||
return "Add Row";
|
||||
}
|
||||
return "Add Entity";
|
||||
};
|
||||
|
||||
export const getCassandraDefaultEntities = (
|
||||
headers: string[],
|
||||
entityTypes: { [key: string]: string }
|
||||
): EntityRowType[] => {
|
||||
const defaultEntities: EntityRowType[] = [];
|
||||
headers.forEach((header: string) => {
|
||||
const entityRow = {
|
||||
property: header,
|
||||
type: entityTypes[header],
|
||||
value: "",
|
||||
isPropertyTypeDisable: true,
|
||||
isDeleteOptionVisible: true,
|
||||
id: 1,
|
||||
entityValuePlaceholder: defaultStringPlaceHolder,
|
||||
isEntityTypeDate: entityTypes[header] === "DateTime",
|
||||
};
|
||||
defaultEntities.push(entityRow);
|
||||
});
|
||||
return defaultEntities;
|
||||
};
|
||||
|
||||
// Type of entity row
|
||||
export interface EntityRowType {
|
||||
property: string;
|
||||
type: string;
|
||||
value: string;
|
||||
isPropertyTypeDisable: boolean;
|
||||
isDeleteOptionVisible: boolean;
|
||||
id: number;
|
||||
entityValuePlaceholder: string;
|
||||
isEntityTypeDate: boolean;
|
||||
entityTimeValue?: string;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,12 @@
|
||||
import React, { ChangeEvent, FunctionComponent, useState } from "react";
|
||||
import { Upload } from "../../../Common/Upload";
|
||||
import { Upload } from "../../../Common/Upload/Upload";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NotebookContentItem } from "../../Notebook/NotebookContentItem";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
export interface UploadFilePanelProps {
|
||||
explorer: Explorer;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { UploadItemsPane } from ".";
|
||||
import Explorer from "../../Explorer";
|
||||
import { UploadItemsPane } from "./UploadItemsPane";
|
||||
const props = {
|
||||
explorer: new Explorer(),
|
||||
closePanel: (): void => undefined,
|
||||
@@ -1,24 +1,21 @@
|
||||
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from "office-ui-fabric-react";
|
||||
import React, { ChangeEvent, FunctionComponent, useState } from "react";
|
||||
import { Upload } from "../../../Common/Upload";
|
||||
import { Upload } from "../../../Common/Upload/Upload";
|
||||
import { UploadDetailsRecord } from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { UploadDetails, UploadDetailsRecord } from "../../../workers/upload/definitions";
|
||||
import Explorer from "../../Explorer";
|
||||
import { getErrorMessage } from "../../Tables/Utilities";
|
||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
GenericRightPaneProps,
|
||||
} from "../GenericRightPaneComponent/GenericRightPaneComponent";
|
||||
|
||||
export interface UploadItemsPaneProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
}
|
||||
|
||||
interface IUploadFileData {
|
||||
numSucceeded: number;
|
||||
numFailed: number;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
const getTitle = (): string => {
|
||||
if (userContext.apiType === "Cassandra" || userContext.apiType === "Tables") {
|
||||
return "Upload Tables";
|
||||
@@ -54,7 +51,7 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({
|
||||
selectedCollection
|
||||
?.uploadFiles(files)
|
||||
.then(
|
||||
(uploadDetails: UploadDetails) => {
|
||||
(uploadDetails) => {
|
||||
setUploadFileData(uploadDetails.data);
|
||||
setFiles(undefined);
|
||||
},
|
||||
@@ -84,6 +81,7 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({
|
||||
onClose: closePanel,
|
||||
onSubmit,
|
||||
};
|
||||
|
||||
const columns: IColumn[] = [
|
||||
{
|
||||
key: "fileName",
|
||||
@@ -105,12 +103,12 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({
|
||||
},
|
||||
];
|
||||
|
||||
const _renderItemColumn = (item: IUploadFileData, index: number, column: IColumn) => {
|
||||
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||
switch (column.key) {
|
||||
case "status":
|
||||
return <span>{item.numSucceeded + " items created, " + item.numFailed + " errors"}</span>;
|
||||
return `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||
default:
|
||||
return <span>{item.fileName}</span>;
|
||||
return item.fileName;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,42 +153,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
@@ -223,22 +187,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -300,20 +248,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -443,42 +377,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"visible": [Function],
|
||||
},
|
||||
"addDatabaseText": [Function],
|
||||
"addTableEntityPane": AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"arcadiaToken": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
@@ -614,7 +512,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"hasStorageAnalyticsAfecFeature": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isAutoscaleDefaultEnabled": [Function],
|
||||
"isCopyNotebookPaneEnabled": [Function],
|
||||
"isEnableMongoCapabilityPresent": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isGitHubPaneEnabled": [Function],
|
||||
@@ -623,10 +520,7 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiCassandra": [Function],
|
||||
"isPreferredApiGraph": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -637,22 +531,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
@@ -700,20 +578,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"setupNotebooksPane": SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"signInAad": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
@@ -154,42 +154,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
@@ -224,22 +188,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -301,20 +249,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -444,42 +378,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"visible": [Function],
|
||||
},
|
||||
"addDatabaseText": [Function],
|
||||
"addTableEntityPane": AddTableEntityPane {
|
||||
"addButtonLabel": "Add Property",
|
||||
"attributeNameLabel": "Property Name",
|
||||
"attributeValueLabel": "Value",
|
||||
"canAdd": [Function],
|
||||
"canApply": [Function],
|
||||
"container": [Circular],
|
||||
"dataTypeLabel": "Type",
|
||||
"displayedAttributes": [Function],
|
||||
"editAttribute": [Function],
|
||||
"editButtonLabel": "Edit",
|
||||
"editingProperty": [Function],
|
||||
"edmTypes": [Function],
|
||||
"enterRequiredValueLabel": "Enter identifier value.",
|
||||
"enterValueLabel": "Enter value to keep property.",
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "addtableentitypane",
|
||||
"insertAttribute": [Function],
|
||||
"isEditing": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onAddPropertyKeyDown": [Function],
|
||||
"onBackButtonKeyDown": [Function],
|
||||
"onDeletePropertyKeyDown": [Function],
|
||||
"onEditPropertyKeyDown": [Function],
|
||||
"onKeyUp": [Function],
|
||||
"removeAttribute": [Function],
|
||||
"removeButtonLabel": "Remove",
|
||||
"scrollId": [Function],
|
||||
"submitButtonText": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"arcadiaToken": [Function],
|
||||
"canExceedMaximumValue": [Function],
|
||||
"canSaveQueries": [Function],
|
||||
@@ -615,7 +513,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"hasStorageAnalyticsAfecFeature": [Function],
|
||||
"isAccountReady": [Function],
|
||||
"isAutoscaleDefaultEnabled": [Function],
|
||||
"isCopyNotebookPaneEnabled": [Function],
|
||||
"isEnableMongoCapabilityPresent": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isGitHubPaneEnabled": [Function],
|
||||
@@ -626,10 +523,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiCassandra": [Function],
|
||||
"isPreferredApiGraph": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPreferredApiTable": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -641,22 +535,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"isSynapseLinkUpdating": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"memoryUsageInfo": [Function],
|
||||
"newVertexPane": NewVertexPane {
|
||||
"buildString": [Function],
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "newvertexpane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onMoreDetailsKeyPress": [Function],
|
||||
"onSubmitCreateCallback": null,
|
||||
"partitionKeyProperty": [Function],
|
||||
"tempVertexData": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"notebookBasePath": [Function],
|
||||
"notebookServerInfo": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
@@ -705,20 +583,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
|
||||
"setInProgressConsoleDataIdToBeDeleted": undefined,
|
||||
"setIsNotificationConsoleExpanded": undefined,
|
||||
"setNotificationConsoleData": undefined,
|
||||
"setupNotebooksPane": SetupNotebooksPane {
|
||||
"container": [Circular],
|
||||
"description": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "setupnotebookspane",
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"onCompleteSetupClick": [Function],
|
||||
"onCompleteSetupKeyPress": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"signInAad": [Function],
|
||||
"sparkClusterConnectionInfo": [Function],
|
||||
"splitter": Splitter {
|
||||
|
||||
Reference in New Issue
Block a user