mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-24 11:21:23 +00:00
Compare commits
19 Commits
Remove-Exp
...
cleanup/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a10b61b8af | ||
|
|
9878bf0d5e | ||
|
|
5e0523c7d9 | ||
|
|
9d0bc86197 | ||
|
|
531df811da | ||
|
|
5a019eb431 | ||
|
|
8f3cb7282b | ||
|
|
154db1dcd5 | ||
|
|
e8b79d6260 | ||
|
|
10c4dd0f19 | ||
|
|
5cf16d01b5 | ||
|
|
127784abdd | ||
|
|
c7b9ff6794 | ||
|
|
71e7ad4547 | ||
|
|
67062c18aa | ||
|
|
ab283cb8ff | ||
|
|
045a28b7a4 | ||
|
|
b7c911d19a | ||
|
|
5323f6ca4b |
@@ -5,7 +5,6 @@ src/Api/Apis.ts
|
||||
src/AuthType.ts
|
||||
src/Bindings/BindingHandlersRegisterer.ts
|
||||
src/Bindings/ReactBindingHandler.ts
|
||||
src/Common/ArrayHashMap.ts
|
||||
src/Common/Constants.ts
|
||||
src/Common/CosmosClient.test.ts
|
||||
src/Common/CosmosClient.ts
|
||||
@@ -13,15 +12,12 @@ src/Common/DataAccessUtilityBase.test.ts
|
||||
src/Common/DataAccessUtilityBase.ts
|
||||
src/Common/EditableUtility.ts
|
||||
src/Common/HashMap.test.ts
|
||||
src/Common/HashMap.ts
|
||||
src/Common/Logger.test.ts
|
||||
src/Common/MessageHandler.test.ts
|
||||
src/Common/MessageHandler.ts
|
||||
src/Common/MongoProxyClient.test.ts
|
||||
src/Common/MongoUtility.ts
|
||||
src/Common/NotificationsClientBase.ts
|
||||
src/Common/ObjectCache.test.ts
|
||||
src/Common/ObjectCache.ts
|
||||
src/Common/QueriesClient.ts
|
||||
src/Common/Splitter.ts
|
||||
src/Config.ts
|
||||
@@ -125,7 +121,7 @@ src/Explorer/Panes/GraphStylingPane.ts
|
||||
# src/Explorer/Panes/NewVertexPane.ts
|
||||
src/Explorer/Panes/PaneComponents.ts
|
||||
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
||||
src/Explorer/Panes/StringInputPane.ts
|
||||
src/Explorer/Panes/SetupNotebooksPane.ts
|
||||
src/Explorer/Panes/SwitchDirectoryPane.ts
|
||||
src/Explorer/Panes/Tables/EditTableEntityPane.ts
|
||||
src/Explorer/Panes/Tables/EntityPropertyViewModel.ts
|
||||
|
||||
30519
package-lock.json
generated
30519
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -176,7 +176,7 @@
|
||||
"typescript": "4.2.4",
|
||||
"url-loader": "1.1.1",
|
||||
"wait-on": "4.0.2",
|
||||
"webpack": "4.43.0",
|
||||
"webpack": "4.46.0",
|
||||
"webpack-bundle-analyzer": "3.6.1",
|
||||
"webpack-cli": "3.3.10",
|
||||
"webpack-dev-server": "3.11.0"
|
||||
|
||||
@@ -22,13 +22,7 @@ export interface ReactAdapter {
|
||||
export class Registerer {
|
||||
public static register(): void {
|
||||
ko.bindingHandlers.react = {
|
||||
init: (
|
||||
element: any,
|
||||
wrappedValueAccessor: () => any,
|
||||
allBindings?: ko.AllBindings,
|
||||
viewModel?: any,
|
||||
bindingContext?: ko.BindingContext
|
||||
) => {
|
||||
init: (element: any, wrappedValueAccessor: () => any) => {
|
||||
const adapter: ReactAdapter = wrappedValueAccessor();
|
||||
|
||||
if (adapter.setElement) {
|
||||
|
||||
@@ -1,49 +1,9 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
/**
|
||||
* Hash map of arrays which allows to:
|
||||
* - push an item by key: add to array and create array if needed
|
||||
* - remove item by key: remove from array and delete array if needed
|
||||
*/
|
||||
|
||||
export class ArrayHashMap<T> {
|
||||
private store: HashMap<T[]>;
|
||||
|
||||
constructor() {
|
||||
this.store = new HashMap();
|
||||
}
|
||||
|
||||
public has(key: string): boolean {
|
||||
return this.store.has(key);
|
||||
}
|
||||
|
||||
public get(key: string): T[] {
|
||||
return this.store.get(key);
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return this.store.size();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return this.store.keys();
|
||||
}
|
||||
|
||||
public delete(key: string): boolean {
|
||||
return this.store.delete(key);
|
||||
}
|
||||
|
||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||
const values = this.store.get(key);
|
||||
if (values) {
|
||||
values.forEach((value) => iteratorFct(value));
|
||||
}
|
||||
}
|
||||
|
||||
export class ArrayHashMap<T> extends Map<string, T[]> {
|
||||
/**
|
||||
* Insert item into array.
|
||||
* If no array, create one.
|
||||
@@ -52,16 +12,8 @@ export class ArrayHashMap<T> {
|
||||
* @param item
|
||||
*/
|
||||
public push(key: string, item: T): void {
|
||||
let itemsArray: T[] = this.store.get(key);
|
||||
if (!itemsArray) {
|
||||
itemsArray = [item];
|
||||
this.store.set(key, itemsArray);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemsArray.indexOf(item) === -1) {
|
||||
itemsArray.push(item);
|
||||
}
|
||||
const array = this.get(key);
|
||||
array ? array.includes(item) || array.push(item) : this.set(key, [item]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,18 +22,11 @@ export class ArrayHashMap<T> {
|
||||
* @param key
|
||||
* @param itemToRemove
|
||||
*/
|
||||
public remove(key: string, itemToRemove: T) {
|
||||
if (!this.store.has(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsArray = this.store.get(key);
|
||||
const index = itemsArray.indexOf(itemToRemove);
|
||||
if (index >= 0) {
|
||||
itemsArray.splice(index, 1);
|
||||
if (itemsArray.length === 0) {
|
||||
this.store.delete(key);
|
||||
}
|
||||
public remove(key: string, itemToRemove: T): void {
|
||||
const array = this.get(key);
|
||||
if (array) {
|
||||
const remaining = array.filter((item) => item !== itemToRemove);
|
||||
remaining.length ? this.set(key, remaining) : this.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
||||
import { userContext } from "../UserContext";
|
||||
|
||||
export const getEntityName = (): string => {
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return "document";
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface TableEntityProps {
|
||||
entityValuePlaceholder: string;
|
||||
entityValue: string | Date;
|
||||
isEntityTypeDate: boolean;
|
||||
isEntityValueDisable?: boolean;
|
||||
entityTimeValue: string;
|
||||
entityValueType: string;
|
||||
onEntityValueChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
@@ -22,6 +23,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
entityValueType,
|
||||
onEntityValueChange,
|
||||
onSelectDate,
|
||||
isEntityValueDisable,
|
||||
onEntityTimeValueChange,
|
||||
}: TableEntityProps): JSX.Element => {
|
||||
if (isEntityTypeDate) {
|
||||
@@ -33,6 +35,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
value={entityValue && new Date(entityValue)}
|
||||
ariaLabel={entityValuePlaceholder}
|
||||
onSelectDate={onSelectDate}
|
||||
disabled={isEntityValueDisable}
|
||||
/>
|
||||
<TextField
|
||||
label={entityValueLabel && entityValueLabel}
|
||||
@@ -41,6 +44,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
type="time"
|
||||
value={entityTimeValue}
|
||||
onChange={onEntityTimeValueChange}
|
||||
disabled={isEntityValueDisable}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -52,6 +56,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
className="addEntityTextField"
|
||||
id="entityValueId"
|
||||
autoFocus
|
||||
disabled={isEntityValueDisable}
|
||||
type={entityValueType}
|
||||
placeholder={entityValuePlaceholder}
|
||||
value={typeof entityValue === "string" && entityValue}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
describe("HashMap", () => {
|
||||
it("should test if key/val exists", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 123);
|
||||
|
||||
expect(map.has("a")).toBe(true);
|
||||
expect(map.has("b")).toBe(false);
|
||||
});
|
||||
|
||||
it("should get object back", () => {
|
||||
const map = new HashMap<string>();
|
||||
map.set("a", "123");
|
||||
map.set("a", "456");
|
||||
|
||||
expect(map.get("a")).toBe("456");
|
||||
expect(map.get("a")).not.toBe("123");
|
||||
});
|
||||
|
||||
it("should return the right size", () => {
|
||||
const map = new HashMap<string>();
|
||||
map.set("a", "123");
|
||||
map.set("b", "456");
|
||||
|
||||
expect(map.size()).toBe(2);
|
||||
});
|
||||
|
||||
it("should be iterable", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 10);
|
||||
map.set("c", 100);
|
||||
map.set("d", 1000);
|
||||
|
||||
let i = 0;
|
||||
map.forEach((key: string, value: number) => {
|
||||
i += value;
|
||||
});
|
||||
expect(i).toBe(1111);
|
||||
});
|
||||
|
||||
it("should be deleted", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 10);
|
||||
|
||||
expect(map.delete("a")).toBe(true);
|
||||
expect(map.delete("c")).toBe(false);
|
||||
expect(map.has("a")).toBe(false);
|
||||
expect(map.has("b")).toBe(true);
|
||||
});
|
||||
|
||||
it("should clear", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.clear();
|
||||
expect(map.size()).toBe(0);
|
||||
expect(map.has("a")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return all keys", () => {
|
||||
const map = new HashMap<number>();
|
||||
map.set("a", 1);
|
||||
map.set("b", 1);
|
||||
expect(map.keys()).toEqual(["a", "b"]);
|
||||
map.clear();
|
||||
expect(map.keys().length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Simple hashmap implementation that doesn't rely on ES6 Map nor polyfills
|
||||
*/
|
||||
export class HashMap<T> {
|
||||
constructor(private container: { [key: string]: T } = {}) {}
|
||||
|
||||
public has(key: string): boolean {
|
||||
return this.container.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
public set(key: string, value: T): void {
|
||||
this.container[key] = value;
|
||||
}
|
||||
|
||||
public get(key: string): T {
|
||||
return this.container[key];
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return Object.keys(this.container).length;
|
||||
}
|
||||
|
||||
public delete(key: string): boolean {
|
||||
if (this.has(key)) {
|
||||
delete this.container[key];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.container = {};
|
||||
}
|
||||
|
||||
public keys(): string[] {
|
||||
return Object.keys(this.container);
|
||||
}
|
||||
|
||||
public forEach(iteratorFct: (key: string, value: T) => void) {
|
||||
for (const k in this.container) {
|
||||
iteratorFct(k, this.container[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ describe("Object cache", () => {
|
||||
cache.set("b", 2);
|
||||
cache.set("c", 3);
|
||||
cache.set("d", 4);
|
||||
expect(cache.size()).toBe(2);
|
||||
expect(cache.size).toBe(2);
|
||||
});
|
||||
|
||||
it("should remove first added element to keep size at limit", () => {
|
||||
|
||||
@@ -1,56 +1,27 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
export class ObjectCache<T> extends HashMap<T> {
|
||||
private keyQueue: string[]; // Last touched key FIFO to purge cache if too big.
|
||||
private maxNbElements: number;
|
||||
|
||||
public constructor(maxNbElements: number) {
|
||||
export class ObjectCache<T> extends Map<string, T> {
|
||||
constructor(private limit: number) {
|
||||
super();
|
||||
this.keyQueue = [];
|
||||
this.maxNbElements = maxNbElements;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
super.clear();
|
||||
this.keyQueue = [];
|
||||
public get(key: string): T | undefined {
|
||||
return this.touch(key);
|
||||
}
|
||||
|
||||
public get(key: string): T {
|
||||
this.markKeyAsTouched(key);
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
public set(key: string, value: T): void {
|
||||
super.set(key, value);
|
||||
|
||||
this.markKeyAsTouched(key);
|
||||
|
||||
if (super.size() > this.maxNbElements && key !== this.keyQueue[0]) {
|
||||
this.reduceCacheSize();
|
||||
public set(key: string, value: T): this {
|
||||
if (this.size === this.limit) {
|
||||
this.delete(this.keys().next().value);
|
||||
}
|
||||
|
||||
return this.touch(key, value), this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate elements to keep the total number below the limit
|
||||
*/
|
||||
private reduceCacheSize(): void {
|
||||
// remove a key
|
||||
const oldKey = this.keyQueue.shift();
|
||||
if (oldKey) {
|
||||
super.delete(oldKey);
|
||||
private touch(key: string, value = super.get(key)) {
|
||||
// Map keeps (re) insertion order according to ES6 spec
|
||||
if (value) {
|
||||
this.delete(key);
|
||||
super.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble up this key as new.
|
||||
* @param key
|
||||
*/
|
||||
private markKeyAsTouched(key: string) {
|
||||
const n = this.keyQueue.indexOf(key);
|
||||
if (n > -1) {
|
||||
this.keyQueue.splice(n, 1);
|
||||
}
|
||||
this.keyQueue.push(key);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@ import * as ViewModels from "../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { userContext } from "../UserContext";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import * as QueryUtils from "../Utils/QueryUtils";
|
||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||
import { createCollection } from "./dataAccess/createCollection";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
import { createDocument } from "./dataAccess/createDocument";
|
||||
import { deleteDocument } from "./dataAccess/deleteDocument";
|
||||
import { queryDocuments } from "./dataAccess/queryDocuments";
|
||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
|
||||
export class QueriesClient {
|
||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||
@@ -211,7 +211,7 @@ export class QueriesClient {
|
||||
}
|
||||
|
||||
private fetchQueriesQuery(): string {
|
||||
if (this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return QueriesClient.FetchMongoQuery;
|
||||
}
|
||||
return QueriesClient.FetchQuery;
|
||||
|
||||
@@ -73,7 +73,7 @@ export class Splitter {
|
||||
$(this.leftSide).resizable(splitterOptions);
|
||||
}
|
||||
|
||||
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
private onResizeStart: JQueryUI.ResizableEvent = () => {
|
||||
if (this.direction === SplitterDirection.Vertical) {
|
||||
$(".ui-resizable-helper").height("100%");
|
||||
} else {
|
||||
@@ -82,9 +82,7 @@ export class Splitter {
|
||||
$("iframe").css("pointer-events", "none");
|
||||
};
|
||||
|
||||
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
$("iframe").css("pointer-events", "auto");
|
||||
};
|
||||
private onResizeStop: JQueryUI.ResizableEvent = () => $("iframe").css("pointer-events", "auto");
|
||||
|
||||
public collapseLeft() {
|
||||
this.lastX = $(this.splitter).position().left;
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface TableEntityProps {
|
||||
options: { key: string; text: string }[];
|
||||
isPropertyTypeDisable: boolean;
|
||||
entityTimeValue: string;
|
||||
isEntityValueDisable?: boolean;
|
||||
onDeleteEntity?: () => void;
|
||||
onEditEntity?: () => void;
|
||||
onEntityPropertyChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
@@ -55,6 +56,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
isPropertyTypeDisable,
|
||||
isEntityTypeDate,
|
||||
entityTimeValue,
|
||||
isEntityValueDisable,
|
||||
onEditEntity,
|
||||
onDeleteEntity,
|
||||
onEntityPropertyChange,
|
||||
@@ -113,6 +115,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
<EntityValue
|
||||
entityValueLabel={entityValueLabel}
|
||||
entityValueType={getEntityValueType()}
|
||||
isEntityValueDisable={isEntityValueDisable}
|
||||
entityValuePlaceholder={entityValuePlaceholder}
|
||||
entityValue={entityValue}
|
||||
isEntityTypeDate={isEntityTypeDate}
|
||||
@@ -121,10 +124,11 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
onSelectDate={onSelectDate}
|
||||
onEntityTimeValueChange={onEntityTimeValueChange}
|
||||
/>
|
||||
<TooltipHost content="Edit property" id="editTooltip">
|
||||
<Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
|
||||
</TooltipHost>
|
||||
|
||||
{!isEntityValueDisable && (
|
||||
<TooltipHost content="Edit property" id="editTooltip">
|
||||
<Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
|
||||
</TooltipHost>
|
||||
)}
|
||||
{isDeleteOptionVisible && userContext.apiType !== "Cassandra" && (
|
||||
<TooltipHost content="Delete property" id="deleteTooltip">
|
||||
<Image {...imageProps} src={DeleteIcon} alt="delete entity" id="deleteEntity" onClick={onDeleteEntity} />
|
||||
|
||||
@@ -2,11 +2,10 @@ jest.mock("../../Utils/arm/request");
|
||||
jest.mock("../CosmosClient");
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { CreateCollectionParams, DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createCollection, constructRpOptions } from "./createCollection";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { constructRpOptions, createCollection } from "./createCollection";
|
||||
|
||||
describe("createCollection", () => {
|
||||
const createCollectionParams: CreateCollectionParams = {
|
||||
@@ -22,7 +21,7 @@ describe("createCollection", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { ContainerResponse, DatabaseResponse } from "@azure/cosmos";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
|
||||
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import * as ARMTypes from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createMongoCollectionWithProxy } from "../MongoProxyClient";
|
||||
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateCassandraTable,
|
||||
getCassandraTable,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
createUpdateMongoDBCollection,
|
||||
getMongoDBCollection,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
createUpdateGremlinGraph,
|
||||
getGremlinGraph,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import {
|
||||
createUpdateMongoDBCollection,
|
||||
getMongoDBCollection,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { createDatabase } from "./createDatabase";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as ARMTypes from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { createMongoCollectionWithProxy } from "../MongoProxyClient";
|
||||
import { createDatabase } from "./createDatabase";
|
||||
|
||||
export const createCollection = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||
const clearMessage = logConsoleProgress(
|
||||
@@ -46,7 +45,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
|
||||
await createDatabase(createDatabaseParams);
|
||||
}
|
||||
collection = await createCollectionWithARM(params);
|
||||
} else if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
collection = await createMongoCollectionWithProxy(params);
|
||||
} else {
|
||||
collection = await createCollectionWithSDK(params);
|
||||
@@ -63,17 +62,17 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
|
||||
};
|
||||
|
||||
const createCollectionWithARM = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
return createSqlContainer(params);
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return createMongoCollection(params);
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return createCassandraTable(params);
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return createGraph(params);
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
return createTable(params);
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DatabaseResponse } from "@azure/cosmos";
|
||||
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import {
|
||||
CassandraKeyspaceCreateUpdateParameters,
|
||||
GremlinDatabaseCreateUpdateParameters,
|
||||
MongoDBDatabaseCreateUpdateParameters,
|
||||
SqlDatabaseCreateUpdateParameters,
|
||||
CreateUpdateOptions,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateCassandraKeyspace,
|
||||
getCassandraKeyspace,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
createUpdateMongoDBDatabase,
|
||||
getMongoDBDatabase,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
createUpdateGremlinDatabase,
|
||||
getGremlinDatabase,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import {
|
||||
createUpdateMongoDBDatabase,
|
||||
getMongoDBDatabase,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
CassandraKeyspaceCreateUpdateParameters,
|
||||
CreateUpdateOptions,
|
||||
GremlinDatabaseCreateUpdateParameters,
|
||||
MongoDBDatabaseCreateUpdateParameters,
|
||||
SqlDatabaseCreateUpdateParameters,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export async function createDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
||||
const clearMessage = logConsoleProgress(`Creating a new database ${params.databaseId}`);
|
||||
try {
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
throw new Error("Creating database resources is not allowed for tables accounts");
|
||||
}
|
||||
const database: DataModels.Database = await (userContext.authType === AuthType.AAD && !userContext.useSDKOperations
|
||||
@@ -49,15 +48,15 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
|
||||
}
|
||||
|
||||
async function createDatabaseWithARM(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
return createSqlDatabase(params);
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return createMongoDatabase(params);
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return createCassandraKeyspace(params);
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return createGremlineDatabase(params);
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createStoredProcedure(
|
||||
databaseId: string,
|
||||
@@ -21,11 +20,7 @@ export async function createStoredProcedure(
|
||||
): Promise<StoredProcedureDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, TriggerDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
SqlTriggerCreateUpdateParameters,
|
||||
SqlTriggerResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createTrigger(
|
||||
databaseId: string,
|
||||
@@ -18,11 +17,7 @@ export async function createTrigger(
|
||||
): Promise<TriggerDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createUserDefinedFunction(
|
||||
databaseId: string,
|
||||
@@ -21,11 +20,7 @@ export async function createUserDefinedFunction(
|
||||
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
jest.mock("../../Utils/arm/request");
|
||||
jest.mock("../MessageHandler");
|
||||
jest.mock("../CosmosClient");
|
||||
import { deleteCollection } from "./deleteCollection";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { client } from "../CosmosClient";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { client } from "../CosmosClient";
|
||||
import { deleteCollection } from "./deleteCollection";
|
||||
|
||||
describe("deleteCollection", () => {
|
||||
beforeAll(() => {
|
||||
@@ -15,7 +14,7 @@ describe("deleteCollection", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { deleteCassandraTable } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { deleteGremlinGraph } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { deleteTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteCassandraTable } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { deleteGremlinGraph } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { deleteTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
|
||||
@@ -31,18 +30,18 @@ function deleteCollectionWithARM(databaseId: string, collectionId: string): Prom
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
return deleteSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return deleteMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return deleteCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return deleteGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
return deleteTable(subscriptionId, resourceGroup, accountName, collectionId);
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
jest.mock("../../Utils/arm/request");
|
||||
jest.mock("../MessageHandler");
|
||||
jest.mock("../CosmosClient");
|
||||
import { deleteDatabase } from "./deleteDatabase";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { client } from "../CosmosClient";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { client } from "../CosmosClient";
|
||||
import { deleteDatabase } from "./deleteDatabase";
|
||||
|
||||
describe("deleteDatabase", () => {
|
||||
beforeAll(() => {
|
||||
@@ -15,7 +14,7 @@ describe("deleteDatabase", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { deleteCassandraKeyspace } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteCassandraKeyspace } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`);
|
||||
|
||||
try {
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
throw new Error("Deleting database resources is not allowed for tables accounts");
|
||||
}
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
@@ -34,16 +33,16 @@ function deleteDatabaseWithARM(databaseId: string): Promise<void> {
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
return deleteSqlDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return deleteMongoDBDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return deleteCassandraKeyspace(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return deleteGremlinDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { client } from "../CosmosClient";
|
||||
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteStoredProcedure(
|
||||
databaseId: string,
|
||||
@@ -13,11 +12,7 @@ export async function deleteStoredProcedure(
|
||||
): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { client } from "../CosmosClient";
|
||||
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { client } from "../CosmosClient";
|
||||
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
jest.mock("../CosmosClient");
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { readCollection } from "./readCollection";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
|
||||
describe("readCollection", () => {
|
||||
beforeAll(() => {
|
||||
@@ -13,7 +12,7 @@ describe("readCollection", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Offer, ReadCollectionOfferParams } from "../../Contracts/DataModels";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getSqlContainerThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { getMongoDBCollectionThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getCassandraTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { getGremlinGraphThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { getMongoDBCollectionThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { getSqlContainerThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { getTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { readOfferWithSDK } from "./readOfferWithSDK";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export const readCollectionOffer = async (params: ReadCollectionOfferParams): Promise<Offer> => {
|
||||
const clearMessage = logConsoleProgress(`Querying offer for collection ${params.collectionId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readCollectionOfferWithARM(params.databaseId, params.collectionId);
|
||||
}
|
||||
|
||||
@@ -36,12 +31,12 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
let rpResponse;
|
||||
try {
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
rpResponse = await getSqlContainerThroughput(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
@@ -50,7 +45,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
collectionId
|
||||
);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
rpResponse = await getMongoDBCollectionThroughput(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
@@ -59,7 +54,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
collectionId
|
||||
);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
rpResponse = await getCassandraTableThroughput(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
@@ -68,7 +63,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
collectionId
|
||||
);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
rpResponse = await getGremlinGraphThroughput(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
@@ -77,7 +72,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
collectionId
|
||||
);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
rpResponse = await getTableThroughput(subscriptionId, resourceGroup, accountName, collectionId);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -2,11 +2,10 @@ jest.mock("../../Utils/arm/request");
|
||||
jest.mock("../CosmosClient");
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { client } from "../CosmosClient";
|
||||
import { readCollections } from "./readCollections";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
|
||||
describe("readCollections", () => {
|
||||
beforeAll(() => {
|
||||
@@ -14,7 +13,7 @@ describe("readCollections", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listCassandraTables } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
@@ -14,11 +13,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readCollectionsWithARM(databaseId);
|
||||
}
|
||||
|
||||
@@ -37,22 +32,22 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
rpResponse = await listSqlContainers(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
rpResponse = await listMongoDBCollections(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
rpResponse = await listCassandraTables(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
rpResponse = await listGremlinGraphs(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
rpResponse = await listTables(subscriptionId, resourceGroup, accountName);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
|
||||
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { readOfferWithSDK } from "./readOfferWithSDK";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => {
|
||||
const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readDatabaseOfferWithARM(params.databaseId);
|
||||
}
|
||||
|
||||
@@ -35,21 +30,21 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
let rpResponse;
|
||||
try {
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
rpResponse = await getSqlDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
rpResponse = await getMongoDBDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
rpResponse = await getCassandraKeyspaceThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
rpResponse = await getGremlinDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -2,11 +2,10 @@ jest.mock("../../Utils/arm/request");
|
||||
jest.mock("../CosmosClient");
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
import { armRequest } from "../../Utils/arm/request";
|
||||
import { client } from "../CosmosClient";
|
||||
import { readDatabases } from "./readDatabases";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
|
||||
describe("readDatabases", () => {
|
||||
beforeAll(() => {
|
||||
@@ -14,7 +13,7 @@ describe("readDatabases", () => {
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { listSqlDatabases } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { listSqlDatabases } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export async function readDatabases(): Promise<DataModels.Database[]> {
|
||||
let databases: DataModels.Database[];
|
||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
databases = await readDatabasesWithARM();
|
||||
} else {
|
||||
const sdkResponse = await client().databases.readAll().fetchAll();
|
||||
@@ -37,19 +32,19 @@ async function readDatabasesWithARM(): Promise<DataModels.Database[]> {
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
rpResponse = await listSqlDatabases(subscriptionId, resourceGroup, accountName);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
rpResponse = await listMongoDBDatabases(subscriptionId, resourceGroup, accountName);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
rpResponse = await listCassandraKeyspaces(subscriptionId, resourceGroup, accountName);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
rpResponse = await listGremlinDatabases(subscriptionId, resourceGroup, accountName);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readStoredProcedures(
|
||||
databaseId: string,
|
||||
@@ -13,11 +12,7 @@ export async function readStoredProcedures(
|
||||
): Promise<(StoredProcedureDefinition & Resource)[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const rpResponse = await listSqlStoredProcedures(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, TriggerDefinition } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listSqlTriggers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readTriggers(
|
||||
@@ -13,11 +12,7 @@ export async function readTriggers(
|
||||
): Promise<(TriggerDefinition & Resource)[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const rpResponse = await listSqlTriggers(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listSqlUserDefinedFunctions } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readUserDefinedFunctions(
|
||||
databaseId: string,
|
||||
@@ -13,11 +12,7 @@ export async function readUserDefinedFunctions(
|
||||
): Promise<(UserDefinedFunctionDefinition & Resource)[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const rpResponse = await listSqlUserDefinedFunctions(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ContainerDefinition } from "@azure/cosmos";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { Collection } from "../../Contracts/DataModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateCassandraTable,
|
||||
@@ -38,11 +37,7 @@ export async function updateCollection(
|
||||
const clearMessage = logConsoleProgress(`Updating container ${collectionId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
collection = await updateCollectionWithARM(databaseId, collectionId, newCollection);
|
||||
} else {
|
||||
const sdkResponse = await client()
|
||||
@@ -71,18 +66,18 @@ async function updateCollectionWithARM(
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const resourceGroup = userContext.resourceGroup;
|
||||
const accountName = userContext.databaseAccount.name;
|
||||
const defaultExperience = userContext.defaultExperience;
|
||||
const defaultExperience = userContext.apiType;
|
||||
|
||||
switch (defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case "SQL":
|
||||
return updateSqlContainer(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return updateCassandraTable(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return updateMongoDBCollection(
|
||||
databaseId,
|
||||
collectionId,
|
||||
|
||||
@@ -1,54 +1,53 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { HttpHeaders } from "../Constants";
|
||||
import { Offer, SDKOfferDefinition, UpdateOfferParams } from "../../Contracts/DataModels";
|
||||
import { OfferDefinition } from "@azure/cosmos";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { parseSDKOfferResponse } from "../OfferUtility";
|
||||
import { readCollectionOffer } from "./readCollectionOffer";
|
||||
import { readDatabaseOffer } from "./readDatabaseOffer";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { Offer, SDKOfferDefinition, UpdateOfferParams } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
updateSqlDatabaseThroughput,
|
||||
migrateSqlDatabaseToAutoscale,
|
||||
migrateSqlDatabaseToManualThroughput,
|
||||
migrateSqlContainerToAutoscale,
|
||||
migrateSqlContainerToManualThroughput,
|
||||
updateSqlContainerThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
updateCassandraKeyspaceThroughput,
|
||||
migrateCassandraKeyspaceToAutoscale,
|
||||
migrateCassandraKeyspaceToManualThroughput,
|
||||
migrateCassandraTableToAutoscale,
|
||||
migrateCassandraTableToManualThroughput,
|
||||
updateCassandraKeyspaceThroughput,
|
||||
updateCassandraTableThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
updateMongoDBDatabaseThroughput,
|
||||
migrateMongoDBDatabaseToAutoscale,
|
||||
migrateMongoDBDatabaseToManualThroughput,
|
||||
migrateMongoDBCollectionToAutoscale,
|
||||
migrateMongoDBCollectionToManualThroughput,
|
||||
updateMongoDBCollectionThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
updateGremlinDatabaseThroughput,
|
||||
migrateGremlinDatabaseToAutoscale,
|
||||
migrateGremlinDatabaseToManualThroughput,
|
||||
migrateGremlinGraphToAutoscale,
|
||||
migrateGremlinGraphToManualThroughput,
|
||||
updateGremlinDatabaseThroughput,
|
||||
updateGremlinGraphThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
migrateMongoDBCollectionToAutoscale,
|
||||
migrateMongoDBCollectionToManualThroughput,
|
||||
migrateMongoDBDatabaseToAutoscale,
|
||||
migrateMongoDBDatabaseToManualThroughput,
|
||||
updateMongoDBCollectionThroughput,
|
||||
updateMongoDBDatabaseThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
migrateSqlContainerToAutoscale,
|
||||
migrateSqlContainerToManualThroughput,
|
||||
migrateSqlDatabaseToAutoscale,
|
||||
migrateSqlDatabaseToManualThroughput,
|
||||
updateSqlContainerThroughput,
|
||||
updateSqlDatabaseThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
migrateTableToAutoscale,
|
||||
migrateTableToManualThroughput,
|
||||
updateTableThroughput,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { HttpHeaders } from "../Constants";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { parseSDKOfferResponse } from "../OfferUtility";
|
||||
import { readCollectionOffer } from "./readCollectionOffer";
|
||||
import { readDatabaseOffer } from "./readDatabaseOffer";
|
||||
|
||||
export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => {
|
||||
let updatedOffer: Offer;
|
||||
@@ -61,7 +60,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (params.collectionId) {
|
||||
updatedOffer = await updateCollectionOfferWithARM(params);
|
||||
} else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
} else if (userContext.apiType === "Tables") {
|
||||
// update table's database offer with SDK since RP doesn't support it
|
||||
updatedOffer = await updateOfferWithSDK(params);
|
||||
} else {
|
||||
@@ -82,24 +81,24 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
|
||||
const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<Offer> => {
|
||||
try {
|
||||
switch (userContext.defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
switch (userContext.apiType) {
|
||||
case "SQL":
|
||||
await updateSqlContainerOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
await updateMongoCollectionOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
await updateCassandraTableOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
await updateGremlinGraphOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Tables":
|
||||
await updateTableOffer(params);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${userContext.defaultExperience}`);
|
||||
throw new Error(`Unsupported default experience type: ${userContext.apiType}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code !== "MethodNotAllowed") {
|
||||
@@ -116,21 +115,21 @@ const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<
|
||||
|
||||
const updateDatabaseOfferWithARM = async (params: UpdateOfferParams): Promise<Offer> => {
|
||||
try {
|
||||
switch (userContext.defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
switch (userContext.apiType) {
|
||||
case "SQL":
|
||||
await updateSqlDatabaseOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
await updateMongoDatabaseOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
await updateCassandraKeyspaceOffer(params);
|
||||
break;
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
await updateGremlinDatabaseOffer(params);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${userContext.defaultExperience}`);
|
||||
throw new Error(`Unsupported default experience type: ${userContext.apiType}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code !== "MethodNotAllowed") {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateStoredProcedure(
|
||||
databaseId: string,
|
||||
@@ -21,11 +20,7 @@ export async function updateStoredProcedure(
|
||||
): Promise<StoredProcedureDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const getResponse = await getSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
SqlTriggerCreateUpdateParameters,
|
||||
SqlTriggerResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateTrigger(
|
||||
databaseId: string,
|
||||
@@ -18,11 +17,7 @@ export async function updateTrigger(
|
||||
): Promise<TriggerDefinition> {
|
||||
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const getResponse = await getSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateUserDefinedFunction(
|
||||
databaseId: string,
|
||||
@@ -21,11 +20,7 @@ export async function updateUserDefinedFunction(
|
||||
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const getResponse = await getSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
export enum DefaultAccountExperienceType {
|
||||
DocumentDB = "DocumentDB",
|
||||
Graph = "Graph",
|
||||
MongoDB = "MongoDB",
|
||||
Table = "Table",
|
||||
Cassandra = "Cassandra",
|
||||
ApiForMongoDB = "Azure Cosmos DB for MongoDB API",
|
||||
}
|
||||
@@ -20,51 +20,6 @@ describe("Component Registerer", () => {
|
||||
expect(ko.components.isRegistered("json-editor")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register documents-tab component", () => {
|
||||
expect(ko.components.isRegistered("documents-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register stored-procedure-tab component", () => {
|
||||
expect(ko.components.isRegistered("stored-procedure-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register trigger-tab component", () => {
|
||||
expect(ko.components.isRegistered("trigger-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register user-defined-function-tab component", () => {
|
||||
expect(ko.components.isRegistered("user-defined-function-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register settings-tab-v2 component", () => {
|
||||
expect(ko.components.isRegistered("database-settings-tab-v2")).toBe(true);
|
||||
expect(ko.components.isRegistered("collection-settings-tab-v2")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register query-tab component", () => {
|
||||
expect(ko.components.isRegistered("query-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register tables-query-tab component", () => {
|
||||
expect(ko.components.isRegistered("tables-query-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register graph-tab component", () => {
|
||||
expect(ko.components.isRegistered("graph-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register notebookv2-tab component", () => {
|
||||
expect(ko.components.isRegistered("notebookv2-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register terminal-tab component", () => {
|
||||
expect(ko.components.isRegistered("terminal-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register mongo-shell-tab component", () => {
|
||||
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
|
||||
});
|
||||
|
||||
it("should registeradd-collection-pane component", () => {
|
||||
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
||||
});
|
||||
@@ -73,10 +28,6 @@ describe("Component Registerer", () => {
|
||||
expect(ko.components.isRegistered("graph-styling-pane")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register string-input-pane component", () => {
|
||||
expect(ko.components.isRegistered("string-input-pane")).toBe(true);
|
||||
});
|
||||
|
||||
it("should register dynamic-list component", () => {
|
||||
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -8,21 +8,6 @@ import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||
import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleComponent";
|
||||
import * as PaneComponents from "./Panes/PaneComponents";
|
||||
import ConflictsTab from "./Tabs/ConflictsTab";
|
||||
import DocumentsTab from "./Tabs/DocumentsTab";
|
||||
import GalleryTab from "./Tabs/GalleryTab";
|
||||
import GraphTab from "./Tabs/GraphTab";
|
||||
import MongoShellTab from "./Tabs/MongoShellTab";
|
||||
import NotebookTabV2 from "./Tabs/NotebookV2Tab";
|
||||
import NotebookViewerTab from "./Tabs/NotebookViewerTab";
|
||||
import QueryTab from "./Tabs/QueryTab";
|
||||
import QueryTablesTab from "./Tabs/QueryTablesTab";
|
||||
import SchemaAnalyzerTab from "./Tabs/SchemaAnalyzerTab";
|
||||
import { DatabaseSettingsTabV2, SettingsTabV2 } from "./Tabs/SettingsTabV2";
|
||||
import StoredProcedureTab from "./Tabs/StoredProcedureTab";
|
||||
import TerminalTab from "./Tabs/TerminalTab";
|
||||
import TriggerTab from "./Tabs/TriggerTab";
|
||||
import UserDefinedFunctionTab from "./Tabs/UserDefinedFunctionTab";
|
||||
|
||||
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
||||
ko.components.register("error-display", new ErrorDisplayComponent());
|
||||
@@ -33,26 +18,6 @@ ko.components.register("diff-editor", new DiffEditorComponent());
|
||||
ko.components.register("dynamic-list", DynamicListComponent);
|
||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||
|
||||
// Collection Tabs
|
||||
[
|
||||
DocumentsTab,
|
||||
StoredProcedureTab,
|
||||
TriggerTab,
|
||||
UserDefinedFunctionTab,
|
||||
SettingsTabV2,
|
||||
QueryTab,
|
||||
QueryTablesTab,
|
||||
GraphTab,
|
||||
MongoShellTab,
|
||||
ConflictsTab,
|
||||
NotebookTabV2,
|
||||
TerminalTab,
|
||||
GalleryTab,
|
||||
NotebookViewerTab,
|
||||
DatabaseSettingsTabV2,
|
||||
SchemaAnalyzerTab,
|
||||
].forEach(({ component: { name, template } }) => ko.components.register(name, { template }));
|
||||
|
||||
// Panes
|
||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||
@@ -60,5 +25,4 @@ ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPane
|
||||
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
|
||||
ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEntityPaneComponent());
|
||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
||||
|
||||
@@ -10,7 +10,6 @@ import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
|
||||
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
|
||||
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
||||
import { userContext } from "../UserContext";
|
||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "./Explorer";
|
||||
@@ -39,7 +38,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
||||
},
|
||||
];
|
||||
|
||||
if (userContext.defaultExperience !== DefaultAccountExperienceType.Table) {
|
||||
if (userContext.apiType !== "Tables") {
|
||||
items.push({
|
||||
iconSrc: DeleteDatabaseIcon,
|
||||
onClick: () => container.openDeleteDatabaseConfirmationPane(),
|
||||
@@ -63,7 +62,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
||||
});
|
||||
}
|
||||
|
||||
if (container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||
import template from "./diff-editor-component.html";
|
||||
import * as monaco from "monaco-editor";
|
||||
|
||||
/**
|
||||
* Helper class for ko component registration
|
||||
@@ -92,7 +92,7 @@ export class DiffEditorViewModel {
|
||||
/**
|
||||
* Create the monaco editor on diff mode and attach to DOM
|
||||
*/
|
||||
protected createDiffEditor(
|
||||
protected async createDiffEditor(
|
||||
originalContent: string,
|
||||
modifiedContent: string,
|
||||
createCallback: (e: monaco.editor.IStandaloneDiffEditor) => void
|
||||
@@ -111,7 +111,7 @@ export class DiffEditorViewModel {
|
||||
}
|
||||
|
||||
const language = this.params.editorLanguage || "json";
|
||||
|
||||
const monaco = await loadMonaco();
|
||||
const originalModel = monaco.editor.createModel(originalContent, language);
|
||||
const modifiedModel = monaco.editor.createModel(modifiedContent, language);
|
||||
const diffEditor: monaco.editor.IStandaloneDiffEditor = monaco.editor.createDiffEditor(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||
import { JsonEditorParams, JsonEditorViewModel } from "../JsonEditor/JsonEditorComponent";
|
||||
import template from "./editor-component.html";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { SqlCompletionItemProvider, ErrorMarkProvider } from "@azure/cosmos-language-service";
|
||||
|
||||
/**
|
||||
* Helper class for ko component registration
|
||||
@@ -49,15 +48,17 @@ class EditorViewModel extends JsonEditorViewModel {
|
||||
return this.params.contentType;
|
||||
}
|
||||
|
||||
protected registerCompletionItemProvider() {
|
||||
let sqlCompletionItemProvider = new SqlCompletionItemProvider();
|
||||
protected async registerCompletionItemProvider() {
|
||||
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
|
||||
monaco.languages.registerCompletionItemProvider("sql", sqlCompletionItemProvider);
|
||||
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
|
||||
const monaco = await loadMonaco();
|
||||
monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider());
|
||||
EditorViewModel.providerRegistered.push("sql");
|
||||
}
|
||||
}
|
||||
|
||||
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
|
||||
protected async getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
|
||||
const { ErrorMarkProvider } = await import("@azure/cosmos-language-service");
|
||||
return ErrorMarkProvider.getErrorMark(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||
|
||||
export interface EditorReactProps {
|
||||
language: string;
|
||||
@@ -61,7 +61,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
|
||||
/**
|
||||
* Create the monaco editor and attach to DOM
|
||||
*/
|
||||
private createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
const options: monaco.editor.IEditorConstructionOptions = {
|
||||
value: this.props.content,
|
||||
language: this.props.language,
|
||||
@@ -74,6 +74,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
|
||||
};
|
||||
|
||||
this.rootNode.innerHTML = "";
|
||||
const monaco = await loadMonaco();
|
||||
createCallback(monaco.editor.create(this.rootNode, options));
|
||||
}
|
||||
|
||||
|
||||
@@ -128,21 +128,21 @@ class InputTypeaheadViewModel {
|
||||
},
|
||||
},
|
||||
callback: {
|
||||
onClick: (node: any, a: any, item: OnClickItem, event: any) => {
|
||||
onClick: (_node: unknown, _a: unknown, item: OnClickItem) => {
|
||||
cache.selection = item;
|
||||
|
||||
if (params.selection) {
|
||||
params.selection(item);
|
||||
}
|
||||
},
|
||||
onResult(node: any, query: any, result: any, resultCount: any, resultCountPerGroup: any) {
|
||||
onResult(_node: unknown, query: any) {
|
||||
cache.inputValue = query;
|
||||
if (params.inputValue) {
|
||||
params.inputValue(query);
|
||||
}
|
||||
},
|
||||
},
|
||||
template: (query: string, item: any) => {
|
||||
template: (_query: string, item: any) => {
|
||||
// Don't display id if caption *IS* the id
|
||||
return item.caption === item.value
|
||||
? "<span>{{caption}}</span>"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Q from "q";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { loadMonaco, monaco } from "../../LazyMonaco";
|
||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||
import template from "./json-editor-component.html";
|
||||
|
||||
@@ -88,7 +87,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
||||
/**
|
||||
* Create the monaco editor and attach to DOM
|
||||
*/
|
||||
protected createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
this.registerCompletionItemProvider();
|
||||
this.editorContainer = document.getElementById(this.getEditorId());
|
||||
const options: monaco.editor.IEditorConstructionOptions = {
|
||||
@@ -102,6 +101,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
||||
};
|
||||
|
||||
this.editorContainer.innerHTML = "";
|
||||
const monaco = await loadMonaco();
|
||||
createCallback(monaco.editor.create(this.editorContainer, options));
|
||||
}
|
||||
|
||||
@@ -109,15 +109,16 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
||||
protected registerCompletionItemProvider() {}
|
||||
|
||||
// Interface. Will be implemented in children editor view model such as EditorViewModel.
|
||||
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
|
||||
return Q.Promise(() => {});
|
||||
protected async getErrorMarkers(_: string): Promise<monaco.editor.IMarkerData[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected getEditorLanguage(): string {
|
||||
return "json";
|
||||
}
|
||||
|
||||
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
||||
protected async configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
|
||||
const monaco = await loadMonaco();
|
||||
this.editor = editor;
|
||||
const queryEditorModel = this.editor.getModel();
|
||||
if (!this.params.isReadOnly && this.params.updatedContent) {
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { Card } from "@uifabric/react-cards";
|
||||
import {
|
||||
BaseButton,
|
||||
Button,
|
||||
FontWeights,
|
||||
Icon,
|
||||
IconButton,
|
||||
Image,
|
||||
ImageFit,
|
||||
Persona,
|
||||
Text,
|
||||
Link,
|
||||
BaseButton,
|
||||
Button,
|
||||
LinkBase,
|
||||
Persona,
|
||||
Separator,
|
||||
TooltipHost,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
Text,
|
||||
TooltipHost,
|
||||
} from "office-ui-fabric-react";
|
||||
import * as React from "react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
|
||||
import { IGalleryItem } from "../../../../Juno/JunoClient";
|
||||
import * as FileSystemUtil from "../../../Notebook/FileSystemUtil";
|
||||
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
|
||||
|
||||
export interface GalleryCardComponentProps {
|
||||
data: IGalleryItem;
|
||||
@@ -34,166 +34,48 @@ export interface GalleryCardComponentProps {
|
||||
onDeleteClick: (beforeDelete: () => void, afterDelete: () => void) => void;
|
||||
}
|
||||
|
||||
interface GalleryCardComponentState {
|
||||
isDeletingPublishedNotebook: boolean;
|
||||
}
|
||||
export const GalleryCardComponent: FunctionComponent<GalleryCardComponentProps> = ({
|
||||
data,
|
||||
isFavorite,
|
||||
showDownload,
|
||||
showDelete,
|
||||
onClick,
|
||||
onTagClick,
|
||||
onFavoriteClick,
|
||||
onUnfavoriteClick,
|
||||
onDownloadClick,
|
||||
onDeleteClick,
|
||||
}: GalleryCardComponentProps) => {
|
||||
const CARD_WIDTH = 256;
|
||||
const cardImageHeight = 144;
|
||||
const cardDescriptionMaxChars = 80;
|
||||
const cardItemGapBig = 10;
|
||||
const cardItemGapSmall = 8;
|
||||
const cardDeleteSpinnerHeight = 360;
|
||||
const smallTextLineHeight = 18;
|
||||
|
||||
export class GalleryCardComponent extends React.Component<GalleryCardComponentProps, GalleryCardComponentState> {
|
||||
public static readonly CARD_WIDTH = 256;
|
||||
private static readonly cardImageHeight = 144;
|
||||
public static readonly cardHeightToWidthRatio =
|
||||
GalleryCardComponent.cardImageHeight / GalleryCardComponent.CARD_WIDTH;
|
||||
private static readonly cardDescriptionMaxChars = 80;
|
||||
private static readonly cardItemGapBig = 10;
|
||||
private static readonly cardItemGapSmall = 8;
|
||||
private static readonly cardDeleteSpinnerHeight = 360;
|
||||
private static readonly smallTextLineHeight = 18;
|
||||
const [isDeletingPublishedNotebook, setIsDeletingPublishedNotebook] = useState<boolean>(false);
|
||||
|
||||
constructor(props: GalleryCardComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isDeletingPublishedNotebook: false,
|
||||
};
|
||||
}
|
||||
const cardButtonsVisible = isFavorite !== undefined || showDownload || showDelete;
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
};
|
||||
const dateString = new Date(data.created).toLocaleString("default", options);
|
||||
const cardTitle = FileSystemUtil.stripExtension(data.name, "ipynb");
|
||||
|
||||
public render(): JSX.Element {
|
||||
const cardButtonsVisible = this.props.isFavorite !== undefined || this.props.showDownload || this.props.showDelete;
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
};
|
||||
const dateString = new Date(this.props.data.created).toLocaleString("default", options);
|
||||
const cardTitle = FileSystemUtil.stripExtension(this.props.data.name, "ipynb");
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ background: "white" }}
|
||||
aria-label={cardTitle}
|
||||
data-is-focusable="true"
|
||||
tokens={{ width: GalleryCardComponent.CARD_WIDTH, childrenGap: 0 }}
|
||||
onClick={(event) => this.onClick(event, this.props.onClick)}
|
||||
>
|
||||
{this.state.isDeletingPublishedNotebook && (
|
||||
<Card.Item tokens={{ padding: GalleryCardComponent.cardItemGapBig }}>
|
||||
<Spinner
|
||||
size={SpinnerSize.large}
|
||||
label={`Deleting '${cardTitle}'`}
|
||||
styles={{ root: { height: GalleryCardComponent.cardDeleteSpinnerHeight } }}
|
||||
/>
|
||||
</Card.Item>
|
||||
)}
|
||||
{!this.state.isDeletingPublishedNotebook && (
|
||||
<>
|
||||
<Card.Item tokens={{ padding: GalleryCardComponent.cardItemGapBig }}>
|
||||
<Persona
|
||||
imageUrl={this.props.data.isSample && CosmosDBLogo}
|
||||
text={this.props.data.author}
|
||||
secondaryText={dateString}
|
||||
/>
|
||||
</Card.Item>
|
||||
|
||||
<Card.Item>
|
||||
<Image
|
||||
src={this.props.data.thumbnailUrl}
|
||||
width={GalleryCardComponent.CARD_WIDTH}
|
||||
height={GalleryCardComponent.cardImageHeight}
|
||||
imageFit={ImageFit.cover}
|
||||
alt={`${cardTitle} cover image`}
|
||||
/>
|
||||
</Card.Item>
|
||||
|
||||
<Card.Section styles={{ root: { padding: GalleryCardComponent.cardItemGapBig } }}>
|
||||
<Text variant="small" nowrap styles={{ root: { height: GalleryCardComponent.smallTextLineHeight } }}>
|
||||
{this.props.data.tags ? (
|
||||
this.props.data.tags.map((tag, index, array) => (
|
||||
<span key={tag}>
|
||||
<Link onClick={(event) => this.onClick(event, () => this.props.onTagClick(tag))}>{tag}</Link>
|
||||
{index === array.length - 1 ? <></> : ", "}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<br />
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
styles={{
|
||||
root: {
|
||||
fontWeight: FontWeights.semibold,
|
||||
paddingTop: GalleryCardComponent.cardItemGapSmall,
|
||||
paddingBottom: GalleryCardComponent.cardItemGapSmall,
|
||||
},
|
||||
}}
|
||||
nowrap
|
||||
>
|
||||
{cardTitle}
|
||||
</Text>
|
||||
|
||||
<Text variant="small" styles={{ root: { height: GalleryCardComponent.smallTextLineHeight * 2 } }}>
|
||||
{this.renderTruncatedDescription()}
|
||||
</Text>
|
||||
|
||||
<span>
|
||||
{this.props.data.views !== undefined &&
|
||||
this.generateIconText("RedEye", this.props.data.views.toString())}
|
||||
{this.props.data.downloads !== undefined &&
|
||||
this.generateIconText("Download", this.props.data.downloads.toString())}
|
||||
{this.props.data.favorites !== undefined &&
|
||||
this.generateIconText("Heart", this.props.data.favorites.toString())}
|
||||
</span>
|
||||
</Card.Section>
|
||||
|
||||
{cardButtonsVisible && (
|
||||
<Card.Section
|
||||
styles={{
|
||||
root: {
|
||||
marginLeft: GalleryCardComponent.cardItemGapBig,
|
||||
marginRight: GalleryCardComponent.cardItemGapBig,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Separator styles={{ root: { padding: 0, height: 1 } }} />
|
||||
|
||||
<span>
|
||||
{this.props.isFavorite !== undefined &&
|
||||
this.generateIconButtonWithTooltip(
|
||||
this.props.isFavorite ? "HeartFill" : "Heart",
|
||||
this.props.isFavorite ? "Unfavorite" : "Favorite",
|
||||
"left",
|
||||
this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick
|
||||
)}
|
||||
|
||||
{this.props.showDownload &&
|
||||
this.generateIconButtonWithTooltip("Download", "Download", "left", this.props.onDownloadClick)}
|
||||
|
||||
{this.props.showDelete &&
|
||||
this.generateIconButtonWithTooltip("Delete", "Remove", "right", () =>
|
||||
this.props.onDeleteClick(
|
||||
() => this.setState({ isDeletingPublishedNotebook: true }),
|
||||
() => this.setState({ isDeletingPublishedNotebook: false })
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</Card.Section>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
private renderTruncatedDescription = (): string => {
|
||||
let truncatedDescription = this.props.data.description.substr(0, GalleryCardComponent.cardDescriptionMaxChars);
|
||||
if (this.props.data.description.length > GalleryCardComponent.cardDescriptionMaxChars) {
|
||||
const renderTruncatedDescription = (): string => {
|
||||
let truncatedDescription = data.description.substr(0, cardDescriptionMaxChars);
|
||||
if (data.description.length > cardDescriptionMaxChars) {
|
||||
truncatedDescription = `${truncatedDescription} ...`;
|
||||
}
|
||||
return truncatedDescription;
|
||||
};
|
||||
|
||||
private generateIconText = (iconName: string, text: string): JSX.Element => {
|
||||
const generateIconText = (iconName: string, text: string): JSX.Element => {
|
||||
return (
|
||||
<Text variant="tiny" styles={{ root: { color: "#605E5C", paddingRight: GalleryCardComponent.cardItemGapSmall } }}>
|
||||
<Text variant="tiny" styles={{ root: { color: "#605E5C", paddingRight: cardItemGapSmall } }}>
|
||||
<Icon iconName={iconName} styles={{ root: { verticalAlign: "middle" } }} /> {text}
|
||||
</Text>
|
||||
);
|
||||
@@ -203,7 +85,7 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
* Fluent UI doesn't support tooltips on IconButtons out of the box. In the meantime the recommendation is
|
||||
* to do the following (from https://developer.microsoft.com/en-us/fluentui#/controls/web/button)
|
||||
*/
|
||||
private generateIconButtonWithTooltip = (
|
||||
const generateIconButtonWithTooltip = (
|
||||
iconName: string,
|
||||
title: string,
|
||||
horizontalAlign: "right" | "left",
|
||||
@@ -220,13 +102,13 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
iconProps={{ iconName }}
|
||||
title={title}
|
||||
ariaLabel={title}
|
||||
onClick={(event) => this.onClick(event, activate)}
|
||||
onClick={(event) => handlerOnClick(event, activate)}
|
||||
/>
|
||||
</TooltipHost>
|
||||
);
|
||||
};
|
||||
|
||||
private onClick = (
|
||||
const handlerOnClick = (
|
||||
event:
|
||||
| React.MouseEvent<HTMLElement | HTMLAnchorElement | HTMLButtonElement | LinkBase, MouseEvent>
|
||||
| React.MouseEvent<
|
||||
@@ -239,4 +121,112 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
event.preventDefault();
|
||||
activate();
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ background: "white" }}
|
||||
aria-label={cardTitle}
|
||||
data-is-focusable="true"
|
||||
tokens={{ width: CARD_WIDTH, childrenGap: 0 }}
|
||||
onClick={(event) => handlerOnClick(event, onClick)}
|
||||
>
|
||||
{isDeletingPublishedNotebook && (
|
||||
<Card.Item tokens={{ padding: cardItemGapBig }}>
|
||||
<Spinner
|
||||
size={SpinnerSize.large}
|
||||
label={`Deleting '${cardTitle}'`}
|
||||
styles={{ root: { height: cardDeleteSpinnerHeight } }}
|
||||
/>
|
||||
</Card.Item>
|
||||
)}
|
||||
{!isDeletingPublishedNotebook && (
|
||||
<>
|
||||
<Card.Item tokens={{ padding: cardItemGapBig }}>
|
||||
<Persona imageUrl={data.isSample && CosmosDBLogo} text={data.author} secondaryText={dateString} />
|
||||
</Card.Item>
|
||||
|
||||
<Card.Item>
|
||||
<Image
|
||||
src={data.thumbnailUrl}
|
||||
width={CARD_WIDTH}
|
||||
height={cardImageHeight}
|
||||
imageFit={ImageFit.cover}
|
||||
alt={`${cardTitle} cover image`}
|
||||
/>
|
||||
</Card.Item>
|
||||
|
||||
<Card.Section styles={{ root: { padding: cardItemGapBig } }}>
|
||||
<Text variant="small" nowrap styles={{ root: { height: smallTextLineHeight } }}>
|
||||
{data.tags ? (
|
||||
data.tags.map((tag, index, array) => (
|
||||
<span key={tag}>
|
||||
<Link onClick={(event) => handlerOnClick(event, () => onTagClick(tag))}>{tag}</Link>
|
||||
{index === array.length - 1 ? <></> : ", "}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<br />
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
styles={{
|
||||
root: {
|
||||
fontWeight: FontWeights.semibold,
|
||||
paddingTop: cardItemGapSmall,
|
||||
paddingBottom: cardItemGapSmall,
|
||||
},
|
||||
}}
|
||||
nowrap
|
||||
>
|
||||
{cardTitle}
|
||||
</Text>
|
||||
|
||||
<Text variant="small" styles={{ root: { height: smallTextLineHeight * 2 } }}>
|
||||
{renderTruncatedDescription()}
|
||||
</Text>
|
||||
|
||||
<span>
|
||||
{data.views !== undefined && generateIconText("RedEye", data.views.toString())}
|
||||
{data.downloads !== undefined && generateIconText("Download", data.downloads.toString())}
|
||||
{data.favorites !== undefined && generateIconText("Heart", data.favorites.toString())}
|
||||
</span>
|
||||
</Card.Section>
|
||||
|
||||
{cardButtonsVisible && (
|
||||
<Card.Section
|
||||
styles={{
|
||||
root: {
|
||||
marginLeft: cardItemGapBig,
|
||||
marginRight: cardItemGapBig,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Separator styles={{ root: { padding: 0, height: 1 } }} />
|
||||
|
||||
<span>
|
||||
{isFavorite !== undefined &&
|
||||
generateIconButtonWithTooltip(
|
||||
isFavorite ? "HeartFill" : "Heart",
|
||||
isFavorite ? "Unfavorite" : "Favorite",
|
||||
"left",
|
||||
isFavorite ? onUnfavoriteClick : onFavoriteClick
|
||||
)}
|
||||
|
||||
{showDownload && generateIconButtonWithTooltip("Download", "Download", "left", onDownloadClick)}
|
||||
|
||||
{showDelete &&
|
||||
generateIconButtonWithTooltip("Delete", "Remove", "right", () =>
|
||||
onDeleteClick(
|
||||
() => setIsDeletingPublishedNotebook(true),
|
||||
() => setIsDeletingPublishedNotebook(false)
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
</Card.Section>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { JunoClient } from "../../../Juno/JunoClient";
|
||||
import { HttpStatusCodes, CodeOfConductEndpoints } from "../../../Common/Constants";
|
||||
import { Stack, Text, Checkbox, PrimaryButton, Link } from "office-ui-fabric-react";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
|
||||
export interface CodeOfConductComponentProps {
|
||||
junoClient: JunoClient;
|
||||
onAcceptCodeOfConduct: (result: boolean) => void;
|
||||
}
|
||||
|
||||
interface CodeOfConductComponentState {
|
||||
readCodeOfConduct: boolean;
|
||||
}
|
||||
|
||||
export class CodeOfConductComponent extends React.Component<CodeOfConductComponentProps, CodeOfConductComponentState> {
|
||||
private viewCodeOfConductTraced: boolean;
|
||||
private descriptionPara1: string;
|
||||
private descriptionPara2: string;
|
||||
private descriptionPara3: string;
|
||||
private link1: { label: string; url: string };
|
||||
|
||||
constructor(props: CodeOfConductComponentProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
readCodeOfConduct: false,
|
||||
};
|
||||
|
||||
this.descriptionPara1 = "Azure Cosmos DB Notebook Gallery - Code of Conduct";
|
||||
this.descriptionPara2 = "The notebook public gallery contains notebook samples shared by users of Azure Cosmos DB.";
|
||||
this.descriptionPara3 = "In order to view and publish your samples to the gallery, you must accept the ";
|
||||
this.link1 = { label: "code of conduct.", url: CodeOfConductEndpoints.codeOfConduct };
|
||||
}
|
||||
|
||||
private async acceptCodeOfConduct(): Promise<void> {
|
||||
const startKey = traceStart(Action.NotebooksGalleryAcceptCodeOfConduct);
|
||||
|
||||
try {
|
||||
const response = await this.props.junoClient.acceptCodeOfConduct();
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
||||
}
|
||||
|
||||
traceSuccess(Action.NotebooksGalleryAcceptCodeOfConduct, {}, startKey);
|
||||
|
||||
this.props.onAcceptCodeOfConduct(response.data);
|
||||
} catch (error) {
|
||||
traceFailure(
|
||||
Action.NotebooksGalleryAcceptCodeOfConduct,
|
||||
{
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
|
||||
handleError(error, "CodeOfConductComponent/acceptCodeOfConduct", "Failed to accept code of conduct");
|
||||
}
|
||||
}
|
||||
|
||||
private onChangeCheckbox = (): void => {
|
||||
this.setState({ readCodeOfConduct: !this.state.readCodeOfConduct });
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
if (!this.viewCodeOfConductTraced) {
|
||||
this.viewCodeOfConductTraced = true;
|
||||
trace(Action.NotebooksGalleryViewCodeOfConduct);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }}>
|
||||
<Stack.Item>
|
||||
<Text style={{ fontWeight: 500, fontSize: "20px" }}>{this.descriptionPara1}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>{this.descriptionPara2}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>
|
||||
{this.descriptionPara3}
|
||||
<Link href={this.link1.url} target="_blank">
|
||||
{this.link1.label}
|
||||
</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Checkbox
|
||||
styles={{
|
||||
label: {
|
||||
margin: 0,
|
||||
padding: "2 0 2 0",
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
},
|
||||
}}
|
||||
label="I have read and accept the code of conduct."
|
||||
onChange={this.onChangeCheckbox}
|
||||
/>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<PrimaryButton
|
||||
ariaLabel="Continue"
|
||||
title="Continue"
|
||||
onClick={async () => await this.acceptCodeOfConduct()}
|
||||
tabIndex={0}
|
||||
className="genericPaneSubmitBtn"
|
||||
text="Continue"
|
||||
disabled={!this.state.readCodeOfConduct}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
jest.mock("../../../Juno/JunoClient");
|
||||
jest.mock("../../../../Juno/JunoClient");
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { CodeOfConductComponent, CodeOfConductComponentProps } from "./CodeOfConductComponent";
|
||||
import { JunoClient } from "../../../Juno/JunoClient";
|
||||
import { HttpStatusCodes } from "../../../Common/Constants";
|
||||
import { CodeOfConductComponent, CodeOfConductComponentProps } from ".";
|
||||
import { HttpStatusCodes } from "../../../../Common/Constants";
|
||||
import { JunoClient } from "../../../../Juno/JunoClient";
|
||||
|
||||
describe("CodeOfConductComponent", () => {
|
||||
let codeOfConductProps: CodeOfConductComponentProps;
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Checkbox, Link, PrimaryButton, Stack, Text } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import { CodeOfConductEndpoints, HttpStatusCodes } from "../../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "../../../../Common/ErrorHandlingUtils";
|
||||
import { JunoClient } from "../../../../Juno/JunoClient";
|
||||
import { Action } from "../../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { trace, traceFailure, traceStart, traceSuccess } from "../../../../Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
export interface CodeOfConductComponentProps {
|
||||
junoClient: JunoClient;
|
||||
onAcceptCodeOfConduct: (result: boolean) => void;
|
||||
}
|
||||
|
||||
export const CodeOfConductComponent: FunctionComponent<CodeOfConductComponentProps> = ({
|
||||
junoClient,
|
||||
onAcceptCodeOfConduct,
|
||||
}: CodeOfConductComponentProps) => {
|
||||
const descriptionPara1 = "Azure Cosmos DB Notebook Gallery - Code of Conduct";
|
||||
const descriptionPara2 = "The notebook public gallery contains notebook samples shared by users of Azure Cosmos DB.";
|
||||
const descriptionPara3 = "In order to view and publish your samples to the gallery, you must accept the ";
|
||||
const link1: { label: string; url: string } = {
|
||||
label: "code of conduct.",
|
||||
url: CodeOfConductEndpoints.codeOfConduct,
|
||||
};
|
||||
|
||||
const [readCodeOfConduct, setReadCodeOfConduct] = useState<boolean>(false);
|
||||
|
||||
const acceptCodeOfConduct = async (): Promise<void> => {
|
||||
const startKey = traceStart(Action.NotebooksGalleryAcceptCodeOfConduct);
|
||||
|
||||
try {
|
||||
const response = await junoClient.acceptCodeOfConduct();
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
||||
}
|
||||
|
||||
traceSuccess(Action.NotebooksGalleryAcceptCodeOfConduct, {}, startKey);
|
||||
|
||||
onAcceptCodeOfConduct(response.data);
|
||||
} catch (error) {
|
||||
traceFailure(
|
||||
Action.NotebooksGalleryAcceptCodeOfConduct,
|
||||
{
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
|
||||
handleError(error, "CodeOfConductComponent/acceptCodeOfConduct", "Failed to accept code of conduct");
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeCheckbox = (): void => {
|
||||
setReadCodeOfConduct(!readCodeOfConduct);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
trace(Action.NotebooksGalleryViewCodeOfConduct);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }}>
|
||||
<Stack.Item>
|
||||
<Text style={{ fontWeight: 500, fontSize: "20px" }}>{descriptionPara1}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>{descriptionPara2}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>
|
||||
{descriptionPara3}
|
||||
<Link href={link1.url} target="_blank">
|
||||
{link1.label}
|
||||
</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Checkbox
|
||||
styles={{
|
||||
label: {
|
||||
margin: 0,
|
||||
padding: "2 0 2 0",
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
},
|
||||
}}
|
||||
label="I have read and accept the code of conduct."
|
||||
onChange={onChangeCheckbox}
|
||||
/>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<PrimaryButton
|
||||
ariaLabel="Continue"
|
||||
title="Continue"
|
||||
onClick={async () => await acceptCodeOfConduct()}
|
||||
tabIndex={0}
|
||||
className="genericPaneSubmitBtn"
|
||||
text="Continue"
|
||||
disabled={!readCodeOfConduct}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
import ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import {
|
||||
GalleryAndNotebookViewerComponentProps,
|
||||
GalleryAndNotebookViewerComponent,
|
||||
} from "./GalleryAndNotebookViewerComponent";
|
||||
|
||||
export class GalleryAndNotebookViewerComponentAdapter implements ReactAdapter {
|
||||
private key: string;
|
||||
public parameters: ko.Observable<number>;
|
||||
|
||||
constructor(private props: GalleryAndNotebookViewerComponentProps) {
|
||||
this.reset();
|
||||
this.parameters = ko.observable<number>(Date.now());
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return <GalleryAndNotebookViewerComponent key={this.key} {...this.props} />;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.key = `GalleryAndNotebookViewerComponent-${Date.now()}`;
|
||||
}
|
||||
|
||||
public triggerRender(): void {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
||||
import "./GalleryViewerComponent.less";
|
||||
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
||||
|
||||
const CARD_WIDTH = 256;
|
||||
export interface GalleryViewerComponentProps {
|
||||
container?: Explorer;
|
||||
junoClient: JunoClient;
|
||||
@@ -643,7 +644,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
|
||||
private getPageSpecification = (itemIndex?: number, visibleRect?: IRectangle): IPageSpecification => {
|
||||
if (itemIndex === 0) {
|
||||
this.columnCount = Math.floor(visibleRect.width / GalleryCardComponent.CARD_WIDTH) || this.columnCount;
|
||||
this.columnCount = Math.floor(visibleRect.width / CARD_WIDTH) || this.columnCount;
|
||||
this.rowCount = GalleryViewerComponent.rowsPerPage;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,15 +136,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.container = this.collection?.container;
|
||||
this.offer = this.collection?.offer();
|
||||
this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl();
|
||||
this.shouldShowIndexingPolicyEditor =
|
||||
this.container && userContext.apiType !== "Cassandra" && !this.container.isPreferredApiMongoDB();
|
||||
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
||||
|
||||
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
||||
|
||||
// Mongo container with system partition key still treat as "Fixed"
|
||||
this.isFixedContainer =
|
||||
this.container.isPreferredApiMongoDB() &&
|
||||
(!this.collection?.partitionKey || this.collection?.partitionKey.systemKey);
|
||||
userContext.apiType === "Mongo" && (!this.collection?.partitionKey || this.collection?.partitionKey.systemKey);
|
||||
} else {
|
||||
this.database = this.props.settingsTab.database;
|
||||
this.container = this.database?.container;
|
||||
@@ -236,7 +234,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
|
||||
public loadMongoIndexes = async (): Promise<void> => {
|
||||
if (
|
||||
this.container.isPreferredApiMongoDB() &&
|
||||
userContext.apiType === "Mongo" &&
|
||||
this.container.isEnableMongoCapabilityPresent() &&
|
||||
this.container.databaseAccount()
|
||||
) {
|
||||
@@ -1002,7 +1000,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
tab: SettingsV2TabTypes.IndexingPolicyTab,
|
||||
content: <IndexingPolicyComponent {...indexingPolicyComponentProps} />,
|
||||
});
|
||||
} else if (this.container.isPreferredApiMongoDB()) {
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
const mongoIndexTabContext = this.getMongoIndexTabContent(mongoIndexingPolicyComponentProps);
|
||||
if (mongoIndexTabContext) {
|
||||
tabs.push({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
|
||||
import { loadMonaco, monaco } from "../../../LazyMonaco";
|
||||
import { indexingPolicynUnsavedWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
import { IndexingPolicyRefreshComponent } from "./IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||
|
||||
export interface IndexingPolicyComponentProps {
|
||||
@@ -84,9 +84,9 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
return false;
|
||||
};
|
||||
|
||||
private createIndexingPolicyEditor = (): void => {
|
||||
private async createIndexingPolicyEditor(): Promise<void> {
|
||||
const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4);
|
||||
|
||||
const monaco = await loadMonaco();
|
||||
this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, {
|
||||
value: value,
|
||||
language: "json",
|
||||
@@ -98,7 +98,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
indexingPolicyEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
|
||||
this.props.logIndexingPolicySuccessMessage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private onEditorContentChange = (): void => {
|
||||
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();
|
||||
|
||||
@@ -323,7 +323,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
userContext.apiType === "Cassandra" ||
|
||||
userContext.apiType === "Tables" ||
|
||||
!this.props.collection.partitionKeyProperty ||
|
||||
(this.props.container.isPreferredApiMongoDB() && this.props.collection.partitionKey.systemKey)
|
||||
(userContext.apiType === "Mongo" && this.props.collection.partitionKey.systemKey)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -177,40 +177,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -233,9 +199,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -258,20 +222,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -426,9 +376,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -479,40 +427,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"defaultExperience": [Function],
|
||||
"deleteCollectionText": [Function],
|
||||
"deleteDatabaseText": [Function],
|
||||
"editTableEntityPane": EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
"graphStylingPane": GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -544,7 +458,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -576,21 +489,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsDatabaseIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
@@ -616,20 +517,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"stringInputPane": StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
@@ -805,40 +692,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -861,9 +714,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -886,20 +737,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -1054,9 +891,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -1107,40 +942,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"defaultExperience": [Function],
|
||||
"deleteCollectionText": [Function],
|
||||
"deleteDatabaseText": [Function],
|
||||
"editTableEntityPane": EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
"graphStylingPane": GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -1172,7 +973,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -1204,21 +1004,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsDatabaseIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
@@ -1244,20 +1032,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"stringInputPane": StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
@@ -1446,40 +1220,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -1502,9 +1242,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -1527,20 +1265,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -1695,9 +1419,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -1748,40 +1470,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"defaultExperience": [Function],
|
||||
"deleteCollectionText": [Function],
|
||||
"deleteDatabaseText": [Function],
|
||||
"editTableEntityPane": EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
"graphStylingPane": GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -1813,7 +1501,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -1845,21 +1532,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsDatabaseIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
@@ -1885,20 +1560,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"stringInputPane": StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
@@ -2074,40 +1735,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
CassandraAddCollectionPane {
|
||||
"autoPilotUsageCost": [Function],
|
||||
"canConfigureThroughput": [Function],
|
||||
@@ -2130,9 +1757,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -2155,20 +1780,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"userTableQuery": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
],
|
||||
"_refreshSparkEnabledStateForAccount": [Function],
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
@@ -2323,9 +1934,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"keyspaceHasSharedOffer": [Function],
|
||||
"keyspaceId": [Function],
|
||||
"keyspaceIds": [Function],
|
||||
"keyspaceOffers": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
"keyspaceOffers": Map {},
|
||||
"keyspaceThroughput": [Function],
|
||||
"maxThroughputRU": [Function],
|
||||
"minThroughputRU": [Function],
|
||||
@@ -2376,40 +1985,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"defaultExperience": [Function],
|
||||
"deleteCollectionText": [Function],
|
||||
"deleteDatabaseText": [Function],
|
||||
"editTableEntityPane": EditTableEntityPane {
|
||||
"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],
|
||||
"finishEditingAttribute": [Function],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "edittableentitypane",
|
||||
"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],
|
||||
},
|
||||
"graphStylingPane": GraphStylingPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
@@ -2441,7 +2016,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"isMongoIndexingEnabled": [Function],
|
||||
"isNotebookEnabled": [Function],
|
||||
"isNotebooksEnabledForAccount": [Function],
|
||||
"isPreferredApiMongoDB": [Function],
|
||||
"isPublishNotebookPaneEnabled": [Function],
|
||||
"isResourceTokenCollectionNodeSelected": [Function],
|
||||
"isRightPanelV2Enabled": [Function],
|
||||
@@ -2473,21 +2047,9 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"resourceTree": ResourceTreeAdapter {
|
||||
"container": [Circular],
|
||||
"copyNotebook": [Function],
|
||||
"databaseCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsCollectionIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"koSubsDatabaseIdMap": ArrayHashMap {
|
||||
"store": HashMap {
|
||||
"container": Object {},
|
||||
},
|
||||
},
|
||||
"databaseCollectionIdMap": Map {},
|
||||
"koSubsCollectionIdMap": Map {},
|
||||
"koSubsDatabaseIdMap": Map {},
|
||||
"parameters": [Function],
|
||||
},
|
||||
"resourceTreeForResourceToken": ResourceTreeAdapterForResourceToken {
|
||||
@@ -2513,20 +2075,6 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onResizeStop": [Function],
|
||||
"splitterId": "h_splitter1",
|
||||
},
|
||||
"stringInputPane": StringInputPane {
|
||||
"container": [Circular],
|
||||
"firstFieldHasFocus": [Function],
|
||||
"formErrors": [Function],
|
||||
"formErrorsDetails": [Function],
|
||||
"id": "stringinputpane",
|
||||
"inputLabel": [Function],
|
||||
"isExecuting": [Function],
|
||||
"isTemplateReady": [Function],
|
||||
"stringInput": [Function],
|
||||
"submitButtonLabel": [Function],
|
||||
"title": [Function],
|
||||
"visible": [Function],
|
||||
},
|
||||
"tabsManager": TabsManager {
|
||||
"activeTab": [Function],
|
||||
"openedTabs": [Function],
|
||||
|
||||
@@ -14,7 +14,6 @@ describe("ContainerSampleGenerator", () => {
|
||||
const createExplorerStub = (database: ViewModels.Database): Explorer => {
|
||||
const explorerStub = {} as Explorer;
|
||||
explorerStub.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||
explorerStub.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
explorerStub.canExceedMaximumValue = ko.computed<boolean>(() => false);
|
||||
explorerStub.findDatabaseWithId = () => database;
|
||||
explorerStub.refreshAllDatabases = () => Q.resolve();
|
||||
|
||||
@@ -6,7 +6,6 @@ import React from "react";
|
||||
import _ from "underscore";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { ExplorerMetrics } from "../Common/Constants";
|
||||
import { readCollection } from "../Common/dataAccess/readCollection";
|
||||
@@ -26,13 +25,12 @@ import { ResourceProviderClientFactory } from "../ResourceProvider/ResourceProvi
|
||||
import { RouteHandler } from "../RouteHandlers/RouteHandler";
|
||||
import { trackEvent } from "../Shared/appInsights";
|
||||
import * as SharedConstants from "../Shared/Constants";
|
||||
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
|
||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||
import { ArcadiaResourceManager } from "../SparkClusterManager/ArcadiaResourceManager";
|
||||
import { updateUserContext, userContext } from "../UserContext";
|
||||
import { decryptJWTToken, getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
import { userContext } from "../UserContext";
|
||||
import { decryptJWTToken } from "../Utils/AuthorizationUtils";
|
||||
import { stringToBlob } from "../Utils/BlobUtils";
|
||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
@@ -42,11 +40,13 @@ import * as ComponentRegisterer from "./ComponentRegisterer";
|
||||
import { ArcadiaWorkspaceItem } from "./Controls/Arcadia/ArcadiaMenuPicker";
|
||||
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
|
||||
import { DialogProps, TextFieldProps } from "./Controls/Dialog";
|
||||
import { GalleryTab } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { ConsoleData } from "./Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem";
|
||||
import type NotebookManager from "./Notebook/NotebookManager";
|
||||
import type { NotebookPaneContent } from "./Notebook/NotebookManager";
|
||||
import { NotebookUtil } from "./Notebook/NotebookUtil";
|
||||
import AddCollectionPane from "./Panes/AddCollectionPane";
|
||||
import { AddCollectionPanel } from "./Panes/AddCollectionPanel";
|
||||
@@ -62,9 +62,9 @@ import { LoadQueryPane } from "./Panes/LoadQueryPane/LoadQueryPane";
|
||||
import { SaveQueryPane } from "./Panes/SaveQueryPane/SaveQueryPane";
|
||||
import { SettingsPane } from "./Panes/SettingsPane/SettingsPane";
|
||||
import { SetupNoteBooksPanel } from "./Panes/SetupNotebooksPanel/SetupNotebooksPanel";
|
||||
import { StringInputPane } from "./Panes/StringInputPane";
|
||||
import { StringInputPane } from "./Panes/StringInputPane/StringInputPane";
|
||||
import { AddTableEntityPanel } from "./Panes/Tables/AddTableEntityPanel";
|
||||
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
|
||||
import { EditTableEntityPanel } from "./Panes/Tables/EditTableEntityPanel";
|
||||
import { TableQuerySelectPanel } from "./Panes/Tables/TableQuerySelectPanel";
|
||||
import { UploadFilePane } from "./Panes/UploadFilePane/UploadFilePane";
|
||||
import { UploadItemsPane } from "./Panes/UploadItemsPane/UploadItemsPane";
|
||||
@@ -73,7 +73,6 @@ import QueryViewModel from "./Tables/QueryBuilder/QueryViewModel";
|
||||
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
|
||||
import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab";
|
||||
import QueryTablesTab from "./Tabs/QueryTablesTab";
|
||||
import TabsBase from "./Tabs/TabsBase";
|
||||
import { TabsManager } from "./Tabs/TabsManager";
|
||||
import TerminalTab from "./Tabs/TerminalTab";
|
||||
import Database from "./Tree/Database";
|
||||
@@ -95,6 +94,8 @@ export interface ExplorerParams {
|
||||
closeDialog: () => void;
|
||||
openDialog: (props: DialogProps) => void;
|
||||
tabsManager: TabsManager;
|
||||
refreshNotebooksEnabledStateForAccount: () => void;
|
||||
isNotebooksEnabledForAccount: ko.Observable<boolean>;
|
||||
}
|
||||
|
||||
export default class Explorer {
|
||||
@@ -118,11 +119,6 @@ export default class Explorer {
|
||||
* Use userContext.apiType instead
|
||||
* */
|
||||
public defaultExperience: ko.Observable<string>;
|
||||
/**
|
||||
* @deprecated
|
||||
* Compare a string with userContext.apiType instead: userContext.apiType === "Mongo"
|
||||
* */
|
||||
public isPreferredApiMongoDB: ko.Computed<boolean>;
|
||||
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
|
||||
/**
|
||||
* @deprecated
|
||||
@@ -165,19 +161,14 @@ export default class Explorer {
|
||||
|
||||
// Tabs
|
||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||
public galleryTab: any;
|
||||
public notebookViewerTab: any;
|
||||
public tabsManager: TabsManager;
|
||||
|
||||
// Contextual panes
|
||||
public addDatabasePane: AddDatabasePane;
|
||||
public addCollectionPane: AddCollectionPane;
|
||||
public graphStylingPane: GraphStylingPane;
|
||||
public editTableEntityPane: EditTableEntityPane;
|
||||
public cassandraAddCollectionPane: CassandraAddCollectionPane;
|
||||
public stringInputPane: StringInputPane;
|
||||
public gitHubReposPane: ContextualPaneBase;
|
||||
public publishNotebookPaneAdapter: ReactAdapter;
|
||||
|
||||
// features
|
||||
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||
@@ -203,9 +194,10 @@ export default class Explorer {
|
||||
public hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
||||
public isSynapseLinkUpdating: ko.Observable<boolean>;
|
||||
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||
public notebookManager?: any; // This is dynamically loaded
|
||||
public notebookManager?: NotebookManager;
|
||||
public openDialog: ExplorerParams["openDialog"];
|
||||
public closeDialog: ExplorerParams["closeDialog"];
|
||||
public refreshNotebooksEnabledStateForAccount: ExplorerParams["refreshNotebooksEnabledStateForAccount"];
|
||||
|
||||
private _panes: ContextualPaneBase[] = [];
|
||||
private _isInitializingNotebooks: boolean;
|
||||
@@ -229,6 +221,8 @@ export default class Explorer {
|
||||
this.closeSidePanel = params?.closeSidePanel;
|
||||
this.closeDialog = params?.closeDialog;
|
||||
this.openDialog = params?.openDialog;
|
||||
this.refreshNotebooksEnabledStateForAccount = params?.refreshNotebooksEnabledStateForAccount;
|
||||
this.isNotebooksEnabledForAccount = params?.isNotebooksEnabledForAccount;
|
||||
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
@@ -253,7 +247,6 @@ export default class Explorer {
|
||||
});
|
||||
}
|
||||
});
|
||||
this.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
this.isNotebooksEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons());
|
||||
this.isSparkEnabledForAccount = ko.observable(false);
|
||||
this.isSparkEnabledForAccount.subscribe((isEnabledForAccount: boolean) => this.refreshCommandBarButtons());
|
||||
@@ -272,7 +265,7 @@ export default class Explorer {
|
||||
this._isAfecFeatureRegistered(Constants.AfecFeatures.StorageAnalytics).then((isRegistered) =>
|
||||
this.hasStorageAnalyticsAfecFeature(isRegistered)
|
||||
);
|
||||
Promise.all([this._refreshNotebooksEnabledStateForAccount(), this._refreshSparkEnabledStateForAccount()]).then(
|
||||
Promise.all([this.refreshNotebooksEnabledStateForAccount(), this._refreshSparkEnabledStateForAccount()]).then(
|
||||
async () => {
|
||||
this.isNotebookEnabled(
|
||||
userContext.authType !== AuthType.ResourceToken &&
|
||||
@@ -383,16 +376,16 @@ export default class Explorer {
|
||||
direction: SplitterDirection.Vertical,
|
||||
});
|
||||
this.defaultExperience = ko.observable<string>();
|
||||
this.databaseAccount.subscribe((databaseAccount) => {
|
||||
const defaultExperience: string = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(
|
||||
databaseAccount
|
||||
);
|
||||
this.defaultExperience(defaultExperience);
|
||||
// TODO. Remove this entirely
|
||||
updateUserContext({
|
||||
defaultExperience: DefaultExperienceUtility.mapDefaultExperienceStringToEnum(defaultExperience),
|
||||
});
|
||||
});
|
||||
// this.databaseAccount.subscribe((databaseAccount) => {
|
||||
// const defaultExperience: string = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(
|
||||
// databaseAccount
|
||||
// );
|
||||
// this.defaultExperience(defaultExperience);
|
||||
// // TODO. Remove this entirely
|
||||
// updateUserContext({
|
||||
// apiType: DefaultExperienceUtility.mapDefaultExperienceStringToEnum(defaultExperience),
|
||||
// });
|
||||
// });
|
||||
|
||||
this.isFixedCollectionWithSharedThroughputSupported = ko.computed(() => {
|
||||
if (userContext.features.enableFixedCollectionWithSharedThroughput) {
|
||||
@@ -414,27 +407,6 @@ export default class Explorer {
|
||||
) !== undefined
|
||||
);
|
||||
|
||||
this.isPreferredApiMongoDB = ko.computed(() => {
|
||||
const defaultExperience = (this.defaultExperience && this.defaultExperience()) || "";
|
||||
if (defaultExperience.toLowerCase() === Constants.DefaultAccountExperience.MongoDB.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (defaultExperience.toLowerCase() === Constants.DefaultAccountExperience.ApiForMongoDB.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
this.databaseAccount &&
|
||||
this.databaseAccount() &&
|
||||
this.databaseAccount().kind.toLowerCase() === Constants.AccountKind.MongoDB
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
this.isEnableMongoCapabilityPresent = ko.computed(() => {
|
||||
const capabilities = this.databaseAccount && this.databaseAccount()?.properties?.capabilities;
|
||||
if (!capabilities) {
|
||||
@@ -500,13 +472,6 @@ export default class Explorer {
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.editTableEntityPane = new EditTableEntityPane({
|
||||
id: "edittableentitypane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.cassandraAddCollectionPane = new CassandraAddCollectionPane({
|
||||
id: "cassandraaddcollectionpane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
@@ -514,13 +479,6 @@ export default class Explorer {
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.stringInputPane = new StringInputPane({
|
||||
id: "stringinputpane",
|
||||
visible: ko.observable<boolean>(false),
|
||||
|
||||
container: this,
|
||||
});
|
||||
|
||||
this.tabsManager = params?.tabsManager ?? new TabsManager();
|
||||
this.tabsManager.openedTabs.subscribe((tabs) => {
|
||||
if (tabs.length === 0) {
|
||||
@@ -533,9 +491,7 @@ export default class Explorer {
|
||||
this.addDatabasePane,
|
||||
this.addCollectionPane,
|
||||
this.graphStylingPane,
|
||||
this.editTableEntityPane,
|
||||
this.cassandraAddCollectionPane,
|
||||
this.stringInputPane,
|
||||
];
|
||||
this.addDatabaseText.subscribe((addDatabaseText: string) => this.addDatabasePane.title(addDatabaseText));
|
||||
this.isTabsContentExpanded = ko.observable(false);
|
||||
@@ -604,7 +560,6 @@ export default class Explorer {
|
||||
this.addCollectionPane.collectionIdTitle("Table id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle("Provision dedicated throughput for this table");
|
||||
this.refreshTreeTitle("Refresh tables");
|
||||
this.editTableEntityPane.title("Edit Table Entity");
|
||||
this.tableDataClient = new TablesAPIDataClient();
|
||||
break;
|
||||
case "Cassandra":
|
||||
@@ -618,7 +573,6 @@ export default class Explorer {
|
||||
this.addCollectionPane.collectionIdTitle("Table id");
|
||||
this.addCollectionPane.collectionWithThroughputInSharedTitle("Provision dedicated throughput for this table");
|
||||
this.refreshTreeTitle("Refresh tables");
|
||||
this.editTableEntityPane.title("Edit Table Row");
|
||||
this.tableDataClient = new CassandraAPIDataClient();
|
||||
break;
|
||||
}
|
||||
@@ -636,10 +590,10 @@ export default class Explorer {
|
||||
this.isNotebookEnabled = ko.observable(false);
|
||||
this.isNotebookEnabled.subscribe(async () => {
|
||||
if (!this.notebookManager) {
|
||||
const notebookManagerModule = await import(
|
||||
/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager"
|
||||
);
|
||||
this.notebookManager = new notebookManagerModule.default();
|
||||
const NotebookManager = await (
|
||||
await import(/* webpackChunkName: "NotebookManager" */ "./Notebook/NotebookManager")
|
||||
).default;
|
||||
this.notebookManager = new NotebookManager();
|
||||
this.notebookManager.initialize({
|
||||
container: this,
|
||||
notebookBasePath: this.notebookBasePath,
|
||||
@@ -1424,10 +1378,13 @@ export default class Explorer {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
public async publishNotebook(name: string, content: string | unknown, parentDomElement?: HTMLElement): Promise<void> {
|
||||
public async publishNotebook(
|
||||
name: string,
|
||||
content: NotebookPaneContent,
|
||||
parentDomElement?: HTMLElement
|
||||
): Promise<void> {
|
||||
if (this.notebookManager) {
|
||||
await this.notebookManager.openPublishNotebookPane(name, content, parentDomElement);
|
||||
this.publishNotebookPaneAdapter = this.notebookManager.publishNotebookPaneAdapter;
|
||||
this.isPublishNotebookPaneEnabled(true);
|
||||
}
|
||||
}
|
||||
@@ -1540,7 +1497,7 @@ export default class Explorer {
|
||||
return true;
|
||||
}
|
||||
|
||||
public renameNotebook(notebookFile: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
||||
public renameNotebook(notebookFile: NotebookContentItem): void {
|
||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to rename notebook, but notebook is not enabled";
|
||||
handleError(error, "Explorer/renameNotebook");
|
||||
@@ -1556,57 +1513,59 @@ export default class Explorer {
|
||||
);
|
||||
if (openedNotebookTabs.length > 0) {
|
||||
this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
|
||||
return Q.reject();
|
||||
} else {
|
||||
this.openSidePanel(
|
||||
"",
|
||||
<StringInputPane
|
||||
explorer={this}
|
||||
closePanel={() => {
|
||||
this.closeSidePanel();
|
||||
this.resourceTree.triggerRender();
|
||||
}}
|
||||
inputLabel="Enter new notebook name"
|
||||
submitButtonLabel="Rename"
|
||||
errorMessage="Could not rename notebook"
|
||||
inProgressMessage="Renaming notebook to"
|
||||
successMessage="Renamed notebook to"
|
||||
paneTitle="Rename Notebook"
|
||||
defaultInput={FileSystemUtil.stripExtension(notebookFile.name, "ipynb")}
|
||||
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
|
||||
this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input)
|
||||
}
|
||||
notebookFile={notebookFile}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const originalPath = notebookFile.path;
|
||||
const result = this.stringInputPane
|
||||
.openWithOptions<NotebookContentItem>({
|
||||
errorMessage: "Could not rename notebook",
|
||||
inProgressMessage: "Renaming notebook to",
|
||||
successMessage: "Renamed notebook to",
|
||||
inputLabel: "Enter new notebook name",
|
||||
paneTitle: "Rename Notebook",
|
||||
submitButtonLabel: "Rename",
|
||||
defaultInput: FileSystemUtil.stripExtension(notebookFile.name, "ipynb"),
|
||||
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input),
|
||||
})
|
||||
.then((newNotebookFile) => {
|
||||
const notebookTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.NotebookV2,
|
||||
(tab: NotebookV2Tab) => tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), originalPath)
|
||||
);
|
||||
notebookTabs.forEach((tab) => {
|
||||
tab.tabTitle(newNotebookFile.name);
|
||||
tab.tabPath(newNotebookFile.path);
|
||||
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
|
||||
});
|
||||
|
||||
return newNotebookFile;
|
||||
});
|
||||
result.then(() => this.resourceTree.triggerRender());
|
||||
return result;
|
||||
}
|
||||
|
||||
public onCreateDirectory(parent: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
||||
public onCreateDirectory(parent: NotebookContentItem): void {
|
||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
||||
handleError(error, "Explorer/onCreateDirectory");
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
const result = this.stringInputPane.openWithOptions<NotebookContentItem>({
|
||||
errorMessage: "Could not create directory ",
|
||||
inProgressMessage: "Creating directory ",
|
||||
successMessage: "Created directory ",
|
||||
inputLabel: "Enter new directory name",
|
||||
paneTitle: "Create new directory",
|
||||
submitButtonLabel: "Create",
|
||||
defaultInput: "",
|
||||
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.createDirectory(parent, input),
|
||||
});
|
||||
result.then(() => this.resourceTree.triggerRender());
|
||||
return result;
|
||||
this.openSidePanel(
|
||||
"",
|
||||
<StringInputPane
|
||||
explorer={this}
|
||||
closePanel={() => {
|
||||
this.closeSidePanel();
|
||||
this.resourceTree.triggerRender();
|
||||
}}
|
||||
errorMessage="Could not create directory "
|
||||
inProgressMessage="Creating directory "
|
||||
successMessage="Created directory "
|
||||
inputLabel="Enter new directory name"
|
||||
paneTitle="Create new directory"
|
||||
submitButtonLabel="Create"
|
||||
defaultInput=""
|
||||
onSubmit={(notebookFile: NotebookContentItem, input: string): Promise<NotebookContentItem> =>
|
||||
this.notebookManager?.notebookContentClient.createDirectory(notebookFile, input)
|
||||
}
|
||||
notebookFile={parent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
||||
@@ -1657,53 +1616,6 @@ export default class Explorer {
|
||||
);
|
||||
}
|
||||
|
||||
private async _refreshNotebooksEnabledStateForAccount(): Promise<void> {
|
||||
const authType = userContext.authType;
|
||||
if (
|
||||
authType === AuthType.EncryptedToken ||
|
||||
authType === AuthType.ResourceToken ||
|
||||
authType === AuthType.MasterKey
|
||||
) {
|
||||
this.isNotebooksEnabledForAccount(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const databaseAccount = this.databaseAccount();
|
||||
const databaseAccountLocation = databaseAccount && databaseAccount.location.toLowerCase();
|
||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
try {
|
||||
const response = await fetch(disallowedLocationsUri, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
resourceTypes: [Constants.ArmResourceTypes.notebookWorkspaces],
|
||||
}),
|
||||
headers: {
|
||||
[authorizationHeader.header]: authorizationHeader.token,
|
||||
[Constants.HttpHeaders.contentType]: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch disallowed locations");
|
||||
}
|
||||
|
||||
const disallowedLocations: string[] = await response.json();
|
||||
if (!disallowedLocations) {
|
||||
Logger.logInfo("No disallowed locations found", "Explorer/isNotebooksEnabledForAccount");
|
||||
this.isNotebooksEnabledForAccount(true);
|
||||
return;
|
||||
}
|
||||
const isAccountInAllowedLocation = !disallowedLocations.some(
|
||||
(disallowedLocation) => disallowedLocation === databaseAccountLocation
|
||||
);
|
||||
this.isNotebooksEnabledForAccount(isAccountInAllowedLocation);
|
||||
} catch (error) {
|
||||
Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount");
|
||||
this.isNotebooksEnabledForAccount(false);
|
||||
}
|
||||
}
|
||||
|
||||
public _refreshSparkEnabledStateForAccount = async (): Promise<void> => {
|
||||
const subscriptionId = userContext.subscriptionId;
|
||||
const armEndpoint = configContext.ARM_ENDPOINT;
|
||||
@@ -1922,86 +1834,68 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public async openGallery(
|
||||
selectedTab?: GalleryTab,
|
||||
selectedTab?: GalleryTabKind,
|
||||
notebookUrl?: string,
|
||||
galleryItem?: IGalleryItem,
|
||||
isFavorite?: boolean
|
||||
) {
|
||||
let title: string = "Gallery";
|
||||
let hashLocation: string = "gallery";
|
||||
const title = "Gallery";
|
||||
const hashLocation = "gallery";
|
||||
const GalleryTab = await (await import(/* webpackChunkName: "GalleryTab" */ "./Tabs/GalleryTab")).default;
|
||||
const galleryTab = this.tabsManager
|
||||
.getTabs(ViewModels.CollectionTabKind.Gallery)
|
||||
.find((tab) => tab.hashLocation() == hashLocation);
|
||||
|
||||
const galleryTabOptions: any = {
|
||||
// GalleryTabOptions
|
||||
account: userContext.databaseAccount,
|
||||
container: this,
|
||||
junoClient: this.notebookManager?.junoClient,
|
||||
selectedTab: selectedTab || GalleryTab.PublicGallery,
|
||||
notebookUrl,
|
||||
galleryItem,
|
||||
isFavorite,
|
||||
// TabOptions
|
||||
tabKind: ViewModels.CollectionTabKind.Gallery,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
isActive: ko.observable(false),
|
||||
hashLocation: hashLocation,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
};
|
||||
|
||||
const galleryTabs = this.tabsManager.getTabs(
|
||||
ViewModels.CollectionTabKind.Gallery,
|
||||
(tab) => tab.hashLocation() == hashLocation
|
||||
);
|
||||
let galleryTab = galleryTabs && galleryTabs[0];
|
||||
|
||||
if (galleryTab) {
|
||||
if (galleryTab instanceof GalleryTab) {
|
||||
this.tabsManager.activateTab(galleryTab);
|
||||
(galleryTab as any).reset(galleryTabOptions);
|
||||
} else {
|
||||
if (!this.galleryTab) {
|
||||
this.galleryTab = await import(/* webpackChunkName: "GalleryTab" */ "./Tabs/GalleryTab");
|
||||
}
|
||||
const newTab = new this.galleryTab.default(galleryTabOptions);
|
||||
this.tabsManager.activateNewTab(newTab);
|
||||
this.tabsManager.activateNewTab(
|
||||
new GalleryTab(
|
||||
{
|
||||
tabKind: ViewModels.CollectionTabKind.Gallery,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
hashLocation: hashLocation,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
onLoadStartKey: null,
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
},
|
||||
{
|
||||
account: userContext.databaseAccount,
|
||||
container: this,
|
||||
junoClient: this.notebookManager?.junoClient,
|
||||
selectedTab: selectedTab || GalleryTabKind.PublicGallery,
|
||||
notebookUrl,
|
||||
galleryItem,
|
||||
isFavorite,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async openNotebookViewer(notebookUrl: string) {
|
||||
const title = path.basename(notebookUrl);
|
||||
const hashLocation = notebookUrl;
|
||||
const NotebookViewerTab = await (
|
||||
await import(/* webpackChunkName: "NotebookViewerTab" */ "./Tabs/NotebookViewerTab")
|
||||
).default;
|
||||
|
||||
if (!this.notebookViewerTab) {
|
||||
this.notebookViewerTab = await import(/* webpackChunkName: "NotebookViewerTab" */ "./Tabs/NotebookViewerTab");
|
||||
}
|
||||
|
||||
const notebookViewerTabModule = this.notebookViewerTab;
|
||||
|
||||
let isNotebookViewerOpen = (tab: TabsBase) => {
|
||||
const notebookViewerTab = tab as typeof notebookViewerTabModule.default;
|
||||
return notebookViewerTab.notebookUrl === notebookUrl;
|
||||
};
|
||||
|
||||
const notebookViewerTabs = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2, (tab) => {
|
||||
return tab.hashLocation() == hashLocation && isNotebookViewerOpen(tab);
|
||||
const notebookViewerTab = this.tabsManager.getTabs(ViewModels.CollectionTabKind.NotebookV2).find((tab) => {
|
||||
return tab.hashLocation() == hashLocation && tab instanceof NotebookViewerTab && tab.notebookUrl === notebookUrl;
|
||||
});
|
||||
let notebookViewerTab = notebookViewerTabs && notebookViewerTabs[0];
|
||||
|
||||
if (notebookViewerTab) {
|
||||
this.tabsManager.activateNewTab(notebookViewerTab);
|
||||
} else {
|
||||
notebookViewerTab = new this.notebookViewerTab.default({
|
||||
const notebookViewerTab = new NotebookViewerTab({
|
||||
account: userContext.databaseAccount,
|
||||
tabKind: ViewModels.CollectionTabKind.NotebookViewer,
|
||||
node: null,
|
||||
title: title,
|
||||
tabPath: title,
|
||||
documentClientUtility: null,
|
||||
collection: null,
|
||||
hashLocation: hashLocation,
|
||||
isActive: ko.observable(false),
|
||||
isTabsContentExpanded: ko.observable(true),
|
||||
onLoadStartKey: null,
|
||||
onUpdateTabsButtons: this.onUpdateTabsButtons,
|
||||
@@ -2124,7 +2018,7 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public openDeleteCollectionConfirmationPane(): void {
|
||||
let collectionName = PricingUtils.getCollectionName(userContext.defaultExperience);
|
||||
let collectionName = PricingUtils.getCollectionName(userContext.apiType);
|
||||
this.openSidePanel(
|
||||
"Delete " + collectionName,
|
||||
<DeleteCollectionConfirmationPane
|
||||
@@ -2152,15 +2046,18 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public openSettingPane(): void {
|
||||
this.openSidePanel("Settings", <SettingsPane explorer={this} closePanel={this.closeSidePanel} />);
|
||||
this.openSidePanel(
|
||||
"Settings",
|
||||
<SettingsPane expandConsole={() => this.expandConsole()} closePanel={this.closeSidePanel} />
|
||||
);
|
||||
}
|
||||
|
||||
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
||||
this.openSidePanel(
|
||||
"Input parameters",
|
||||
<ExecuteSprocParamsPane
|
||||
explorer={this}
|
||||
storedProcedure={storedProcedure}
|
||||
expandConsole={() => this.expandConsole()}
|
||||
closePanel={() => this.closeSidePanel()}
|
||||
/>
|
||||
);
|
||||
@@ -2195,7 +2092,7 @@ export default class Explorer {
|
||||
this.openSidePanel(
|
||||
"Upload File",
|
||||
<UploadFilePane
|
||||
explorer={this}
|
||||
expandConsole={() => this.expandConsole()}
|
||||
closePanel={this.closeSidePanel}
|
||||
uploadFile={(name: string, content: string) => this.uploadFile(name, content, parent)}
|
||||
/>
|
||||
@@ -2227,6 +2124,19 @@ export default class Explorer {
|
||||
);
|
||||
}
|
||||
|
||||
public openEditTableEntityPanel(queryTablesTab: QueryTablesTab, tableEntityListViewModel: TableListViewModal): void {
|
||||
this.openSidePanel(
|
||||
"Edit Table Entity",
|
||||
<EditTableEntityPanel
|
||||
explorer={this}
|
||||
closePanel={this.closeSidePanel}
|
||||
queryTablesTab={queryTablesTab}
|
||||
tableEntityListViewModel={tableEntityListViewModel}
|
||||
cassandraApiClient={new CassandraAPIDataClient()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public openTableSelectQueryPanel(queryViewModal: QueryViewModel): void {
|
||||
this.openSidePanel(
|
||||
"Select Column",
|
||||
|
||||
@@ -11,7 +11,6 @@ import * as ko from "knockout";
|
||||
import Q from "q";
|
||||
import _ from "underscore";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { HashMap } from "../../../Common/HashMap";
|
||||
import { NeighborType } from "../../../Contracts/ViewModels";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { GraphConfig } from "../../Tabs/GraphTab";
|
||||
@@ -195,8 +194,8 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
* Count edges and store in a hashmap: vertex id <--> number of links
|
||||
* @param linkSelection
|
||||
*/
|
||||
public static countEdges(links: D3Link[]): HashMap<number> {
|
||||
const countMap = new HashMap<number>();
|
||||
public static countEdges(links: D3Link[]): Map<string, number> {
|
||||
const countMap = new Map<string, number>();
|
||||
links.forEach((l: D3Link) => {
|
||||
let val = countMap.get(l.inV) || 0;
|
||||
val += 1;
|
||||
@@ -407,7 +406,7 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
const rootId = graph.findRootNodeId();
|
||||
|
||||
// Remember nodes current position
|
||||
const posMap = new HashMap<Point2D>();
|
||||
const posMap = new Map<string, Point2D>();
|
||||
this.simulation.nodes().forEach((d: D3Node) => {
|
||||
if (d.x == undefined || d.y == undefined) {
|
||||
return;
|
||||
@@ -501,8 +500,8 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const nodeFinalPositionMap = new HashMap<Point2D>();
|
||||
|
||||
const nodeFinalPositionMap = new Map<string, Point2D>();
|
||||
const viewCenter = this.viewCenter;
|
||||
const nonFixedNodes = _.filter(nodes, (node: D3Node) => {
|
||||
return !node._isFixedPosition && node.x === viewCenter.x && node.y === viewCenter.y;
|
||||
@@ -559,7 +558,7 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
newNodes.selectAll(".loadmore").attr("visibility", "hidden").transition().delay(600).attr("visibility", "visible");
|
||||
}
|
||||
|
||||
private restartSimulation(graph: GraphData<D3Node, D3Link>, posMap: HashMap<Point2D>) {
|
||||
private restartSimulation(graph: GraphData<D3Node, D3Link>, posMap: Map<string, Point2D>) {
|
||||
if (!graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ObjectCache } from "../../../Common/ObjectCache";
|
||||
import { GremlinVertex, GraphData } from "./GraphData";
|
||||
import { GraphData, GremlinVertex } from "./GraphData";
|
||||
|
||||
/**
|
||||
* Remember vertex edge information
|
||||
@@ -10,9 +10,8 @@ export class EdgeInfoCache extends ObjectCache<GremlinVertex> {
|
||||
* @param vertex
|
||||
*/
|
||||
public addVertex(vertex: GremlinVertex): void {
|
||||
let v: GremlinVertex;
|
||||
if (super.has(vertex.id)) {
|
||||
v = super.get(vertex.id);
|
||||
let v = super.get(vertex.id);
|
||||
if (super.has(vertex.id) && v) {
|
||||
GraphData.addEdgeInfoToVertex(v, vertex);
|
||||
v._outEdgeIds = vertex._outEdgeIds;
|
||||
v._inEdgeIds = vertex._inEdgeIds;
|
||||
@@ -29,8 +28,8 @@ export class EdgeInfoCache extends ObjectCache<GremlinVertex> {
|
||||
* @param id
|
||||
*/
|
||||
public mergeEdgeInfo(target: GremlinVertex): void {
|
||||
if (super.has(target.id)) {
|
||||
const cachedVertex = super.get(target.id);
|
||||
const cachedVertex = super.get(target.id);
|
||||
if (super.has(target.id) && cachedVertex) {
|
||||
GraphData.addEdgeInfoToVertex(target, cachedVertex);
|
||||
target._outEdgeIds = cachedVertex._outEdgeIds;
|
||||
target._inEdgeIds = cachedVertex._inEdgeIds;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||
import { GraphConfig } from "../../Tabs/GraphTab";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { GraphExplorer, GraphAccessor } from "./GraphExplorer";
|
||||
|
||||
interface Parameter {
|
||||
onIsNewVertexDisabledChange: (isEnabled: boolean) => void;
|
||||
onGraphAccessorCreated: (instance: GraphAccessor) => void;
|
||||
onIsFilterQueryLoading: (isFilterQueryLoading: boolean) => void;
|
||||
onIsValidQuery: (isValidQuery: boolean) => void;
|
||||
onIsPropertyEditing: (isEditing: boolean) => void;
|
||||
onIsGraphDisplayed: (isDisplayed: boolean) => void;
|
||||
onResetDefaultGraphConfigValues: () => void;
|
||||
|
||||
graphConfigUiData: ViewModels.GraphConfigUiData;
|
||||
graphConfig?: GraphConfig;
|
||||
|
||||
collectionPartitionKeyProperty: string;
|
||||
graphBackendEndpoint: string;
|
||||
databaseId: string;
|
||||
collectionId: string;
|
||||
masterKey: string;
|
||||
|
||||
onLoadStartKey: number;
|
||||
onLoadStartKeyChange: (newKey: number) => void;
|
||||
resourceId: string;
|
||||
}
|
||||
|
||||
export class GraphExplorerAdapter implements ReactAdapter {
|
||||
public params: Parameter;
|
||||
public parameters = {};
|
||||
public isNewVertexDisabled: boolean;
|
||||
|
||||
public constructor(params: Parameter) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
return (
|
||||
<GraphExplorer
|
||||
onIsNewVertexDisabledChange={this.params.onIsNewVertexDisabledChange}
|
||||
onGraphAccessorCreated={this.params.onGraphAccessorCreated}
|
||||
onIsFilterQueryLoadingChange={this.params.onIsFilterQueryLoading}
|
||||
onIsValidQueryChange={this.params.onIsValidQuery}
|
||||
onIsPropertyEditing={this.params.onIsPropertyEditing}
|
||||
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
|
||||
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
|
||||
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
|
||||
graphBackendEndpoint={this.params.graphBackendEndpoint}
|
||||
databaseId={this.params.databaseId}
|
||||
collectionId={this.params.collectionId}
|
||||
masterKey={this.params.masterKey}
|
||||
onLoadStartKey={this.params.onLoadStartKey}
|
||||
onLoadStartKeyChange={this.params.onLoadStartKeyChange}
|
||||
resourceId={this.params.resourceId}
|
||||
/* TODO Figure out how to make this Knockout-free */
|
||||
graphConfigUiData={this.params.graphConfigUiData}
|
||||
graphConfig={this.params.graphConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as sinon from "sinon";
|
||||
import { GremlinClient, GremlinClientParameters } from "./GremlinClient";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import * as Logger from "../../../Common/Logger";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import { GremlinClient, GremlinClientParameters } from "./GremlinClient";
|
||||
|
||||
describe("Gremlin Client", () => {
|
||||
const emptyParams: GremlinClientParameters = {
|
||||
@@ -70,7 +70,7 @@ describe("Gremlin Client", () => {
|
||||
gremlinClient.execute("fake query");
|
||||
gremlinClient.execute("fake query");
|
||||
gremlinClient.execute("fake query");
|
||||
expect(gremlinClient.pendingResults.size()).toBe(3);
|
||||
expect(gremlinClient.pendingResults.size).toBe(3);
|
||||
});
|
||||
|
||||
it("should clean up pending request ids after success", async () => {
|
||||
@@ -89,7 +89,7 @@ describe("Gremlin Client", () => {
|
||||
return requestId;
|
||||
});
|
||||
await gremlinClient.execute("fake query");
|
||||
expect(gremlinClient.pendingResults.size()).toBe(0);
|
||||
expect(gremlinClient.pendingResults.size).toBe(0);
|
||||
});
|
||||
|
||||
it("should log and display error out on unknown requestId", () => {
|
||||
@@ -247,7 +247,7 @@ describe("Gremlin Client", () => {
|
||||
sinon.stub(gremlinClient.client, "executeGremlinQuery").callsFake((query: string): string => requestId);
|
||||
gremlinClient.execute("fake query").finally(() => {
|
||||
try {
|
||||
expect(gremlinClient.pendingResults.size()).toBe(0);
|
||||
expect(gremlinClient.pendingResults.size).toBe(0);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import * as Q from "q";
|
||||
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||
import { HashMap } from "../../../Common/HashMap";
|
||||
import { logConsoleInfo } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { GremlinSimpleClient, Result } from "./GremlinSimpleClient";
|
||||
|
||||
@@ -30,7 +29,7 @@ interface PendingResultData {
|
||||
|
||||
export class GremlinClient {
|
||||
public client: GremlinSimpleClient;
|
||||
public pendingResults: HashMap<PendingResultData>; // public for testing purposes
|
||||
public pendingResults: Map<string, PendingResultData>; // public for testing purposes
|
||||
private maxResultSize: number;
|
||||
private static readonly PENDING_REQUEST_TIMEOUT_MS = 6 /* minutes */ * 60 /* seconds */ * 1000 /* ms */;
|
||||
private static readonly TIMEOUT_ERROR_MSG = `Pending request timed out (${GremlinClient.PENDING_REQUEST_TIMEOUT_MS} ms)`;
|
||||
@@ -38,7 +37,7 @@ export class GremlinClient {
|
||||
|
||||
public initialize(params: GremlinClientParameters) {
|
||||
this.destroy();
|
||||
this.pendingResults = new HashMap();
|
||||
this.pendingResults = new Map();
|
||||
this.maxResultSize = params.maxResultSize;
|
||||
|
||||
this.client = new GremlinSimpleClient({
|
||||
@@ -68,9 +67,9 @@ export class GremlinClient {
|
||||
|
||||
// Fail all pending requests if no request id (fatal)
|
||||
if (!requestId) {
|
||||
this.pendingResults.keys().forEach((reqId: string) => {
|
||||
for (const reqId of this.pendingResults.keys()) {
|
||||
this.abortPendingRequest(reqId, errorMessage, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.abortPendingRequest(requestId, errorMessage, result.requestCharge);
|
||||
|
||||
5
src/Explorer/LazyMonaco.ts
Normal file
5
src/Explorer/LazyMonaco.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type * as monaco from "monaco-editor/esm/vs/editor/editor.api";
|
||||
export type { monaco };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");
|
||||
@@ -29,7 +29,6 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
||||
|
||||
// These are the parameters watched by the react binding that will trigger a renderComponent() if one of the ko mutates
|
||||
const toWatch = [
|
||||
container.isPreferredApiMongoDB,
|
||||
container.deleteCollectionText,
|
||||
container.deleteDatabaseText,
|
||||
container.addCollectionText,
|
||||
|
||||
@@ -23,7 +23,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
|
||||
@@ -67,7 +66,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
@@ -128,6 +126,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
|
||||
beforeAll(() => {
|
||||
mockExplorer = {} as Explorer;
|
||||
mockExplorer.addDatabaseText = ko.observable("mockText");
|
||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
@@ -143,16 +142,25 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
updateUserContext({
|
||||
apiType: "SQL",
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => true);
|
||||
updateUserContext({
|
||||
apiType: "Mongo",
|
||||
});
|
||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
});
|
||||
|
||||
it("Mongo Api not available - button should be hidden", () => {
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
|
||||
updateUserContext({
|
||||
apiType: "SQL",
|
||||
});
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||
const openMongoShellBtn = buttons.find((button) => button.commandButtonLabel === openMongoShellBtnLabel);
|
||||
expect(openMongoShellBtn).toBeUndefined();
|
||||
@@ -222,7 +230,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
|
||||
@@ -321,7 +328,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
|
||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
|
||||
@@ -70,7 +70,7 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
||||
buttons.push(createEnableNotebooksButton(container));
|
||||
}
|
||||
|
||||
if (container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
buttons.push(createOpenMongoTerminalButton(container));
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
||||
}
|
||||
|
||||
const isSupportedOpenQueryApi =
|
||||
userContext.apiType === "SQL" || container.isPreferredApiMongoDB() || userContext.apiType === "Gremlin";
|
||||
userContext.apiType === "SQL" || userContext.apiType === "Mongo" || userContext.apiType === "Gremlin";
|
||||
const isSupportedOpenQueryFromDiskApi = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||
if (isSupportedOpenQueryApi && container.selectedNode() && container.findSelectedCollection()) {
|
||||
const openQueryBtn = createOpenQueryButton(container);
|
||||
@@ -133,7 +133,7 @@ export function createStaticCommandBarButtons(container: Explorer): CommandButto
|
||||
export function createContextCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
if (!container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()) {
|
||||
if (!container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||
const label = "New Shell";
|
||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
@@ -145,7 +145,7 @@ export function createContextCommandBarButtons(container: Explorer): CommandButt
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB(),
|
||||
disabled: container.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo",
|
||||
};
|
||||
buttons.push(newMongoShellBtn);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ let fakeAjaxResponse: AjaxResponse = {
|
||||
responseType: "json",
|
||||
};
|
||||
export const sessions = {
|
||||
create: (serverConfig: unknown, body: object): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
||||
create: (): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
||||
__setResponse: (response: AjaxResponse) => {
|
||||
fakeAjaxResponse = response;
|
||||
},
|
||||
|
||||
@@ -220,7 +220,7 @@ export class NotebookContentClient {
|
||||
return this.contentProvider
|
||||
.remove(this.getServerConfig(), path)
|
||||
.toPromise()
|
||||
.then((xhr: AjaxResponse) => path);
|
||||
.then(() => path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ImmutableNotebook } from "@nteract/commutable";
|
||||
import { IContentProvider } from "@nteract/core";
|
||||
import type { IContentProvider } from "@nteract/core";
|
||||
import ko from "knockout";
|
||||
import React from "react";
|
||||
import { contents } from "rx-jupyter";
|
||||
@@ -22,12 +22,16 @@ import Explorer from "../Explorer";
|
||||
import { ContextualPaneBase } from "../Panes/ContextualPaneBase";
|
||||
import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane";
|
||||
import { GitHubReposPane } from "../Panes/GitHubReposPane";
|
||||
import { PublishNotebookPaneAdapter } from "../Panes/PublishNotebookPaneAdapter";
|
||||
import { PublishNotebookPane } from "../Panes/PublishNotebookPane/PublishNotebookPane";
|
||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||
import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider";
|
||||
import { NotebookContainerClient } from "./NotebookContainerClient";
|
||||
import { NotebookContentClient } from "./NotebookContentClient";
|
||||
|
||||
type NotebookPaneContent = string | ImmutableNotebook;
|
||||
|
||||
export type { NotebookPaneContent };
|
||||
|
||||
export interface NotebookManagerOptions {
|
||||
container: Explorer;
|
||||
notebookBasePath: ko.Observable<string>;
|
||||
@@ -49,7 +53,6 @@ export default class NotebookManager {
|
||||
private gitHubClient: GitHubClient;
|
||||
|
||||
public gitHubReposPane: ContextualPaneBase;
|
||||
public publishNotebookPaneAdapter: PublishNotebookPaneAdapter;
|
||||
|
||||
public initialize(params: NotebookManagerOptions): void {
|
||||
this.params = params;
|
||||
@@ -87,8 +90,6 @@ export default class NotebookManager {
|
||||
this.notebookContentProvider
|
||||
);
|
||||
|
||||
this.publishNotebookPaneAdapter = new PublishNotebookPaneAdapter(this.params.container, this.junoClient);
|
||||
|
||||
this.gitHubOAuthService.getTokenObservable().subscribe((token) => {
|
||||
this.gitHubClient.setToken(token?.access_token);
|
||||
|
||||
@@ -116,10 +117,23 @@ export default class NotebookManager {
|
||||
|
||||
public async openPublishNotebookPane(
|
||||
name: string,
|
||||
content: string | ImmutableNotebook,
|
||||
content: NotebookPaneContent,
|
||||
parentDomElement: HTMLElement
|
||||
): Promise<void> {
|
||||
await this.publishNotebookPaneAdapter.open(name, getFullName(), content, parentDomElement);
|
||||
const explorer = this.params.container;
|
||||
explorer.openSidePanel(
|
||||
"New Collection",
|
||||
<PublishNotebookPane
|
||||
explorer={this.params.container}
|
||||
junoClient={this.junoClient}
|
||||
closePanel={this.params.container.closeSidePanel}
|
||||
openNotificationConsole={this.params.container.expandConsole}
|
||||
name={name}
|
||||
author={getFullName()}
|
||||
notebookContent={content}
|
||||
parentDomElement={parentDomElement}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public openCopyNotebookPane(name: string, content: string): void {
|
||||
|
||||
@@ -251,10 +251,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Indexing For Shared Throughput - start -->
|
||||
<div
|
||||
class="seconddivpadding"
|
||||
data-bind="visible: showIndexingOptionsForSharedThroughput() && !container.isPreferredApiMongoDB()"
|
||||
>
|
||||
<div class="seconddivpadding" data-bind="visible: showIndexingOptionsForSharedThroughput() && !isMongo()">
|
||||
<div
|
||||
class="useIndexingForSharedThroughput createNewDatabaseOrUseExisting"
|
||||
aria-label="Indexing For Shared Throughput"
|
||||
@@ -297,7 +294,7 @@
|
||||
|
||||
<p
|
||||
class="seconddivpadding"
|
||||
data-bind="visible: container.isPreferredApiMongoDB() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
data-bind="visible: isMongo() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
>
|
||||
<span class="mandatoryStar">*</span>
|
||||
<span class="addCollectionLabel">Storage capacity</span>
|
||||
@@ -312,7 +309,7 @@
|
||||
<div class="tabs">
|
||||
<div
|
||||
tabindex="0"
|
||||
data-bind="event: { keydown: onStorageOptionsKeyDown }, visible: container.isPreferredApiMongoDB() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
data-bind="event: { keydown: onStorageOptionsKeyDown }, visible: isMongo() && !databaseHasSharedOffer() || container.isFixedCollectionWithSharedThroughputSupported"
|
||||
aria-label="Storage capacity"
|
||||
>
|
||||
<!-- Fixed option button - Start -->
|
||||
|
||||
@@ -119,7 +119,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
this.isPreferredApiTable = options.isPreferredApiTable;
|
||||
this.partitionKey = ko.observable<string>();
|
||||
this.partitionKey.subscribe((newPartitionKey: string) => {
|
||||
if (this.container.isPreferredApiMongoDB() || !newPartitionKey || newPartitionKey[0] === "/") {
|
||||
if (userContext.apiType === "Mongo" || !newPartitionKey || newPartitionKey[0] === "/") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
|
||||
// TODO: Create derived classes for Tables and Mongo to replace the If statements below
|
||||
this.partitionKeyName = ko.computed<string>(() => {
|
||||
if (this.container && !!this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return "Shard key";
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
this.lowerCasePartitionKeyName = ko.computed<string>(() => this.partitionKeyName().toLowerCase());
|
||||
|
||||
this.partitionKeyPlaceholder = ko.computed<string>(() => {
|
||||
if (this.container && !!this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return "e.g., address.zipCode";
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
});
|
||||
|
||||
this.uniqueKeysPlaceholder = ko.pureComputed<string>(() => {
|
||||
if (this.container && !!this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return "Comma separated paths e.g. firstName,address.zipCode";
|
||||
}
|
||||
|
||||
@@ -396,11 +396,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.container.isPreferredApiMongoDB() &&
|
||||
!this.isUnlimitedStorageSelected() &&
|
||||
this.databaseHasSharedOffer()
|
||||
) {
|
||||
if (userContext.apiType === "Mongo" && !this.isUnlimitedStorageSelected() && this.databaseHasSharedOffer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -480,7 +476,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
userContext.portalEnv,
|
||||
this.isFreeTierAccount(),
|
||||
this.container.isFirstResourceCreated(),
|
||||
this.container.defaultExperience(),
|
||||
userContext.apiType,
|
||||
true
|
||||
);
|
||||
});
|
||||
@@ -589,7 +585,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -728,6 +724,10 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
}
|
||||
}
|
||||
|
||||
private isMongo(): boolean {
|
||||
return userContext.apiType === "Mongo";
|
||||
}
|
||||
|
||||
private _onDatabasesChange(newDatabaseIds: ViewModels.Database[]) {
|
||||
this.databaseIds(newDatabaseIds?.map((database: ViewModels.Database) => database.id()));
|
||||
}
|
||||
@@ -810,7 +810,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
let indexingPolicy: DataModels.IndexingPolicy;
|
||||
let createMongoWildcardIndex: boolean;
|
||||
// todo - remove mongo indexing policy ticket # 616274
|
||||
if (this.container.isPreferredApiMongoDB() && this.container.isEnableMongoCapabilityPresent()) {
|
||||
if (userContext.apiType === "Mongo" && this.container.isEnableMongoCapabilityPresent()) {
|
||||
createMongoWildcardIndex = this.shouldCreateMongoWildcardIndex();
|
||||
} else if (this.showIndexingOptionsForSharedThroughput()) {
|
||||
if (this.useIndexingForSharedThroughput()) {
|
||||
@@ -1145,7 +1145,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
||||
let transform = (value: string) => {
|
||||
return value;
|
||||
};
|
||||
if (this.container.isPreferredApiMongoDB()) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
transform = (value: string) => {
|
||||
return this._convertShardKeyToPartitionKey(value);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
|
||||
import { configContext, Platform } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { SubscriptionType } from "../../Contracts/SubscriptionType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { CollectionCreation, IndexingPolicies } from "../../Shared/Constants";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
@@ -68,16 +67,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
createNewDatabase: userContext.defaultExperience !== DefaultAccountExperienceType.Table,
|
||||
createNewDatabase: userContext.apiType !== "Tables",
|
||||
newDatabaseId: "",
|
||||
isSharedThroughputChecked: this.getSharedThroughputDefault(),
|
||||
selectedDatabaseId:
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.Table
|
||||
? CollectionCreation.TablesAPIDefaultDatabase
|
||||
: undefined,
|
||||
selectedDatabaseId: userContext.apiType === "Tables" ? CollectionCreation.TablesAPIDefaultDatabase : undefined,
|
||||
collectionId: "",
|
||||
enableIndexing: true,
|
||||
isSharded: userContext.defaultExperience !== DefaultAccountExperienceType.Table,
|
||||
isSharded: userContext.apiType !== "Tables",
|
||||
partitionKey: "",
|
||||
enableDedicatedThroughput: false,
|
||||
createMongoWildCardIndex: true,
|
||||
@@ -108,7 +104,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
userContext.portalEnv,
|
||||
true,
|
||||
this.props.explorer.isFirstResourceCreated(),
|
||||
userContext.defaultExperience,
|
||||
userContext.apiType,
|
||||
true
|
||||
)}
|
||||
messageType="info"
|
||||
@@ -120,7 +116,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
)}
|
||||
|
||||
<div className="panelMainContent">
|
||||
<Stack hidden={userContext.defaultExperience === DefaultAccountExperienceType.Table}>
|
||||
<Stack hidden={userContext.apiType === "Tables"}>
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
@@ -317,7 +313,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{userContext.defaultExperience === DefaultAccountExperienceType.MongoDB &&
|
||||
{userContext.apiType === "Mongo" &&
|
||||
(!this.state.isSharedThroughputChecked ||
|
||||
this.props.explorer.isFixedCollectionWithSharedThroughputSupported()) && (
|
||||
<Stack>
|
||||
@@ -393,12 +389,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
className="panelTextField"
|
||||
placeholder={this.getPartitionKeyPlaceHolder()}
|
||||
aria-label={this.getPartitionKeyName()}
|
||||
pattern={userContext.defaultExperience === DefaultAccountExperienceType.Graph ? "^/[^/]*" : ".*"}
|
||||
title={
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.Graph
|
||||
? "May not use composite partition key"
|
||||
: ""
|
||||
}
|
||||
pattern={userContext.apiType === "Gremlin" ? "^/[^/]*" : ".*"}
|
||||
title={userContext.apiType === "Gremlin" ? "May not use composite partition key" : ""}
|
||||
value={this.state.partitionKey}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState({ partitionKey: event.target.value })
|
||||
@@ -447,7 +439,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
/>
|
||||
)}
|
||||
|
||||
{userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB && (
|
||||
{userContext.apiType === "SQL" && (
|
||||
<Stack>
|
||||
<Stack horizontal>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
@@ -471,7 +463,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
placeholder={
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.MongoDB
|
||||
userContext.apiType === "Mongo"
|
||||
? "Comma separated paths e.g. firstName,address.zipCode"
|
||||
: "Comma separated paths e.g. /firstName,/address/zipCode"
|
||||
}
|
||||
@@ -545,7 +537,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB && (
|
||||
{userContext.apiType === "SQL" && (
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Stack horizontal verticalAlign="start">
|
||||
<Checkbox
|
||||
@@ -657,30 +649,30 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
}
|
||||
|
||||
private getCollectionName(): string {
|
||||
switch (userContext.defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
switch (userContext.apiType) {
|
||||
case "SQL":
|
||||
return "Container";
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
case "Mongo":
|
||||
return "Collection";
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case DefaultAccountExperienceType.Table:
|
||||
case "Cassandra":
|
||||
case "Tables":
|
||||
return "Table";
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return "Graph";
|
||||
default:
|
||||
throw new Error(`Unsupported default experience type: ${userContext.defaultExperience}`);
|
||||
throw new Error(`Unsupported default experience type: ${userContext.apiType}`);
|
||||
}
|
||||
}
|
||||
|
||||
private getPartitionKeyName(): string {
|
||||
return userContext.defaultExperience === DefaultAccountExperienceType.MongoDB ? "Shard key" : "Partition key";
|
||||
return userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||
}
|
||||
|
||||
private getPartitionKeyPlaceHolder(): string {
|
||||
switch (userContext.defaultExperience) {
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
switch (userContext.apiType) {
|
||||
case "Mongo":
|
||||
return "e.g., address.zipCode";
|
||||
case DefaultAccountExperienceType.Graph:
|
||||
case "Gremlin":
|
||||
return "e.g., /address";
|
||||
default:
|
||||
return "e.g., /address/zipCode";
|
||||
@@ -817,11 +809,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (userContext.defaultExperience) {
|
||||
case DefaultAccountExperienceType.DocumentDB:
|
||||
case DefaultAccountExperienceType.MongoDB:
|
||||
switch (userContext.apiType) {
|
||||
case "SQL":
|
||||
case "Mongo":
|
||||
return true;
|
||||
case DefaultAccountExperienceType.Cassandra:
|
||||
case "Cassandra":
|
||||
return this.props.explorer.hasStorageAnalyticsAfecFeature();
|
||||
default:
|
||||
return false;
|
||||
@@ -855,7 +847,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
const validPaths: string[] = uniqueKey.split(",")?.filter((path) => path?.length > 0);
|
||||
const trimmedPaths: string[] = validPaths?.map((path) => path.trim());
|
||||
if (trimmedPaths?.length > 0) {
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
|
||||
if (userContext.apiType === "Mongo") {
|
||||
trimmedPaths.map((path) => {
|
||||
const transformedPath = path.split(".").join("/");
|
||||
if (transformedPath[0] !== "/") {
|
||||
@@ -888,7 +880,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
}
|
||||
|
||||
if (
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.Graph &&
|
||||
userContext.apiType === "Gremlin" &&
|
||||
(this.state.partitionKey === "/id" || this.state.partitionKey === "/label")
|
||||
) {
|
||||
this.setState({ errorMessage: "/id and /label as partition keys are not allowed for graph." });
|
||||
@@ -924,7 +916,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
let databaseId = this.state.createNewDatabase ? this.state.newDatabaseId.trim() : this.state.selectedDatabaseId;
|
||||
let partitionKeyString = this.state.partitionKey.trim();
|
||||
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
// Table require fixed Database: TablesDB, and fixed Partition Key: /'$pk'
|
||||
databaseId = CollectionCreation.TablesAPIDefaultDatabase;
|
||||
partitionKeyString = "/'$pk'";
|
||||
|
||||
@@ -238,7 +238,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
||||
userContext.portalEnv,
|
||||
this.isFreeTierAccount(),
|
||||
this.container.isFirstResourceCreated(),
|
||||
this.container.defaultExperience(),
|
||||
userContext.apiType,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as ko from "knockout";
|
||||
import * as _ from "underscore";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||
import { HashMap } from "../../Common/HashMap";
|
||||
import { configContext, Platform } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
@@ -51,7 +50,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
||||
public ruToolTipText: ko.Computed<string>;
|
||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||
|
||||
private keyspaceOffers: HashMap<DataModels.Offer>;
|
||||
private keyspaceOffers: Map<string, DataModels.Offer>;
|
||||
|
||||
constructor(options: ViewModels.PaneOptions) {
|
||||
super(options);
|
||||
@@ -60,7 +59,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
||||
this.keyspaceCreateNew = ko.observable<boolean>(true);
|
||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||
this.keyspaceOffers = new HashMap<DataModels.Offer>();
|
||||
this.keyspaceOffers = new Map();
|
||||
this.keyspaceIds = ko.observableArray<string>();
|
||||
this.keyspaceHasSharedOffer = ko.observable<boolean>(false);
|
||||
this.keyspaceThroughput = ko.observable<number>();
|
||||
|
||||
@@ -131,7 +131,6 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||
};
|
||||
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container,
|
||||
formError,
|
||||
formErrorDetail,
|
||||
id: "copynotebookpane",
|
||||
@@ -140,6 +139,7 @@ export const CopyNotebookPane: FunctionComponent<CopyNotebookPanelProps> = ({
|
||||
submitButtonText: "OK",
|
||||
onClose: closePanel,
|
||||
onSubmit: () => submit(),
|
||||
expandConsole: () => container.expandConsole(),
|
||||
};
|
||||
|
||||
const copyNotebookPaneProps: CopyNotebookPaneProps = {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { deleteCollection } from "../../../Common/dataAccess/deleteCollection";
|
||||
import DeleteFeedback from "../../../Common/DeleteFeedback";
|
||||
import { ApiKind, DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { Collection, Database, TreeNode } from "../../../Contracts/ViewModels";
|
||||
import { DefaultAccountExperienceType } from "../../../DefaultAccountExperienceType";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { updateUserContext } from "../../../UserContext";
|
||||
@@ -107,7 +106,7 @@ describe("Delete Collection Confirmation Pane", () => {
|
||||
},
|
||||
id: "testDatabaseAccountId",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
(deleteCollection as jest.Mock).mockResolvedValue(undefined);
|
||||
(TelemetryProcessor.trace as jest.Mock).mockReturnValue(undefined);
|
||||
|
||||
@@ -73,7 +73,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
const deleteFeedback = new DeleteFeedback(
|
||||
userContext.databaseAccount?.id,
|
||||
userContext.databaseAccount?.name,
|
||||
DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.defaultExperience),
|
||||
DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType),
|
||||
deleteCollectionFeedback
|
||||
);
|
||||
|
||||
@@ -101,7 +101,6 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
}
|
||||
};
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: explorer,
|
||||
formError: formError,
|
||||
formErrorDetail: formError,
|
||||
id: "deleteCollectionpane",
|
||||
@@ -110,6 +109,7 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
|
||||
submitButtonText: "OK",
|
||||
onClose: closePanel,
|
||||
onSubmit: submit,
|
||||
expandConsole: () => explorer.expandConsole(),
|
||||
};
|
||||
return (
|
||||
<GenericRightPaneComponent {...genericPaneProps}>
|
||||
|
||||
@@ -16,16 +16,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
|
||||
}
|
||||
>
|
||||
<GenericRightPaneComponent
|
||||
container={
|
||||
Object {
|
||||
"findSelectedCollection": [Function],
|
||||
"isLastCollection": [Function],
|
||||
"isSelectedDatabaseShared": [Function],
|
||||
"refreshAllDatabases": [Function],
|
||||
"selectedCollectionId": [Function],
|
||||
"selectedNode": [Function],
|
||||
}
|
||||
}
|
||||
expandConsole={[Function]}
|
||||
formError=""
|
||||
formErrorDetail=""
|
||||
id="deleteCollectionpane"
|
||||
|
||||
@@ -7,7 +7,6 @@ import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
||||
import DeleteFeedback from "../../Common/DeleteFeedback";
|
||||
import { ApiKind, DatabaseAccount } from "../../Contracts/DataModels";
|
||||
import { Collection, Database } from "../../Contracts/ViewModels";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { updateUserContext } from "../../UserContext";
|
||||
@@ -68,7 +67,7 @@ describe("Delete Database Confirmation Pane", () => {
|
||||
},
|
||||
id: "testDatabaseAccountId",
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
apiType: "SQL",
|
||||
});
|
||||
(deleteDatabase as jest.Mock).mockResolvedValue(undefined);
|
||||
(TelemetryProcessor.trace as jest.Mock).mockReturnValue(undefined);
|
||||
|
||||
@@ -94,7 +94,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
const deleteFeedback = new DeleteFeedback(
|
||||
userContext?.databaseAccount.id,
|
||||
userContext?.databaseAccount.name,
|
||||
DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.defaultExperience),
|
||||
DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType),
|
||||
databaseFeedbackInput
|
||||
);
|
||||
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { mount } from "enzyme";
|
||||
import React from "react";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { ExecuteSprocParamsPane } from "./ExecuteSprocParamsPane";
|
||||
|
||||
describe("Excute Sproc Param Pane", () => {
|
||||
const fakeExplorer = {} as Explorer;
|
||||
const fakeSproc = {} as StoredProcedure;
|
||||
const props = {
|
||||
explorer: fakeExplorer,
|
||||
storedProcedure: fakeSproc,
|
||||
expandConsole: (): void => undefined,
|
||||
closePanel: (): void => undefined,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useBoolean } from "@uifabric/react-hooks";
|
||||
import { IDropdownOption, IImageProps, Image, Stack, Text } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import Explorer from "../../Explorer";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import {
|
||||
GenericRightPaneComponent,
|
||||
@@ -11,7 +10,7 @@ import {
|
||||
import { InputParameter } from "./InputParameter";
|
||||
|
||||
interface ExecuteSprocParamsPaneProps {
|
||||
explorer: Explorer;
|
||||
expandConsole: () => void;
|
||||
storedProcedure: StoredProcedure;
|
||||
closePanel: () => void;
|
||||
}
|
||||
@@ -27,7 +26,7 @@ interface UnwrappedExecuteSprocParam {
|
||||
}
|
||||
|
||||
export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
|
||||
explorer,
|
||||
expandConsole,
|
||||
storedProcedure,
|
||||
closePanel,
|
||||
}: ExecuteSprocParamsPaneProps): JSX.Element => {
|
||||
@@ -43,7 +42,7 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
|
||||
};
|
||||
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: explorer,
|
||||
expandConsole,
|
||||
formError: formError,
|
||||
formErrorDetail: formErrorsDetails,
|
||||
id: "executesprocparamspane",
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
exports[`Excute Sproc Param Pane should render Default properly 1`] = `
|
||||
<ExecuteSprocParamsPane
|
||||
closePanel={[Function]}
|
||||
explorer={Object {}}
|
||||
expandConsole={[Function]}
|
||||
storedProcedure={Object {}}
|
||||
>
|
||||
<GenericRightPaneComponent
|
||||
container={Object {}}
|
||||
expandConsole={[Function]}
|
||||
formError=""
|
||||
formErrorDetail=""
|
||||
id="executesprocparamspane"
|
||||
|
||||
@@ -3,10 +3,9 @@ import React, { FunctionComponent, ReactNode } from "react";
|
||||
import ErrorRedIcon from "../../../../images/error_red.svg";
|
||||
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||
import { KeyCodes } from "../../../Common/Constants";
|
||||
import Explorer from "../../Explorer";
|
||||
|
||||
export interface GenericRightPaneProps {
|
||||
container: Explorer;
|
||||
expandConsole: () => void;
|
||||
formError: string;
|
||||
formErrorDetail: string;
|
||||
id: string;
|
||||
@@ -24,7 +23,7 @@ export interface GenericRightPaneState {
|
||||
}
|
||||
|
||||
export const GenericRightPaneComponent: FunctionComponent<GenericRightPaneProps> = ({
|
||||
container,
|
||||
expandConsole,
|
||||
formError,
|
||||
formErrorDetail,
|
||||
id,
|
||||
@@ -72,7 +71,7 @@ export const GenericRightPaneComponent: FunctionComponent<GenericRightPaneProps>
|
||||
<span className="formErrors" title={formError}>
|
||||
{formError}
|
||||
</span>
|
||||
<a className="errorLink" role="link" hidden={!formErrorDetail} onClick={showErrorDetail}>
|
||||
<a className="errorLink" role="link" hidden={!formErrorDetail} onClick={expandConsole}>
|
||||
More details
|
||||
</a>
|
||||
</span>
|
||||
@@ -114,10 +113,6 @@ export const GenericRightPaneComponent: FunctionComponent<GenericRightPaneProps>
|
||||
}
|
||||
};
|
||||
|
||||
const showErrorDetail = (): void => {
|
||||
container.expandConsole();
|
||||
};
|
||||
|
||||
return (
|
||||
<div tabIndex={-1} onKeyDown={onKeyDown}>
|
||||
<div className="contextual-pane-out" onClick={onClose}></div>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const LoadQueryPane: FunctionComponent<LoadQueryPaneProps> = ({
|
||||
|
||||
const title = "Load Query";
|
||||
const genericPaneProps: GenericRightPaneProps = {
|
||||
container: explorer,
|
||||
expandConsole: () => explorer.expandConsole(),
|
||||
formError: formError,
|
||||
formErrorDetail: formErrorsDetails,
|
||||
id: "loadQueryPane",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`Load Query Pane should render Default properly 1`] = `
|
||||
<GenericRightPaneComponent
|
||||
container={Object {}}
|
||||
expandConsole={[Function]}
|
||||
formError=""
|
||||
formErrorDetail=""
|
||||
id="loadQueryPane"
|
||||
|
||||
@@ -3,7 +3,6 @@ import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
||||
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
||||
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
||||
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
||||
import StringInputPaneTemplate from "./StringInputPane.html";
|
||||
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
|
||||
import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html";
|
||||
|
||||
@@ -66,15 +65,6 @@ export class CassandraAddCollectionPaneComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export class StringInputPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
viewModel: PaneComponent,
|
||||
template: StringInputPaneTemplate,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class GitHubReposPaneComponent {
|
||||
constructor() {
|
||||
return {
|
||||
|
||||
@@ -8,14 +8,15 @@ describe("PublishNotebookPaneComponent", () => {
|
||||
notebookName: "SampleNotebook.ipynb",
|
||||
notebookDescription: "sample description",
|
||||
notebookTags: "tag1, tag2",
|
||||
imageSrc: "https://i.ytimg.com/vi/E_lByLdKeKY/maxresdefault.jpg",
|
||||
notebookAuthor: "CosmosDB",
|
||||
notebookCreatedDate: "2020-07-17T00:00:00Z",
|
||||
notebookObject: undefined,
|
||||
notebookParentDomElement: undefined,
|
||||
onChangeName: undefined,
|
||||
onChangeDescription: undefined,
|
||||
onChangeTags: undefined,
|
||||
onChangeImageSrc: undefined,
|
||||
setNotebookName: undefined,
|
||||
setNotebookDescription: undefined,
|
||||
setNotebookTags: undefined,
|
||||
setImageSrc: undefined,
|
||||
onError: undefined,
|
||||
clearFormError: undefined,
|
||||
};
|
||||
205
src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx
Normal file
205
src/Explorer/Panes/PublishNotebookPane/PublishNotebookPane.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import { ImmutableNotebook, toJS } from "@nteract/commutable";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import { HttpStatusCodes } from "../../../Common/Constants";
|
||||
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 interface PublishNotebookPaneAProps {
|
||||
explorer: Explorer;
|
||||
closePanel: () => void;
|
||||
openNotificationConsole: () => void;
|
||||
junoClient: JunoClient;
|
||||
name: string;
|
||||
author: string;
|
||||
notebookContent: string | ImmutableNotebook;
|
||||
parentDomElement: HTMLElement;
|
||||
}
|
||||
export const PublishNotebookPane: FunctionComponent<PublishNotebookPaneAProps> = ({
|
||||
explorer: container,
|
||||
junoClient,
|
||||
closePanel,
|
||||
name,
|
||||
author,
|
||||
notebookContent,
|
||||
parentDomElement,
|
||||
}: PublishNotebookPaneAProps): JSX.Element => {
|
||||
const [isCodeOfConductAccepted, setIsCodeOfConductAccepted] = useState<boolean>(false);
|
||||
const [content, setContent] = useState<string>("");
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
const [formErrorDetail, setFormErrorDetail] = useState<string>("");
|
||||
const [isExecuting, setIsExecuting] = useState<boolean>();
|
||||
|
||||
const [notebookName, setNotebookName] = useState<string>(name);
|
||||
const [notebookDescription, setNotebookDescription] = useState<string>("");
|
||||
const [notebookTags, setNotebookTags] = useState<string>("");
|
||||
const [imageSrc, setImageSrc] = useState<string>();
|
||||
|
||||
const CodeOfConductAccepted = async () => {
|
||||
try {
|
||||
const response = await junoClient.isCodeOfConductAccepted();
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
||||
}
|
||||
setIsCodeOfConductAccepted(response.data);
|
||||
} catch (error) {
|
||||
handleError(
|
||||
error,
|
||||
"PublishNotebookPaneAdapter/isCodeOfConductAccepted",
|
||||
"Failed to check if code of conduct was accepted"
|
||||
);
|
||||
}
|
||||
};
|
||||
const [notebookObject, setNotebookObject] = useState<ImmutableNotebook>();
|
||||
useEffect(() => {
|
||||
CodeOfConductAccepted();
|
||||
let newContent;
|
||||
if (typeof notebookContent === "string") {
|
||||
newContent = notebookContent as string;
|
||||
} else {
|
||||
newContent = JSON.stringify(toJS(notebookContent));
|
||||
setNotebookObject(notebookContent);
|
||||
}
|
||||
setContent(newContent);
|
||||
}, []);
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
const clearPublishingMessage = NotificationConsoleUtils.logConsoleProgress(`Publishing ${name} to gallery`);
|
||||
setIsExecuting(true);
|
||||
|
||||
let startKey: number;
|
||||
|
||||
if (!notebookName || !notebookDescription || !author || !imageSrc) {
|
||||
setFormError(`Failed to publish ${notebookName} to gallery`);
|
||||
setFormErrorDetail("Name, description, author and cover image are required");
|
||||
createFormError(formError, formErrorDetail, "PublishNotebookPaneAdapter/submit");
|
||||
setIsExecuting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
startKey = traceStart(Action.NotebooksGalleryPublish, {});
|
||||
|
||||
const response = await junoClient.publishNotebook(
|
||||
notebookName,
|
||||
notebookDescription,
|
||||
notebookTags?.split(","),
|
||||
author,
|
||||
imageSrc,
|
||||
content
|
||||
);
|
||||
|
||||
const data = response.data;
|
||||
if (data) {
|
||||
let isPublishPending = false;
|
||||
|
||||
if (data.pendingScanJobIds?.length > 0) {
|
||||
isPublishPending = true;
|
||||
NotificationConsoleUtils.logConsoleInfo(
|
||||
`Content of ${name} is currently being scanned for illegal content. It will not be available in the public gallery until the review is complete (may take a few days).`
|
||||
);
|
||||
} else {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Published ${notebookName} to gallery`);
|
||||
container.openGallery(GalleryTab.Published);
|
||||
}
|
||||
|
||||
traceSuccess(
|
||||
Action.NotebooksGalleryPublish,
|
||||
{
|
||||
notebookId: data.id,
|
||||
isPublishPending,
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
traceFailure(
|
||||
Action.NotebooksGalleryPublish,
|
||||
{
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
|
||||
const errorMessage = getErrorMessage(error);
|
||||
setFormError(`Failed to publish ${FileSystemUtil.stripExtension(notebookName, "ipynb")} to gallery`);
|
||||
setFormErrorDetail(`${errorMessage}`);
|
||||
handleError(errorMessage, "PublishNotebookPaneAdapter/submit", formError);
|
||||
return;
|
||||
} finally {
|
||||
clearPublishingMessage();
|
||||
setIsExecuting(false);
|
||||
}
|
||||
|
||||
closePanel();
|
||||
};
|
||||
|
||||
const createFormError = (formError: string, formErrorDetail: string, area: string): void => {
|
||||
setFormError(formError);
|
||||
setFormErrorDetail(formErrorDetail);
|
||||
handleError(formErrorDetail, area, formError);
|
||||
};
|
||||
|
||||
const clearFormError = (): void => {
|
||||
setFormError("");
|
||||
setFormErrorDetail("");
|
||||
};
|
||||
|
||||
const props: GenericRightPaneProps = {
|
||||
formError: formError,
|
||||
formErrorDetail: formErrorDetail,
|
||||
id: "publishnotebookpane",
|
||||
isExecuting: isExecuting,
|
||||
title: "Publish to gallery",
|
||||
submitButtonText: "Publish",
|
||||
onSubmit: () => submit(),
|
||||
onClose: closePanel,
|
||||
expandConsole: () => container.expandConsole(),
|
||||
isSubmitButtonHidden: !isCodeOfConductAccepted,
|
||||
};
|
||||
|
||||
const publishNotebookPaneProps: PublishNotebookPaneProps = {
|
||||
notebookDescription,
|
||||
notebookTags,
|
||||
imageSrc,
|
||||
notebookName,
|
||||
notebookAuthor: author,
|
||||
notebookCreatedDate: new Date().toISOString(),
|
||||
notebookObject: notebookObject,
|
||||
notebookParentDomElement: parentDomElement,
|
||||
onError: createFormError,
|
||||
clearFormError: clearFormError,
|
||||
setNotebookName,
|
||||
setNotebookDescription,
|
||||
setNotebookTags,
|
||||
setImageSrc,
|
||||
};
|
||||
return (
|
||||
<GenericRightPaneComponent {...props}>
|
||||
{!isCodeOfConductAccepted ? (
|
||||
<div style={{ padding: "25px", marginTop: "10px" }}>
|
||||
<CodeOfConductComponent
|
||||
junoClient={junoClient}
|
||||
onAcceptCodeOfConduct={(isAccepted) => {
|
||||
setIsCodeOfConductAccepted(isAccepted);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PublishNotebookPaneComponent {...publishNotebookPaneProps} />
|
||||
)}
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,297 @@
|
||||
import { ImmutableNotebook } from "@nteract/commutable";
|
||||
import Html2Canvas from "html2canvas";
|
||||
import { Dropdown, IDropdownProps, ITextFieldProps, Stack, Text, TextField } from "office-ui-fabric-react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { GalleryCardComponent } from "../../Controls/NotebookGallery/Cards/GalleryCardComponent";
|
||||
import * as FileSystemUtil from "../../Notebook/FileSystemUtil";
|
||||
import { NotebookUtil } from "../../Notebook/NotebookUtil";
|
||||
import "./styled.less";
|
||||
|
||||
export interface PublishNotebookPaneProps {
|
||||
notebookName: string;
|
||||
notebookAuthor: string;
|
||||
notebookTags: string;
|
||||
imageSrc: string;
|
||||
notebookDescription: string;
|
||||
notebookCreatedDate: string;
|
||||
notebookObject: ImmutableNotebook;
|
||||
notebookParentDomElement?: HTMLElement;
|
||||
onError: (formError: string, formErrorDetail: string, area: string) => void;
|
||||
clearFormError: () => void;
|
||||
setNotebookName: (newValue: string) => void;
|
||||
setNotebookDescription: (newValue: string) => void;
|
||||
setNotebookTags: (newValue: string) => void;
|
||||
setImageSrc: (newValue: string) => void;
|
||||
}
|
||||
|
||||
enum ImageTypes {
|
||||
Url = "URL",
|
||||
CustomImage = "Custom Image",
|
||||
TakeScreenshot = "Take Screenshot",
|
||||
UseFirstDisplayOutput = "Use First Display Output",
|
||||
}
|
||||
|
||||
export const PublishNotebookPaneComponent: FunctionComponent<PublishNotebookPaneProps> = ({
|
||||
notebookName,
|
||||
notebookTags,
|
||||
imageSrc,
|
||||
notebookDescription,
|
||||
notebookAuthor,
|
||||
notebookCreatedDate,
|
||||
notebookObject,
|
||||
notebookParentDomElement,
|
||||
onError,
|
||||
clearFormError,
|
||||
setNotebookName,
|
||||
setNotebookDescription,
|
||||
setNotebookTags,
|
||||
setImageSrc,
|
||||
}: PublishNotebookPaneProps) => {
|
||||
const [type, setType] = useState<string>(ImageTypes.CustomImage);
|
||||
const CARD_WIDTH = 256;
|
||||
const cardImageHeight = 144;
|
||||
const cardHeightToWidthRatio = cardImageHeight / CARD_WIDTH;
|
||||
|
||||
const maxImageSizeInMib = 1.5;
|
||||
|
||||
const descriptionPara1 =
|
||||
"When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing.";
|
||||
|
||||
const descriptionPara2 = `Would you like to publish and share "${FileSystemUtil.stripExtension(
|
||||
notebookName,
|
||||
"ipynb"
|
||||
)}" to the gallery?`;
|
||||
|
||||
const options: ImageTypes[] = [ImageTypes.CustomImage, ImageTypes.Url];
|
||||
const thumbnailSelectorProps: IDropdownProps = {
|
||||
label: "Cover image",
|
||||
defaultSelectedKey: ImageTypes.CustomImage,
|
||||
ariaLabel: "Cover image",
|
||||
options: options.map((value: string) => ({ text: value, key: value })),
|
||||
onChange: async (event, options) => {
|
||||
setImageSrc("");
|
||||
clearFormError();
|
||||
if (options.text === ImageTypes.TakeScreenshot) {
|
||||
try {
|
||||
await takeScreenshot(notebookParentDomElement, screenshotErrorHandler);
|
||||
} catch (error) {
|
||||
screenshotErrorHandler(error);
|
||||
}
|
||||
} else if (options.text === ImageTypes.UseFirstDisplayOutput) {
|
||||
try {
|
||||
await takeScreenshot(findFirstOutput(), firstOutputErrorHandler);
|
||||
} catch (error) {
|
||||
firstOutputErrorHandler(error);
|
||||
}
|
||||
}
|
||||
setType(options.text);
|
||||
},
|
||||
};
|
||||
|
||||
const thumbnailUrlProps: ITextFieldProps = {
|
||||
label: "Cover image url",
|
||||
ariaLabel: "Cover image url",
|
||||
required: true,
|
||||
onChange: (event, newValue) => {
|
||||
setImageSrc(newValue);
|
||||
},
|
||||
};
|
||||
|
||||
const screenshotErrorHandler = (error: Error) => {
|
||||
const formError = "Failed to take screen shot";
|
||||
const formErrorDetail = `${error}`;
|
||||
const area = "PublishNotebookPaneComponent/takeScreenshot";
|
||||
onError(formError, formErrorDetail, area);
|
||||
};
|
||||
|
||||
const firstOutputErrorHandler = (error: Error) => {
|
||||
const formError = "Failed to capture first output";
|
||||
const formErrorDetail = `${error}`;
|
||||
const area = "PublishNotebookPaneComponent/UseFirstOutput";
|
||||
onError(formError, formErrorDetail, area);
|
||||
};
|
||||
|
||||
if (notebookParentDomElement) {
|
||||
options.push(ImageTypes.TakeScreenshot);
|
||||
if (notebookObject) {
|
||||
options.push(ImageTypes.UseFirstDisplayOutput);
|
||||
}
|
||||
}
|
||||
|
||||
const imageToBase64 = (file: File, updateImageSrc: (result: string) => void) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
updateImageSrc(reader.result.toString());
|
||||
};
|
||||
|
||||
reader.onerror = (error) => {
|
||||
const formError = `Failed to convert ${file.name} to base64 format`;
|
||||
const formErrorDetail = `${error}`;
|
||||
const area = "PublishNotebookPaneComponent/selectImageFile";
|
||||
onError(formError, formErrorDetail, area);
|
||||
};
|
||||
};
|
||||
|
||||
const takeScreenshot = (target: HTMLElement, onError: (error: Error) => void): void => {
|
||||
const updateImageSrcWithScreenshot = (canvasUrl: string): void => {
|
||||
setImageSrc(canvasUrl);
|
||||
};
|
||||
|
||||
target.scrollIntoView();
|
||||
Html2Canvas(target, {
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
scale: 1,
|
||||
logging: true,
|
||||
})
|
||||
.then((canvas) => {
|
||||
//redraw canvas to fit Card Cover Image dimensions
|
||||
const originalImageData = canvas.toDataURL();
|
||||
const requiredHeight = parseInt(canvas.style.width.split("px")[0]) * cardHeightToWidthRatio;
|
||||
canvas.height = requiredHeight;
|
||||
const context = canvas.getContext("2d");
|
||||
const image = new Image();
|
||||
image.src = originalImageData;
|
||||
image.onload = () => {
|
||||
context.drawImage(image, 0, 0);
|
||||
updateImageSrcWithScreenshot(canvas.toDataURL());
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
const renderThumbnailSelectors = (type: string) => {
|
||||
switch (type) {
|
||||
case ImageTypes.Url:
|
||||
return <TextField {...thumbnailUrlProps} />;
|
||||
case ImageTypes.CustomImage:
|
||||
return (
|
||||
<input
|
||||
id="selectImageFile"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file.size / 1024 ** 2 > maxImageSizeInMib) {
|
||||
event.target.value = "";
|
||||
const formError = `Failed to upload ${file.name}`;
|
||||
const formErrorDetail = `Image is larger than ${maxImageSizeInMib} MiB. Please Choose a different image.`;
|
||||
const area = "PublishNotebookPaneComponent/selectImageFile";
|
||||
|
||||
onError(formError, formErrorDetail, area);
|
||||
setImageSrc("");
|
||||
return;
|
||||
} else {
|
||||
clearFormError();
|
||||
}
|
||||
imageToBase64(file, (result: string) => {
|
||||
setImageSrc(result);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
const findFirstOutput = (): HTMLElement => {
|
||||
const indexOfFirstCodeCellWithDisplay = NotebookUtil.findFirstCodeCellWithDisplay(notebookObject);
|
||||
const cellOutputDomElements = notebookParentDomElement.querySelectorAll<HTMLElement>(".nteract-cell-outputs");
|
||||
return cellOutputDomElements[indexOfFirstCodeCellWithDisplay];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="publishNotebookPanelContent">
|
||||
<Stack className="panelMainContent" tokens={{ childrenGap: 20 }}>
|
||||
<Stack.Item>
|
||||
<Text>{descriptionPara1}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>{descriptionPara2}</Text>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<TextField
|
||||
label="Name"
|
||||
ariaLabel="Name"
|
||||
defaultValue={FileSystemUtil.stripExtension(notebookName, "ipynb")}
|
||||
required
|
||||
onChange={(event, newValue) => {
|
||||
const notebookName = newValue + ".ipynb";
|
||||
setNotebookName(notebookName);
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<TextField
|
||||
label="Description"
|
||||
ariaLabel="Description"
|
||||
multiline
|
||||
rows={3}
|
||||
required
|
||||
onChange={(event, newValue) => {
|
||||
setNotebookDescription(newValue);
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<TextField
|
||||
label="Tags"
|
||||
ariaLabel="Tags"
|
||||
placeholder="Optional tag 1, Optional tag 2"
|
||||
onChange={(event, newValue) => {
|
||||
setNotebookTags(newValue);
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Dropdown {...thumbnailSelectorProps} />
|
||||
</Stack.Item>
|
||||
|
||||
<Stack.Item>{renderThumbnailSelectors(type)}</Stack.Item>
|
||||
|
||||
<Stack.Item>
|
||||
<Text>Preview</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<GalleryCardComponent
|
||||
data={{
|
||||
id: undefined,
|
||||
name: notebookName,
|
||||
description: notebookDescription,
|
||||
gitSha: undefined,
|
||||
tags: notebookTags.split(","),
|
||||
author: notebookAuthor,
|
||||
thumbnailUrl: imageSrc,
|
||||
created: notebookCreatedDate,
|
||||
isSample: false,
|
||||
downloads: undefined,
|
||||
favorites: undefined,
|
||||
views: undefined,
|
||||
newCellId: undefined,
|
||||
policyViolations: undefined,
|
||||
pendingScanJobIds: undefined,
|
||||
}}
|
||||
isFavorite={undefined}
|
||||
showDownload={false}
|
||||
showDelete={false}
|
||||
onClick={() => undefined}
|
||||
onTagClick={undefined}
|
||||
onFavoriteClick={undefined}
|
||||
onUnfavoriteClick={undefined}
|
||||
onDownloadClick={undefined}
|
||||
onDeleteClick={undefined}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -88,7 +88,7 @@ exports[`PublishNotebookPaneComponent renders 1`] = `
|
||||
Object {
|
||||
"author": "CosmosDB",
|
||||
"created": "2020-07-17T00:00:00Z",
|
||||
"description": "",
|
||||
"description": "sample description",
|
||||
"downloads": undefined,
|
||||
"favorites": undefined,
|
||||
"gitSha": undefined,
|
||||
@@ -99,12 +99,14 @@ exports[`PublishNotebookPaneComponent renders 1`] = `
|
||||
"pendingScanJobIds": undefined,
|
||||
"policyViolations": undefined,
|
||||
"tags": Array [
|
||||
"",
|
||||
"tag1",
|
||||
" tag2",
|
||||
],
|
||||
"thumbnailUrl": undefined,
|
||||
"thumbnailUrl": "https://i.ytimg.com/vi/E_lByLdKeKY/maxresdefault.jpg",
|
||||
"views": undefined,
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
showDelete={false}
|
||||
showDownload={false}
|
||||
/>
|
||||
@@ -1,244 +0,0 @@
|
||||
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 { HttpStatusCodes } from "../../Common/Constants";
|
||||
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>;
|
||||
private isOpened: boolean;
|
||||
private isExecuting: boolean;
|
||||
private formError: string;
|
||||
private formErrorDetail: string;
|
||||
|
||||
private name: string;
|
||||
private author: string;
|
||||
private content: string;
|
||||
private description: string;
|
||||
private tags: string;
|
||||
private imageSrc: string;
|
||||
private notebookObject: ImmutableNotebook;
|
||||
private parentDomElement: HTMLElement;
|
||||
private isCodeOfConductAccepted: boolean;
|
||||
|
||||
constructor(private container: Explorer, private junoClient: JunoClient) {
|
||||
this.parameters = ko.observable(Date.now());
|
||||
this.reset();
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
if (!this.isOpened) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const props: GenericRightPaneProps = {
|
||||
container: this.container,
|
||||
formError: this.formError,
|
||||
formErrorDetail: this.formErrorDetail,
|
||||
id: "publishnotebookpane",
|
||||
isExecuting: this.isExecuting,
|
||||
title: "Publish to gallery",
|
||||
submitButtonText: "Publish",
|
||||
onClose: () => this.close(),
|
||||
onSubmit: () => this.submit(),
|
||||
isSubmitButtonHidden: !this.isCodeOfConductAccepted,
|
||||
};
|
||||
|
||||
const publishNotebookPaneProps: PublishNotebookPaneProps = {
|
||||
notebookName: this.name,
|
||||
notebookDescription: "",
|
||||
notebookTags: "",
|
||||
notebookAuthor: this.author,
|
||||
notebookCreatedDate: new Date().toISOString(),
|
||||
notebookObject: this.notebookObject,
|
||||
notebookParentDomElement: this.parentDomElement,
|
||||
onChangeName: (newValue: string) => (this.name = newValue),
|
||||
onChangeDescription: (newValue: string) => (this.description = newValue),
|
||||
onChangeTags: (newValue: string) => (this.tags = newValue),
|
||||
onChangeImageSrc: (newValue: string) => (this.imageSrc = newValue),
|
||||
onError: this.createFormError,
|
||||
clearFormError: this.clearFormError,
|
||||
};
|
||||
|
||||
return (
|
||||
<GenericRightPaneComponent {...props}>
|
||||
{!this.isCodeOfConductAccepted ? (
|
||||
<div style={{ padding: "15px", marginTop: "10px" }}>
|
||||
<CodeOfConductComponent
|
||||
junoClient={this.junoClient}
|
||||
onAcceptCodeOfConduct={() => {
|
||||
this.isCodeOfConductAccepted = true;
|
||||
this.triggerRender();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PublishNotebookPaneComponent {...publishNotebookPaneProps} />
|
||||
)}
|
||||
</GenericRightPaneComponent>
|
||||
);
|
||||
}
|
||||
|
||||
public triggerRender(): void {
|
||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||
}
|
||||
|
||||
public async open(
|
||||
name: string,
|
||||
author: string,
|
||||
notebookContent: string | ImmutableNotebook,
|
||||
parentDomElement: HTMLElement
|
||||
): Promise<void> {
|
||||
try {
|
||||
const response = await this.junoClient.isCodeOfConductAccepted();
|
||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||
throw new Error(`Received HTTP ${response.status} when accepting code of conduct`);
|
||||
}
|
||||
|
||||
this.isCodeOfConductAccepted = response.data;
|
||||
} catch (error) {
|
||||
handleError(
|
||||
error,
|
||||
"PublishNotebookPaneAdapter/isCodeOfConductAccepted",
|
||||
"Failed to check if code of conduct was accepted"
|
||||
);
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.author = author;
|
||||
if (typeof notebookContent === "string") {
|
||||
this.content = notebookContent as string;
|
||||
} else {
|
||||
this.content = JSON.stringify(toJS(notebookContent));
|
||||
this.notebookObject = notebookContent;
|
||||
}
|
||||
this.parentDomElement = parentDomElement;
|
||||
|
||||
this.isOpened = true;
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.reset();
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
public async submit(): Promise<void> {
|
||||
const clearPublishingMessage = NotificationConsoleUtils.logConsoleProgress(`Publishing ${this.name} to gallery`);
|
||||
this.isExecuting = true;
|
||||
this.triggerRender();
|
||||
|
||||
let startKey: number;
|
||||
|
||||
if (!this.name || !this.description || !this.author || !this.imageSrc) {
|
||||
const formError = `Failed to publish ${this.name} to gallery`;
|
||||
const formErrorDetail = "Name, description, author and cover image are required";
|
||||
this.createFormError(formError, formErrorDetail, "PublishNotebookPaneAdapter/submit");
|
||||
this.isExecuting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
startKey = traceStart(Action.NotebooksGalleryPublish, {});
|
||||
|
||||
const response = await this.junoClient.publishNotebook(
|
||||
this.name,
|
||||
this.description,
|
||||
this.tags?.split(","),
|
||||
this.author,
|
||||
this.imageSrc,
|
||||
this.content
|
||||
);
|
||||
|
||||
const data = response.data;
|
||||
if (data) {
|
||||
let isPublishPending = false;
|
||||
|
||||
if (data.pendingScanJobIds?.length > 0) {
|
||||
isPublishPending = true;
|
||||
NotificationConsoleUtils.logConsoleInfo(
|
||||
`Content of ${this.name} is currently being scanned for illegal content. It will not be available in the public gallery until the review is complete (may take a few days).`
|
||||
);
|
||||
} else {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Published ${this.name} to gallery`);
|
||||
this.container.openGallery(GalleryTab.Published);
|
||||
}
|
||||
|
||||
traceSuccess(
|
||||
Action.NotebooksGalleryPublish,
|
||||
{
|
||||
notebookId: data.id,
|
||||
isPublishPending,
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
traceFailure(
|
||||
Action.NotebooksGalleryPublish,
|
||||
{
|
||||
error: getErrorMessage(error),
|
||||
errorStack: getErrorStack(error),
|
||||
},
|
||||
startKey
|
||||
);
|
||||
|
||||
const errorMessage = getErrorMessage(error);
|
||||
this.formError = `Failed to publish ${FileSystemUtil.stripExtension(this.name, "ipynb")} to gallery`;
|
||||
this.formErrorDetail = `${errorMessage}`;
|
||||
handleError(errorMessage, "PublishNotebookPaneAdapter/submit", this.formError);
|
||||
return;
|
||||
} finally {
|
||||
clearPublishingMessage();
|
||||
this.isExecuting = false;
|
||||
this.triggerRender();
|
||||
}
|
||||
|
||||
this.close();
|
||||
}
|
||||
|
||||
private createFormError = (formError: string, formErrorDetail: string, area: string): void => {
|
||||
this.formError = formError;
|
||||
this.formErrorDetail = formErrorDetail;
|
||||
handleError(formErrorDetail, area, formError);
|
||||
this.triggerRender();
|
||||
};
|
||||
|
||||
private clearFormError = (): void => {
|
||||
this.formError = undefined;
|
||||
this.formErrorDetail = undefined;
|
||||
this.triggerRender();
|
||||
};
|
||||
|
||||
private reset = (): void => {
|
||||
this.isOpened = false;
|
||||
this.isExecuting = false;
|
||||
this.formError = undefined;
|
||||
this.formErrorDetail = undefined;
|
||||
this.name = undefined;
|
||||
this.author = undefined;
|
||||
this.content = undefined;
|
||||
this.description = undefined;
|
||||
this.tags = undefined;
|
||||
this.imageSrc = undefined;
|
||||
this.notebookObject = undefined;
|
||||
this.parentDomElement = undefined;
|
||||
this.isCodeOfConductAccepted = undefined;
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user