checkpoint

This commit is contained in:
Steve Faulkner
2020-12-30 13:18:03 -06:00
parent 5f5d9176af
commit 7116f25ce4
10 changed files with 316 additions and 378 deletions

View File

@@ -6,25 +6,9 @@ import { DefaultButton, IButtonStyles, IButtonProps } from "office-ui-fabric-rea
import { IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu"; import { IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
import { Dropdown, IDropdownOption, IDropdownProps } from "office-ui-fabric-react/lib/Dropdown"; import { Dropdown, IDropdownOption, IDropdownProps } from "office-ui-fabric-react/lib/Dropdown";
import { useSubscriptions } from "../../../hooks/useSubscriptions"; import { useSubscriptions } from "../../../hooks/useSubscriptions";
import { useDatabaseAccounts } from "../../../hooks/useDatabaseAccounts";
export const AccountSwitchComponent: React.FunctionComponent = () => { const buttonStyles: IButtonStyles = {
const subscriptions = useSubscriptions();
const menuProps: IContextualMenuProps = {
directionalHintFixed: true,
className: "accountSwitchContextualMenu",
items: [
{
key: "switchSubscription",
onRender: () => renderSubscriptionDropdown(subscriptions)
},
{
key: "switchAccount",
onRender: renderAccountDropDown
}
]
};
const buttonStyles: IButtonStyles = {
root: { root: {
fontSize: StyleConstants.DefaultFontSize, fontSize: StyleConstants.DefaultFontSize,
height: 40, height: 40,
@@ -53,70 +37,60 @@ export const AccountSwitchComponent: React.FunctionComponent = () => {
textContainer: { textContainer: {
flexGrow: "initial" flexGrow: "initial"
} }
};
const buttonProps: IButtonProps = {
text: "foo",
menuProps: menuProps,
styles: buttonStyles,
className: "accountSwitchButton",
id: "accountSwitchButton"
};
return <DefaultButton {...buttonProps} />;
}; };
function renderSubscriptionDropdown(subscriptions: Subscription[]): JSX.Element { export const AccountSwitchComponent: React.FunctionComponent = () => {
const selectedSubscriptionId = ""; const subscriptions = useSubscriptions();
const isLoadingSubscriptions = false; const [selectedSubscriptionId, setSelectedSubscriptionId] = React.useState<string>();
const accounts = useDatabaseAccounts(selectedSubscriptionId);
const [selectedAccountName, setSelectedAccoutName] = React.useState<string>();
const options: IDropdownOption[] = subscriptions.map(sub => { const menuProps: IContextualMenuProps = {
directionalHintFixed: true,
className: "accountSwitchContextualMenu",
items: [
{
key: "switchSubscription",
onRender: () => {
// const placeHolderText = isLoadingSubscriptions
// ? "Loading subscriptions"
// : !options || !options.length
// ? "No subscriptions found in current directory"
// : "Select subscription from list";
const dropdownProps: IDropdownProps = {
label: "Subscription",
className: "accountSwitchSubscriptionDropdown",
options: subscriptions.map(sub => {
return { return {
key: sub.subscriptionId, key: sub.subscriptionId,
text: sub.displayName, text: sub.displayName,
data: sub data: sub
}; };
}); }),
onChange: (event, option) => {
const placeHolderText = isLoadingSubscriptions setSelectedSubscriptionId(String(option.key));
? "Loading subscriptions" },
: !options || !options.length
? "No subscriptions found in current directory"
: "Select subscription from list";
const dropdownProps: IDropdownProps = {
label: "Subscription",
className: "accountSwitchSubscriptionDropdown",
options: options,
onChange: () => {},
defaultSelectedKey: selectedSubscriptionId, defaultSelectedKey: selectedSubscriptionId,
placeholder: placeHolderText, placeholder: "Select subscription from list",
styles: { styles: {
callout: "accountSwitchSubscriptionDropdownMenu" callout: "accountSwitchSubscriptionDropdownMenu"
} }
}; };
return <Dropdown {...dropdownProps} />; return <Dropdown {...dropdownProps} />;
} }
},
function renderAccountDropDown(): JSX.Element { {
const accounts = []; key: "switchAccount",
const selectedAccountName = "foo"; onRender: () => {
const isLoadingAccounts = false; const isLoadingAccounts = false;
const options: IDropdownOption[] = accounts.map(account => {
return { const options = accounts.map(account => ({
key: account.name, key: account.name,
text: account.name, text: account.name,
data: account data: account
}; }));
});
// Fabric UI will also try to select the first non-disabled option from dropdown.
// Add a option to prevent pop the message when user click on dropdown on first time.
options.unshift({
key: "select from list",
text: "Select Cosmos DB account from list",
data: undefined
});
const placeHolderText = isLoadingAccounts const placeHolderText = isLoadingAccounts
? "Loading Cosmos DB accounts" ? "Loading Cosmos DB accounts"
@@ -127,8 +101,10 @@ function renderAccountDropDown(): JSX.Element {
const dropdownProps: IDropdownProps = { const dropdownProps: IDropdownProps = {
label: "Cosmos DB Account Name", label: "Cosmos DB Account Name",
className: "accountSwitchAccountDropdown", className: "accountSwitchAccountDropdown",
options: options, options,
onChange: () => {}, onChange: (event, option) => {
setSelectedAccoutName(String(option.key));
},
defaultSelectedKey: selectedAccountName, defaultSelectedKey: selectedAccountName,
placeholder: placeHolderText, placeholder: placeHolderText,
styles: { styles: {
@@ -137,4 +113,18 @@ function renderAccountDropDown(): JSX.Element {
}; };
return <Dropdown {...dropdownProps} />; return <Dropdown {...dropdownProps} />;
} }
}
]
};
const buttonProps: IButtonProps = {
text: selectedAccountName || "Select Database Account",
menuProps: menuProps,
styles: buttonStyles,
className: "accountSwitchButton",
id: "accountSwitchButton"
};
return <DefaultButton {...buttonProps} />;
};

View File

@@ -1,5 +1,4 @@
import { Configuration, PublicClientApplication } from "@azure/msal-browser"; import "./Platform/Hosted/ConnectScreen.less";
import { AuthenticatedTemplate, MsalProvider, UnauthenticatedTemplate, useMsal } from "@azure/msal-react";
import { useBoolean } from "@uifabric/react-hooks"; import { useBoolean } from "@uifabric/react-hooks";
import { import {
DefaultButton, DefaultButton,
@@ -17,65 +16,19 @@ import FeedbackIcon from "../images/Feedback.svg";
import ConnectIcon from "../images/HostedConnectwhite.svg"; import ConnectIcon from "../images/HostedConnectwhite.svg";
import ChevronRight from "../images/chevron-right.svg"; import ChevronRight from "../images/chevron-right.svg";
import "../less/hostedexplorer.less"; import "../less/hostedexplorer.less";
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
import { DefaultDirectoryDropdownComponent } from "./Explorer/Controls/Directory/DefaultDirectoryDropdownComponent";
import { DirectoryListComponent } from "./Explorer/Controls/Directory/DirectoryListComponent";
import "./Explorer/Menus/NavBar/MeControlComponent.less"; import "./Explorer/Menus/NavBar/MeControlComponent.less";
import { useGraphPhoto } from "./hooks/useGraphPhoto"; import { useGraphPhoto } from "./hooks/useGraphPhoto";
import { ConnectScreen } from "./Platform/Hosted/ConnectScreen";
import "./Shared/appInsights"; import "./Shared/appInsights";
import { useAADAccount } from "./hooks/useAADAccount"; import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
import * as Msal from "msal"; import { AuthContext, AuthProvider } from "./contexts/authContext";
import { fetchMe } from "./hooks/useGraphProfile";
import { fetchSubscriptions } from "./hooks/useSubscriptions";
initializeIcons(); initializeIcons();
// MSAL configuration
const configuration: Configuration = {
auth: {
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
redirectUri: "https://localhost:1234/hostedExplorer.html",
authority: "https://login.windows-ppe.net/common"
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false
}
};
// const configuration: Configuration = {
// auth: {
// clientId: "b4d07291-7936-4c8e-b413-f58b6d1c67e8",
// redirectUri: "https://localhost:1234/hostedExplorer.html",
// authority: "https://login.microsoftonline.com/907765e9-1846-4d84-ad7f-a2f5030ef5ba"
// },
// cache: {
// cacheLocation: "sessionStorage"
// }
// };
// const application = new PublicClientApplication(configuration);
const msal = new Msal.UserAgentApplication({
auth: {
authority: "https://login.microsoft.com/common",
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
redirectUri: "https://dataexplorer-dev.azurewebsites.net"
}
});
// msal.handleRedirectCallback((error, response) => {
// console.log(error, response);
// debugger;
// // handle redirect response or error
// });
const App: React.FunctionComponent = () => { const App: React.FunctionComponent = () => {
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false); const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
const { instance } = useMsal(); const { isLoggedIn, login, account, logout } = React.useContext(AuthContext);
const account = useAADAccount(); const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
const photo = useGraphPhoto(); const photo = useGraphPhoto();
const menuProps = { const menuProps = {
@@ -105,7 +58,7 @@ const App: React.FunctionComponent = () => {
<div <div
className="signOutLink" className="signOutLink"
onClick={() => { onClick={() => {
instance.logout(); logout();
}} }}
> >
Sign out Sign out
@@ -114,7 +67,6 @@ const App: React.FunctionComponent = () => {
} }
] ]
}; };
const personaProps = {};
// { // {
// id: "commandbutton-settings", // id: "commandbutton-settings",
@@ -154,6 +106,7 @@ const App: React.FunctionComponent = () => {
rootExpanded: { backgroundColor: "#393939" } rootExpanded: { backgroundColor: "#393939" }
} }
}; };
return ( return (
<div> <div>
<header> <header>
@@ -168,14 +121,16 @@ const App: React.FunctionComponent = () => {
Microsoft Azure Microsoft Azure
</span> </span>
<span className="accontSplitter" /> <span className="serviceTitle">Cosmos DB</span> <span className="accontSplitter" /> <span className="serviceTitle">Cosmos DB</span>
<img className="chevronRight" src={ChevronRight} alt="account separator" /> {isLoggedIn && <img className="chevronRight" src={ChevronRight} alt="account separator" />}
{isLoggedIn && (
<span className="accountSwitchComponentContainer"> <span className="accountSwitchComponentContainer">
{/* <AccountSwitchComponent /> */} <AccountSwitchComponent />
<span className="accountNameHeader">REPLACE ME - Connection string mode</span>; <span className="accountNameHeader">REPLACE ME - Connection string mode</span>;
</span> </span>
)}
</div> </div>
<div className="feedbackConnectSettingIcons"> <div className="feedbackConnectSettingIcons">
<AuthenticatedTemplate> {isLoggedIn && (
<CommandButtonComponent <CommandButtonComponent
id="commandbutton-connect" id="commandbutton-connect"
iconSrc={ConnectIcon} iconSrc={ConnectIcon}
@@ -186,32 +141,28 @@ const App: React.FunctionComponent = () => {
hasPopup={true} hasPopup={true}
disabled={false} disabled={false}
/> />
</AuthenticatedTemplate> )}
<UnauthenticatedTemplate>
<CommandButtonComponent <CommandButtonComponent
id="commandbutton-feedback" id="commandbutton-feedback"
iconSrc={FeedbackIcon} iconSrc={FeedbackIcon}
iconAlt="feeback button" iconAlt="feeback button"
onCommandClick={() => onCommandClick={() =>
window.open( window.open("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback")
"https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback"
)
} }
ariaLabel="feeback button" ariaLabel="feeback button"
tooltipText="Send feedback" tooltipText="Send feedback"
hasPopup={true} hasPopup={true}
disabled={false} disabled={false}
/> />
</UnauthenticatedTemplate>
</div> </div>
<div className="meControl"> <div className="meControl">
<AuthenticatedTemplate> {isLoggedIn ? (
<FocusZone> <FocusZone>
<DefaultButton {...buttonProps}> <DefaultButton {...buttonProps}>
<Persona <Persona
imageUrl={photo} imageUrl={photo}
text={account?.name} text={account?.name}
secondaryText={account?.username} secondaryText={account?.userName}
showSecondaryText={true} showSecondaryText={true}
showInitialsUntilImageLoads={true} showInitialsUntilImageLoads={true}
initialsColor={PersonaInitialsColor.teal} initialsColor={PersonaInitialsColor.teal}
@@ -220,38 +171,62 @@ const App: React.FunctionComponent = () => {
/> />
</DefaultButton> </DefaultButton>
</FocusZone> </FocusZone>
</AuthenticatedTemplate> ) : (
<UnauthenticatedTemplate>
<DefaultButton <DefaultButton
className="mecontrolSigninButton" className="mecontrolSigninButton"
text="Sign In" text="Sign In"
onClick={() => { onClick={login}
msal.loginPopup().then(foo => {
msal.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] }).then(bar => {
debugger;
// const token =
// "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjVPZjlQNUY5Z0NDd0NtRjJCT0hIeEREUS1EayIsImtpZCI6IjVPZjlQNUY5Z0NDd0NtRjJCT0hIeEREUS1EayJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0Ny8iLCJpYXQiOjE2MDkyMDYwNDQsIm5iZiI6MTYwOTIwNjA0NCwiZXhwIjoxNjA5MjA5OTQ0LCJhY3IiOiIxIiwiYWlvIjoiQVpRQWEvOFNBQUFBYzZ3RmNHOWRDYTRDS2tXL2YxdnM5b3Z2Y0tqN3NLazJqc3c5MG1MRlJkclJLelZ3cnJpS0hMNXBua0tFejVlQWlXYTRwd2JHNXdKY240dkpLVUpXb2JGdmdkVXhteWd4NGlxOXk1Z0l6TW9zM25DRGdodCtxa3lyVGlrVzJ0WTA0amRLeFA2Q2wzWm10UzYxMmhmdkFkQVRBZ3Arc0w1TUdzU05KcElUR1dBZ1RKZXZ4c1dHek5Sb2hPOXdWVUN4IiwiYW1yIjpbInB3ZCIsInJzYSIsIm1mYSJdLCJhcHBpZCI6IjIwM2YxMTQ1LTg1NmEtNDIzMi04M2Q0LWE0MzU2OGZiYTIzZCIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiMzI3NjNiYjktMDNlNS00ZDBkLTliZmEtZmEyY2U5OGQ1ZGVlIiwiZmFtaWx5X25hbWUiOiJGYXVsa25lciIsImdpdmVuX25hbWUiOiJTdGV2ZSIsImhhc2dyb3VwcyI6InRydWUiLCJpcGFkZHIiOiI0NS4yMi4xMjIuMjIwIiwibmFtZSI6IlN0ZXZlIEZhdWxrbmVyIiwib2lkIjoiN2M4Yjk4ZGItOTA3OC00NGM3LWE5YWItYzJiOGYxOGRiZDM2Iiwib25wcmVtX3NpZCI6IlMtMS01LTIxLTIxMjc1MjExODQtMTYwNDAxMjkyMC0xODg3OTI3NTI3LTMyMjM1ODIxIiwicHVpZCI6IjEwMDM3RkZFQUJDNTk0QjYiLCJyaCI6IjAuQVJvQXY0ajVjdkdHcjBHUnF5MTgwQkhiUjBVUlB5QnFoVEpDZzlTa05XajdvajBhQUprLiIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6InNJV3JwSTFoQVNUWXJoUFVrYWp1NUtQb3Z6SHdzUkdnOTN3U2t1OEs0aW8iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0ZmF1bEBtaWNyb3NvZnQuY29tIiwidXBuIjoic3RmYXVsQG1pY3Jvc29mdC5jb20iLCJ1dGkiOiJ2ME0xNVdjSWkwcVpwNTdEd1QwSUFnIiwidmVyIjoiMS4wIiwieG1zX3RjZHQiOjEyODkyNDE1NDd9.WD_NlNg2C9rOeES_zRDEIn9MQaNTElyd1NjmQ89dGg8PxCurhZpnuSNmv6J8KuAaiVtifppu64zP5nPDouAsdq5lWrJ5N6WZE9Aox0RuVMqoQRTYEYSeC0R-2wh_77G77zPHVq2qMTOHKz60_Que6_T5VTOFsNpfPzRQwqXmnIvUZnKqf6cBvxLyJYE2IsXSuOdB7jDNWfnsGv19Ew7IdScS4PoIrsVGX5E7rQ4B_bUoYm74ooiH8W0TmVXah21Z66fVAEzuWYlIX5G6ylmT9ncDefVon5JKKe5ksN5GrNjPpVVm3tyCwJeRO_zmtd7nqOZ6GLrn5hzR4CpKB63Fng";
fetchMe(bar.accessToken).then(resp => {
debugger;
});
});
// msal.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] }).then(bar => {
// debugger;
// });
});
}}
styles={{ styles={{
rootHovered: { backgroundColor: "#393939", color: "#fff" }, rootHovered: { backgroundColor: "#393939", color: "#fff" },
rootFocused: { backgroundColor: "#393939", color: "#fff" }, rootFocused: { backgroundColor: "#393939", color: "#fff" },
rootPressed: { backgroundColor: "#393939", color: "#fff" } rootPressed: { backgroundColor: "#393939", color: "#fff" }
}} }}
/> />
</UnauthenticatedTemplate> )}
</div> </div>
</div> </div>
</header> </header>
<AuthenticatedTemplate> {isLoggedIn ? (
<p>LOGGED IN!</p> <p>LOGGED IN!</p>
) : (
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
<div className="connectExplorerFormContainer">
<div className="connectExplorer">
<p className="connectExplorerContent">
<img src="images/HdeConnectCosmosDB.svg" alt="Azure Cosmos DB" />
</p>
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
{isConnectionStringVisible ? (
<form id="connectWithConnectionString">
<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" />
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
<img className="errorImg" src="images/error.svg" alt="Error notification" />
<span className="errorDetails" />
</span>
</p>
<p className="connectExplorerContent">
<input className="filterbtnstyle" type="submit" value="Connect" />
</p>
<p className="switchConnectTypeText" onClick={login}>
Sign In with Azure Account
</p>
</form>
) : (
<div id="connectWithAad">
<input className="filterbtnstyle" type="button" value="Sign In" onClick={login} />
<p className="switchConnectTypeText" onClick={showConnectionString}>
Connect to your account with connection string
</p>
</div>
)}
</div>
</div>
</div>
)}
{/* <iframe {/* <iframe
id="explorerMenu" id="explorerMenu"
name="explorer" name="explorer"
@@ -259,11 +234,6 @@ const App: React.FunctionComponent = () => {
title="explorer" title="explorer"
src="explorer.html?v=1.0.1&platform=Portal" src="explorer.html?v=1.0.1&platform=Portal"
></iframe> */} ></iframe> */}
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<ConnectScreen />
</UnauthenticatedTemplate>
<ConnectScreen />
<div data-bind="react: firewallWarningComponentAdapter" /> <div data-bind="react: firewallWarningComponentAdapter" />
<div data-bind="react: dialogComponentAdapter" /> <div data-bind="react: dialogComponentAdapter" />
<Panel headerText="Select Directory" isOpen={isOpen} onDismiss={dismissPanel} closeButtonAriaLabel="Close"> <Panel headerText="Select Directory" isOpen={isOpen} onDismiss={dismissPanel} closeButtonAriaLabel="Close">
@@ -279,4 +249,9 @@ const App: React.FunctionComponent = () => {
); );
}; };
render(<App />, document.body); render(
<AuthProvider>
<App />
</AuthProvider>,
document.body
);

View File

@@ -1,52 +0,0 @@
import "./ConnectScreen.less";
import * as React from "react";
import { useMsal } from "@azure/msal-react";
import { useBoolean } from "@uifabric/react-hooks";
export const ConnectScreen: React.FunctionComponent = () => {
const { instance } = useMsal();
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
return (
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
<div className="connectExplorerFormContainer">
<div className="connectExplorer">
<p className="connectExplorerContent">
<img src="images/HdeConnectCosmosDB.svg" alt="Azure Cosmos DB" />
</p>
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
{isConnectionStringVisible ? (
<form id="connectWithConnectionString">
<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" />
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
<img className="errorImg" src="images/error.svg" alt="Error notification" />
<span className="errorDetails" />
</span>
</p>
<p className="connectExplorerContent">
<input className="filterbtnstyle" type="submit" value="Connect" />
</p>
<p className="switchConnectTypeText" onClick={() => instance.loginPopup()}>
Sign In with Azure Account
</p>
</form>
) : (
<div id="connectWithAad">
<input className="filterbtnstyle" type="button" value="Sign In" onClick={() => instance.loginPopup()} />
<p
className="switchConnectTypeText"
onClick={() => {
showConnectionString();
}}
>
Connect to your account with connection string
</p>
</div>
)}
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,74 @@
import * as React from "react";
import * as Msal from "msal";
import { createContext, useState, useCallback } from "react";
import { useBoolean } from "@uifabric/react-hooks";
const defaultError = "Auth context method was called witout a AuthProvider component in the component tree";
const msal = new Msal.UserAgentApplication({
auth: {
authority: "https://login.microsoft.com/common",
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
redirectUri: "https://dataexplorer-dev.azurewebsites.net"
}
});
interface AuthContext {
isLoggedIn: boolean;
account?: Msal.Account;
graphToken?: string;
armToken?: string;
logout: () => unknown;
login: () => unknown;
}
export const AuthContext = createContext<AuthContext>({
isLoggedIn: false,
login: () => {
throw Error(defaultError);
},
logout: () => {
throw Error(defaultError);
}
});
export const AuthProvider: React.FunctionComponent = ({ children }) => {
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false);
const [account, setAccount] = useState<Msal.Account>();
const [graphToken, setGraphToken] = useState<string>();
const [armToken, setArmToken] = useState<string>();
const login = useCallback(() => {
msal.loginPopup().then(response => {
setLoggedIn();
setAccount(response.account);
msal
.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] })
.then(resp => {
setGraphToken(resp.accessToken);
})
.catch(e => {
console.error(e);
});
msal
.acquireTokenSilent({ scopes: ["https://management.azure.com//.default"] })
.then(resp => {
setArmToken(resp.accessToken);
})
.catch(e => {
console.error(e);
});
});
}, []);
const logout = useCallback(() => {
msal.logout();
setLoggedOut();
}, []);
return (
<AuthContext.Provider value={{ isLoggedIn, account, login, logout, graphToken, armToken }}>
{children}
</AuthContext.Provider>
);
};

View File

@@ -1,8 +0,0 @@
import { AccountInfo } from "@azure/msal-browser";
import { useAccount, useMsal } from "@azure/msal-react";
export function useAADAccount(): AccountInfo {
const { accounts } = useMsal();
const account = useAccount(accounts[0] || {});
return account;
}

View File

@@ -1,20 +0,0 @@
import { useMsal } from "@azure/msal-react";
import { useEffect, useState } from "react";
import { useAADAccount } from "./useAADAccount";
export function useAADToken(): string {
const { instance } = useMsal();
const account = useAADAccount();
const [state, setState] = useState<string>();
useEffect(() => {
console.log("Current account", account);
if (account) {
instance.acquireTokenSilent({ account, scopes: ["User.Read"] }).then(response => {
console.log("Fetched token", response.accessToken);
setState(response.accessToken);
});
}
}, [account]);
return state;
}

View File

@@ -0,0 +1,52 @@
import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../contexts/authContext";
import { DatabaseAccount } from "../Contracts/DataModels";
interface AccountListResult {
nextLink: string;
value: DatabaseAccount[];
}
export async function fetchDatabaseAccounts(
subscriptionIds: string[],
accessToken: string
): Promise<DatabaseAccount[]> {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
if (!subscriptionIds || !subscriptionIds.length) {
return Promise.reject("No subscription passed in");
}
let accounts: Array<DatabaseAccount> = [];
const subscriptionFilter = "subscriptionId eq '" + subscriptionIds.join("' or subscriptionId eq '") + "'";
const urlFilter = `$filter=(${subscriptionFilter}) and (resourceType eq 'microsoft.documentdb/databaseaccounts')`;
let nextLink = `https://management.azure.com/resources?api-version=2020-01-01&${urlFilter}`;
while (nextLink) {
const response: Response = await fetch(nextLink, { headers });
const result: AccountListResult =
response.status === 204 || response.status === 304 ? undefined : await response.json();
if (!response.ok) {
throw result;
}
nextLink = result.nextLink;
accounts = [...accounts, ...result.value];
}
return accounts;
}
export function useDatabaseAccounts(subscriptionId: string): DatabaseAccount[] {
const { armToken } = useContext(AuthContext);
const [state, setState] = useState<DatabaseAccount[]>();
useEffect(() => {
if (subscriptionId && armToken) {
fetchDatabaseAccounts([subscriptionId], armToken).then(response => setState(response));
}
}, [subscriptionId, armToken]);
return state || [];
}

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useAADToken } from "./useAADToken"; import { AuthContext } from "../contexts/authContext";
export async function fetchPhoto(accessToken: string): Promise<Blob | void> { export async function fetchPhoto(accessToken: string): Promise<Blob | void> {
const headers = new Headers(); const headers = new Headers();
@@ -13,19 +13,19 @@ export async function fetchPhoto(accessToken: string): Promise<Blob | void> {
headers: headers headers: headers
}; };
return fetch("https://graph.microsoft.com/v1.0/me/photo/$value", options) return fetch("https://graph.windows.net/me/thumbnailPhoto?api-version=1.6", options)
.then(response => response.blob()) .then(response => response.blob())
.catch(error => console.log(error)); .catch(error => console.log(error));
} }
export function useGraphPhoto(): string { export function useGraphPhoto(): string {
const token = useAADToken();
const [photo, setPhoto] = useState<string>(); const [photo, setPhoto] = useState<string>();
const { graphToken } = useContext(AuthContext);
useEffect(() => { useEffect(() => {
if (token) { if (graphToken) {
fetchPhoto(token).then(response => setPhoto(URL.createObjectURL(response))); fetchPhoto(graphToken).then(response => setPhoto(URL.createObjectURL(response)));
} }
}, [token]); }, [graphToken]);
return photo; return photo;
} }

