mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-22 18:32:00 +00:00
Checkpoint
This commit is contained in:
@@ -2,23 +2,19 @@ import "./Platform/Hosted/ConnectScreen.less";
|
||||
import { useBoolean } from "@uifabric/react-hooks";
|
||||
import {
|
||||
DefaultButton,
|
||||
DetailsList,
|
||||
DirectionalHint,
|
||||
FocusZone,
|
||||
IContextualMenuProps,
|
||||
initializeIcons,
|
||||
Panel,
|
||||
PanelType,
|
||||
Persona,
|
||||
PersonaInitialsColor,
|
||||
PersonaSize,
|
||||
SelectionMode,
|
||||
Selection
|
||||
ChoiceGroup
|
||||
} from "office-ui-fabric-react";
|
||||
import * as React from "react";
|
||||
import { render } from "react-dom";
|
||||
import FeedbackIcon from "../images/Feedback.svg";
|
||||
import ConnectIcon from "../images/HostedConnectwhite.svg";
|
||||
import ChevronRight from "../images/chevron-right.svg";
|
||||
import "../less/hostedexplorer.less";
|
||||
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
@@ -26,111 +22,74 @@ import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
||||
import { useGraphPhoto } from "./hooks/useGraphPhoto";
|
||||
import "./Shared/appInsights";
|
||||
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
||||
import { AuthContext, AuthProvider } from "./contexts/authContext";
|
||||
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
||||
import { useDirectories } from "./hooks/useDirectories";
|
||||
import * as Msal from "msal";
|
||||
import { configContext } from "./ConfigContext";
|
||||
import { HttpHeaders } from "./Common/Constants";
|
||||
import { GenerateTokenResponse } from "./Contracts/DataModels";
|
||||
import { AuthType } from "./AuthType";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
const msal = new Msal.UserAgentApplication({
|
||||
auth: {
|
||||
authority: "https://login.microsoft.com/common",
|
||||
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||
redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development
|
||||
}
|
||||
});
|
||||
|
||||
const App: React.FunctionComponent = () => {
|
||||
// Hooks for handling encrypted portal tokens
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const encryptedToken = params && params.get("key");
|
||||
const [encryptedToken, setEncryptedToken] = React.useState<string>(params && params.get("key"));
|
||||
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
||||
|
||||
// Hooks for showing/hiding UI
|
||||
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
||||
const { isLoggedIn, aadlogin: login, account, aadlogout: logout, tenantId } = React.useContext(AuthContext);
|
||||
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
||||
const photo = useGraphPhoto();
|
||||
const directories = useDirectories();
|
||||
// const [selectedItem, setSelectedItem] = React.useState<any>(undefined);
|
||||
const selection = new Selection({
|
||||
getKey: item => item.tenantId,
|
||||
items: directories,
|
||||
onSelectionChanged: () => {
|
||||
const selected = selection.getSelection()[0];
|
||||
if (selected.tenantId !== tenantId) {
|
||||
console.log("new Tenant", selected.tenantId);
|
||||
}
|
||||
},
|
||||
selectionMode: SelectionMode.single
|
||||
});
|
||||
selection.setKeySelected(tenantId, true, false);
|
||||
|
||||
// private _renderPersonaComponent = (): JSX.Element => {
|
||||
// const { user } = this.props;
|
||||
// const personaProps: IPersonaSharedProps = {
|
||||
// imageUrl: user.imageUrl,
|
||||
// text: user.name,
|
||||
// secondaryText: user.email,
|
||||
// showSecondaryText: true,
|
||||
// showInitialsUntilImageLoads: true,
|
||||
// initialsColor: PersonaInitialsColor.teal,
|
||||
// size: PersonaSize.size72,
|
||||
// className: "mecontrolContextualMenuPersona"
|
||||
// };
|
||||
// Hooks for AAD authentication
|
||||
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false);
|
||||
const [account, setAccount] = React.useState<Msal.Account>();
|
||||
const [graphToken, setGraphToken] = React.useState<string>();
|
||||
const [armToken, setArmToken] = React.useState<string>();
|
||||
const [tenantId, setTenantId] = React.useState<string>();
|
||||
const [connectionString, setConnectionString] = React.useState<string>("");
|
||||
|
||||
// return <Persona {...personaProps} />;
|
||||
// };
|
||||
const login = React.useCallback(async () => {
|
||||
const response = await msal.loginPopup();
|
||||
setLoggedIn();
|
||||
setAccount(response.account);
|
||||
setTenantId(response.tenantId);
|
||||
}, []);
|
||||
|
||||
const menuProps: IContextualMenuProps = {
|
||||
className: "mecontrolContextualMenu",
|
||||
isBeakVisible: false,
|
||||
directionalHintFixed: true,
|
||||
directionalHint: DirectionalHint.bottomRightEdge,
|
||||
calloutProps: {
|
||||
minPagePadding: 0
|
||||
},
|
||||
items: [
|
||||
{
|
||||
key: "SwitchDirectory",
|
||||
text: "Switch Directory",
|
||||
onClick: openPanel
|
||||
},
|
||||
{
|
||||
key: "SignOut",
|
||||
text: "Sign Out",
|
||||
onClick: logout
|
||||
}
|
||||
]
|
||||
};
|
||||
const logout = React.useCallback(() => {
|
||||
msal.logout();
|
||||
setLoggedOut();
|
||||
}, []);
|
||||
|
||||
// {
|
||||
// id: "commandbutton-settings",
|
||||
// iconSrc: SettingsIcon,
|
||||
// iconAlt: "setting button",
|
||||
// onCommandClick: () => {},
|
||||
// commandButtonLabel: undefined,
|
||||
// ariaLabel: "setting button",
|
||||
// tooltipText: "Global settings",
|
||||
// hasPopup: true,
|
||||
// disabled: false
|
||||
// },
|
||||
// {
|
||||
// id: "commandbutton-feedback",
|
||||
// iconSrc: FeedbackIcon,
|
||||
// iconAlt: "feeback button",
|
||||
// onCommandClick: () =>
|
||||
// window.open(
|
||||
// "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback"
|
||||
// ),
|
||||
// commandButtonLabel: undefined,
|
||||
// ariaLabel: "feeback button",
|
||||
// tooltipText: "Send feedback",
|
||||
// hasPopup: true,
|
||||
// disabled: false
|
||||
// }
|
||||
|
||||
const buttonProps = {
|
||||
id: "mecontrolHeader",
|
||||
className: "mecontrolHeaderButton",
|
||||
menuProps,
|
||||
styles: {
|
||||
rootHovered: { backgroundColor: "#393939" },
|
||||
rootFocused: { backgroundColor: "#393939" },
|
||||
rootPressed: { backgroundColor: "#393939" },
|
||||
rootExpanded: { backgroundColor: "#393939" }
|
||||
React.useEffect(() => {
|
||||
if (account && tenantId) {
|
||||
console.log(msal.authority);
|
||||
console.log("Getting tokens for", tenantId);
|
||||
Promise.all([
|
||||
msal.acquireTokenSilent({
|
||||
scopes: ["https://graph.windows.net//.default"]
|
||||
}),
|
||||
msal.acquireTokenSilent({
|
||||
scopes: ["https://management.azure.com//.default"]
|
||||
})
|
||||
]).then(([graphTokenResponse, armTokenResponse]) => {
|
||||
setGraphToken(graphTokenResponse.accessToken);
|
||||
setArmToken(armTokenResponse.accessToken);
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [account, tenantId]);
|
||||
|
||||
const photo = useGraphPhoto(graphToken);
|
||||
const directories = useDirectories(armToken);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -151,7 +110,7 @@ const App: React.FunctionComponent = () => {
|
||||
)}
|
||||
{isLoggedIn && (
|
||||
<span className="accountSwitchComponentContainer">
|
||||
<AccountSwitchComponent />
|
||||
<AccountSwitchComponent armToken={armToken} />
|
||||
</span>
|
||||
)}
|
||||
{!isLoggedIn && encryptedTokenMetadata?.accountName && (
|
||||
@@ -161,18 +120,6 @@ const App: React.FunctionComponent = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="feedbackConnectSettingIcons">
|
||||
{isLoggedIn && (
|
||||
<CommandButtonComponent
|
||||
id="commandbutton-connect"
|
||||
iconSrc={ConnectIcon}
|
||||
iconAlt="connect button"
|
||||
onCommandClick={() => {}}
|
||||
ariaLabel="connect button"
|
||||
tooltipText="Connect to a Cosmos DB account"
|
||||
hasPopup={true}
|
||||
disabled={false}
|
||||
/>
|
||||
)}
|
||||
<CommandButtonComponent
|
||||
id="commandbutton-feedback"
|
||||
iconSrc={FeedbackIcon}
|
||||
@@ -189,7 +136,37 @@ const App: React.FunctionComponent = () => {
|
||||
<div className="meControl">
|
||||
{isLoggedIn ? (
|
||||
<FocusZone>
|
||||
<DefaultButton {...buttonProps}>
|
||||
<DefaultButton
|
||||
id="mecontrolHeader"
|
||||
className="mecontrolHeaderButton"
|
||||
menuProps={{
|
||||
className: "mecontrolContextualMenu",
|
||||
isBeakVisible: false,
|
||||
directionalHintFixed: true,
|
||||
directionalHint: DirectionalHint.bottomRightEdge,
|
||||
calloutProps: {
|
||||
minPagePadding: 0
|
||||
},
|
||||
items: [
|
||||
{
|
||||
key: "SwitchDirectory",
|
||||
text: "Switch Directory",
|
||||
onClick: openPanel
|
||||
},
|
||||
{
|
||||
key: "SignOut",
|
||||
text: "Sign Out",
|
||||
onClick: logout
|
||||
}
|
||||
]
|
||||
}}
|
||||
styles={{
|
||||
rootHovered: { backgroundColor: "#393939" },
|
||||
rootFocused: { backgroundColor: "#393939" },
|
||||
rootPressed: { backgroundColor: "#393939" },
|
||||
rootExpanded: { backgroundColor: "#393939" }
|
||||
}}
|
||||
>
|
||||
<Persona
|
||||
imageUrl={photo}
|
||||
text={account?.name}
|
||||
@@ -228,7 +205,7 @@ const App: React.FunctionComponent = () => {
|
||||
)}&metadata=${JSON.stringify(encryptedTokenMetadata)}`}
|
||||
></iframe>
|
||||
)}
|
||||
{!encryptedTokenMetadata && isLoggedIn && (
|
||||
{/* {!encryptedTokenMetadata && isLoggedIn && (
|
||||
<iframe
|
||||
id="explorerMenu"
|
||||
name="explorer"
|
||||
@@ -236,7 +213,7 @@ const App: React.FunctionComponent = () => {
|
||||
title="explorer"
|
||||
src={`explorer.html?v=1.0.1&platform=Hosted&authType=${AuthType.AAD}`}
|
||||
></iframe>
|
||||
)}
|
||||
)} */}
|
||||
{!isLoggedIn && !encryptedTokenMetadata && (
|
||||
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
|
||||
<div className="connectExplorerFormContainer">
|
||||
@@ -246,15 +223,42 @@ const App: React.FunctionComponent = () => {
|
||||
</p>
|
||||
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
|
||||
{isConnectionStringVisible ? (
|
||||
<form id="connectWithConnectionString">
|
||||
<form
|
||||
id="connectWithConnectionString"
|
||||
onSubmit={async event => {
|
||||
event.preventDefault();
|
||||
// const foo = parseConnectionString(connectionString);
|
||||
const headers = new Headers();
|
||||
headers.append(HttpHeaders.connectionString, connectionString);
|
||||
const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken";
|
||||
const response = await fetch(url, { headers, method: "POST" });
|
||||
if (!response.ok) {
|
||||
throw response;
|
||||
}
|
||||
// This API has a quirk where it must be parsed twice
|
||||
const result: GenerateTokenResponse = JSON.parse(await response.json());
|
||||
console.log(result.readWrite || result.read);
|
||||
setEncryptedToken(decodeURIComponent(result.readWrite || result.read));
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<p className="connectExplorerContent connectStringText">
|
||||
Connect to your account with connection string
|
||||
</p>
|
||||
<p className="connectExplorerContent">
|
||||
<input className="inputToken" type="text" required placeholder="Please enter a connection string" />
|
||||
<input
|
||||
className="inputToken"
|
||||
type="text"
|
||||
required
|
||||
placeholder="Please enter a connection string"
|
||||
value={connectionString}
|
||||
onChange={event => {
|
||||
setConnectionString(event.target.value);
|
||||
}}
|
||||
/>
|
||||
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
|
||||
<img className="errorImg" src="images/error.svg" alt="Error notification" />
|
||||
<span className="errorDetails" />
|
||||
<span className="errorDetails"></span>
|
||||
</span>
|
||||
</p>
|
||||
<p className="connectExplorerContent">
|
||||
@@ -285,35 +289,30 @@ const App: React.FunctionComponent = () => {
|
||||
onDismiss={dismissPanel}
|
||||
closeButtonAriaLabel="Close"
|
||||
>
|
||||
<DetailsList
|
||||
items={selection.getItems()}
|
||||
columns={[
|
||||
{
|
||||
key: "name",
|
||||
name: "Name",
|
||||
minWidth: 200,
|
||||
maxWidth: 200,
|
||||
fieldName: "displayName"
|
||||
},
|
||||
{
|
||||
key: "id",
|
||||
name: "ID",
|
||||
minWidth: 200,
|
||||
maxWidth: 200,
|
||||
fieldName: "tenantId"
|
||||
}
|
||||
]}
|
||||
selectionMode={SelectionMode.single}
|
||||
selection={selection}
|
||||
<ChoiceGroup
|
||||
options={directories.map(dir => ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))}
|
||||
selectedKey={tenantId}
|
||||
onChange={async () => {
|
||||
dismissPanel();
|
||||
// TODO!!! This does not work. Still not sure why. Tried lots of stuff.
|
||||
// const response = await msal.loginPopup({
|
||||
// authority: `https://login.microsoftonline.com/${option.key}`
|
||||
// });
|
||||
// // msal = new Msal.UserAgentApplication({
|
||||
// // auth: {
|
||||
// // authority: `https://login.microsoftonline.com/${option.key}`,
|
||||
// // clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||
// // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development
|
||||
// // }
|
||||
// // });
|
||||
// setTenantId(option.key);
|
||||
// setAccount(response.account);
|
||||
// console.log(account);
|
||||
}}
|
||||
/>
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render(
|
||||
<AuthProvider>
|
||||
<App />
|
||||
</AuthProvider>,
|
||||
document.body
|
||||
);
|
||||
render(<App />, document.body);
|
||||
|
||||
Reference in New Issue
Block a user