mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-25 12:44:06 +00:00
Compare commits
7 Commits
release/bu
...
users/aisa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f304600eca | ||
|
|
34edd96c76 | ||
|
|
7c0aae6ffa | ||
|
|
86e8bf3c80 | ||
|
|
e98c9a83b8 | ||
|
|
7d57a90d50 | ||
|
|
0f896f556b |
File diff suppressed because one or more lines are too long
@@ -187,7 +187,8 @@ if (process.env.NODE_ENV === "development") {
|
|||||||
PROXY_PATH: "/proxy",
|
PROXY_PATH: "/proxy",
|
||||||
EMULATOR_ENDPOINT: "https://localhost:8081",
|
EMULATOR_ENDPOINT: "https://localhost:8081",
|
||||||
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
|
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
|
// MONGO_PROXY_ENDPOINT: "https://cosmos-db-portal-mongoproxy1-mpac-westus.azurewebsites.net",
|
||||||
|
MONGO_PROXY_ENDPOINT: "https://localhost:7238",
|
||||||
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac,
|
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AuthType } from "AuthType";
|
import { AuthType } from "AuthType";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
|
import { Features } from "Platform/Hosted/extractFeatures";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
||||||
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
||||||
@@ -252,7 +253,7 @@ describe("SettingsComponent", () => {
|
|||||||
it("should save throughput bucket changes when Save button is clicked", async () => {
|
it("should save throughput bucket changes when Save button is clicked", async () => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
apiType: "SQL",
|
apiType: "SQL",
|
||||||
throughputBucketsEnabled: true,
|
features: { enableThroughputBuckets: true } as Features,
|
||||||
authType: AuthType.AAD,
|
authType: AuthType.AAD,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
this.isFullTextSearchEnabled = isFullTextSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
|
this.isFullTextSearchEnabled = isFullTextSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
|
||||||
|
|
||||||
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
||||||
this.throughputBucketsEnabled = userContext.throughputBucketsEnabled;
|
this.throughputBucketsEnabled =
|
||||||
|
userContext.apiType === "SQL" &&
|
||||||
|
userContext.features.enableThroughputBuckets &&
|
||||||
|
userContext.authType === AuthType.AAD;
|
||||||
|
|
||||||
// Mongo container with system partition key still treat as "Fixed"
|
// Mongo container with system partition key still treat as "Fixed"
|
||||||
this.isFixedContainer =
|
this.isFixedContainer =
|
||||||
@@ -1071,11 +1074,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
databaseId: this.collection.databaseId,
|
databaseId: this.collection.databaseId,
|
||||||
collectionId: this.collection.id(),
|
collectionId: this.collection.id(),
|
||||||
currentOffer: this.collection.offer(),
|
currentOffer: this.collection.offer(),
|
||||||
autopilotThroughput: this.collection.offer?.()?.autoscaleMaxThroughput
|
autopilotThroughput: this.collection.offer().autoscaleMaxThroughput
|
||||||
? this.collection.offer?.()?.autoscaleMaxThroughput
|
? this.collection.offer().autoscaleMaxThroughput
|
||||||
: undefined,
|
: undefined,
|
||||||
manualThroughput: this.collection.offer?.()?.manualThroughput
|
manualThroughput: this.collection.offer().manualThroughput
|
||||||
? this.collection.offer?.()?.manualThroughput
|
? this.collection.offer().manualThroughput
|
||||||
: undefined,
|
: undefined,
|
||||||
throughputBuckets: this.state.throughputBuckets,
|
throughputBuckets: this.state.throughputBuckets,
|
||||||
});
|
});
|
||||||
@@ -1341,7 +1344,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.throughputBucketsEnabled && !hasDatabaseSharedThroughput(this.collection) && this.offer) {
|
if (this.throughputBucketsEnabled) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
tab: SettingsV2TabTypes.ThroughputBucketsTab,
|
tab: SettingsV2TabTypes.ThroughputBucketsTab,
|
||||||
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,
|
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,
|
||||||
|
|||||||
@@ -143,4 +143,39 @@ describe("SubSettingsComponent", () => {
|
|||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
|
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uniqueKey is visible", () => {
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
properties: {
|
||||||
|
capabilities: [{ name: "EnableSQL" }],
|
||||||
|
},
|
||||||
|
} as DatabaseAccount,
|
||||||
|
});
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(baseProps);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uniqueKey not visible due to no keys", () => {
|
||||||
|
const props = {
|
||||||
|
...baseProps,
|
||||||
|
...(baseProps.collection.rawDataModel.uniqueKeyPolicy.uniqueKeys = []),
|
||||||
|
};
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(props);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uniqueKey not visible for API", () => {
|
||||||
|
const newContainer = new Explorer();
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
properties: {
|
||||||
|
capabilities: [{ name: "EnableMongo" }],
|
||||||
|
},
|
||||||
|
} as DatabaseAccount,
|
||||||
|
});
|
||||||
|
const props = { ...baseProps, container: newContainer };
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(props);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,12 +63,16 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
private geospatialVisible: boolean;
|
private geospatialVisible: boolean;
|
||||||
private partitionKeyValue: string;
|
private partitionKeyValue: string;
|
||||||
private partitionKeyName: string;
|
private partitionKeyName: string;
|
||||||
|
private uniqueKeyName: string;
|
||||||
|
private uniqueKeyValue: string;
|
||||||
|
|
||||||
constructor(props: SubSettingsComponentProps) {
|
constructor(props: SubSettingsComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.geospatialVisible = userContext.apiType === "SQL";
|
this.geospatialVisible = userContext.apiType === "SQL";
|
||||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||||
this.partitionKeyValue = this.getPartitionKeyValue();
|
this.partitionKeyValue = this.getPartitionKeyValue();
|
||||||
|
this.uniqueKeyName = "Unique keys";
|
||||||
|
this.uniqueKeyValue = this.getUniqueKeyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -351,6 +355,28 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
||||||
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
||||||
|
|
||||||
|
public getUniqueKeyVisible = (): boolean => {
|
||||||
|
return this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys.length > 0 && userContext.apiType === "SQL";
|
||||||
|
};
|
||||||
|
|
||||||
|
private getUniqueKeyValue = (): string => {
|
||||||
|
const paths = this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys?.[0]?.paths;
|
||||||
|
return paths?.join(", ") || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
private getUniqueKeyComponent = (): JSX.Element => (
|
||||||
|
<Stack {...titleAndInputStackProps}>
|
||||||
|
{this.getUniqueKeyVisible() && (
|
||||||
|
<TextField
|
||||||
|
label={this.uniqueKeyName}
|
||||||
|
disabled
|
||||||
|
styles={getTextFieldStyles(undefined, undefined)}
|
||||||
|
defaultValue={this.uniqueKeyValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Stack {...subComponentStackProps}>
|
<Stack {...subComponentStackProps}>
|
||||||
@@ -363,6 +389,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
||||||
|
|
||||||
{this.getPartitionKeyComponent()}
|
{this.getPartitionKeyComponent()}
|
||||||
|
|
||||||
|
{this.getUniqueKeyComponent()}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
it("renders the correct number of buckets", () => {
|
it("renders the correct number of buckets", () => {
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} />);
|
render(<ThroughputBucketsComponent {...defaultProps} />);
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders buckets in the correct order even if input is unordered", () => {
|
it("renders buckets in the correct order even if input is unordered", () => {
|
||||||
@@ -36,14 +36,8 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
];
|
];
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
|
||||||
|
|
||||||
const bucketLabels = screen.getAllByText(/Bucket \d+/).map((el) => el.textContent);
|
const bucketLabels = screen.getAllByText(/Group \d+/).map((el) => el.textContent);
|
||||||
expect(bucketLabels).toEqual([
|
expect(bucketLabels).toEqual(["Group 1 (Data Explorer Query Bucket)", "Group 2", "Group 3", "Group 4", "Group 5"]);
|
||||||
"Bucket 1 (Data Explorer Query Bucket)",
|
|
||||||
"Bucket 2",
|
|
||||||
"Bucket 3",
|
|
||||||
"Bucket 4",
|
|
||||||
"Bucket 5",
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders all provided buckets even if they exceed the max default bucket count", () => {
|
it("renders all provided buckets even if they exceed the max default bucket count", () => {
|
||||||
@@ -59,7 +53,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />);
|
||||||
|
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(7);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(7);
|
||||||
|
|
||||||
expect(screen.getByDisplayValue("50")).toBeInTheDocument();
|
expect(screen.getByDisplayValue("50")).toBeInTheDocument();
|
||||||
expect(screen.getByDisplayValue("60")).toBeInTheDocument();
|
expect(screen.getByDisplayValue("60")).toBeInTheDocument();
|
||||||
@@ -177,7 +171,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
it("ensures default buckets are used when no buckets are provided", () => {
|
it("ensures default buckets are used when no buckets are provided", () => {
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
|
||||||
expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
|
expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
|||||||
value={bucket.maxThroughputPercentage}
|
value={bucket.maxThroughputPercentage}
|
||||||
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
||||||
showValue={false}
|
showValue={false}
|
||||||
label={`Bucket ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
||||||
styles={{ root: { flex: 2, maxWidth: 400 } }}
|
styles={{ root: { flex: 2, maxWidth: 400 } }}
|
||||||
disabled={bucket.maxThroughputPercentage === 100}
|
disabled={bucket.maxThroughputPercentage === 100}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
serverId,
|
serverId,
|
||||||
numberOfRegions,
|
numberOfRegions,
|
||||||
isMultimaster,
|
isMultimaster,
|
||||||
false,
|
true,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -231,6 +231,34 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -520,6 +548,34 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -769,6 +825,34 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1083,6 +1167,34 @@ exports[`SubSettingsComponent renders 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1371,5 +1483,33 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -17,7 +17,15 @@ export const collection = {
|
|||||||
includedPaths: [],
|
includedPaths: [],
|
||||||
excludedPaths: [],
|
excludedPaths: [],
|
||||||
}),
|
}),
|
||||||
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
rawDataModel: {
|
||||||
|
uniqueKeyPolicy: {
|
||||||
|
uniqueKeys: [
|
||||||
|
{
|
||||||
|
paths: ["/id"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
usageSizeInKB: ko.observable(100),
|
usageSizeInKB: ko.observable(100),
|
||||||
offer: ko.observable<DataModels.Offer>({
|
offer: ko.observable<DataModels.Offer>({
|
||||||
autoscaleMaxThroughput: undefined,
|
autoscaleMaxThroughput: undefined,
|
||||||
|
|||||||
@@ -71,8 +71,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -153,8 +163,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -274,8 +294,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -404,8 +434,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { isFabricMirrored, isFabricMirroredKey, scheduleRefreshFabricToken } fro
|
|||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
|
|
||||||
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
@@ -284,12 +283,43 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public openInVsCode(): void {
|
public openInVsCode(): void {
|
||||||
|
TelemetryProcessor.traceStart(Action.OpenVSCode);
|
||||||
|
this.openVsCodeButtonClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private openVsCodeButtonClick(): void {
|
||||||
const activeTab = useTabs.getState().activeTab;
|
const activeTab = useTabs.getState().activeTab;
|
||||||
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
||||||
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
||||||
const container = encodeURIComponent(activeTab?.collection?.id());
|
const container = encodeURIComponent(activeTab?.collection?.id());
|
||||||
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
|
const baseUrl = `vscod://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
|
||||||
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
|
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
|
||||||
|
const startTime = Date.now();
|
||||||
|
let vsCodeNotOpened = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const timeOutTime = Date.now() - startTime;
|
||||||
|
if (!vsCodeNotOpened && timeOutTime < 1050) {
|
||||||
|
vsCodeNotOpened = true;
|
||||||
|
useDialog.getState().openDialog(openVSCodeDialogProps);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = vscodeUrl;
|
||||||
|
link.rel = "noopener noreferrer";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
|
||||||
|
try {
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
TelemetryProcessor.traceStart(Action.OpenVSCode);
|
||||||
|
} catch (error) {
|
||||||
|
if (!vsCodeNotOpened) {
|
||||||
|
vsCodeNotOpened = true;
|
||||||
|
logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const openVSCodeDialogProps: DialogProps = {
|
const openVSCodeDialogProps: DialogProps = {
|
||||||
linkProps: {
|
linkProps: {
|
||||||
@@ -304,19 +334,15 @@ export default class Explorer {
|
|||||||
secondaryButtonText: "Cancel",
|
secondaryButtonText: "Cancel",
|
||||||
|
|
||||||
onPrimaryButtonClick: () => {
|
onPrimaryButtonClick: () => {
|
||||||
try {
|
vsCodeNotOpened = false;
|
||||||
window.location.href = vscodeUrl;
|
this.openVsCodeButtonClick();
|
||||||
TelemetryProcessor.traceStart(Action.OpenVSCode);
|
useDialog.getState().closeDialog();
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onSecondaryButtonClick: () => {
|
onSecondaryButtonClick: () => {
|
||||||
useDialog.getState().closeDialog();
|
useDialog.getState().closeDialog();
|
||||||
TelemetryProcessor.traceCancel(Action.OpenVSCode);
|
TelemetryProcessor.traceCancel(Action.OpenVSCode);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
useDialog.getState().openDialog(openVSCodeDialogProps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openCESCVAFeedbackBlade(): Promise<void> {
|
public async openCESCVAFeedbackBlade(): Promise<void> {
|
||||||
@@ -1170,11 +1196,6 @@ export default class Explorer {
|
|||||||
await this.initNotebooks(userContext.databaseAccount);
|
await this.initNotebooks(userContext.databaseAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL") {
|
|
||||||
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
|
|
||||||
updateUserContext({ throughputBucketsEnabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshSampleData();
|
this.refreshSampleData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -517,7 +517,6 @@ export function createPostgreButtons(container: Explorer): CommandButtonComponen
|
|||||||
|
|
||||||
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
||||||
|
|
||||||
const addVsCode = createOpenVsCodeDialogButton(container);
|
const addVsCode = createOpenVsCodeDialogButton(container);
|
||||||
return [openVCoreMongoTerminalButton, addVsCode];
|
return [openVCoreMongoTerminalButton, addVsCode];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
<RightPaneForm {...genericPaneProps}>
|
<RightPaneForm {...genericPaneProps}>
|
||||||
<div className={`paneMainContent ${styles.container}`}>
|
<div className={`paneMainContent ${styles.container}`}>
|
||||||
{!isFabricNative() && (
|
{!isFabricNative() && (
|
||||||
<Accordion className={`customAccordion ${styles.firstItem}`}>
|
<Accordion className={`customAccordion ${styles.firstItem}`} collapsible>
|
||||||
{shouldShowQueryPageOptions && (
|
{shouldShowQueryPageOptions && (
|
||||||
<AccordionItem value="1">
|
<AccordionItem value="1">
|
||||||
<AccordionHeader>
|
<AccordionHeader>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ exports[`Settings Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
<Accordion
|
<Accordion
|
||||||
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
||||||
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value="1"
|
value="1"
|
||||||
@@ -573,6 +574,7 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
|||||||
>
|
>
|
||||||
<Accordion
|
<Accordion
|
||||||
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
||||||
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value="7"
|
value="7"
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
value=""
|
value=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField is-required root-110"
|
className="ms-TextField is-required root-116"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-wrapper"
|
className="ms-TextField-wrapper"
|
||||||
@@ -647,7 +647,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
className="ms-Label root-121"
|
className="ms-Label root-127"
|
||||||
htmlFor="TextField0"
|
htmlFor="TextField0"
|
||||||
id="TextFieldLabel2"
|
id="TextFieldLabel2"
|
||||||
>
|
>
|
||||||
@@ -656,13 +656,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
</LabelBase>
|
</LabelBase>
|
||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-fieldGroup fieldGroup-111"
|
className="ms-TextField-fieldGroup fieldGroup-117"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-invalid={false}
|
aria-invalid={false}
|
||||||
aria-labelledby="TextFieldLabel2"
|
aria-labelledby="TextFieldLabel2"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className="ms-TextField-field field-112"
|
className="ms-TextField-field field-118"
|
||||||
id="TextField0"
|
id="TextField0"
|
||||||
name="collectionIdConfirmation"
|
name="collectionIdConfirmation"
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
@@ -2464,7 +2464,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Create"
|
aria-label="Create"
|
||||||
className="ms-Button ms-Button--primary root-122"
|
className="ms-Button ms-Button--primary root-128"
|
||||||
data-is-focusable={true}
|
data-is-focusable={true}
|
||||||
data-test="Panel/OkButton"
|
data-test="Panel/OkButton"
|
||||||
id="sidePanelOkButton"
|
id="sidePanelOkButton"
|
||||||
@@ -2477,14 +2477,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-flexContainer flexContainer-123"
|
className="ms-Button-flexContainer flexContainer-129"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-textContainer textContainer-124"
|
className="ms-Button-textContainer textContainer-130"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-label label-126"
|
className="ms-Button-label label-132"
|
||||||
id="id__5"
|
id="id__5"
|
||||||
key="id__5"
|
key="id__5"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ import {
|
|||||||
DetailsList,
|
DetailsList,
|
||||||
DetailsListLayoutMode,
|
DetailsListLayoutMode,
|
||||||
DirectionalHint,
|
DirectionalHint,
|
||||||
|
FontIcon,
|
||||||
IColumn,
|
IColumn,
|
||||||
SelectionMode,
|
SelectionMode,
|
||||||
TooltipHost,
|
TooltipHost,
|
||||||
|
getTheme,
|
||||||
|
mergeStyles,
|
||||||
|
mergeStyleSets,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { Upload } from "Common/Upload/Upload";
|
import { Upload } from "Common/Upload/Upload";
|
||||||
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
||||||
@@ -14,6 +18,36 @@ import { getErrorMessage } from "../../Tables/Utilities";
|
|||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
|
|
||||||
|
const theme = getTheme();
|
||||||
|
const iconClass = mergeStyles({
|
||||||
|
verticalAlign: "middle",
|
||||||
|
maxHeight: "16px",
|
||||||
|
maxWidth: "16px",
|
||||||
|
});
|
||||||
|
|
||||||
|
const classNames = mergeStyleSets({
|
||||||
|
fileIconHeaderIcon: {
|
||||||
|
padding: 0,
|
||||||
|
fontSize: "16px",
|
||||||
|
},
|
||||||
|
fileIconCell: {
|
||||||
|
textAlign: "center",
|
||||||
|
selectors: {
|
||||||
|
"&:before": {
|
||||||
|
content: ".",
|
||||||
|
display: "inline-block",
|
||||||
|
verticalAlign: "middle",
|
||||||
|
height: "100%",
|
||||||
|
width: "0px",
|
||||||
|
visibility: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: [{ color: theme.semanticColors.errorIcon }, iconClass],
|
||||||
|
accept: [{ color: theme.semanticColors.successIcon }, iconClass],
|
||||||
|
warning: [{ color: theme.semanticColors.warningIcon }, iconClass],
|
||||||
|
});
|
||||||
|
|
||||||
export const UploadItemsPane: FunctionComponent = () => {
|
export const UploadItemsPane: FunctionComponent = () => {
|
||||||
const [files, setFiles] = useState<FileList>();
|
const [files, setFiles] = useState<FileList>();
|
||||||
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
||||||
@@ -60,44 +94,94 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: IColumn[] = [
|
const columns: IColumn[] = [
|
||||||
|
{
|
||||||
|
key: "icons",
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
className: classNames.fileIconCell,
|
||||||
|
iconClassName: classNames.fileIconHeaderIcon,
|
||||||
|
isIconOnly: true,
|
||||||
|
minWidth: 16,
|
||||||
|
maxWidth: 16,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
if (item.numFailed) {
|
||||||
|
const errorList = (
|
||||||
|
<ul
|
||||||
|
aria-label={"error list"}
|
||||||
|
style={{
|
||||||
|
margin: "5px 0",
|
||||||
|
paddingLeft: "20px",
|
||||||
|
listStyleType: "disc", // Explicitly set to use bullets (dots)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.errors.map((error, i) => (
|
||||||
|
<li key={i} style={{ display: "list-item" }}>
|
||||||
|
{error}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={errorList}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
<FontIcon iconName="Error" className={classNames.error} aria-label="error" />
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
} else if (item.numThrottled) {
|
||||||
|
return <FontIcon iconName="Warning" className={classNames.warning} aria-label="warning" />;
|
||||||
|
} else {
|
||||||
|
return <FontIcon iconName="Accept" className={classNames.accept} aria-label="accept" />;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "fileName",
|
key: "fileName",
|
||||||
name: "FILE NAME",
|
name: "FILE NAME",
|
||||||
fieldName: "fileName",
|
fieldName: "fileName",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = item.fileName;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
name: "STATUS",
|
name: "STATUS",
|
||||||
fieldName: "numSucceeded",
|
fieldName: "numSucceeded",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
isRowHeader: true,
|
isRowHeader: true,
|
||||||
isResizable: true,
|
isResizable: true,
|
||||||
data: "string",
|
data: "string",
|
||||||
isPadded: true,
|
isPadded: true,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
|
||||||
let fieldContent: string;
|
|
||||||
const tooltipId = `tooltip-${index}-${column.key}`;
|
|
||||||
|
|
||||||
switch (column.key) {
|
|
||||||
case "status":
|
|
||||||
fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fieldContent = item.fileName;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TooltipHost content={fieldContent} id={tooltipId} directionalHint={DirectionalHint.rightCenter}>
|
|
||||||
{fieldContent}
|
|
||||||
</TooltipHost>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
<div className="paneMainContent">
|
<div className="paneMainContent">
|
||||||
@@ -115,7 +199,6 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
<DetailsList
|
<DetailsList
|
||||||
items={uploadFileData}
|
items={uploadFileData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRenderItemColumn={_renderItemColumn}
|
|
||||||
selectionMode={SelectionMode.none}
|
selectionMode={SelectionMode.none}
|
||||||
layoutMode={DetailsListLayoutMode.justified}
|
layoutMode={DetailsListLayoutMode.justified}
|
||||||
isHeaderVisible={true}
|
isHeaderVisible={true}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import LinkIcon from "../../../images/Link_blue.svg";
|
|||||||
import PowerShellIcon from "../../../images/PowerShell.svg";
|
import PowerShellIcon from "../../../images/PowerShell.svg";
|
||||||
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||||
import QuickStartIcon from "../../../images/Quickstart_Lightning.svg";
|
import QuickStartIcon from "../../../images/Quickstart_Lightning.svg";
|
||||||
import VisualStudioIcon from "../../../images/VisualStudio.svg";
|
|
||||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
@@ -459,10 +458,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.apiType === "VCoreMongo") {
|
if (userContext.apiType === "VCoreMongo") {
|
||||||
icon = VisualStudioIcon;
|
icon = ContainersIcon;
|
||||||
title = "Connect with VS Code";
|
title = "Connect with Studio 3T";
|
||||||
description = "Query and Manage your MongoDB cluster in Visual Studio Code";
|
description = "Prefer Studio 3T? Find your connection strings here";
|
||||||
onClick = () => this.container.openInVsCode();
|
onClick = () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -729,7 +729,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
|||||||
} else if (result.statusCode >= 400) {
|
} else if (result.statusCode >= 400) {
|
||||||
newFailed.push(result.documentId);
|
newFailed.push(result.documentId);
|
||||||
logConsoleError(
|
logConsoleError(
|
||||||
`Failed to delete document ${result.documentId.id} with status code ${result.statusCode}`,
|
`Failed to delete document ${result.documentId.id()} with status code ${result.statusCode}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default class MongoShellTabComponent extends Component<
|
|||||||
apiEndpoint: apiEndpoint,
|
apiEndpoint: apiEndpoint,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
window.origin,
|
"https://localhost:443",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ export function getMongoShellUrl(): string {
|
|||||||
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
|
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
|
||||||
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
|
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
|
||||||
|
|
||||||
return `/mongoshell/index.html?${queryString}`;
|
return `https://localhost:443/index.html?${queryString}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1122,6 +1122,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
stats.numSucceeded++;
|
stats.numSucceeded++;
|
||||||
} else if (response.statusCode === 429) {
|
} else if (response.statusCode === 429) {
|
||||||
documentsToAttempt.push(attemptedDocuments[index]);
|
documentsToAttempt.push(attemptedDocuments[index]);
|
||||||
|
} else if (response.statusCode === 409) {
|
||||||
|
stats.numFailed++;
|
||||||
|
stats.errors.push(`Document with id ${attemptedDocuments[index].id} already exists.`);
|
||||||
} else {
|
} else {
|
||||||
stats.numFailed++;
|
stats.numFailed++;
|
||||||
stats.errors.push(JSON.stringify(response.resourceBody));
|
stats.errors.push(JSON.stringify(response.resourceBody));
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ export interface UserContext {
|
|||||||
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
|
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
|
||||||
readonly dataPlaneRbacEnabled?: boolean;
|
readonly dataPlaneRbacEnabled?: boolean;
|
||||||
readonly refreshCosmosClient?: boolean;
|
readonly refreshCosmosClient?: boolean;
|
||||||
throughputBucketsEnabled?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
|
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export function validateEndpoint(
|
|||||||
endpointToValidate: string | undefined,
|
endpointToValidate: string | undefined,
|
||||||
allowedEndpoints: ReadonlyArray<string>,
|
allowedEndpoints: ReadonlyArray<string>,
|
||||||
): boolean {
|
): boolean {
|
||||||
|
return true;
|
||||||
try {
|
try {
|
||||||
return validateEndpointInternal(
|
return validateEndpointInternal(
|
||||||
endpointToValidate,
|
endpointToValidate,
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import { configContext } from "ConfigContext";
|
|
||||||
import { FeatureRegistration } from "Contracts/DataModels";
|
|
||||||
import { AuthorizationTokenHeaderMetadata } from "Contracts/ViewModels";
|
|
||||||
import { getAuthorizationHeader } from "Utils/AuthorizationUtils";
|
|
||||||
|
|
||||||
export const featureRegistered = async (subscriptionId: string, feature: string) => {
|
|
||||||
const api_version = "2021-07-01";
|
|
||||||
const url = `${configContext.ARM_ENDPOINT}/subscriptions/${subscriptionId}/providers/Microsoft.Features/featureProviders/Microsoft.DocumentDB/subscriptionFeatureRegistrations/${feature}?api-version=${api_version}`;
|
|
||||||
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
|
||||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
|
||||||
|
|
||||||
let response;
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await _fetchWithTimeout(url, headers);
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response?.ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const featureRegistration = (await response?.json()) as FeatureRegistration;
|
|
||||||
return featureRegistration?.properties?.state === "Registered";
|
|
||||||
};
|
|
||||||
|
|
||||||
async function _fetchWithTimeout(
|
|
||||||
url: string,
|
|
||||||
headers: {
|
|
||||||
[x: string]: string;
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
const timeout = 10000;
|
|
||||||
const options = { timeout };
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
const id = setTimeout(() => controller.abort(), timeout);
|
|
||||||
|
|
||||||
const response = await window.fetch(url, {
|
|
||||||
headers,
|
|
||||||
...options,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
clearTimeout(id);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean {
|
function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean {
|
||||||
|
return true;
|
||||||
const eventOrigin = (event && event.origin) || "";
|
const eventOrigin = (event && event.origin) || "";
|
||||||
const windowOrigin = (window && window.origin) || "";
|
const windowOrigin = (window && window.origin) || "";
|
||||||
if (eventOrigin === windowOrigin) {
|
if (eventOrigin === windowOrigin) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<clear />
|
<clear />
|
||||||
<add name="X-Xss-Protection" value="1; mode=block" />
|
<add name="X-Xss-Protection" value="1; mode=block" />
|
||||||
<add name="X-Content-Type-Options" value="nosniff" />
|
<add name="X-Content-Type-Options" value="nosniff" />
|
||||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" />
|
<add name="Content-Security-Policy" value="frame-src 'vscode:' frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" />
|
||||||
</customHeaders>
|
</customHeaders>
|
||||||
<redirectHeaders>
|
<redirectHeaders>
|
||||||
<clear />
|
<clear />
|
||||||
|
|||||||
Reference in New Issue
Block a user