Checkpoint

This commit is contained in:
Steve Faulkner
2021-01-01 00:09:38 -06:00
parent bf30c3190a
commit 15cb4a8fc4
16 changed files with 155 additions and 619 deletions

View File

@@ -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);