Merge branch 'master' into users/kcheekuri/updatePhoenixEndpoints

This commit is contained in:
kcheekuri 2021-12-11 16:04:46 -05:00
commit a9e1c2fba8
19 changed files with 5245 additions and 5151 deletions

View File

@ -2077,7 +2077,7 @@ a:link {
.resourceTreeAndTabs { .resourceTreeAndTabs {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
overflow-x: auto; overflow-x: clip;
overflow-y: auto; overflow-y: auto;
height: 100%; height: 100%;
} }
@ -2245,7 +2245,7 @@ a:link {
} }
.refreshColHeader { .refreshColHeader {
padding: 3px 6px 6px 6px; padding: 3px 6px 10px 0px !important;
} }
.refreshColHeader:hover { .refreshColHeader:hover {
@ -2869,14 +2869,14 @@ a:link {
} }
} }
settings-pane { .settingsSection {
.settingsSection {
border-bottom: 1px solid @BaseMedium; border-bottom: 1px solid @BaseMedium;
margin-right: 24px; margin-right: 24px;
padding: @MediumSpace 0px; padding: @MediumSpace 0px;
&:first-child { &:first-child {
padding-top: 0px; padding-top: 0px;
padding-bottom: 10px;
} }
&:last-child { &:last-child {
@ -2889,12 +2889,12 @@ settings-pane {
.settingsSectionLabel { .settingsSectionLabel {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
margin-right: 5px;
} }
.pageOptionsPart { .pageOptionsPart {
padding-bottom: @MediumSpace; padding-bottom: @MediumSpace;
} }
}
} }
// TODO: Remove these styles once we refactor all buttons to use the command button component // TODO: Remove these styles once we refactor all buttons to use the command button component

View File

@ -412,3 +412,11 @@ export class TerminalQueryParams {
public static readonly SubscriptionId = "subscriptionId"; public static readonly SubscriptionId = "subscriptionId";
public static readonly TerminalEndpoint = "terminalEndpoint"; public static readonly TerminalEndpoint = "terminalEndpoint";
} }
export class JunoEndpoints {
public static readonly Test = "https://juno-test.documents-dev.windows-int.net";
public static readonly Test2 = "https://juno-test2.documents-dev.windows-int.net";
public static readonly Test3 = "https://juno-test3.documents-dev.windows-int.net";
public static readonly Prod = "https://tools.cosmos.azure.com";
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
}

View File

@ -1,3 +1,5 @@
import { JunoEndpoints } from "Common/Constants";
export enum Platform { export enum Platform {
Portal = "Portal", Portal = "Portal",
Hosted = "Hosted", Hosted = "Hosted",
@ -23,6 +25,7 @@ export interface ConfigContext {
PROXY_PATH?: string; PROXY_PATH?: string;
JUNO_ENDPOINT: string; JUNO_ENDPOINT: string;
GITHUB_CLIENT_ID: string; GITHUB_CLIENT_ID: string;
GITHUB_TEST_ENV_CLIENT_ID: string;
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it. GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
IS_MPAC: boolean; IS_MPAC: boolean;
hostedExplorerURL: string; hostedExplorerURL: string;
@ -53,16 +56,17 @@ let configContext: Readonly<ConfigContext> = {
GRAPH_API_VERSION: "1.6", GRAPH_API_VERSION: "1.6",
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net", ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306 GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
IS_MPAC: false, IS_MPAC: false,
JUNO_ENDPOINT: "https://tools.cosmos.azure.com", JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com", BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
allowedJunoOrigins: [ allowedJunoOrigins: [
"https://juno-test.documents-dev.windows-int.net", JunoEndpoints.Test,
"https://juno-test2.documents-dev.windows-int.net", JunoEndpoints.Test2,
"https://juno-test3.documents-dev.windows-int.net", JunoEndpoints.Test3,
"https://tools.cosmos.azure.com", JunoEndpoints.Prod,
"https://tools-staging.cosmos.azure.com", JunoEndpoints.Stage,
"https://localhost", "https://localhost",
], ],
}; };
@ -148,3 +152,4 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
} }
export { configContext }; export { configContext };

View File

@ -7,7 +7,7 @@ import shallow from "zustand/shallow";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer"; import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
import * as Constants from "../Common/Constants"; import * as Constants from "../Common/Constants";
import { ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants"; import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants";
import { readCollection } from "../Common/dataAccess/readCollection"; import { readCollection } from "../Common/dataAccess/readCollection";
import { readDatabases } from "../Common/dataAccess/readDatabases"; import { readDatabases } from "../Common/dataAccess/readDatabases";
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
@ -16,7 +16,6 @@ import { QueriesClient } from "../Common/QueriesClient";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { import {
ContainerConnectionInfo, ContainerConnectionInfo,
IContainerData,
IPhoenixConnectionInfoResult, IPhoenixConnectionInfoResult,
IProvisionData, IProvisionData,
IResponse, IResponse,
@ -377,28 +376,46 @@ export default class Explorer {
}; };
useNotebook.getState().setConnectionInfo(connectionStatus); useNotebook.getState().setConnectionInfo(connectionStatus);
try { try {
TelemetryProcessor.traceStart(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
});
useNotebook.getState().setIsAllocating(true); useNotebook.getState().setIsAllocating(true);
const connectionInfo = await this.phoenixClient.allocateContainer(provisionData); const connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
if (connectionInfo.status !== HttpStatusCodes.OK) {
throw new Error(`Received status code: ${connectionInfo?.status}`);
}
if (!connectionInfo?.data?.notebookServerUrl) {
throw new Error(`NotebookServerUrl is invalid!`);
}
await this.setNotebookInfo(connectionInfo, connectionStatus); await this.setNotebookInfo(connectionInfo, connectionStatus);
TelemetryProcessor.traceSuccess(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
});
} catch (error) { } catch (error) {
TelemetryProcessor.traceFailure(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook,
error: getErrorMessage(error),
errorStack: getErrorStack(error),
});
connectionStatus.status = ConnectionStatusType.Failed; connectionStatus.status = ConnectionStatusType.Failed;
useNotebook.getState().resetContainerConnection(connectionStatus); useNotebook.getState().resetContainerConnection(connectionStatus);
throw error; throw error;
} } finally {
useNotebook.getState().setIsAllocating(false);
this.refreshCommandBarButtons(); this.refreshCommandBarButtons();
this.refreshNotebookList(); this.refreshNotebookList();
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
} }
} }
}
private async setNotebookInfo( private async setNotebookInfo(
connectionInfo: IResponse<IPhoenixConnectionInfoResult>, connectionInfo: IResponse<IPhoenixConnectionInfoResult>,
connectionStatus: DataModels.ContainerConnectionInfo connectionStatus: DataModels.ContainerConnectionInfo
) { ) {
if (connectionInfo.status === HttpStatusCodes.OK && connectionInfo.data && connectionInfo.data.notebookServerUrl) { const containerData = {
const containerData: IContainerData = {
forwardingId: connectionInfo.data.forwardingId, forwardingId: connectionInfo.data.forwardingId,
dbAccountName: userContext.databaseAccount.name,
}; };
await this.phoenixClient.initiateContainerHeartBeat(containerData); await this.phoenixClient.initiateContainerHeartBeat(containerData);
@ -412,11 +429,6 @@ export default class Explorer {
this.notebookManager?.notebookClient this.notebookManager?.notebookClient
.getMemoryUsage() .getMemoryUsage()
.then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo)); .then((memoryUsageInfo) => useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo));
} else {
connectionStatus.status = ConnectionStatusType.Failed;
useNotebook.getState().resetContainerConnection(connectionStatus);
}
useNotebook.getState().setIsAllocating(false);
} }
public resetNotebookWorkspace(): void { public resetNotebookWorkspace(): void {
@ -501,7 +513,9 @@ export default class Explorer {
logConsoleError(error); logConsoleError(error);
return; return;
} }
TelemetryProcessor.traceStart(Action.PhoenixResetWorkspace, {
dataExplorerArea: Areas.Notebook,
});
if (useNotebook.getState().isPhoenix) { if (useNotebook.getState().isPhoenix) {
useTabs.getState().closeAllNotebookTabs(true); useTabs.getState().closeAllNotebookTabs(true);
connectionStatus = { connectionStatus = {
@ -510,27 +524,24 @@ export default class Explorer {
useNotebook.getState().setConnectionInfo(connectionStatus); useNotebook.getState().setConnectionInfo(connectionStatus);
} }
const connectionInfo = await this.notebookManager?.notebookClient.resetWorkspace(); const connectionInfo = await this.notebookManager?.notebookClient.resetWorkspace();
if (connectionInfo && connectionInfo.status && connectionInfo.status === HttpStatusCodes.OK) { if (connectionInfo?.status !== HttpStatusCodes.OK) {
if (useNotebook.getState().isPhoenix && connectionInfo.data && connectionInfo.data.notebookServerUrl) { throw new Error(`Reset Workspace: Received status code- ${connectionInfo?.status}`);
}
if (!connectionInfo?.data?.notebookServerUrl) {
throw new Error(`Reset Workspace: NotebookServerUrl is invalid!`);
}
if (useNotebook.getState().isPhoenix) {
await this.setNotebookInfo(connectionInfo, connectionStatus); await this.setNotebookInfo(connectionInfo, connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed); useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
} }
logConsoleInfo("Successfully reset notebook workspace"); logConsoleInfo("Successfully reset notebook workspace");
TelemetryProcessor.traceSuccess(Action.ResetNotebookWorkspace); TelemetryProcessor.traceSuccess(Action.PhoenixResetWorkspace, {
} else { dataExplorerArea: Areas.Notebook,
logConsoleError(`Failed to reset notebook workspace`); });
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace);
if (useNotebook.getState().isPhoenix) {
connectionStatus = {
status: ConnectionStatusType.Reconnect,
};
useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
}
}
} catch (error) { } catch (error) {
logConsoleError(`Failed to reset notebook workspace: ${error}`); logConsoleError(`Failed to reset notebook workspace: ${error}`);
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, { TelemetryProcessor.traceFailure(Action.PhoenixResetWorkspace, {
dataExplorerArea: Areas.Notebook,
error: getErrorMessage(error), error: getErrorMessage(error),
errorStack: getErrorStack(error), errorStack: getErrorStack(error),
}); });
@ -538,7 +549,7 @@ export default class Explorer {
connectionStatus = { connectionStatus = {
status: ConnectionStatusType.Failed, status: ConnectionStatusType.Failed,
}; };
useNotebook.getState().setConnectionInfo(connectionStatus); useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed); useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
} }
throw error; throw error;