View File

@@ -1,43 +0,0 @@
import { useEffect, useState } from "react";
import { useAADToken } from "./useAADToken";
export async function fetchMe(accessToken: string): Promise<ProfileResponse> {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers
};
return fetch("https://graph.windows.net/me?api-version=1.6", options)
.then(response => response.json())
.catch(error => console.log(error));
}
interface ProfileResponse {
displayName: string;
givenName: string;
jobTitle: string;
mail: string;
mobilePhone: null;
officeLocation: string;
preferredLanguage: null;
surname: string;
userPrincipalName: string;
id: string;
}
export function useGraphProfile(): ProfileResponse {
const token = useAADToken();
const [profileResponse, setProfileResponse] = useState<ProfileResponse>();
useEffect(() => {
if (token) {
fetchMe(token).then(response => setProfileResponse(response));
}
}, [token]);
return profileResponse;
}

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../contexts/authContext";
import { Subscription } from "../Contracts/DataModels"; import { Subscription } from "../Contracts/DataModels";
import { useAADToken } from "./useAADToken";
interface SubscriptionListResult { interface SubscriptionListResult {
nextLink: string; nextLink: string;
@@ -33,43 +33,13 @@ export async function fetchSubscriptions(accessToken: string): Promise<Subscript
} }
export function useSubscriptions(): Subscription[] { export function useSubscriptions(): Subscription[] {
const token = useAADToken(); const { armToken } = useContext(AuthContext);
const [state, setState] = useState<Subscription[]>(); const [state, setState] = useState<Subscription[]>();
useEffect(() => { useEffect(() => {
if (token) { if (armToken) {
fetchSubscriptions(token).then(response => setState(response)); fetchSubscriptions(armToken).then(response => setState(response));
} }
}, [token]); }, [armToken]);
return state || []; return state || [];
} }
// const { accounts } = useMsal();
// const account = useAccount(accounts[0] || {});
// const { isLoading, isError, data, error } = useQuery(
// ["subscriptions", account.tenantId],
// async () => {
// let subscriptions: Array<Subscription> = [];
// const fetchHeaders = await ArmResourceUtils._getAuthHeader(ArmResourceUtils._armAuthArea, tenantId);
// let nextLink = `${ArmResourceUtils._armEndpoint}/subscriptions?api-version=${ArmResourceUtils._armApiVersion}`;
// while (nextLink) {
// const response: Response = await fetch(nextLink, { headers: fetchHeaders });
// const result: SubscriptionListResult =
// response.status === 204 || response.status === 304 ? null : await response.json();
// if (!response.ok) {
// throw result;
// }
// nextLink = result.nextLink;
// const validSubscriptions = result.value.filter(
// sub => sub.state === "Enabled" || sub.state === "Warned" || sub.state === "PastDue"
// );
// subscriptions = [...subscriptions, ...validSubscriptions];
// }
// return subscriptions;
// },
// { enabled: account.tenantId }
// );
// console.log(isLoading, isError, data, error);