Resolve conflict

This commit is contained in:
Steve Faulkner
2021-05-05 15:23:37 -05:00
40 changed files with 803 additions and 30537 deletions

View File

@@ -1,4 +1,5 @@
**/node_modules/ **/node_modules/
src/**/__mocks__/**/*
dist/ dist/
Contracts/ Contracts/
src/Api/Apis.ts src/Api/Apis.ts

View File

@@ -11,6 +11,7 @@ module.exports = {
}, },
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
project: ["./tsconfig.json", "./tsconfig.test.json"],
ecmaFeatures: { ecmaFeatures: {
jsx: true, jsx: true,
}, },
@@ -35,6 +36,7 @@ module.exports = {
rules: { rules: {
"no-console": ["error", { allow: ["error", "warn", "dir"] }], "no-console": ["error", { allow: ["error", "warn", "dir"] }],
curly: "error", curly: "error",
"@typescript-eslint/switch-exhaustiveness-check": "error",
"@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-extraneous-class": "error",
"no-null/no-null": "error", "no-null/no-null": "error",

View File

@@ -138,6 +138,7 @@ jobs:
matrix: matrix:
test-file: test-file:
- ./test/cassandra/container.spec.ts - ./test/cassandra/container.spec.ts
- ./test/graph/container.spec.ts
- ./test/sql/container.spec.ts - ./test/sql/container.spec.ts
- ./test/mongo/container.spec.ts - ./test/mongo/container.spec.ts
- ./test/selfServe/selfServeExample.spec.ts - ./test/selfServe/selfServeExample.spec.ts

30730
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -130,8 +130,8 @@
"@types/sinon": "2.3.3", "@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1", "@types/styled-components": "5.1.1",
"@types/underscore": "1.7.36", "@types/underscore": "1.7.36",
"@typescript-eslint/eslint-plugin": "4.0.1", "@typescript-eslint/eslint-plugin": "4.22.0",
"@typescript-eslint/parser": "4.0.1", "@typescript-eslint/parser": "4.22.0",
"babel-jest": "24.9.0", "babel-jest": "24.9.0",
"babel-loader": "8.1.0", "babel-loader": "8.1.0",
"buffer": "5.1.0", "buffer": "5.1.0",
@@ -193,7 +193,7 @@
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress", "pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
"copyToConsumers": "node copyToConsumers", "copyToConsumers": "node copyToConsumers",
"test": "rimraf coverage && jest", "test": "rimraf coverage && jest",
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles", "test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",
"watch": "npm run start", "watch": "npm run start",
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/", "wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
"build:ase": "gulp build:ase", "build:ase": "gulp build:ase",

View File

@@ -0,0 +1,10 @@
.schema-analyzer-cell-outputs {
padding: 10px;
}
.schema-analyzer-cell-output {
margin-bottom: 20px;
padding: 10px;
border-radius: 2px;
box-shadow: rgba(0, 0, 0, 13%) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 11%) 0px 0.3px 0.9px 0px;
}

View File

@@ -11,13 +11,14 @@ import * as ReactDOM from "react-dom";
import "../../externals/iframeResizer.contentWindow.min.js"; // Required for iFrameResizer to work import "../../externals/iframeResizer.contentWindow.min.js"; // Required for iFrameResizer to work
import "../Explorer/Notebook/NotebookRenderer/base.css"; import "../Explorer/Notebook/NotebookRenderer/base.css";
import "../Explorer/Notebook/NotebookRenderer/default.css"; import "../Explorer/Notebook/NotebookRenderer/default.css";
import "./CellOutputViewer.less";
import { TransformMedia } from "./TransformMedia"; import { TransformMedia } from "./TransformMedia";
export interface CellOutputViewerProps { export interface CellOutputViewerProps {
id: string; id: string;
contentRef: ContentRef; contentRef: ContentRef;
hidden: boolean; outputsContainerClassName: string;
expanded: boolean; outputClassName: string;
outputs: OnDiskOutput[]; outputs: OnDiskOutput[];
onMetadataChange: (metadata: JSONObject, mediaType: string, index?: number) => void; onMetadataChange: (metadata: JSONObject, mediaType: string, index?: number) => void;
} }
@@ -34,27 +35,26 @@ const onInit = async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const props = (event as any).data as CellOutputViewerProps; const props = (event as any).data as CellOutputViewerProps;
const outputs = ( const outputs = (
<div <div data-iframe-height className={props.outputsContainerClassName}>
data-iframe-height
className={`nteract-cell-outputs ${props.hidden ? "hidden" : ""} ${props.expanded ? "expanded" : ""}`}
>
{props.outputs?.map((output, index) => ( {props.outputs?.map((output, index) => (
<Output output={createImmutableOutput(output)} key={index}> <div className={props.outputClassName} key={index}>
<TransformMedia <Output output={createImmutableOutput(output)} key={index}>
output_type={"display_data"} <TransformMedia
id={props.id} output_type={"display_data"}
contentRef={props.contentRef} id={props.id}
onMetadataChange={(metadata, mediaType) => props.onMetadataChange(metadata, mediaType, index)} contentRef={props.contentRef}
/> onMetadataChange={(metadata, mediaType) => props.onMetadataChange(metadata, mediaType, index)}
<TransformMedia />
output_type={"execute_result"} <TransformMedia
id={props.id} output_type={"execute_result"}
contentRef={props.contentRef} id={props.id}
onMetadataChange={(metadata, mediaType) => props.onMetadataChange(metadata, mediaType, index)} contentRef={props.contentRef}
/> onMetadataChange={(metadata, mediaType) => props.onMetadataChange(metadata, mediaType, index)}
<KernelOutputError /> />
<StreamText /> <KernelOutputError />
</Output> <StreamText />
</Output>
</div>
))} ))}
</div> </div>
); );

View File

@@ -65,28 +65,18 @@ export class ClientDefaults {
public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000; public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000;
} }
export class AccountKind { export enum AccountKind {
public static DocumentDB: string = "DocumentDB"; DocumentDB = "DocumentDB",
public static MongoDB: string = "MongoDB"; MongoDB = "MongoDB",
public static Parse: string = "Parse"; Parse = "Parse",
public static GlobalDocumentDB: string = "GlobalDocumentDB"; GlobalDocumentDB = "GlobalDocumentDB",
public static Default: string = AccountKind.DocumentDB; Default = "DocumentDB",
} }
export class CorrelationBackend { export class CorrelationBackend {
public static Url: string = "https://aka.ms/cosmosdbanalytics"; public static Url: string = "https://aka.ms/cosmosdbanalytics";
} }
export class DefaultAccountExperience {
public static DocumentDB: string = "DocumentDB";
public static Graph: string = "Graph";
public static MongoDB: string = "MongoDB";
public static ApiForMongoDB: string = "Azure Cosmos DB for MongoDB API";
public static Table: string = "Table";
public static Cassandra: string = "Cassandra";
public static Default: string = DefaultAccountExperience.DocumentDB;
}
export class CapabilityNames { export class CapabilityNames {
public static EnableTable: string = "EnableTable"; public static EnableTable: string = "EnableTable";
public static EnableGremlin: string = "EnableGremlin"; public static EnableGremlin: string = "EnableGremlin";

View File

@@ -1,5 +1,5 @@
import { ResourceType } from "@azure/cosmos/dist-esm/common/constants"; import { ResourceType } from "@azure/cosmos/dist-esm/common/constants";
import { configContext, Platform, updateConfigContext, resetConfigContext } from "../ConfigContext"; import { Platform, resetConfigContext, updateConfigContext } from "../ConfigContext";
import { updateUserContext } from "../UserContext"; import { updateUserContext } from "../UserContext";
import { endpoint, getTokenFromAuthService, requestPlugin, tokenProvider } from "./CosmosClient"; import { endpoint, getTokenFromAuthService, requestPlugin, tokenProvider } from "./CosmosClient";
@@ -91,7 +91,6 @@ describe("endpoint", () => {
location: "foo", location: "foo",
type: "foo", type: "foo",
kind: "foo", kind: "foo",
tags: [],
properties: { properties: {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",

View File

@@ -4,7 +4,6 @@ export interface DatabaseAccount {
location: string; location: string;
type: string; type: string;
kind: string; kind: string;
tags: any;
properties: DatabaseAccountExtendedProperties; properties: DatabaseAccountExtendedProperties;
} }

View File

@@ -13,7 +13,6 @@ const createTestDatabaseAccount = (): DataModels.DatabaseAccount => {
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null,
}, },
tags: "testTags",
type: "testType", type: "testType",
}; };
}; };
@@ -30,7 +29,6 @@ const createTestMongo32DatabaseAccount = (): DataModels.DatabaseAccount => {
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null,
}, },
tags: "testTags",
type: "testType", type: "testType",
}; };
}; };
@@ -48,7 +46,6 @@ const createTestMongo36DatabaseAccount = (): DataModels.DatabaseAccount => {
tableEndpoint: null, tableEndpoint: null,
mongoEndpoint: "https://testMongoEndpoint.azure.com/", mongoEndpoint: "https://testMongoEndpoint.azure.com/",
}, },
tags: "testTags",
type: "testType", type: "testType",
}; };
}; };
@@ -65,7 +62,6 @@ const createTestCassandraDatabaseAccount = (): DataModels.DatabaseAccount => {
gremlinEndpoint: null, gremlinEndpoint: null,
tableEndpoint: null, tableEndpoint: null,
}, },
tags: "testTags",
type: "testType", type: "testType",
}; };
}; };