View File

@ -23,10 +23,12 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
export interface AddDatabasePaneProps { export interface AddDatabasePaneProps {
explorer: Explorer; explorer: Explorer;
buttonElement?: HTMLElement;
} }
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
explorer: container, explorer: container,
buttonElement,
}: AddDatabasePaneProps) => { }: AddDatabasePaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
let throughput: number; let throughput: number;
@ -78,6 +80,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
}; };
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage); TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
if (buttonElement) {
buttonElement.focus();
}
}, []); }, []);
const onSubmit = () => { const onSubmit = () => {

View File

@ -1,6 +1,6 @@
import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/react"; import { IDropdownOption, IImageProps, Image, Stack, Text } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks"; import { useBoolean } from "@fluentui/react-hooks";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useRef, useState } from "react";
import AddPropertyIcon from "../../../../images/Add-property.svg"; import AddPropertyIcon from "../../../../images/Add-property.svg";
import { useSidePanel } from "../../../hooks/useSidePanel"; import { useSidePanel } from "../../../hooks/useSidePanel";
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
@ -25,19 +25,16 @@ interface UnwrappedExecuteSprocParam {
export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPaneProps> = ({
storedProcedure, storedProcedure,
}: ExecuteSprocParamsPaneProps): JSX.Element => { }: ExecuteSprocParamsPaneProps): JSX.Element => {
const paramKeyValuesRef = useRef<UnwrappedExecuteSprocParam[]>([{ key: "string", text: "" }]);
const partitionValueRef = useRef<string>();
const partitionKeyRef = useRef<string>("string");
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [numberOfParams, setNumberOfParams] = useState<number>(1);
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false); const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
const [paramKeyValues, setParamKeyValues] = useState<UnwrappedExecuteSprocParam[]>([{ key: "string", text: "" }]);
const [partitionValue, setPartitionValue] = useState<string>(); // Defaulting to undefined here is important. It is not the same partition key as ""
const [selectedKey, setSelectedKey] = React.useState<IDropdownOption>({ key: "string", text: "" });
const [formError, setFormError] = useState<string>(""); const [formError, setFormError] = useState<string>("");
const onPartitionKeyChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
setSelectedKey(item);
};
const validateUnwrappedParams = (): boolean => { const validateUnwrappedParams = (): boolean => {
const unwrappedParams: UnwrappedExecuteSprocParam[] = paramKeyValues; const unwrappedParams: UnwrappedExecuteSprocParam[] = paramKeyValuesRef.current;
for (let i = 0; i < unwrappedParams.length; i++) { for (let i = 0; i < unwrappedParams.length; i++) {
const { key: paramType, text: paramValue } = unwrappedParams[i]; const { key: paramType, text: paramValue } = unwrappedParams[i];
if (paramType === "custom" && (paramValue === "" || paramValue === undefined)) { if (paramType === "custom" && (paramValue === "" || paramValue === undefined)) {
@ -53,8 +50,9 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
}; };
const submit = (): void => { const submit = (): void => {
const wrappedSprocParams: UnwrappedExecuteSprocParam[] = paramKeyValues; const wrappedSprocParams: UnwrappedExecuteSprocParam[] = paramKeyValuesRef.current;
const { key: partitionKey } = selectedKey; const partitionValue: string = partitionValueRef.current;
const partitionKey: string = partitionKeyRef.current;
if (partitionKey === "custom" && (partitionValue === "" || partitionValue === undefined)) { if (partitionKey === "custom" && (partitionValue === "" || partitionValue === undefined)) {
setInvalidParamError(partitionValue); setInvalidParamError(partitionValue);
return; return;
@ -78,37 +76,21 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
}; };
const deleteParamAtIndex = (indexToRemove: number): void => { const deleteParamAtIndex = (indexToRemove: number): void => {
const cloneParamKeyValue = [...paramKeyValues]; paramKeyValuesRef.current.splice(indexToRemove, 1);
cloneParamKeyValue.splice(indexToRemove, 1); setNumberOfParams(numberOfParams - 1);
setParamKeyValues(cloneParamKeyValue);
}; };
const addNewParamAtIndex = (indexToAdd: number): void => { const addNewParamAtIndex = (indexToAdd: number): void => {
const cloneParamKeyValue = [...paramKeyValues]; paramKeyValuesRef.current.splice(indexToAdd, 0, { key: "string", text: "" });
cloneParamKeyValue.splice(indexToAdd, 0, { key: "string", text: "" }); setNumberOfParams(numberOfParams + 1);
setParamKeyValues(cloneParamKeyValue);
};
const paramValueChange = (value: string, indexOfInput: number): void => {
const cloneParamKeyValue = [...paramKeyValues];
cloneParamKeyValue[indexOfInput].text = value;
setParamKeyValues(cloneParamKeyValue);
};
const paramKeyChange = (
_event: React.FormEvent<HTMLDivElement>,
selectedParam: IDropdownOption,
indexOfParam: number
): void => {
const cloneParamKeyValue = [...paramKeyValues];
cloneParamKeyValue[indexOfParam].key = selectedParam.key.toString();
setParamKeyValues(cloneParamKeyValue);
}; };
const addNewParamAtLastIndex = (): void => { const addNewParamAtLastIndex = (): void => {
const cloneParamKeyValue = [...paramKeyValues]; paramKeyValuesRef.current.push({
cloneParamKeyValue.splice(cloneParamKeyValue.length, 0, { key: "string", text: "" }); key: "string",
setParamKeyValues(cloneParamKeyValue); text: "",
});
setNumberOfParams(numberOfParams + 1);
}; };
const props: RightPaneFormProps = { const props: RightPaneFormProps = {
@ -118,47 +100,53 @@ export const ExecuteSprocParamsPane: FunctionComponent<ExecuteSprocParamsPanePro
onSubmit: () => submit(), onSubmit: () => submit(),
}; };
const getInputParameterComponent = (): JSX.Element[] => {
const inputParameters: JSX.Element[] = [];
for (let i = 0; i < numberOfParams; i++) {
const paramKeyValue = paramKeyValuesRef.current[i];
inputParameters.push(
<InputParameter
key={paramKeyValue.text + i}
dropdownLabel={i === 0 ? "Key" : ""}
inputParameterTitle={i === 0 ? "Enter input parameters (if any)" : ""}
inputLabel={i === 0 ? "Param" : ""}
isAddRemoveVisible={true}
onDeleteParamKeyPress={() => deleteParamAtIndex(i)}
onAddNewParamKeyPress={() => addNewParamAtIndex(i + 1)}
onParamValueChange={(_event, newInput?: string) => (paramKeyValuesRef.current[i].text = newInput)}
onParamKeyChange={(_event, selectedParam: IDropdownOption) =>
(paramKeyValuesRef.current[i].key = selectedParam.key.toString())
}
paramValue={paramKeyValue.text}
selectedKey={paramKeyValue.key}
/>
);
}
return inputParameters;
};
return ( return (
<RightPaneForm {...props}> <RightPaneForm {...props}>
<div className="panelFormWrapper">
<div className="panelMainContent"> <div className="panelMainContent">
<InputParameter <InputParameter
dropdownLabel="Key" dropdownLabel="Key"
inputParameterTitle="Partition key value" inputParameterTitle="Partition key value"
inputLabel="Value" inputLabel="Value"
isAddRemoveVisible={false} isAddRemoveVisible={false}
onParamValueChange={(_event, newInput?: string) => { onParamValueChange={(_event, newInput?: string) => (partitionValueRef.current = newInput)}
setPartitionValue(newInput); onParamKeyChange={(_event: React.FormEvent<HTMLDivElement>, item: IDropdownOption) =>
}} (partitionKeyRef.current = item.key.toString())
onParamKeyChange={onPartitionKeyChange} }
paramValue={partitionValue} paramValue={partitionValueRef.current}
selectedKey={selectedKey.key} selectedKey={partitionKeyRef.current}
/> />
{paramKeyValues.map((paramKeyValue, index) => ( {getInputParameterComponent()}
<InputParameter <Stack horizontal onClick={() => addNewParamAtLastIndex()} tabIndex={0}>
key={paramKeyValue && paramKeyValue.text + index}
dropdownLabel={!index && "Key"}
inputParameterTitle={!index && "Enter input parameters (if any)"}
inputLabel={!index && "Param"}
isAddRemoveVisible={true}
onDeleteParamKeyPress={() => deleteParamAtIndex(index)}
onAddNewParamKeyPress={() => addNewParamAtIndex(index + 1)}
onParamValueChange={(event, newInput?: string) => {
paramValueChange(newInput, index);
}}
onParamKeyChange={(event: React.FormEvent<HTMLDivElement>, selectedParam: IDropdownOption) => {
paramKeyChange(event, selectedParam, index);
}}
paramValue={paramKeyValue && paramKeyValue.text}
selectedKey={paramKeyValue && paramKeyValue.key}
/>
))}
<Stack horizontal onClick={addNewParamAtLastIndex} tabIndex={0}>
<Image {...imageProps} src={AddPropertyIcon} alt="Add param" /> <Image {...imageProps} src={AddPropertyIcon} alt="Add param" />
<Text className="addNewParamStyle">Add New Param</Text> <Text className="addNewParamStyle">Add New Param</Text>
</Stack> </Stack>
</div> </div>
</div>
</RightPaneForm> </RightPaneForm>
); );
}; };

View File

@ -55,7 +55,7 @@ export const InputParameter: FunctionComponent<InputParameterProps> = ({
<Stack horizontal> <Stack horizontal>
<Dropdown <Dropdown
label={dropdownLabel && dropdownLabel} label={dropdownLabel && dropdownLabel}
selectedKey={selectedKey} defaultSelectedKey={selectedKey}
onChange={onParamKeyChange} onChange={onParamKeyChange}
options={options} options={options}
styles={dropdownStyles} styles={dropdownStyles}
@ -64,8 +64,9 @@ export const InputParameter: FunctionComponent<InputParameterProps> = ({
<TextField <TextField
label={inputLabel && inputLabel} label={inputLabel && inputLabel}
id="confirmCollectionId" id="confirmCollectionId"
value={paramValue} defaultValue={paramValue}
onChange={onParamValueChange} onChange={onParamValueChange}
tabIndex={0}
/> />
{isAddRemoveVisible && ( {isAddRemoveVisible && (
<> <>

View File

@ -15,9 +15,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
<form <form
className="panelFormWrapper" className="panelFormWrapper"
onSubmit={[Function]} onSubmit={[Function]}
>
<div
className="panelFormWrapper"
> >
<div <div
className="panelMainContent" className="panelMainContent"
@ -322,6 +319,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
className="ms-Stack css-54" className="ms-Stack css-54"
> >
<Dropdown <Dropdown
defaultSelectedKey="string"
key=".0:$.0" key=".0:$.0"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
@ -337,7 +335,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
selectedKey="string"
styles={ styles={
Object { Object {
"dropdown": Object { "dropdown": Object {
@ -348,6 +345,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
tabIndex={0} tabIndex={0}
> >
<DropdownBase <DropdownBase
defaultSelectedKey="string"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
options={ options={
@ -362,7 +360,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
selectedKey="string"
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@ -640,6 +637,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<DropdownInternal <DropdownInternal
defaultSelectedKey="string"
hoisted={ hoisted={
Object { Object {
"rootRef": [Function], "rootRef": [Function],
@ -664,7 +662,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
] ]
} }
responsiveMode={3} responsiveMode={3}
selectedKey="string"
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@ -1569,6 +1566,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
key=".0:$.1" key=".0:$.1"
label="Value" label="Value"
onChange={[Function]} onChange={[Function]}
tabIndex={0}
> >
<TextFieldBase <TextFieldBase
deferredValidationTime={200} deferredValidationTime={200}
@ -1577,6 +1575,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
onChange={[Function]} onChange={[Function]}
resizable={true} resizable={true}
styles={[Function]} styles={[Function]}
tabIndex={0}
theme={ theme={
Object { Object {
"disableGlobalClassNames": false, "disableGlobalClassNames": false,
@ -2162,6 +2161,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
onChange={[Function]} onChange={[Function]}
onFocus={[Function]} onFocus={[Function]}
onInput={[Function]} onInput={[Function]}
tabIndex={0}
type="text" type="text"
value="" value=""
/> />
@ -2477,6 +2477,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
className="ms-Stack css-54" className="ms-Stack css-54"
> >
<Dropdown <Dropdown
defaultSelectedKey="string"
key=".0:$.0" key=".0:$.0"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
@ -2492,7 +2493,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
selectedKey="string"
styles={ styles={
Object { Object {
"dropdown": Object { "dropdown": Object {
@ -2503,6 +2503,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
tabIndex={0} tabIndex={0}
> >
<DropdownBase <DropdownBase
defaultSelectedKey="string"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
options={ options={
@ -2517,7 +2518,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
selectedKey="string"
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@ -2795,6 +2795,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<DropdownInternal <DropdownInternal
defaultSelectedKey="string"
hoisted={ hoisted={
Object { Object {
"rootRef": [Function], "rootRef": [Function],
@ -2819,7 +2820,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
] ]
} }
responsiveMode={3} responsiveMode={3}
selectedKey="string"
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@ -3720,19 +3720,22 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</DropdownBase> </DropdownBase>
</Dropdown> </Dropdown>
<StyledTextFieldBase <StyledTextFieldBase
defaultValue=""
id="confirmCollectionId" id="confirmCollectionId"
key=".0:$.1" key=".0:$.1"
label="Param" label="Param"
onChange={[Function]} onChange={[Function]}
value="" tabIndex={0}
> >
<TextFieldBase <TextFieldBase
defaultValue=""
deferredValidationTime={200} deferredValidationTime={200}
id="confirmCollectionId" id="confirmCollectionId"
label="Param" label="Param"
onChange={[Function]} onChange={[Function]}
resizable={true} resizable={true}
styles={[Function]} styles={[Function]}
tabIndex={0}
theme={ theme={
Object { Object {
"disableGlobalClassNames": false, "disableGlobalClassNames": false,
@ -4007,7 +4010,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
} }
validateOnLoad={true} validateOnLoad={true}
value=""
> >
<div <div
className="ms-TextField root-75" className="ms-TextField root-75"
@ -4319,6 +4321,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
onChange={[Function]} onChange={[Function]}
onFocus={[Function]} onFocus={[Function]}
onInput={[Function]} onInput={[Function]}
tabIndex={0}
type="text" type="text"
value="" value=""
/> />
@ -5302,7 +5305,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</div> </div>
</Stack> </Stack>
</div> </div>
</div>
<PanelFooterComponent <PanelFooterComponent
buttonLabel="Execute" buttonLabel="Execute"
isButtonDisabled={false} isButtonDisabled={false}

View File

@ -1,4 +1,4 @@
import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton } from "@fluentui/react"; import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton, Stack, Text } from "@fluentui/react";
import * as Constants from "Common/Constants"; import * as Constants from "Common/Constants";
import { InfoTooltip } from "Common/Tooltip/InfoTooltip"; import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
import { configContext } from "ConfigContext"; import { configContext } from "ConfigContext";
@ -113,20 +113,44 @@ export const SettingsPane: FunctionComponent = () => {
const handleOnPageOptionChange = (ev: React.FormEvent<HTMLInputElement>, option: IChoiceGroupOption): void => { const handleOnPageOptionChange = (ev: React.FormEvent<HTMLInputElement>, option: IChoiceGroupOption): void => {
setPageOption(option.key); setPageOption(option.key);
}; };
const choiceButtonStyles = {
flexContainer: [
{
selectors: {
".ms-ChoiceField-wrapper label": {
fontSize: 12,
paddingTop: 0,
},
".ms-ChoiceField": {
marginTop: 0,
},
},
},
],
};
return ( return (
<RightPaneForm {...genericPaneProps}> <RightPaneForm {...genericPaneProps}>
<div className="paneMainContent"> <div className="paneMainContent">
{shouldShowQueryPageOptions && ( {shouldShowQueryPageOptions && (
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart pageOptionsPart"> <div className="settingsSectionPart">
<div className="settingsSectionLabel"> <Stack horizontal>
<Text id="pageOptions" className="settingsSectionLabel" variant="small">
Page options Page options
</Text>
<InfoTooltip> <InfoTooltip>
Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many
query results per page. query results per page.
</InfoTooltip> </InfoTooltip>
</div> </Stack>
<ChoiceGroup selectedKey={pageOption} options={pageOptionList} onChange={handleOnPageOptionChange} /> <ChoiceGroup
ariaLabelledBy="pageOptions"
selectedKey={pageOption}
options={pageOptionList}
styles={choiceButtonStyles}
onChange={handleOnPageOptionChange}
/>
</div> </div>
<div className="tabs settingsSectionPart"> <div className="tabs settingsSectionPart">
{isCustomPageOptionSelected() && ( {isCustomPageOptionSelected() && (

View File

@ -14,17 +14,24 @@ exports[`Settings Pane should render Default properly 1`] = `
className="settingsSection" className="settingsSection"
> >
<div <div
className="settingsSectionPart pageOptionsPart" className="settingsSectionPart"
> >
<div <Stack
horizontal={true}
>
<Text
className="settingsSectionLabel" className="settingsSectionLabel"
id="pageOptions"
variant="small"
> >
Page options Page options
</Text>
<InfoTooltip> <InfoTooltip>
Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many query results per page. Choose Custom to specify a fixed amount of query results to show, or choose Unlimited to show as many query results per page.
</InfoTooltip> </InfoTooltip>
</div> </Stack>
<StyledChoiceGroup <StyledChoiceGroup
ariaLabelledBy="pageOptions"
onChange={[Function]} onChange={[Function]}
options={ options={
Array [ Array [
@ -39,6 +46,23 @@ exports[`Settings Pane should render Default properly 1`] = `
] ]
} }
selectedKey="custom" selectedKey="custom"
styles={
Object {
"flexContainer": Array [
Object {
"selectors": Object {
".ms-ChoiceField": Object {
"marginTop": 0,
},
".ms-ChoiceField-wrapper label": Object {
"fontSize": 12,
"paddingTop": 0,
},
},
},
],
}
}
/> />
</div> </div>
<div <div

View File

@ -33,6 +33,7 @@ const {
Inet, Inet,
Smallint, Smallint,
Tinyint, Tinyint,
Timestamp,
} = TableConstants.CassandraType; } = TableConstants.CassandraType;
export const cassandraOptions = [ export const cassandraOptions = [
{ key: Text, text: Text }, { key: Text, text: Text },
@ -50,6 +51,7 @@ export const cassandraOptions = [
{ key: Inet, text: Inet }, { key: Inet, text: Inet },
{ key: Smallint, text: Smallint }, { key: Smallint, text: Smallint },
{ key: Tinyint, text: Tinyint }, { key: Tinyint, text: Tinyint },
{ key: Timestamp, text: Timestamp },
]; ];
export const imageProps: IImageProps = { export const imageProps: IImageProps = {

View File

@ -314,7 +314,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
} }
useSidePanel useSidePanel
.getState() .getState()
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />); .openSidePanel(
"New " + getDatabaseName(),
<AddDatabasePanel explorer={this.container} buttonElement={document.activeElement as HTMLElement} />
);
}, },
}); });
} }

View File

@ -19,6 +19,7 @@ export const CassandraType = {
Float: "Float", Float: "Float",
Int: "Int", Int: "Int",
Text: "Text", Text: "Text",
Timestamp: "Timestamp",
Uuid: "Uuid", Uuid: "Uuid",
Varchar: "Varchar", Varchar: "Varchar",
Varint: "Varint", Varint: "Varint",

View File

@ -431,7 +431,7 @@ export default class TableEntityListViewModel extends DataTableViewModel {
if (newHeaders.length > 0) { if (newHeaders.length > 0) {
// Any new columns found will be added into headers array, which will trigger a re-render of the DataTable. // Any new columns found will be added into headers array, which will trigger a re-render of the DataTable.
// So there is no need to call it here. // So there is no need to call it here.
this.updateHeaders(newHeaders, /* notifyColumnChanges */ true); this.updateHeaders(selectedHeadersUnion, /* notifyColumnChanges */ true);
} else { } else {
if (columnSortOrder) { if (columnSortOrder) {
this.sortColumns(columnSortOrder, oSettings); this.sortColumns(columnSortOrder, oSettings);

View File

@ -535,7 +535,8 @@ export class CassandraAPIDataClient extends TableDataClient {
dataType === TableConstants.CassandraType.Text || dataType === TableConstants.CassandraType.Text ||
dataType === TableConstants.CassandraType.Inet || dataType === TableConstants.CassandraType.Inet ||
dataType === TableConstants.CassandraType.Ascii || dataType === TableConstants.CassandraType.Ascii ||
dataType === TableConstants.CassandraType.Varchar dataType === TableConstants.CassandraType.Varchar ||
dataType === TableConstants.CassandraType.Timestamp
); );
} }

View File

@ -1,8 +1,8 @@
import ko from "knockout"; import ko from "knockout";
import postRobot from "post-robot"; import postRobot from "post-robot";
import { GetGithubClientId } from "Utils/GitHubUtils";
import { HttpStatusCodes } from "../Common/Constants"; import { HttpStatusCodes } from "../Common/Constants";
import { handleError } from "../Common/ErrorHandlingUtils"; import { handleError } from "../Common/ErrorHandlingUtils";
import { configContext } from "../ConfigContext";
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent"; import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
import { JunoClient } from "../Juno/JunoClient"; import { JunoClient } from "../Juno/JunoClient";
import { logConsoleInfo } from "../Utils/NotificationConsoleUtils"; import { logConsoleInfo } from "../Utils/NotificationConsoleUtils";
@ -55,7 +55,7 @@ export class GitHubOAuthService {
const params = { const params = {
scope, scope,
client_id: configContext.GITHUB_CLIENT_ID, client_id: GetGithubClientId(),
redirect_uri: new URL("./connectToGitHub.html", window.location.href).href, redirect_uri: new URL("./connectToGitHub.html", window.location.href).href,
state: this.resetState(), state: this.resetState(),
}; };

View File

@ -1,4 +1,5 @@
import ko from "knockout"; import ko from "knockout";
import { GetGithubClientId } from "Utils/GitHubUtils";
import { HttpHeaders, HttpStatusCodes } from "../Common/Constants"; import { HttpHeaders, HttpStatusCodes } from "../Common/Constants";
import { configContext } from "../ConfigContext"; import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
@ -522,7 +523,7 @@ export class JunoClient {
private static getGitHubClientParams(): URLSearchParams { private static getGitHubClientParams(): URLSearchParams {
const githubParams = new URLSearchParams({ const githubParams = new URLSearchParams({
client_id: configContext.GITHUB_CLIENT_ID, client_id: GetGithubClientId(),
}); });
if (configContext.GITHUB_CLIENT_SECRET) { if (configContext.GITHUB_CLIENT_SECRET) {

View File

@ -50,7 +50,6 @@ export enum Action {
SubscriptionSwitch, SubscriptionSwitch,
TenantSwitch, TenantSwitch,
DefaultTenantSwitch, DefaultTenantSwitch,
ResetNotebookWorkspace,
CreateNotebookWorkspace, CreateNotebookWorkspace,
NotebookErrorNotification, NotebookErrorNotification,
CreateSparkCluster, CreateSparkCluster,
@ -82,6 +81,8 @@ export enum Action {
NotebooksInsertTextCellBelowFromMenu, NotebooksInsertTextCellBelowFromMenu,
NotebooksMoveCellUpFromMenu, NotebooksMoveCellUpFromMenu,
NotebooksMoveCellDownFromMenu, NotebooksMoveCellDownFromMenu,
PhoenixConnection,
PhoenixResetWorkspace,
DeleteCellFromMenu, DeleteCellFromMenu,
OpenTerminal, OpenTerminal,
CreateMongoCollectionWithWildcardIndex, CreateMongoCollectionWithWildcardIndex,

View File

@ -1,4 +1,9 @@
// https://github.com/<owner>/<repo>/tree/<branch> // https://github.com/<owner>/<repo>/tree/<branch>
import { JunoEndpoints } from "Common/Constants";
import { configContext } from "ConfigContext";
import { userContext } from "UserContext";
// The url when users visit a repo/branch on github.com // The url when users visit a repo/branch on github.com
export const RepoUriPattern = /https:\/\/github.com\/([^/]*)\/([^/]*)\/tree\/([^?]*)/; export const RepoUriPattern = /https:\/\/github.com\/([^/]*)\/([^/]*)\/tree\/([^?]*)/;
@ -60,3 +65,15 @@ export function toContentUri(owner: string, repo: string, branch: string, path:
export function toRawContentUri(owner: string, repo: string, branch: string, path: string): string { export function toRawContentUri(owner: string, repo: string, branch: string, path: string): string {
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`; return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`;
} }
export function GetGithubClientId(): string {
const junoEndpoint = userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
if (
junoEndpoint === JunoEndpoints.Test ||
junoEndpoint === JunoEndpoints.Test2 ||
junoEndpoint === JunoEndpoints.Test3
) {
return configContext.GITHUB_TEST_ENV_CLIENT_ID;
}
return configContext.GITHUB_CLIENT_ID;
}