mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-21 09:51:11 +00:00
checkpoint
This commit is contained in:
@@ -6,57 +6,120 @@ 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";
|
||||||
|
|
||||||
|
const buttonStyles: IButtonStyles = {
|
||||||
|
root: {
|
||||||
|
fontSize: StyleConstants.DefaultFontSize,
|
||||||
|
height: 40,
|
||||||
|
padding: 0,
|
||||||
|
paddingLeft: 10,
|
||||||
|
marginRight: 5,
|
||||||
|
backgroundColor: StyleConstants.BaseDark,
|
||||||
|
color: StyleConstants.BaseLight
|
||||||
|
},
|
||||||
|
rootHovered: {
|
||||||
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
|
color: StyleConstants.BaseLight
|
||||||
|
},
|
||||||
|
rootFocused: {
|
||||||
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
|
color: StyleConstants.BaseLight
|
||||||
|
},
|
||||||
|
rootPressed: {
|
||||||
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
|
color: StyleConstants.BaseLight
|
||||||
|
},
|
||||||
|
rootExpanded: {
|
||||||
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
|
color: StyleConstants.BaseLight
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
flexGrow: "initial"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const AccountSwitchComponent: React.FunctionComponent = () => {
|
export const AccountSwitchComponent: React.FunctionComponent = () => {
|
||||||
const subscriptions = useSubscriptions();
|
const subscriptions = useSubscriptions();
|
||||||
|
const [selectedSubscriptionId, setSelectedSubscriptionId] = React.useState<string>();
|
||||||
|
const accounts = useDatabaseAccounts(selectedSubscriptionId);
|
||||||
|
const [selectedAccountName, setSelectedAccoutName] = React.useState<string>();
|
||||||
|
|
||||||
const menuProps: IContextualMenuProps = {
|
const menuProps: IContextualMenuProps = {
|
||||||
directionalHintFixed: true,
|
directionalHintFixed: true,
|
||||||
className: "accountSwitchContextualMenu",
|
className: "accountSwitchContextualMenu",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "switchSubscription",
|
key: "switchSubscription",
|
||||||
onRender: () => renderSubscriptionDropdown(subscriptions)
|
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 {
|
||||||
|
key: sub.subscriptionId,
|
||||||
|
text: sub.displayName,
|
||||||
|
data: sub
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
onChange: (event, option) => {
|
||||||
|
setSelectedSubscriptionId(String(option.key));
|
||||||
|
},
|
||||||
|
defaultSelectedKey: selectedSubscriptionId,
|
||||||
|
placeholder: "Select subscription from list",
|
||||||
|
styles: {
|
||||||
|
callout: "accountSwitchSubscriptionDropdownMenu"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Dropdown {...dropdownProps} />;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "switchAccount",
|
key: "switchAccount",
|
||||||
onRender: renderAccountDropDown
|
onRender: () => {
|
||||||
|
const isLoadingAccounts = false;
|
||||||
|
|
||||||
|
const options = accounts.map(account => ({
|
||||||
|
key: account.name,
|
||||||
|
text: account.name,
|
||||||
|
data: account
|
||||||
|
}));
|
||||||
|
|
||||||
|
const placeHolderText = isLoadingAccounts
|
||||||
|
? "Loading Cosmos DB accounts"
|
||||||
|
: !options || !options.length
|
||||||
|
? "No Cosmos DB accounts found"
|
||||||
|
: "Select Cosmos DB account from list";
|
||||||
|
|
||||||
|
const dropdownProps: IDropdownProps = {
|
||||||
|
label: "Cosmos DB Account Name",
|
||||||
|
className: "accountSwitchAccountDropdown",
|
||||||
|
options,
|
||||||
|
onChange: (event, option) => {
|
||||||
|
setSelectedAccoutName(String(option.key));
|
||||||
|
},
|
||||||
|
defaultSelectedKey: selectedAccountName,
|
||||||
|
placeholder: placeHolderText,
|
||||||
|
styles: {
|
||||||
|
callout: "accountSwitchAccountDropdownMenu"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Dropdown {...dropdownProps} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonStyles: IButtonStyles = {
|
|
||||||
root: {
|
|
||||||
fontSize: StyleConstants.DefaultFontSize,
|
|
||||||
height: 40,
|
|
||||||
padding: 0,
|
|
||||||
paddingLeft: 10,
|
|
||||||
marginRight: 5,
|
|
||||||
backgroundColor: StyleConstants.BaseDark,
|
|
||||||
color: StyleConstants.BaseLight
|
|
||||||
},
|
|
||||||
rootHovered: {
|
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
|
||||||
color: StyleConstants.BaseLight
|
|
||||||
},
|
|
||||||
rootFocused: {
|
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
|
||||||
color: StyleConstants.BaseLight
|
|
||||||
},
|
|
||||||
rootPressed: {
|
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
|
||||||
color: StyleConstants.BaseLight
|
|
||||||
},
|
|
||||||
rootExpanded: {
|
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
|
||||||
color: StyleConstants.BaseLight
|
|
||||||
},
|
|
||||||
textContainer: {
|
|
||||||
flexGrow: "initial"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonProps: IButtonProps = {
|
const buttonProps: IButtonProps = {
|
||||||
text: "foo",
|
text: selectedAccountName || "Select Database Account",
|
||||||
menuProps: menuProps,
|
menuProps: menuProps,
|
||||||
styles: buttonStyles,
|
styles: buttonStyles,
|
||||||
className: "accountSwitchButton",
|
className: "accountSwitchButton",
|
||||||
@@ -65,76 +128,3 @@ export const AccountSwitchComponent: React.FunctionComponent = () => {
|
|||||||
|
|
||||||
return <DefaultButton {...buttonProps} />;
|
return <DefaultButton {...buttonProps} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderSubscriptionDropdown(subscriptions: Subscription[]): JSX.Element {
|
|
||||||
const selectedSubscriptionId = "";
|
|
||||||
const isLoadingSubscriptions = false;
|
|
||||||
|
|
||||||
const options: IDropdownOption[] = subscriptions.map(sub => {
|
|
||||||
return {
|
|
||||||
key: sub.subscriptionId,
|
|
||||||
text: sub.displayName,
|
|
||||||
data: sub
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
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: options,
|
|
||||||
onChange: () => {},
|
|
||||||
defaultSelectedKey: selectedSubscriptionId,
|
|
||||||
placeholder: placeHolderText,
|
|
||||||
styles: {
|
|
||||||
callout: "accountSwitchSubscriptionDropdownMenu"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderAccountDropDown(): JSX.Element {
|
|
||||||
const accounts = [];
|
|
||||||
const selectedAccountName = "foo";
|
|
||||||
const isLoadingAccounts = false;
|
|
||||||
const options: IDropdownOption[] = accounts.map(account => {
|
|
||||||
return {
|
|
||||||
key: account.name,
|
|
||||||
text: account.name,
|
|
||||||
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
|
|
||||||
? "Loading Cosmos DB accounts"
|
|
||||||
: !options || !options.length
|
|
||||||
? "No Cosmos DB accounts found"
|
|
||||||
: "Select Cosmos DB account from list";
|
|
||||||
|
|
||||||
const dropdownProps: IDropdownProps = {
|
|
||||||
label: "Cosmos DB Account Name",
|
|
||||||
className: "accountSwitchAccountDropdown",
|
|
||||||
options: options,
|
|
||||||
onChange: () => {},
|
|
||||||
defaultSelectedKey: selectedAccountName,
|
|
||||||
placeholder: placeHolderText,
|
|
||||||
styles: {
|
|
||||||
callout: "accountSwitchAccountDropdownMenu"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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" />}
|
||||||
<span className="accountSwitchComponentContainer">
|
{isLoggedIn && (
|
||||||
{/* <AccountSwitchComponent /> */}
|
<span className="accountSwitchComponentContainer">
|
||||||
<span className="accountNameHeader">REPLACE ME - Connection string mode</span>;
|
<AccountSwitchComponent />
|
||||||
</span>
|
<span className="accountNameHeader">REPLACE ME - Connection string mode</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("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback")
|
||||||
window.open(
|
}
|
||||||
"https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback"
|
ariaLabel="feeback button"
|
||||||
)
|
tooltipText="Send feedback"
|
||||||
}
|
hasPopup={true}
|
||||||
ariaLabel="feeback button"
|
disabled={false}
|
||||||
tooltipText="Send feedback"
|
/>
|
||||||
hasPopup={true}
|
|
||||||
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,50 +171,69 @@ 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>
|
||||||
{/* <iframe
|
) : (
|
||||||
|
<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
|
||||||
id="explorerMenu"
|
id="explorerMenu"
|
||||||
name="explorer"
|
name="explorer"
|
||||||
className="iframe"
|
className="iframe"
|
||||||
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
|
||||||
|
);
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
74
src/contexts/authContext.tsx
Normal file
74
src/contexts/authContext.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
52
src/hooks/useDatabaseAccounts.tsx
Normal file
52
src/hooks/useDatabaseAccounts.tsx
Normal 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 || [];
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user