View File

@@ -1,14 +1,14 @@
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import ko from "knockout";
import React from "react"; import React from "react";
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
import { container, collection } from "../TestUtils";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
import Explorer from "../../../Explorer";
import * as Constants from "../../../../Common/Constants"; import * as Constants from "../../../../Common/Constants";
import * as DataModels from "../../../../Contracts/DataModels"; import * as DataModels from "../../../../Contracts/DataModels";
import { throughputUnit } from "../SettingsRenderUtils";
import * as SharedConstants from "../../../../Shared/Constants"; import * as SharedConstants from "../../../../Shared/Constants";
import ko from "knockout"; import Explorer from "../../../Explorer";
import { throughputUnit } from "../SettingsRenderUtils";
import { collection, container } from "../TestUtils";
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
describe("ScaleComponent", () => { describe("ScaleComponent", () => {
const nonNationalCloudContainer = new Explorer(); const nonNationalCloudContainer = new Explorer();
@@ -87,7 +87,6 @@ describe("ScaleComponent", () => {
location: undefined, location: undefined,
type: undefined, type: undefined,
kind: "documentdb", kind: "documentdb",
tags: undefined,
properties: { properties: {
documentEndpoint: undefined, documentEndpoint: undefined,
tableEndpoint: undefined, tableEndpoint: undefined,

View File

@@ -424,7 +424,6 @@ exports[`SettingsComponent renders 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {
@@ -1710,7 +1709,6 @@ exports[`SettingsComponent renders 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {
@@ -3009,7 +3007,6 @@ exports[`SettingsComponent renders 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {
@@ -4295,7 +4292,6 @@ exports[`SettingsComponent renders 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {

View File

@@ -79,7 +79,6 @@ describe("ContainerSampleGenerator", () => {
location: "foo", location: "foo",
type: "foo", type: "foo",
kind: "foo", kind: "foo",
tags: [],
properties: { properties: {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",

View File

@@ -116,11 +116,6 @@ export default class Explorer {
* */ * */
public databaseAccount: ko.Observable<DataModels.DatabaseAccount>; public databaseAccount: ko.Observable<DataModels.DatabaseAccount>;
public collectionCreationDefaults: ViewModels.CollectionCreationDefaults = SharedConstants.CollectionCreationDefaults; public collectionCreationDefaults: ViewModels.CollectionCreationDefaults = SharedConstants.CollectionCreationDefaults;
/**
* @deprecated
* Use userContext.apiType instead
* */
public defaultExperience: ko.Observable<string>;
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>; public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
/** /**
* @deprecated * @deprecated
@@ -377,17 +372,6 @@ export default class Explorer {
bounds: splitterBounds, bounds: splitterBounds,
direction: SplitterDirection.Vertical, 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({
// apiType: DefaultExperienceUtility.mapDefaultExperienceStringToEnum(defaultExperience),
// });
// });
this.isFixedCollectionWithSharedThroughputSupported = ko.computed(() => { this.isFixedCollectionWithSharedThroughputSupported = ko.computed(() => {
if (userContext.features.enableFixedCollectionWithSharedThroughput) { if (userContext.features.enableFixedCollectionWithSharedThroughput) {
@@ -1644,7 +1628,9 @@ export default class Explorer {
} }
const databaseAccount = this.databaseAccount(); const databaseAccount = this.databaseAccount();
const databaseAccountLocation = databaseAccount && databaseAccount.location.toLowerCase(); const firstWriteLocation =
databaseAccount?.properties?.writeLocations &&
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`; const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const authorizationHeader = getAuthorizationHeader(); const authorizationHeader = getAuthorizationHeader();
try { try {
@@ -1669,9 +1655,9 @@ export default class Explorer {
this.isNotebooksEnabledForAccount(true); this.isNotebooksEnabledForAccount(true);
return; return;
} }
const isAccountInAllowedLocation = !disallowedLocations.some(
(disallowedLocation) => disallowedLocation === databaseAccountLocation // firstWriteLocation should not be disallowed
); const isAccountInAllowedLocation = firstWriteLocation && disallowedLocations.indexOf(firstWriteLocation) === -1;
this.isNotebooksEnabledForAccount(isAccountInAllowedLocation); this.isNotebooksEnabledForAccount(isAccountInAllowedLocation);
} catch (error) { } catch (error) {
Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount"); Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount");

View File

@@ -1,5 +1,5 @@
import { actions, ContentRef } from "@nteract/core"; import { actions, ContentRef } from "@nteract/core";
import { Cells, CodeCell, MarkdownCell, RawCell } from "@nteract/stateful-components"; import { Cells, CodeCell, RawCell } from "@nteract/stateful-components";
import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt"; import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt";
@@ -11,6 +11,7 @@ import loadTransform from "../NotebookComponent/loadTransform";
import { AzureTheme } from "./AzureTheme"; import { AzureTheme } from "./AzureTheme";
import "./base.css"; import "./base.css";
import "./default.css"; import "./default.css";
import MarkdownCell from "./markdown-cell";
import "./NotebookReadOnlyRenderer.less"; import "./NotebookReadOnlyRenderer.less";
import SandboxOutputs from "./outputs/SandboxOutputs"; import SandboxOutputs from "./outputs/SandboxOutputs";

View File

@@ -15,6 +15,8 @@ import { CellOutputViewerProps } from "../../../../CellOutputViewer/CellOutputVi
interface ComponentProps { interface ComponentProps {
id: string; id: string;
contentRef: ContentRef; contentRef: ContentRef;
outputsContainerClassName?: string;
outputClassName?: string;
} }
interface StateProps { interface StateProps {
@@ -59,8 +61,10 @@ export class SandboxOutputs extends React.PureComponent<ComponentProps & StatePr
const props: CellOutputViewerProps = { const props: CellOutputViewerProps = {
id: this.props.id, id: this.props.id,
contentRef: this.props.contentRef, contentRef: this.props.contentRef,
hidden: this.props.hidden, outputsContainerClassName: `nteract-cell-outputs ${this.props.hidden ? "hidden" : ""} ${
expanded: this.props.expanded, this.props.expanded ? "expanded" : ""
} ${this.props.outputsContainerClassName}`,
outputClassName: this.props.outputClassName,
outputs: this.props.outputs.toArray().map((output) => outputToJS(output)), outputs: this.props.outputs.toArray().map((output) => outputToJS(output)),
onMetadataChange: this.props.onMetadataChange, onMetadataChange: this.props.onMetadataChange,
}; };

View File

@@ -1,10 +1,5 @@
.shemaAnalyzerComponent { .schemaAnalyzerComponent {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
}
.schemaAnalyzerCard {
max-width: 4096px;
width: 100%;
} }

View File

@@ -1,14 +1,12 @@
import { ImmutableOutput } from "@nteract/commutable"; import { ImmutableOutput } from "@nteract/commutable";
import { actions, AppState, ContentRef, KernelRef, selectors } from "@nteract/core"; import { actions, AppState, ContentRef, KernelRef, selectors } from "@nteract/core";
import { KernelOutputError, Output, StreamText } from "@nteract/outputs";
import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media";
import { Card } from "@uifabric/react-cards";
import Immutable from "immutable"; import Immutable from "immutable";
import { FontIcon, PrimaryButton, Spinner, SpinnerSize, Stack, Text, TextField } from "office-ui-fabric-react"; import { FontIcon, PrimaryButton, Spinner, SpinnerSize, Stack, Text, TextField } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import loadTransform from "../NotebookComponent/loadTransform"; import loadTransform from "../NotebookComponent/loadTransform";
import SandboxOutputs from "../NotebookRenderer/outputs/SandboxOutputs";
import "./SchemaAnalyzerComponent.less"; import "./SchemaAnalyzerComponent.less";
interface SchemaAnalyzerComponentPureProps { interface SchemaAnalyzerComponentPureProps {
@@ -91,70 +89,66 @@ export class SchemaAnalyzerComponent extends React.Component<
const showSchemaOutput = isKernelIdle && outputs.size > 0; const showSchemaOutput = isKernelIdle && outputs.size > 0;
return ( return (
<Stack className="schemaAnalyzerComponent" horizontalAlign="center" tokens={{ childrenGap: 20, padding: 20 }}> <div className="schemaAnalyzerComponent">
<Stack.Item grow styles={{ root: { display: "contents" } }}> <Stack horizontalAlign="center" tokens={{ childrenGap: 20, padding: 20 }}>
<Stack horizontal tokens={{ childrenGap: 20 }} styles={{ root: { width: "100%" } }}> <Stack.Item grow styles={{ root: { display: "contents" } }}>
<Stack.Item grow align="end"> <Stack horizontal tokens={{ childrenGap: 20 }} styles={{ root: { width: "100%" } }}>
<TextField <Stack.Item grow align="end">
value={this.state.filter} <TextField
onChange={this.onFilterTextFieldChange} value={this.state.filter}
label="Filter" onChange={this.onFilterTextFieldChange}
placeholder="{ field: 'value' }" label="Filter"
disabled={!isKernelIdle} placeholder="{ field: 'value' }"
/> disabled={!isKernelIdle}
</Stack.Item> />
<Stack.Item align="end"> </Stack.Item>
<PrimaryButton <Stack.Item align="end">
text={isKernelBusy ? "Analyzing..." : "Analyze"} <PrimaryButton
onClick={this.onAnalyzeButtonClick} text={isKernelBusy ? "Analyzing..." : "Analyze"}
disabled={!isKernelIdle} onClick={this.onAnalyzeButtonClick}
/> disabled={!isKernelIdle}
</Stack.Item> />
</Stack> </Stack.Item>
</Stack.Item> </Stack>
{showSchemaOutput ? (
outputs.map((output, index) => (
<Card className="schemaAnalyzerCard" key={index}>
<Card.Item tokens={{ padding: 10 }}>
<Output output={output}>
<TransformMedia output_type={"display_data"} id={id} contentRef={contentRef} />
<TransformMedia output_type={"execute_result"} id={id} contentRef={contentRef} />
<KernelOutputError />
<StreamText />
</Output>
</Card.Item>
</Card>
))
) : this.state.isFiltering ? (
<Stack.Item>
{isKernelBusy && <Spinner styles={{ root: { marginTop: 40 } }} size={SpinnerSize.large} />}
</Stack.Item> </Stack.Item>
) : (
<> {showSchemaOutput ? (
<SandboxOutputs
id={id}
contentRef={contentRef}
outputsContainerClassName="schema-analyzer-cell-outputs"
outputClassName="schema-analyzer-cell-output"
/>
) : this.state.isFiltering ? (
<Stack.Item> <Stack.Item>
<FontIcon iconName="Chart" style={{ fontSize: 100, color: "#43B1E5", marginTop: 40 }} /> {isKernelBusy && <Spinner styles={{ root: { marginTop: 40 } }} size={SpinnerSize.large} />}
</Stack.Item> </Stack.Item>
<Stack.Item> ) : (
<Text variant="xxLarge">Explore your schema</Text> <>
</Stack.Item> <Stack.Item>
<Stack.Item> <FontIcon iconName="Chart" style={{ fontSize: 100, color: "#43B1E5", marginTop: 40 }} />
<Text variant="large"> </Stack.Item>
Quickly visualize your schema to infer the frequency, types and ranges of fields in your data set. <Stack.Item>
</Text> <Text variant="xxLarge">Explore your schema</Text>
</Stack.Item> </Stack.Item>
<Stack.Item> <Stack.Item>
<PrimaryButton <Text variant="large">
styles={{ root: { fontSize: 18, padding: 30 } }} Quickly visualize your schema to infer the frequency, types and ranges of fields in your data set.
text={isKernelBusy ? "Analyzing..." : "Analyze Schema"} </Text>
onClick={this.onAnalyzeButtonClick} </Stack.Item>
disabled={kernelStatus !== "idle"} <Stack.Item>
/> <PrimaryButton
</Stack.Item> styles={{ root: { fontSize: 18, padding: 30 } }}
<Stack.Item>{isKernelBusy && <Spinner size={SpinnerSize.large} />}</Stack.Item> text={isKernelBusy ? "Analyzing..." : "Analyze Schema"}
</> onClick={this.onAnalyzeButtonClick}
)} disabled={kernelStatus !== "idle"}
</Stack> />
</Stack.Item>
<Stack.Item>{isKernelBusy && <Spinner size={SpinnerSize.large} />}</Stack.Item>
</>
)}
</Stack>
</div>
); );
} }
} }

View File

@@ -20,7 +20,6 @@ describe("Add Collection Pane", () => {
enableFreeTier: false, enableFreeTier: false,
}, },
type: undefined, type: undefined,
tags: [],
}; };
const mockFreeTierDatabaseAccount: DatabaseAccount = { const mockFreeTierDatabaseAccount: DatabaseAccount = {
@@ -36,7 +35,6 @@ describe("Add Collection Pane", () => {
enableFreeTier: true, enableFreeTier: true,
}, },
type: undefined, type: undefined,
tags: [],
}; };
beforeEach(() => { beforeEach(() => {
@@ -44,7 +42,13 @@ describe("Add Collection Pane", () => {
}); });
it("should be true if graph API and partition key is not /id nor /label", () => { it("should be true if graph API and partition key is not /id nor /label", () => {
explorer.defaultExperience(Constants.DefaultAccountExperience.Graph.toLowerCase()); updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableGremlin" }],
},
} as DatabaseAccount,
});
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane; const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
addCollectionPane.partitionKey("/blah"); addCollectionPane.partitionKey("/blah");
expect(addCollectionPane.isValid()).toBe(true); expect(addCollectionPane.isValid()).toBe(true);
@@ -58,7 +62,6 @@ describe("Add Collection Pane", () => {
}, },
} as DatabaseAccount, } as DatabaseAccount,
}); });
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane; const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
addCollectionPane.partitionKey("/id"); addCollectionPane.partitionKey("/id");
expect(addCollectionPane.isValid()).toBe(false); expect(addCollectionPane.isValid()).toBe(false);

View File

@@ -21,7 +21,6 @@ describe("Add Database Pane", () => {
enableFreeTier: false, enableFreeTier: false,
}, },
type: undefined, type: undefined,
tags: [],
}; };
const mockFreeTierDatabaseAccount: DatabaseAccount = { const mockFreeTierDatabaseAccount: DatabaseAccount = {
@@ -37,7 +36,6 @@ describe("Add Database Pane", () => {
enableFreeTier: true, enableFreeTier: true,
}, },
type: undefined, type: undefined,
tags: [],
}; };
beforeEach(() => { beforeEach(() => {

View File

@@ -413,7 +413,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {

View File

@@ -403,7 +403,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {

View File

@@ -401,7 +401,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
}, },
"databaseAccount": [Function], "databaseAccount": [Function],
"databases": [Function], "databases": [Function],
"defaultExperience": [Function],
"deleteCollectionText": [Function], "deleteCollectionText": [Function],
"deleteDatabaseText": [Function], "deleteDatabaseText": [Function],
"gitHubClient": GitHubClient { "gitHubClient": GitHubClient {

View File

@@ -1,5 +1,5 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as Constants from "../../Common/Constants"; import { DatabaseAccount } from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { updateUserContext } from "../../UserContext"; import { updateUserContext } from "../../UserContext";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
@@ -27,7 +27,13 @@ describe("Documents tab", () => {
describe("showPartitionKey", () => { describe("showPartitionKey", () => {
const explorer = new Explorer(); const explorer = new Explorer();
const mongoExplorer = new Explorer(); const mongoExplorer = new Explorer();
mongoExplorer.defaultExperience(Constants.DefaultAccountExperience.MongoDB); updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableGremlin" }],
},
} as DatabaseAccount,
});
const collectionWithoutPartitionKey = <ViewModels.Collection>(<unknown>{ const collectionWithoutPartitionKey = <ViewModels.Collection>(<unknown>{
id: ko.observable<string>("foo"), id: ko.observable<string>("foo"),

View File

@@ -3,6 +3,7 @@ import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import Explorer from "../Explorer"; import Explorer from "../Explorer";
import { NotebookClientV2 } from "../Notebook/NotebookClientV2"; import { NotebookClientV2 } from "../Notebook/NotebookClientV2";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
@@ -29,7 +30,7 @@ export default class NotebookTabBase extends TabsBase {
NotebookTabBase.clientManager = new NotebookClientV2({ NotebookTabBase.clientManager = new NotebookClientV2({
connectionInfo: this.container.notebookServerInfo(), connectionInfo: this.container.notebookServerInfo(),
databaseAccountName: this.container.databaseAccount().name, databaseAccountName: this.container.databaseAccount().name,
defaultExperience: this.container.defaultExperience(), defaultExperience: userContext.apiType,
contentProvider: this.container.notebookManager?.notebookContentProvider, contentProvider: this.container.notebookManager?.notebookContentProvider,
}); });
} }

View File

@@ -23,7 +23,6 @@ describe("Tabs manager tests", () => {
location: "", location: "",
type: "", type: "",
kind: "", kind: "",
tags: "",
properties: undefined, properties: undefined,
}); });

View File

@@ -1,10 +1,10 @@
import * as DataModels from "../../Contracts/DataModels";
import * as ko from "knockout"; import * as ko from "knockout";
import Database from "./Database";
import Explorer from "../Explorer";
import { HttpStatusCodes } from "../../Common/Constants"; import { HttpStatusCodes } from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import { JunoClient } from "../../Juno/JunoClient"; import { JunoClient } from "../../Juno/JunoClient";
import { userContext, updateUserContext } from "../../UserContext"; import { updateUserContext, userContext } from "../../UserContext";
import Explorer from "../Explorer";
import Database from "./Database";
const createMockContainer = (): Explorer => { const createMockContainer = (): Explorer => {
const mockContainer = new Explorer(); const mockContainer = new Explorer();
@@ -19,7 +19,6 @@ updateUserContext({
name: "fakeName", name: "fakeName",
location: "fakeLocation", location: "fakeLocation",
type: "fakeType", type: "fakeType",
tags: undefined,
kind: "fakeKind", kind: "fakeKind",
properties: { properties: {
documentEndpoint: "fakeEndpoint", documentEndpoint: "fakeEndpoint",

View File

@@ -14,7 +14,6 @@ const sampleDatabaseAccount: DataModels.DatabaseAccount = {
location: "location", location: "location",
type: "type", type: "type",
kind: "kind", kind: "kind",
tags: [],
properties: { properties: {
documentEndpoint: "documentEndpoint", documentEndpoint: "documentEndpoint",
gremlinEndpoint: "gremlinEndpoint", gremlinEndpoint: "gremlinEndpoint",

View File

@@ -13,7 +13,6 @@ const sampleDatabaseAccount: DatabaseAccount = {
location: "location", location: "location",
type: "type", type: "type",
kind: "kind", kind: "kind",
tags: [],
properties: { properties: {
documentEndpoint: "documentEndpoint", documentEndpoint: "documentEndpoint",
gremlinEndpoint: "gremlinEndpoint", gremlinEndpoint: "gremlinEndpoint",

View File

@@ -1,4 +1,4 @@
import { AccountKind, DefaultAccountExperience, TagNames } from "../../Common/Constants"; import { AccountKind } from "../../Common/Constants";
export const emulatorAccount = { export const emulatorAccount = {
name: "", name: "",
@@ -6,9 +6,6 @@ export const emulatorAccount = {
location: "", location: "",
type: "", type: "",
kind: AccountKind.DocumentDB, kind: AccountKind.DocumentDB,
tags: {
[TagNames.defaultExperience]: DefaultAccountExperience.DocumentDB,
},
properties: { properties: {
documentEndpoint: "", documentEndpoint: "",
tableEndpoint: "", tableEndpoint: "",

View File

@@ -1,4 +1,3 @@
import Q from "q";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
import { configContext } from "../../ConfigContext"; import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
@@ -21,8 +20,7 @@ export default class AuthHeadersUtil {
const databaseAccount = userContext.databaseAccount; const databaseAccount = userContext.databaseAccount;
const subscriptionId: string = userContext.subscriptionId; const subscriptionId: string = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup; const resourceGroup = userContext.resourceGroup;
const defaultExperience: string = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(databaseAccount); const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
const accountEndpoint = (databaseAccount && databaseAccount.properties.documentEndpoint) || ""; const accountEndpoint = (databaseAccount && databaseAccount.properties.documentEndpoint) || "";
const sid = subscriptionId || ""; const sid = subscriptionId || "";
const rg = resourceGroup || ""; const rg = resourceGroup || "";

View File

@@ -1,27 +1,28 @@
import { DefaultAccountExperience, CapabilityNames, AccountKind } from "../../Common/Constants"; import { AccountKind, CapabilityNames } from "../../Common/Constants";
import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels"; import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext";
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown { export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {
let properties = { documentEndpoint: metadata.documentEndpoint }; let properties = { documentEndpoint: metadata.documentEndpoint };
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(metadata.apiKind); const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(metadata.apiKind);
if (apiExperience === DefaultAccountExperience.Cassandra) { if (apiExperience === "Cassandra") {
properties = Object.assign(properties, { properties = Object.assign(properties, {
cassandraEndpoint: metadata.apiEndpoint, cassandraEndpoint: metadata.apiEndpoint,
capabilities: [{ name: CapabilityNames.EnableCassandra }], capabilities: [{ name: CapabilityNames.EnableCassandra }],
}); });
} else if (apiExperience === DefaultAccountExperience.Table) { } else if (apiExperience === "Tables") {
properties = Object.assign(properties, { properties = Object.assign(properties, {
tableEndpoint: metadata.apiEndpoint, tableEndpoint: metadata.apiEndpoint,
capabilities: [{ name: CapabilityNames.EnableTable }], capabilities: [{ name: CapabilityNames.EnableTable }],
}); });
} else if (apiExperience === DefaultAccountExperience.Graph) { } else if (apiExperience === "Gremlin") {
properties = Object.assign(properties, { properties = Object.assign(properties, {
gremlinEndpoint: metadata.apiEndpoint, gremlinEndpoint: metadata.apiEndpoint,
capabilities: [{ name: CapabilityNames.EnableGremlin }], capabilities: [{ name: CapabilityNames.EnableGremlin }],
}); });
} else if (apiExperience === DefaultAccountExperience.MongoDB) { } else if (apiExperience === "Mongo") {
if (metadata.apiKind === ApiKind.MongoDBCompute) { if (metadata.apiKind === ApiKind.MongoDBCompute) {
properties = Object.assign(properties, { properties = Object.assign(properties, {
mongoEndpoint: metadata.mongoEndpoint, mongoEndpoint: metadata.mongoEndpoint,
@@ -31,12 +32,8 @@ export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMe
return properties; return properties;
} }
export function getDatabaseAccountKindFromExperience(apiExperience: string): string { export function getDatabaseAccountKindFromExperience(apiExperience: typeof userContext.apiType): AccountKind {
if (apiExperience === DefaultAccountExperience.MongoDB) { if (apiExperience === "Mongo") {
return AccountKind.MongoDB;
}
if (apiExperience === DefaultAccountExperience.ApiForMongoDB) {
return AccountKind.MongoDB; return AccountKind.MongoDB;
} }

View File

@@ -1,133 +1,67 @@
import * as Constants from "../Common/Constants";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import { userContext } from "../UserContext";
import { DefaultExperienceUtility } from "./DefaultExperienceUtility"; import { DefaultExperienceUtility } from "./DefaultExperienceUtility";
describe("Default Experience Utility", () => { describe("Default Experience Utility", () => {
describe("getDefaultExperienceFromApiKind()", () => { describe("getDefaultExperienceFromApiKind()", () => {
function runScenario(apiKind: number, expectedExperience: string): void { function runScenario(apiKind: number, expectedExperience: typeof userContext.apiType): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind); const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
expect(resolvedExperience).toEqual(expectedExperience); expect(resolvedExperience).toEqual(expectedExperience);
} }
describe("On SQL", () => { describe("On SQL", () => {
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, Constants.DefaultAccountExperience.DocumentDB)); it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL"));
}); });
describe("On MongoDB", () => { describe("On MongoDB", () => {
it("should return MongoDB", () => it("should return MongoDB", () => runScenario(DataModels.ApiKind.MongoDB, "Mongo"));
runScenario(DataModels.ApiKind.MongoDB, Constants.DefaultAccountExperience.MongoDB));
}); });
describe("On Table", () => { describe("On Table", () => {
it("should return Table", () => runScenario(DataModels.ApiKind.Table, Constants.DefaultAccountExperience.Table)); it("should return Table", () => runScenario(DataModels.ApiKind.Table, "Tables"));
}); });
describe("On Cassandra", () => { describe("On Cassandra", () => {
it("should return Cassandra", () => it("should return Cassandra", () => runScenario(DataModels.ApiKind.Cassandra, "Cassandra"));
runScenario(DataModels.ApiKind.Cassandra, Constants.DefaultAccountExperience.Cassandra));
}); });
describe("On Graph", () => { describe("On Graph", () => {
it("should return Graph", () => runScenario(DataModels.ApiKind.Graph, Constants.DefaultAccountExperience.Graph)); it("should return Graph", () => runScenario(DataModels.ApiKind.Graph, "Gremlin"));
}); });
describe("On unknown", () => { describe("On unknown", () => {
it("should return Default", () => runScenario(-1, Constants.DefaultAccountExperience.Default)); it("should return Default", () => runScenario(-1, "SQL"));
}); });
}); });
describe("getApiKindFromDefaultExperience()", () => { describe("getApiKindFromDefaultExperience()", () => {
function runScenario(defaultExperience: string, expectedApiKind: number): void { function runScenario(defaultExperience: typeof userContext.apiType, expectedApiKind: number): void {
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience); const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
expect(resolvedApiKind).toEqual(expectedApiKind); expect(resolvedApiKind).toEqual(expectedApiKind);
} }
describe("On SQL", () => { describe("On SQL", () => {
it("should return SQL", () => runScenario(Constants.DefaultAccountExperience.DocumentDB, DataModels.ApiKind.SQL)); it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL));
}); });
describe("On MongoDB", () => { describe("On MongoDB", () => {
it("should return MongoDB", () => it("should return MongoDB", () => runScenario("Mongo", DataModels.ApiKind.MongoDB));
runScenario(Constants.DefaultAccountExperience.MongoDB, DataModels.ApiKind.MongoDB));
}); });
describe("On Table", () => { describe("On Table", () => {
it("should return Table", () => runScenario(Constants.DefaultAccountExperience.Table, DataModels.ApiKind.Table)); it("should return Table", () => runScenario("Tables", DataModels.ApiKind.Table));
}); });
describe("On Cassandra", () => { describe("On Cassandra", () => {
it("should return Cassandra", () => it("should return Cassandra", () => runScenario("Cassandra", DataModels.ApiKind.Cassandra));
runScenario(Constants.DefaultAccountExperience.Cassandra, DataModels.ApiKind.Cassandra));
}); });
describe("On Graph", () => { describe("On Graph", () => {
it("should return Graph", () => runScenario(Constants.DefaultAccountExperience.Graph, DataModels.ApiKind.Graph)); it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph));
}); });
describe("On null", () => { describe("On null", () => {
it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL)); it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL));
}); });
}); });
describe("getDefaultExperienceFromDatabaseAccount()", () => {
function runScenario(databaseAccount: DataModels.DatabaseAccount, expectedDefaultExperience: string): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(databaseAccount);
expect(resolvedExperience).toEqual(expectedDefaultExperience);
}
const databaseAccountWithWrongTagsAndCapabilities: DataModels.DatabaseAccount = {
id: "test",
kind: "GlobalDocumentDB",
name: "test",
location: "somewhere",
type: "DocumentDB",
tags: {
defaultExperience: "Gremlin (graph)",
},
properties: {
documentEndpoint: "",
cassandraEndpoint: "",
gremlinEndpoint: "",
tableEndpoint: "",
capabilities: [
{
name: Constants.CapabilityNames.EnableGremlin,
description: "something",
},
],
},
};
const databaseAccountWithApiKind: DataModels.DatabaseAccount = {
id: "test",
kind: Constants.AccountKind.MongoDB,
name: "test",
location: "somewhere",
type: "DocumentDB",
tags: {},
properties: {
documentEndpoint: "",
cassandraEndpoint: "",
gremlinEndpoint: "",
tableEndpoint: "",
capabilities: [
{
name: Constants.CapabilityNames.EnableGremlin,
description: "something",
},
],
},
};
describe("Disregard tags", () => {
it("should return Graph", () =>
runScenario(databaseAccountWithWrongTagsAndCapabilities, Constants.DefaultAccountExperience.Graph));
});
describe("Respect Kind over capabilities", () => {
it("should return MongoDB", () =>
runScenario(databaseAccountWithApiKind, Constants.DefaultAccountExperience.MongoDB));
});
});
}); });

View File

@@ -1,132 +1,47 @@
import * as _ from "underscore";
import * as Constants from "../Common/Constants";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { userContext } from "../UserContext";
export class DefaultExperienceUtility { export class DefaultExperienceUtility {
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string | null { public static getApiKindFromDefaultExperience(defaultExperience: typeof userContext.apiType): DataModels.ApiKind {
if (!databaseAccount) {
return null;
}
const kind: string =
databaseAccount && databaseAccount.kind && databaseAccount.kind && databaseAccount.kind.toLowerCase();
const capabilities = (databaseAccount.properties && databaseAccount.properties.capabilities) || [];
return DefaultExperienceUtility._getDefaultExperience(kind, capabilities);
}
public static getApiKindFromDefaultExperience(defaultExperience: string): DataModels.ApiKind {
if (!defaultExperience) { if (!defaultExperience) {
return DataModels.ApiKind.SQL; return DataModels.ApiKind.SQL;
} }
switch (defaultExperience) { switch (defaultExperience) {
case Constants.DefaultAccountExperience.DocumentDB: case "SQL":
return DataModels.ApiKind.SQL; return DataModels.ApiKind.SQL;
case Constants.DefaultAccountExperience.MongoDB: case "Mongo":
case Constants.DefaultAccountExperience.ApiForMongoDB:
return DataModels.ApiKind.MongoDB; return DataModels.ApiKind.MongoDB;
case Constants.DefaultAccountExperience.Table: case "Tables":
return DataModels.ApiKind.Table; return DataModels.ApiKind.Table;
case Constants.DefaultAccountExperience.Cassandra: case "Cassandra":
return DataModels.ApiKind.Cassandra; return DataModels.ApiKind.Cassandra;
case Constants.DefaultAccountExperience.Graph: case "Gremlin":
return DataModels.ApiKind.Graph; return DataModels.ApiKind.Graph;
default: default:
return DataModels.ApiKind.SQL; return DataModels.ApiKind.SQL;
} }
} }
public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): string { public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): typeof userContext.apiType {
if (apiKind == null) { if (apiKind == null) {
return Constants.DefaultAccountExperience.Default; return "SQL";
} }
switch (apiKind) { switch (apiKind) {
case DataModels.ApiKind.SQL: case DataModels.ApiKind.SQL:
return Constants.DefaultAccountExperience.DocumentDB; return "SQL";
case DataModels.ApiKind.MongoDB: case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute: case DataModels.ApiKind.MongoDBCompute:
return Constants.DefaultAccountExperience.MongoDB; return "Mongo";
case DataModels.ApiKind.Table: case DataModels.ApiKind.Table:
return Constants.DefaultAccountExperience.Table; return "Tables";
case DataModels.ApiKind.Cassandra: case DataModels.ApiKind.Cassandra:
return Constants.DefaultAccountExperience.Cassandra; return "Cassandra";
case DataModels.ApiKind.Graph: case DataModels.ApiKind.Graph:
return Constants.DefaultAccountExperience.Graph; return "Gremlin";
default: default:
return Constants.DefaultAccountExperience.Default; return "SQL";
} }
} }
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind) || "";
const defaultExperienceFromCapabilities: string =
DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(capabilities) || "";
if (!!defaultExperienceFromKind) {
return defaultExperienceFromKind;
}
if (!!defaultExperienceFromCapabilities) {
return defaultExperienceFromCapabilities;
}
return defaultDefaultExperience;
}
private static _getDefaultExperienceFromAccountKind(kind: string): string | null {
if (!kind) {
return null;
}
if (kind.toLowerCase() === Constants.AccountKind.MongoDB.toLowerCase()) {
return Constants.DefaultAccountExperience.MongoDB;
}
if (kind.toLowerCase() === Constants.AccountKind.Parse.toLowerCase()) {
return Constants.DefaultAccountExperience.MongoDB;
}
return null;
}
private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string | null {
if (!capabilities) {
return null;
}
if (!Array.isArray(capabilities)) {
return null;
}
const enableTable = DefaultExperienceUtility._findCapability(capabilities, Constants.CapabilityNames.EnableTable);
if (enableTable) {
return Constants.DefaultAccountExperience.Table;
}
const enableGremlin = DefaultExperienceUtility._findCapability(
capabilities,
Constants.CapabilityNames.EnableGremlin
);
if (enableGremlin) {
return Constants.DefaultAccountExperience.Graph;
}
const enableCassandra = DefaultExperienceUtility._findCapability(
capabilities,
Constants.CapabilityNames.EnableCassandra
);
if (enableCassandra) {
return Constants.DefaultAccountExperience.Cassandra;
}
return null;
}
private static _findCapability(capabilities: DataModels.Capability[], capabilityName: string): DataModels.Capability {
return _.find(capabilities, (capability) => {
return capability && capability.name && capability.name.toLowerCase() === capabilityName.toLowerCase();
});
}
} }

View File

@@ -27,7 +27,7 @@ interface UserContext {
readonly hasWriteAccess: boolean; readonly hasWriteAccess: boolean;
} }
type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra"; export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra";
export type PortalEnv = "localhost" | "blackforest" | "fairfax" | "mooncake" | "prod" | "dev"; export type PortalEnv = "localhost" | "blackforest" | "fairfax" | "mooncake" | "prod" | "dev";
const features = extractFeatures(); const features = extractFeatures();

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { applyExplorerBindings } from "../applyExplorerBindings"; import { applyExplorerBindings } from "../applyExplorerBindings";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import { AccountKind, DefaultAccountExperience } from "../Common/Constants"; import { AccountKind } from "../Common/Constants";
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility"; import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
import { sendMessage, sendReadyMessage } from "../Common/MessageHandler"; import { sendMessage, sendReadyMessage } from "../Common/MessageHandler";
import { configContext, Platform, updateConfigContext } from "../ConfigContext"; import { configContext, Platform, updateConfigContext } from "../ConfigContext";
@@ -141,8 +141,6 @@ function configureHostedWithResourceToken(config: ResourceToken, explorerParams:
name: parsedResourceToken.accountEndpoint, name: parsedResourceToken.accountEndpoint,
kind: AccountKind.GlobalDocumentDB, kind: AccountKind.GlobalDocumentDB,
properties: { documentEndpoint: parsedResourceToken.accountEndpoint }, properties: { documentEndpoint: parsedResourceToken.accountEndpoint },
// Resource tokens can only be used with SQL API
tags: { defaultExperience: DefaultAccountExperience.DocumentDB },
}; };
updateUserContext({ updateUserContext({
databaseAccount, databaseAccount,
@@ -165,9 +163,7 @@ function configureHostedWithEncryptedToken(config: EncryptedToken, explorerParam
authType: AuthType.EncryptedToken, authType: AuthType.EncryptedToken,
accessToken: encodeURIComponent(config.encryptedToken), accessToken: encodeURIComponent(config.encryptedToken),
}); });
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind( const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(config.encryptedTokenMetadata.apiKind);
config.encryptedTokenMetadata.apiKind
);
const explorer = new Explorer(explorerParams); const explorer = new Explorer(explorerParams);
explorer.configure({ explorer.configure({
databaseAccount: { databaseAccount: {

View File

@@ -0,0 +1,37 @@
import { jest } from "@jest/globals";
import "expect-playwright";
import { safeClick } from "../utils/safeClick";
import { generateDatabaseNameWithTimestamp, generateUniqueName } from "../utils/shared";
jest.setTimeout(240000);
test("Graph CRUD", async () => {
const databaseId = generateDatabaseNameWithTimestamp();
const containerId = generateUniqueName("container");
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-gremlin-runner");
await page.waitForSelector("iframe");
const explorer = page.frame({
name: "explorer",
});
// Create new database and graph
await explorer.click('[data-test="New Graph"]');
await explorer.fill('[aria-label="New database id"]', databaseId);
await explorer.fill('[aria-label="Graph id"]', containerId);
await explorer.fill('[aria-label="Partition key"]', "/pk");
await explorer.click("#sidePanelOkButton");
await safeClick(explorer, `.nodeItem >> text=${databaseId}`);
await safeClick(explorer, `.nodeItem >> text=${containerId}`);
// Delete database and graph
await safeClick(explorer, `[data-test="${containerId}"] [aria-label="More"]`);
await safeClick(explorer, 'button[role="menuitem"]:has-text("Delete Graph")');
await explorer.fill('text=* Confirm by typing the graph id >> input[type="text"]', containerId);
await explorer.click('[aria-label="Submit"]');
await explorer.click(`[data-test="${databaseId}"] [aria-label="More"]`);
await explorer.click('button[role="menuitem"]:has-text("Delete Database")');
await explorer.click('text=* Confirm by typing the database id >> input[type="text"]');
await explorer.fill('text=* Confirm by typing the database id >> input[type="text"]', databaseId);
await explorer.click("#sidePanelOkButton");
await expect(explorer).not.toHaveText(".dataResourceTree", databaseId);
await expect(explorer).not.toHaveText(".dataResourceTree", containerId);
});

View File

@@ -21,6 +21,6 @@
"noEmit": true, "noEmit": true,
"types": ["jest"] "types": ["jest"]
}, },
"include": ["./src/**/*", "./test/testExplorer/TestExplorer.ts"], "include": ["./src/**/*", "./utils/**/*"],
"exclude": ["./src/**/__mocks__/**/*"] "exclude": ["./src/**/__mocks__/**/*"]
} }

View File

@@ -1,6 +1,7 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"include": ["./test/**/*"],
"compilerOptions": { "compilerOptions": {
"module": "commonjs" "module": "commonjs"
} }
} }