mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 15:06:55 +00:00
Merge branch 'master' into defect-2276938
This commit is contained in:
commit
7373de3ac8
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -92,11 +92,11 @@ jobs:
|
|||||||
name: dist
|
name: dist
|
||||||
path: dist/
|
path: dist/
|
||||||
- name: Upload build to preview blob storage
|
- name: Upload build to preview blob storage
|
||||||
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}"
|
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||||
env:
|
env:
|
||||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||||
- name: Upload preview config to blob storage
|
- name: Upload preview config to blob storage
|
||||||
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}"
|
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||||
env:
|
env:
|
||||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||||
endtoendemulator:
|
endtoendemulator:
|
||||||
@ -182,7 +182,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
- run: cp ./configs/prod.json config.json
|
- run: cp ./configs/prod.json config.json
|
||||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "vimeng@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
@ -207,7 +207,7 @@ jobs:
|
|||||||
name: dist
|
name: dist
|
||||||
- run: cp ./configs/mpac.json config.json
|
- run: cp ./configs/mpac.json config.json
|
||||||
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
|
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
|
||||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "vimeng@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
File diff suppressed because it is too large
Load Diff
14
src/Common/EnvironmentUtility.test.ts
Normal file
14
src/Common/EnvironmentUtility.test.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import * as EnvironmentUtility from "./EnvironmentUtility";
|
||||||
|
|
||||||
|
describe("Environment Utility Test", () => {
|
||||||
|
it("Test sample URI with /", () => {
|
||||||
|
const uri = "test/";
|
||||||
|
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(uri);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Test sample URI without /", () => {
|
||||||
|
const uri = "test";
|
||||||
|
const expectedResult = "test/";
|
||||||
|
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import * as OfferUtility from "./OfferUtility";
|
|
||||||
import { SDKOfferDefinition, Offer } from "../Contracts/DataModels";
|
|
||||||
import { OfferResponse } from "@azure/cosmos";
|
import { OfferResponse } from "@azure/cosmos";
|
||||||
|
import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
|
||||||
|
import * as OfferUtility from "./OfferUtility";
|
||||||
|
|
||||||
describe("parseSDKOfferResponse", () => {
|
describe("parseSDKOfferResponse", () => {
|
||||||
it("manual throughput", () => {
|
it("manual throughput", () => {
|
||||||
@ -31,6 +31,26 @@ describe("parseSDKOfferResponse", () => {
|
|||||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
|
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("offerContent not defined", () => {
|
||||||
|
const mockOfferDefinition = {
|
||||||
|
id: "test",
|
||||||
|
} as SDKOfferDefinition;
|
||||||
|
|
||||||
|
const mockResponse = {
|
||||||
|
resource: mockOfferDefinition,
|
||||||
|
} as OfferResponse;
|
||||||
|
|
||||||
|
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("offerDefinition is null", () => {
|
||||||
|
const mockResponse = {
|
||||||
|
resource: undefined,
|
||||||
|
} as OfferResponse;
|
||||||
|
|
||||||
|
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
it("autoscale throughput", () => {
|
it("autoscale throughput", () => {
|
||||||
const mockOfferDefinition = {
|
const mockOfferDefinition = {
|
||||||
content: {
|
content: {
|
||||||
|
49
src/Common/UrlUtility.test.ts
Normal file
49
src/Common/UrlUtility.test.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import * as UrlUtility from "./UrlUtility";
|
||||||
|
|
||||||
|
describe("parseDocumentsPath", () => {
|
||||||
|
it("empty resource path", () => {
|
||||||
|
const resourcePath = "";
|
||||||
|
|
||||||
|
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resourcePath does not begin or end with /", () => {
|
||||||
|
const resourcePath = "localhost/portal/home";
|
||||||
|
const expectedResult = {
|
||||||
|
type: "home",
|
||||||
|
objectBody: {
|
||||||
|
id: "portal",
|
||||||
|
self: "/localhost/portal/home/",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual(expectedResult);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resourcePath length is even", () => {
|
||||||
|
const resourcePath = "/localhost/portal/src/home/";
|
||||||
|
const expectedResult = {
|
||||||
|
type: "src",
|
||||||
|
objectBody: {
|
||||||
|
id: "home",
|
||||||
|
self: resourcePath,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual(expectedResult);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createUri", () => {
|
||||||
|
const baseUri = "http://foo.com/bar/";
|
||||||
|
const relativeUri = "/index.html";
|
||||||
|
const expectedUri = "http://foo.com/bar/index.html";
|
||||||
|
|
||||||
|
expect(UrlUtility.createUri(baseUri, relativeUri)).toEqual(expectedUri);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error if baseUri is empty", () => {
|
||||||
|
expect(() => {
|
||||||
|
UrlUtility.createUri("", "/home");
|
||||||
|
}).toThrow("baseUri is null or empty");
|
||||||
|
});
|
||||||
|
});
|
@ -202,6 +202,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
|||||||
if (showOpenFullScreen) {
|
if (showOpenFullScreen) {
|
||||||
const label = "Open Full Screen";
|
const label = "Open Full Screen";
|
||||||
const fullScreenButton: CommandButtonComponentProps = {
|
const fullScreenButton: CommandButtonComponentProps = {
|
||||||
|
id: "openFullScreenBtn",
|
||||||
iconSrc: OpenInTabIcon,
|
iconSrc: OpenInTabIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import * as InMemoryContentProviderUtils from "./ContentProviders/InMemoryContentProviderUtils";
|
||||||
|
|
||||||
|
describe("fromContentUri", () => {
|
||||||
|
it("fromContentUri should return valid result", () => {
|
||||||
|
const contentUri = "memory://resource/path";
|
||||||
|
const result = "resource";
|
||||||
|
|
||||||
|
expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fromContentUri should return undefined on invalid input", () => {
|
||||||
|
const contentUri = "invalid";
|
||||||
|
|
||||||
|
expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toContentUri should return valid result", () => {
|
||||||
|
const path = "resource/path";
|
||||||
|
const result = "memory://resource/path";
|
||||||
|
|
||||||
|
expect(InMemoryContentProviderUtils.toContentUri(path)).toEqual(result);
|
||||||
|
});
|
||||||
|
});
|
@ -984,7 +984,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
private getPartitionKeyPlaceHolder(index?: number): string {
|
private getPartitionKeyPlaceHolder(index?: number): string {
|
||||||
switch (userContext.apiType) {
|
switch (userContext.apiType) {
|
||||||
case "Mongo":
|
case "Mongo":
|
||||||
return "e.g., address.zipCode";
|
return "e.g., categoryId";
|
||||||
case "Gremlin":
|
case "Gremlin":
|
||||||
return "e.g., /address";
|
return "e.g., /address";
|
||||||
case "SQL":
|
case "SQL":
|
||||||
@ -1116,7 +1116,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return userContext.apiType === "SQL" ? "/pk" : "pk";
|
return userContext.apiType === "SQL" ? "/pk" : "pk";
|
||||||
}
|
}
|
||||||
if (this.props.isQuickstart) {
|
if (this.props.isQuickstart) {
|
||||||
return userContext.apiType === "SQL" ? "/address" : "address";
|
return userContext.apiType === "SQL" ? "/categoryId" : "categoryId";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
import { DirectionalHint, Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -18,6 +18,11 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let totalSteps = 9;
|
||||||
|
if (userContext.isTryCosmosDBSubscription) {
|
||||||
|
totalSteps = 10;
|
||||||
|
}
|
||||||
|
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 1:
|
case 1:
|
||||||
return isSampleDBExpanded ? (
|
return isSampleDBExpanded ? (
|
||||||
@ -33,7 +38,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 1 of 8"
|
footerContent={"Step 1 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Start viewing and working with your data by opening Documents under Data
|
Start viewing and working with your data by opening Documents under Data
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -55,7 +60,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(1),
|
onClick: () => setStep(1),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 2 of 8"
|
footerContent={"Step 2 of " + totalSteps}
|
||||||
>
|
>
|
||||||
View documents here using the documents window. You can also use your favorite MongoDB tools and drivers to do
|
View documents here using the documents window. You can also use your favorite MongoDB tools and drivers to do
|
||||||
so.
|
so.
|
||||||
@ -78,7 +83,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(2),
|
onClick: () => setStep(2),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 3 of 8"
|
footerContent={"Step 3 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Add new document by copy / pasting JSON or uploading a JSON. You can also use your favorite MongoDB tools and
|
Add new document by copy / pasting JSON or uploading a JSON. You can also use your favorite MongoDB tools and
|
||||||
drivers to do so.
|
drivers to do so.
|
||||||
@ -99,7 +104,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(3),
|
onClick: () => setStep(3),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 4 of 8"
|
footerContent={"Step 4 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Query your data using the filter function. Azure Cosmos DB for MongoDB provides comprehensive support for
|
Query your data using the filter function. Azure Cosmos DB for MongoDB provides comprehensive support for
|
||||||
MongoDB query language constructs. You can also use your favorite MongoDB tools and drivers to do so.
|
MongoDB query language constructs. You can also use your favorite MongoDB tools and drivers to do so.
|
||||||
@ -120,7 +125,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(4),
|
onClick: () => setStep(4),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 5 of 8"
|
footerContent={"Step 5 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Change throughput provisioned to your collection according to your needs
|
Change throughput provisioned to your collection according to your needs
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -140,7 +145,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(5),
|
onClick: () => setStep(5),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 6 of 8"
|
footerContent={"Step 6 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Use the indexing policy editor to create and edit your indexes.
|
Use the indexing policy editor to create and edit your indexes.
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -160,12 +165,54 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(6),
|
onClick: () => setStep(6),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 7 of 8"
|
footerContent={"Step 7 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Visualize your data, store queries in an interactive document
|
Visualize your data, store queries in an interactive document
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
);
|
);
|
||||||
case 8:
|
case 8:
|
||||||
|
return (
|
||||||
|
<TeachingBubble
|
||||||
|
headline="Launch full screen"
|
||||||
|
target={"#openFullScreenBtn"}
|
||||||
|
hasCloseButton
|
||||||
|
primaryButtonProps={{
|
||||||
|
text: "Next",
|
||||||
|
onClick: () => (userContext.isTryCosmosDBSubscription ? setStep(9) : setStep(10)),
|
||||||
|
}}
|
||||||
|
secondaryButtonProps={{
|
||||||
|
text: "Previous",
|
||||||
|
onClick: () => setStep(7),
|
||||||
|
}}
|
||||||
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
|
footerContent={"Step 8 of " + totalSteps}
|
||||||
|
>
|
||||||
|
This will open a new tab in your browser to use Cosmos DB Explorer. Using the provided URLs you can share
|
||||||
|
read-write or read-only access with other people.
|
||||||
|
</TeachingBubble>
|
||||||
|
);
|
||||||
|
case 9:
|
||||||
|
return (
|
||||||
|
<TeachingBubble
|
||||||
|
headline="Boost your experience"
|
||||||
|
target={"#freeTierTeachingBubble"}
|
||||||
|
hasCloseButton
|
||||||
|
primaryButtonProps={{
|
||||||
|
text: "Next",
|
||||||
|
onClick: () => setStep(10),
|
||||||
|
}}
|
||||||
|
secondaryButtonProps={{
|
||||||
|
text: "Previous",
|
||||||
|
onClick: () => setStep(8),
|
||||||
|
}}
|
||||||
|
calloutProps={{ directionalHint: DirectionalHint.leftCenter }}
|
||||||
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
|
footerContent={"Step 9 of " + totalSteps}
|
||||||
|
>
|
||||||
|
Unlock everything Azure Cosmos DB has to offer When you're ready, upgrade to production.
|
||||||
|
</TeachingBubble>
|
||||||
|
);
|
||||||
|
case 10:
|
||||||
return (
|
return (
|
||||||
<TeachingBubble
|
<TeachingBubble
|
||||||
headline="Congratulations!"
|
headline="Congratulations!"
|
||||||
@ -180,10 +227,10 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
}}
|
}}
|
||||||
secondaryButtonProps={{
|
secondaryButtonProps={{
|
||||||
text: "Previous",
|
text: "Previous",
|
||||||
onClick: () => setStep(7),
|
onClick: () => (userContext.isTryCosmosDBSubscription ? setStep(9) : setStep(8)),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 8 of 8"
|
footerContent={"Step " + totalSteps + " of " + totalSteps}
|
||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text style={{ color: "white" }}>
|
<Text style={{ color: "white" }}>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
import { DirectionalHint, Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -17,6 +17,10 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
if (userContext.apiType !== "SQL") {
|
if (userContext.apiType !== "SQL") {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
let totalSteps = 8;
|
||||||
|
if (userContext.isTryCosmosDBSubscription) {
|
||||||
|
totalSteps = 9;
|
||||||
|
}
|
||||||
|
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -33,7 +37,7 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 1 of 7"
|
footerContent={"Step 1 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Start viewing and working with your data by opening Items under Data
|
Start viewing and working with your data by opening Items under Data
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -55,7 +59,7 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(1),
|
onClick: () => setStep(1),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 2 of 7"
|
footerContent={"Step 2 of " + totalSteps}
|
||||||
>
|
>
|
||||||
View item here using the items window. Additionally you can also filter items to be reviewed with the filter
|
View item here using the items window. Additionally you can also filter items to be reviewed with the filter
|
||||||
function
|
function
|
||||||
@ -78,7 +82,7 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(2),
|
onClick: () => setStep(2),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 3 of 7"
|
footerContent={"Step 3 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Add new item by copy / pasting JSON; or uploading a JSON
|
Add new item by copy / pasting JSON; or uploading a JSON
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -98,7 +102,7 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(3),
|
onClick: () => setStep(3),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 4 of 7"
|
footerContent={"Step 4 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Query your data using either the filter function or new query.
|
Query your data using either the filter function or new query.
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -118,7 +122,7 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(4),
|
onClick: () => setStep(4),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 5 of 7"
|
footerContent={"Step 5 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Change throughput provisioned to your container according to your needs
|
Change throughput provisioned to your container according to your needs
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
@ -138,12 +142,54 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
onClick: () => setStep(5),
|
onClick: () => setStep(5),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 6 of 7"
|
footerContent={"Step 6 of " + totalSteps}
|
||||||
>
|
>
|
||||||
Visualize your data, store queries in an interactive document
|
Visualize your data, store queries in an interactive document
|
||||||
</TeachingBubble>
|
</TeachingBubble>
|
||||||
);
|
);
|
||||||
case 7:
|
case 7:
|
||||||
|
return (
|
||||||
|
<TeachingBubble
|
||||||
|
headline="Launch full screen"
|
||||||
|
target={"#openFullScreenBtn"}
|
||||||
|
hasCloseButton
|
||||||
|
primaryButtonProps={{
|
||||||
|
text: "Next",
|
||||||
|
onClick: () => (userContext.isTryCosmosDBSubscription ? setStep(8) : setStep(9)),
|
||||||
|
}}
|
||||||
|
secondaryButtonProps={{
|
||||||
|
text: "Previous",
|
||||||
|
onClick: () => setStep(6),
|
||||||
|
}}
|
||||||
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
|
footerContent={"Step 7 of " + totalSteps}
|
||||||
|
>
|
||||||
|
This will open a new tab in your browser to use Cosmos DB Explorer. Using the provided URLs you can share
|
||||||
|
read-write or read-only access with other people.
|
||||||
|
</TeachingBubble>
|
||||||
|
);
|
||||||
|
case 8:
|
||||||
|
return (
|
||||||
|
<TeachingBubble
|
||||||
|
headline="Boost your experience"
|
||||||
|
target={"#freeTierTeachingBubble"}
|
||||||
|
hasCloseButton
|
||||||
|
primaryButtonProps={{
|
||||||
|
text: "Next",
|
||||||
|
onClick: () => setStep(9),
|
||||||
|
}}
|
||||||
|
secondaryButtonProps={{
|
||||||
|
text: "Previous",
|
||||||
|
onClick: () => setStep(7),
|
||||||
|
}}
|
||||||
|
calloutProps={{ directionalHint: DirectionalHint.leftCenter }}
|
||||||
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
|
footerContent={"Step 8 of " + totalSteps}
|
||||||
|
>
|
||||||
|
Unlock everything Azure Cosmos DB has to offer When you're ready, upgrade to production.
|
||||||
|
</TeachingBubble>
|
||||||
|
);
|
||||||
|
case 9:
|
||||||
return (
|
return (
|
||||||
<TeachingBubble
|
<TeachingBubble
|
||||||
headline="Congratulations!"
|
headline="Congratulations!"
|
||||||
@ -158,10 +204,10 @@ export const SQLQuickstartTutorial: React.FC = (): JSX.Element => {
|
|||||||
}}
|
}}
|
||||||
secondaryButtonProps={{
|
secondaryButtonProps={{
|
||||||
text: "Previous",
|
text: "Previous",
|
||||||
onClick: () => setStep(6),
|
onClick: () => (userContext.isTryCosmosDBSubscription ? setStep(8) : setStep(7)),
|
||||||
}}
|
}}
|
||||||
onDismiss={() => onDimissTeachingBubble()}
|
onDismiss={() => onDimissTeachingBubble()}
|
||||||
footerContent="Step 7 of 7"
|
footerContent={"Step " + totalSteps + " of " + totalSteps}
|
||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text style={{ color: "white" }}>
|
<Text style={{ color: "white" }}>
|
||||||
|
@ -570,7 +570,17 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getLearningResourceItems(): JSX.Element {
|
private getLearningResourceItems(): JSX.Element {
|
||||||
let items: { link: string; title: string; description: string }[];
|
interface item {
|
||||||
|
link: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
const cdbLiveTv: item = {
|
||||||
|
link: "https://developer.azurecosmosdb.com/tv",
|
||||||
|
title: "Learn the Fundamentals",
|
||||||
|
description: "Watch Azure Cosmos DB Live TV show introductory and how to videos.",
|
||||||
|
};
|
||||||
|
let items: item[];
|
||||||
switch (userContext.apiType) {
|
switch (userContext.apiType) {
|
||||||
case "SQL":
|
case "SQL":
|
||||||
case "Postgres":
|
case "Postgres":
|
||||||
@ -580,11 +590,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
title: "Get Started using an SDK",
|
title: "Get Started using an SDK",
|
||||||
description: "Learn about the Azure Cosmos DB SDK.",
|
description: "Learn about the Azure Cosmos DB SDK.",
|
||||||
},
|
},
|
||||||
{
|
cdbLiveTv,
|
||||||
link: "https://aka.ms/msl-complex-queries",
|
|
||||||
title: "Master Complex Queries",
|
|
||||||
description: "Learn how to author complex queries.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
link: "https://aka.ms/msl-move-data",
|
link: "https://aka.ms/msl-move-data",
|
||||||
title: "Migrate Your Data",
|
title: "Migrate Your Data",
|
||||||
@ -604,11 +610,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
title: "Getting Started Guide",
|
title: "Getting Started Guide",
|
||||||
description: "Learn the basics to get started.",
|
description: "Learn the basics to get started.",
|
||||||
},
|
},
|
||||||
{
|
cdbLiveTv,
|
||||||
link: "http://aka.ms/mongodotnet",
|
|
||||||
title: "Build a web API",
|
|
||||||
description: "Create a web API with the.NET SDK.",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case "Cassandra":
|
case "Cassandra":
|
||||||
@ -618,11 +620,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
title: "Create a Container",
|
title: "Create a Container",
|
||||||
description: "Get to know the create a container options.",
|
description: "Get to know the create a container options.",
|
||||||
},
|
},
|
||||||
{
|
cdbLiveTv,
|
||||||
link: "https://aka.ms/cassandraserverdiagnostics",
|
|
||||||
title: "Run Server Diagnostics",
|
|
||||||
description: "Learn how to run server diagnostics.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
link: "https://aka.ms/Cassandrathroughput",
|
link: "https://aka.ms/Cassandrathroughput",
|
||||||
title: "Provision Throughput",
|
title: "Provision Throughput",
|
||||||
@ -642,11 +640,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
title: "Import Graph Data",
|
title: "Import Graph Data",
|
||||||
description: "Learn Bulk ingestion data using BulkExecutor",
|
description: "Learn Bulk ingestion data using BulkExecutor",
|
||||||
},
|
},
|
||||||
{
|
cdbLiveTv,
|
||||||
link: "https://aka.ms/graphoptimize",
|
|
||||||
title: "Optimize your Queries",
|
|
||||||
description: "Learn how to evaluate your Gremlin queries",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case "Tables":
|
case "Tables":
|
||||||
@ -661,11 +655,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
title: "Build a Java App",
|
title: "Build a Java App",
|
||||||
description: "Create a Azure Cosmos DB for Table app with Java SDK ",
|
description: "Create a Azure Cosmos DB for Table app with Java SDK ",
|
||||||
},
|
},
|
||||||
{
|
cdbLiveTv,
|
||||||
link: "https://aka.ms/tablenodejs",
|
|
||||||
title: "Build a Node.js App",
|
|
||||||
description: "Create a Azure Cosmos DB for Table app with Node.js SDK",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flexContainer">
|
<div className="flexContainer">
|
||||||
<div id="divExplorer" className="flexContainer hideOverflows">
|
<div id="divExplorer" className="flexContainer hideOverflows">
|
||||||
|
<div id="freeTierTeachingBubble"> </div>
|
||||||
{/* Main Command Bar - Start */}
|
{/* Main Command Bar - Start */}
|
||||||
<CommandBar container={explorer} />
|
<CommandBar container={explorer} />
|
||||||
{/* Collections Tree and Tabs - Begin */}
|
{/* Collections Tree and Tabs - Begin */}
|
||||||
|
@ -49,7 +49,7 @@ export function getMsalInstance() {
|
|||||||
cacheLocation: "localStorage",
|
cacheLocation: "localStorage",
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
authority: `${configContext.AAD_ENDPOINT}common`,
|
authority: `${configContext.AAD_ENDPOINT}organizations`,
|
||||||
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,23 @@ function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEven
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldProcessMessage(event: MessageEvent): boolean {
|
||||||
|
if (typeof event.data !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (event.data["signature"] !== "pcIframe") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!("data" in event.data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof event.data["data"] !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function isReadyMessage(event: MessageEvent): boolean {
|
export function isReadyMessage(event: MessageEvent): boolean {
|
||||||
if (!event?.data?.kind && !event?.data?.data) {
|
if (!event?.data?.kind && !event?.data?.data) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -10,7 +10,7 @@ const PortalIPs: { [key: string]: string[] } = {
|
|||||||
usnat: ["7.28.202.68"],
|
usnat: ["7.28.202.68"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNetworkSettingsWarningMessage = (clientIpAddress: string): string => {
|
export const getNetworkSettingsWarningMessage = (): string => {
|
||||||
const accountProperties = userContext.databaseAccount?.properties;
|
const accountProperties = userContext.databaseAccount?.properties;
|
||||||
|
|
||||||
if (!accountProperties) {
|
if (!accountProperties) {
|
||||||
@ -40,13 +40,7 @@ export const getNetworkSettingsWarningMessage = (clientIpAddress: string): strin
|
|||||||
if (numberOfMatches !== portalIPs.length) {
|
if (numberOfMatches !== portalIPs.length) {
|
||||||
return "The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure Portal to proceed.";
|
return "The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure Portal to proceed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
if (!clientIpAddress || ipRules.some((ipRule) => ipRule.ipAddressOrRange === clientIpAddress)) {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "The Network settings for this account are preventing access from Data Explorer. Please add your current IP to the firewall rules to proceed.";
|
return "";
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||||
@ -10,7 +11,6 @@ import { configContext, Platform, updateConfigContext } from "../ConfigContext";
|
|||||||
import { ActionType, DataExplorerAction } from "../Contracts/ActionContracts";
|
import { ActionType, DataExplorerAction } from "../Contracts/ActionContracts";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
import { DataExplorerInputsFrame } from "../Contracts/ViewModels";
|
||||||
import Explorer from "../Explorer/Explorer";
|
|
||||||
import { handleOpenAction } from "../Explorer/OpenActions/OpenActions";
|
import { handleOpenAction } from "../Explorer/OpenActions/OpenActions";
|
||||||
import { useDatabases } from "../Explorer/useDatabases";
|
import { useDatabases } from "../Explorer/useDatabases";
|
||||||
import {
|
import {
|
||||||
@ -33,7 +33,7 @@ import { Node, PortalEnv, updateUserContext, userContext } from "../UserContext"
|
|||||||
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
||||||
import { getMsalInstance } from "../Utils/AuthorizationUtils";
|
import { getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||||
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation";
|
||||||
|
|
||||||
// This hook will create a new instance of Explorer.ts and bind it to the DOM
|
// This hook will create a new instance of Explorer.ts and bind it to the DOM
|
||||||
// This hook has a LOT of magic, but ideally we can delete it once we have removed KO and switched entirely to React
|
// This hook has a LOT of magic, but ideally we can delete it once we have removed KO and switched entirely to React
|
||||||
@ -239,6 +239,7 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
updateUserContext({
|
updateUserContext({
|
||||||
authType: AuthType.AAD,
|
authType: AuthType.AAD,
|
||||||
});
|
});
|
||||||
|
let explorer: Explorer;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// In development mode, try to load the iframe message from session storage.
|
// In development mode, try to load the iframe message from session storage.
|
||||||
// This allows webpack hot reload to function properly in the portal
|
// This allows webpack hot reload to function properly in the portal
|
||||||
@ -251,7 +252,7 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
);
|
);
|
||||||
console.dir(message);
|
console.dir(message);
|
||||||
updateContextsFromPortalMessage(message);
|
updateContextsFromPortalMessage(message);
|
||||||
const explorer = new Explorer();
|
explorer = new Explorer();
|
||||||
// In development mode, save the iframe message from the portal in session storage.
|
// In development mode, save the iframe message from the portal in session storage.
|
||||||
// This allows webpack hot reload to funciton properly
|
// This allows webpack hot reload to funciton properly
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
@ -287,7 +288,7 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateContextsFromPortalMessage(inputs);
|
updateContextsFromPortalMessage(inputs);
|
||||||
const explorer = new Explorer();
|
explorer = new Explorer();
|
||||||
resolve(explorer);
|
resolve(explorer);
|
||||||
if (openAction) {
|
if (openAction) {
|
||||||
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
||||||
@ -300,6 +301,8 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
} else {
|
} else {
|
||||||
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
||||||
}
|
}
|
||||||
|
} else if (message?.type === MessageTypes.RefreshResources) {
|
||||||
|
explorer.onRefreshResourcesClick();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
@ -314,23 +317,6 @@ function shouldForwardMessage(message: PortalMessage, messageOrigin: string) {
|
|||||||
return messageOrigin === window.document.location.origin && message.type === MessageTypes.TelemetryInfo;
|
return messageOrigin === window.document.location.origin && message.type === MessageTypes.TelemetryInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldProcessMessage(event: MessageEvent): boolean {
|
|
||||||
if (typeof event.data !== "object") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (event.data["signature"] !== "pcIframe") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!("data" in event.data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof event.data["data"] !== "object") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
||||||
if (
|
if (
|
||||||
configContext.BACKEND_ENDPOINT &&
|
configContext.BACKEND_ENDPOINT &&
|
||||||
@ -382,7 +368,7 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const warningMessage = getNetworkSettingsWarningMessage(inputs.clientIpAddress);
|
const warningMessage = getNetworkSettingsWarningMessage();
|
||||||
useTabs.getState().setNetworkSettingsWarning(warningMessage);
|
useTabs.getState().setNetworkSettingsWarning(warningMessage);
|
||||||
|
|
||||||
if (inputs.features) {
|
if (inputs.features) {
|
||||||
|
Loading…
Reference in New Issue
Block a user