mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-21 01:41:31 +00:00
Checkpoint
This commit is contained in:
@@ -1,159 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow, mount } from "enzyme";
|
|
||||||
import { AccountSwitchComponent, AccountSwitchComponentProps } from "./AccountSwitchComponent";
|
|
||||||
import { AuthType } from "../../../AuthType";
|
|
||||||
import { DatabaseAccount, Subscription } from "../../../Contracts/DataModels";
|
|
||||||
import { AccountKind } from "../../../Common/Constants";
|
|
||||||
|
|
||||||
const createBlankProps = (): AccountSwitchComponentProps => {
|
|
||||||
return {
|
|
||||||
authType: null,
|
|
||||||
displayText: "",
|
|
||||||
accounts: [],
|
|
||||||
selectedAccountName: null,
|
|
||||||
isLoadingAccounts: false,
|
|
||||||
onAccountChange: jest.fn(),
|
|
||||||
subscriptions: [],
|
|
||||||
selectedSubscriptionId: null,
|
|
||||||
isLoadingSubscriptions: false,
|
|
||||||
onSubscriptionChange: jest.fn()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createBlankAccount = (): DatabaseAccount => {
|
|
||||||
return {
|
|
||||||
id: "",
|
|
||||||
kind: AccountKind.Default,
|
|
||||||
name: "",
|
|
||||||
properties: null,
|
|
||||||
location: "",
|
|
||||||
tags: null,
|
|
||||||
type: ""
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createBlankSubscription = (): Subscription => {
|
|
||||||
return {
|
|
||||||
subscriptionId: "",
|
|
||||||
displayName: "",
|
|
||||||
authorizationSource: "",
|
|
||||||
state: "",
|
|
||||||
subscriptionPolicies: null,
|
|
||||||
tenantId: "",
|
|
||||||
uniqueDisplayName: ""
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFullProps = (): AccountSwitchComponentProps => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
props.authType = AuthType.AAD;
|
|
||||||
const account1 = createBlankAccount();
|
|
||||||
account1.name = "account1";
|
|
||||||
const account2 = createBlankAccount();
|
|
||||||
account2.name = "account2";
|
|
||||||
const account3 = createBlankAccount();
|
|
||||||
account3.name = "superlongaccountnamestringtest";
|
|
||||||
props.accounts = [account1, account2, account3];
|
|
||||||
props.selectedAccountName = "account2";
|
|
||||||
|
|
||||||
const sub1 = createBlankSubscription();
|
|
||||||
sub1.displayName = "sub1";
|
|
||||||
sub1.subscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297";
|
|
||||||
const sub2 = createBlankSubscription();
|
|
||||||
sub2.displayName = "subsubsubsubsubsubsub2";
|
|
||||||
sub2.subscriptionId = "b20b3e93-0185-4326-8a9c-d44bac276b6b";
|
|
||||||
props.subscriptions = [sub1, sub2];
|
|
||||||
props.selectedSubscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297";
|
|
||||||
|
|
||||||
return props;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("test render", () => {
|
|
||||||
it("renders no auth type -> handle error in code", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
|
|
||||||
const wrapper = shallow(<AccountSwitchComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Encrypted Token
|
|
||||||
it("renders auth security token, with selected account name", () => {
|
|
||||||
const props = createBlankProps();
|
|
||||||
props.authType = AuthType.EncryptedToken;
|
|
||||||
props.selectedAccountName = "testaccount";
|
|
||||||
|
|
||||||
const wrapper = shallow(<AccountSwitchComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
// AAD
|
|
||||||
it("renders auth aad, with all information", () => {
|
|
||||||
const props = createFullProps();
|
|
||||||
const wrapper = shallow(<AccountSwitchComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders auth aad all dropdown menus", () => {
|
|
||||||
const props = createFullProps();
|
|
||||||
const wrapper = mount(<AccountSwitchComponent {...props} />);
|
|
||||||
|
|
||||||
expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false);
|
|
||||||
wrapper.find("button.accountSwitchButton").simulate("click");
|
|
||||||
expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(true);
|
|
||||||
|
|
||||||
expect(wrapper.exists("div.accountSwitchSubscriptionDropdown")).toBe(true);
|
|
||||||
wrapper.find("DropdownBase.accountSwitchSubscriptionDropdown").simulate("click");
|
|
||||||
// Click will dismiss the first contextual menu in enzyme. Need to dig deeper to achieve below test
|
|
||||||
|
|
||||||
// expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(true);
|
|
||||||
// expect(wrapper.find("button.ms-Dropdown-item").length).toBe(2);
|
|
||||||
// wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click");
|
|
||||||
// expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(false);
|
|
||||||
|
|
||||||
// expect(wrapper.exists("div.accountSwitchAccountDropdown")).toBe(true);
|
|
||||||
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
|
|
||||||
// expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(true);
|
|
||||||
// expect(wrapper.find("button.ms-Dropdown-item").length).toBe(3);
|
|
||||||
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
|
|
||||||
// expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(false);
|
|
||||||
|
|
||||||
// wrapper.find("button.accountSwitchButton").simulate("click");
|
|
||||||
// expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false);
|
|
||||||
|
|
||||||
wrapper.unmount();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// describe("test function", () => {
|
|
||||||
// it("switch subscription function", () => {
|
|
||||||
// const props = createFullProps();
|
|
||||||
// const wrapper = mount(<AccountSwitchComponent {...props} />);
|
|
||||||
|
|
||||||
// wrapper.find("button.accountSwitchButton").simulate("click");
|
|
||||||
// wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click");
|
|
||||||
// wrapper
|
|
||||||
// .find("button.ms-Dropdown-item")
|
|
||||||
// .at(1)
|
|
||||||
// .simulate("click");
|
|
||||||
// expect(props.onSubscriptionChange).toBeCalled();
|
|
||||||
// expect(props.onSubscriptionChange).toHaveBeenCalled();
|
|
||||||
|
|
||||||
// wrapper.unmount();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it("switch account", () => {
|
|
||||||
// const props = createFullProps();
|
|
||||||
// const wrapper = mount(<AccountSwitchComponent {...props} />);
|
|
||||||
|
|
||||||
// wrapper.find("button.accountSwitchButton").simulate("click");
|
|
||||||
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
|
|
||||||
// wrapper
|
|
||||||
// .find("button.ms-Dropdown-item")
|
|
||||||
// .at(0)
|
|
||||||
// .simulate("click");
|
|
||||||
// expect(props.onAccountChange).toBeCalled();
|
|
||||||
// expect(props.onAccountChange).toHaveBeenCalled();
|
|
||||||
|
|
||||||
// wrapper.unmount();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/Constants";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { DefaultButton, IButtonStyles, IButtonProps } from "office-ui-fabric-react/lib/Button";
|
import { DefaultButton, IButtonStyles, IButtonProps } from "office-ui-fabric-react/lib/Button";
|
||||||
import { IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
|
import { IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
|
||||||
@@ -38,10 +37,10 @@ const buttonStyles: IButtonStyles = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AccountSwitchComponent: React.FunctionComponent = () => {
|
export const AccountSwitchComponent: React.FunctionComponent<{ armToken: string }> = ({ armToken }) => {
|
||||||
const subscriptions = useSubscriptions();
|
const subscriptions = useSubscriptions(armToken);
|
||||||
const [selectedSubscriptionId, setSelectedSubscriptionId] = React.useState<string>();
|
const [selectedSubscriptionId, setSelectedSubscriptionId] = React.useState<string>();
|
||||||
const accounts = useDatabaseAccounts(selectedSubscriptionId);
|
const accounts = useDatabaseAccounts(selectedSubscriptionId, armToken);
|
||||||
const [selectedAccountName, setSelectedAccoutName] = React.useState<string>();
|
const [selectedAccountName, setSelectedAccoutName] = React.useState<string>();
|
||||||
|
|
||||||
const menuProps: IContextualMenuProps = {
|
const menuProps: IContextualMenuProps = {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import { AccountSwitchComponent, AccountSwitchComponentProps } from "./AccountSwitchComponent";
|
|
||||||
|
|
||||||
export class AccountSwitchComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<AccountSwitchComponentProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <AccountSwitchComponent {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`test render renders auth aad, with all information 1`] = `
|
|
||||||
<CustomizedDefaultButton
|
|
||||||
className="accountSwitchButton"
|
|
||||||
id="accountSwitchButton"
|
|
||||||
menuProps={
|
|
||||||
Object {
|
|
||||||
"className": "accountSwitchContextualMenu",
|
|
||||||
"directionalHintFixed": true,
|
|
||||||
"items": Array [
|
|
||||||
Object {
|
|
||||||
"key": "switchSubscription",
|
|
||||||
"onRender": [Function],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "switchAccount",
|
|
||||||
"onRender": [Function],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"backgroundColor": undefined,
|
|
||||||
"color": undefined,
|
|
||||||
"fontSize": undefined,
|
|
||||||
"height": 40,
|
|
||||||
"marginRight": 5,
|
|
||||||
"padding": 0,
|
|
||||||
"paddingLeft": 10,
|
|
||||||
},
|
|
||||||
"rootExpanded": Object {
|
|
||||||
"backgroundColor": undefined,
|
|
||||||
"color": undefined,
|
|
||||||
},
|
|
||||||
"rootFocused": Object {
|
|
||||||
"backgroundColor": undefined,
|
|
||||||
"color": undefined,
|
|
||||||
},
|
|
||||||
"rootHovered": Object {
|
|
||||||
"backgroundColor": undefined,
|
|
||||||
"color": undefined,
|
|
||||||
},
|
|
||||||
"rootPressed": Object {
|
|
||||||
"backgroundColor": undefined,
|
|
||||||
"color": undefined,
|
|
||||||
},
|
|
||||||
"textContainer": Object {
|
|
||||||
"flexGrow": "initial",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text="account2"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`test render renders auth security token, with selected account name 1`] = `
|
|
||||||
<span
|
|
||||||
className="accountNameHeader"
|
|
||||||
>
|
|
||||||
testaccount
|
|
||||||
</span>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`test render renders no auth type -> handle error in code 1`] = `
|
|
||||||
<span
|
|
||||||
className="accountNameHeader"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
@@ -2,7 +2,7 @@ import * as ko from "knockout";
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { ConnectionStringParser } from "../../Platform/Hosted/Helpers/ConnectionStringParser";
|
import { parseConnectionString } from "../../Platform/Hosted/Helpers/ConnectionStringParser";
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||||
@@ -48,9 +48,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private _shouldShowContextSwitchPrompt(): boolean {
|
private _shouldShowContextSwitchPrompt(): boolean {
|
||||||
const inputMetadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
|
const inputMetadata: DataModels.AccessInputMetadata = parseConnectionString(this.accessKey());
|
||||||
this.accessKey()
|
|
||||||
);
|
|
||||||
const apiKind: DataModels.ApiKind =
|
const apiKind: DataModels.ApiKind =
|
||||||
this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience());
|
this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience());
|
||||||
const hasOpenedTabs: boolean =
|
const hasOpenedTabs: boolean =
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import "./Shared/appInsights";
|
|||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import hasher from "hasher";
|
import hasher from "hasher";
|
||||||
import { AccountSwitchComponentProps } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
|
||||||
import { AccountSwitchComponentAdapter } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter";
|
|
||||||
import { Action } from "./Shared/Telemetry/TelemetryConstants";
|
import { Action } from "./Shared/Telemetry/TelemetryConstants";
|
||||||
import { ArmResourceUtils } from "./Platform/Hosted/ArmResourceUtils";
|
import { ArmResourceUtils } from "./Platform/Hosted/ArmResourceUtils";
|
||||||
import AuthHeadersUtil from "./Platform/Hosted/Authorization";
|
import AuthHeadersUtil from "./Platform/Hosted/Authorization";
|
||||||
@@ -50,13 +48,11 @@ class HostedExplorer {
|
|||||||
public firewallWarningComponentAdapter: DialogComponentAdapter;
|
public firewallWarningComponentAdapter: DialogComponentAdapter;
|
||||||
public dialogComponentAdapter: DialogComponentAdapter;
|
public dialogComponentAdapter: DialogComponentAdapter;
|
||||||
public meControlComponentAdapter: MeControlComponentAdapter;
|
public meControlComponentAdapter: MeControlComponentAdapter;
|
||||||
public accountSwitchComponentAdapter: AccountSwitchComponentAdapter;
|
|
||||||
public switchDirectoryPane: SwitchDirectoryPane;
|
public switchDirectoryPane: SwitchDirectoryPane;
|
||||||
|
|
||||||
private _firewallWarningDialogProps: ko.Observable<DialogProps>;
|
private _firewallWarningDialogProps: ko.Observable<DialogProps>;
|
||||||
private _dialogProps: ko.Observable<DialogProps>;
|
private _dialogProps: ko.Observable<DialogProps>;
|
||||||
private _meControlProps: ko.Observable<MeControlComponentProps>;
|
private _meControlProps: ko.Observable<MeControlComponentProps>;
|
||||||
private _accountSwitchProps: ko.Observable<AccountSwitchComponentProps>;
|
|
||||||
private _controlbarCommands: ko.ObservableArray<CommandButtonComponentProps>;
|
private _controlbarCommands: ko.ObservableArray<CommandButtonComponentProps>;
|
||||||
private _directoryDropdownProps: ko.Observable<DefaultDirectoryDropdownProps>;
|
private _directoryDropdownProps: ko.Observable<DefaultDirectoryDropdownProps>;
|
||||||
private _directoryListProps: ko.Observable<DirectoryListProps>;
|
private _directoryListProps: ko.Observable<DirectoryListProps>;
|
||||||
@@ -161,35 +157,10 @@ class HostedExplorer {
|
|||||||
this.meControlComponentAdapter = new MeControlComponentAdapter();
|
this.meControlComponentAdapter = new MeControlComponentAdapter();
|
||||||
this.meControlComponentAdapter.parameters = this._meControlProps;
|
this.meControlComponentAdapter.parameters = this._meControlProps;
|
||||||
|
|
||||||
this._accountSwitchProps = ko.observable<AccountSwitchComponentProps>({
|
|
||||||
authType: AuthType.EncryptedToken,
|
|
||||||
selectedAccountName: "",
|
|
||||||
accounts: [],
|
|
||||||
isLoadingAccounts: false,
|
|
||||||
onAccountChange: this._onAccountChange,
|
|
||||||
selectedSubscriptionId: undefined,
|
|
||||||
subscriptions: [],
|
|
||||||
isLoadingSubscriptions: false,
|
|
||||||
onSubscriptionChange: this._onSubscriptionChange
|
|
||||||
});
|
|
||||||
this.accountSwitchComponentAdapter = new AccountSwitchComponentAdapter();
|
|
||||||
this.accountSwitchComponentAdapter.parameters = this._accountSwitchProps;
|
|
||||||
|
|
||||||
this.isAccountActive = ko.computed<boolean>(() => {
|
|
||||||
if (
|
|
||||||
this._accountSwitchProps() &&
|
|
||||||
(this._accountSwitchProps().displayText || this._accountSwitchProps().selectedAccountName)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
hasher.initialized.add(updateExplorerHash);
|
hasher.initialized.add(updateExplorerHash);
|
||||||
hasher.changed.add(updateExplorerHash);
|
hasher.changed.add(updateExplorerHash);
|
||||||
hasher.init();
|
hasher.init();
|
||||||
window.addEventListener("message", this._handleMessage.bind(this), false);
|
window.addEventListener("message", this._handleMessage.bind(this), false);
|
||||||
this._handleAadLogin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public explorer_click() {
|
public explorer_click() {
|
||||||
@@ -348,43 +319,10 @@ class HostedExplorer {
|
|||||||
this._meControlProps(propsToUpdate);
|
this._meControlProps(propsToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateAccountSwitchProps(props: Partial<AccountSwitchComponentProps>) {
|
private _updateAccountSwitchProps(props: any) {
|
||||||
if (!props) {
|
if (!props) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const propsToUpdate = this._accountSwitchProps();
|
|
||||||
if (props.authType) {
|
|
||||||
if (props.selectedAccountName != null) {
|
|
||||||
propsToUpdate.selectedAccountName = props.selectedAccountName;
|
|
||||||
}
|
|
||||||
if (props.authType === AuthType.EncryptedToken) {
|
|
||||||
propsToUpdate.authType = AuthType.EncryptedToken;
|
|
||||||
} else if (props.authType === AuthType.AAD) {
|
|
||||||
propsToUpdate.authType = AuthType.AAD;
|
|
||||||
if (props.displayText != null) {
|
|
||||||
propsToUpdate.displayText = props.displayText;
|
|
||||||
}
|
|
||||||
if (props.isLoadingAccounts != null) {
|
|
||||||
propsToUpdate.isLoadingAccounts = props.isLoadingAccounts;
|
|
||||||
}
|
|
||||||
if (props.accounts) {
|
|
||||||
propsToUpdate.accounts = props.accounts.sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.isLoadingSubscriptions != null) {
|
|
||||||
propsToUpdate.isLoadingSubscriptions = props.isLoadingSubscriptions;
|
|
||||||
}
|
|
||||||
if (props.subscriptions) {
|
|
||||||
propsToUpdate.subscriptions = props.subscriptions.sort((a, b) => (a.displayName < b.displayName ? -1 : 1));
|
|
||||||
}
|
|
||||||
if (props.selectedSubscriptionId != null) {
|
|
||||||
propsToUpdate.selectedSubscriptionId = props.selectedSubscriptionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._accountSwitchProps(propsToUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onAccountChange = (newAccount: DatabaseAccount) => {
|
private _onAccountChange = (newAccount: DatabaseAccount) => {
|
||||||
@@ -522,36 +460,6 @@ class HostedExplorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleAadLogin() {
|
|
||||||
AuthHeadersUtil.processTokenResponse();
|
|
||||||
if (AuthHeadersUtil.isUserSignedIn()) {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
const user = AuthHeadersUtil.getCachedUser();
|
|
||||||
this._updateMeControlProps({
|
|
||||||
isUserSignedIn: true,
|
|
||||||
user: {
|
|
||||||
name: user.profile.name,
|
|
||||||
email: user.userName,
|
|
||||||
tenantName: undefined,
|
|
||||||
imageUrl: undefined
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AuthHeadersUtil.getPhotoFromGraphAPI().then(blob => {
|
|
||||||
const imageUrl = URL.createObjectURL(blob);
|
|
||||||
this._updateMeControlProps({
|
|
||||||
isUserSignedIn: true,
|
|
||||||
user: {
|
|
||||||
name: undefined,
|
|
||||||
email: undefined,
|
|
||||||
tenantName: undefined,
|
|
||||||
imageUrl: imageUrl
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleGetAccessAadRequest() {
|
private _handleGetAccessAadRequest() {
|
||||||
this._getAccessAad().then(
|
this._getAccessAad().then(
|
||||||
response => {
|
response => {
|
||||||
|
|||||||
@@ -2,23 +2,19 @@ import "./Platform/Hosted/ConnectScreen.less";
|
|||||||
import { useBoolean } from "@uifabric/react-hooks";
|
import { useBoolean } from "@uifabric/react-hooks";
|
||||||
import {
|
import {
|
||||||
DefaultButton,
|
DefaultButton,
|
||||||
DetailsList,
|
|
||||||
DirectionalHint,
|
DirectionalHint,
|
||||||
FocusZone,
|
FocusZone,
|
||||||
IContextualMenuProps,
|
|
||||||
initializeIcons,
|
initializeIcons,
|
||||||
Panel,
|
Panel,
|
||||||
PanelType,
|
PanelType,
|
||||||
Persona,
|
Persona,
|
||||||
PersonaInitialsColor,
|
PersonaInitialsColor,
|
||||||
PersonaSize,
|
PersonaSize,
|
||||||
SelectionMode,
|
ChoiceGroup
|
||||||
Selection
|
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { render } from "react-dom";
|
import { render } from "react-dom";
|
||||||
import FeedbackIcon from "../images/Feedback.svg";
|
import FeedbackIcon from "../images/Feedback.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 { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
@@ -26,111 +22,74 @@ import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
|||||||
import { useGraphPhoto } from "./hooks/useGraphPhoto";
|
import { useGraphPhoto } from "./hooks/useGraphPhoto";
|
||||||
import "./Shared/appInsights";
|
import "./Shared/appInsights";
|
||||||
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
||||||
import { AuthContext, AuthProvider } from "./contexts/authContext";
|
|
||||||
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
||||||
import { useDirectories } from "./hooks/useDirectories";
|
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";
|
import { AuthType } from "./AuthType";
|
||||||
|
|
||||||
initializeIcons();
|
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 = () => {
|
const App: React.FunctionComponent = () => {
|
||||||
|
// Hooks for handling encrypted portal tokens
|
||||||
const params = new URLSearchParams(window.location.search);
|
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);
|
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
||||||
|
|
||||||
|
// Hooks for showing/hiding UI
|
||||||
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
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 [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
||||||
const photo = useGraphPhoto();
|
|
||||||
const directories = useDirectories();
|
// Hooks for AAD authentication
|
||||||
// const [selectedItem, setSelectedItem] = React.useState<any>(undefined);
|
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false);
|
||||||
const selection = new Selection({
|
const [account, setAccount] = React.useState<Msal.Account>();
|
||||||
getKey: item => item.tenantId,
|
const [graphToken, setGraphToken] = React.useState<string>();
|
||||||
items: directories,
|
const [armToken, setArmToken] = React.useState<string>();
|
||||||
onSelectionChanged: () => {
|
const [tenantId, setTenantId] = React.useState<string>();
|
||||||
const selected = selection.getSelection()[0];
|
const [connectionString, setConnectionString] = React.useState<string>("");
|
||||||
if (selected.tenantId !== tenantId) {
|
|
||||||
console.log("new Tenant", selected.tenantId);
|
const login = React.useCallback(async () => {
|
||||||
}
|
const response = await msal.loginPopup();
|
||||||
},
|
setLoggedIn();
|
||||||
selectionMode: SelectionMode.single
|
setAccount(response.account);
|
||||||
|
setTenantId(response.tenantId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const logout = React.useCallback(() => {
|
||||||
|
msal.logout();
|
||||||
|
setLoggedOut();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
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"
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return <Persona {...personaProps} />;
|
|
||||||
// };
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
]
|
}, [account, tenantId]);
|
||||||
};
|
|
||||||
|
|
||||||
// {
|
const photo = useGraphPhoto(graphToken);
|
||||||
// id: "commandbutton-settings",
|
const directories = useDirectories(armToken);
|
||||||
// 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" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -151,7 +110,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
)}
|
)}
|
||||||
{isLoggedIn && (
|
{isLoggedIn && (
|
||||||
<span className="accountSwitchComponentContainer">
|
<span className="accountSwitchComponentContainer">
|
||||||
<AccountSwitchComponent />
|
<AccountSwitchComponent armToken={armToken} />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{!isLoggedIn && encryptedTokenMetadata?.accountName && (
|
{!isLoggedIn && encryptedTokenMetadata?.accountName && (
|
||||||
@@ -161,18 +120,6 @@ const App: React.FunctionComponent = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="feedbackConnectSettingIcons">
|
<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
|
<CommandButtonComponent
|
||||||
id="commandbutton-feedback"
|
id="commandbutton-feedback"
|
||||||
iconSrc={FeedbackIcon}
|
iconSrc={FeedbackIcon}
|
||||||
@@ -189,7 +136,37 @@ const App: React.FunctionComponent = () => {
|
|||||||
<div className="meControl">
|
<div className="meControl">
|
||||||
{isLoggedIn ? (
|
{isLoggedIn ? (
|
||||||
<FocusZone>
|
<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
|
<Persona
|
||||||
imageUrl={photo}
|
imageUrl={photo}
|
||||||
text={account?.name}
|
text={account?.name}
|
||||||
@@ -228,7 +205,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
)}&metadata=${JSON.stringify(encryptedTokenMetadata)}`}
|
)}&metadata=${JSON.stringify(encryptedTokenMetadata)}`}
|
||||||
></iframe>
|
></iframe>
|
||||||
)}
|
)}
|
||||||
{!encryptedTokenMetadata && isLoggedIn && (
|
{/* {!encryptedTokenMetadata && isLoggedIn && (
|
||||||
<iframe
|
<iframe
|
||||||
id="explorerMenu"
|
id="explorerMenu"
|
||||||
name="explorer"
|
name="explorer"
|
||||||
@@ -236,7 +213,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
title="explorer"
|
title="explorer"
|
||||||
src={`explorer.html?v=1.0.1&platform=Hosted&authType=${AuthType.AAD}`}
|
src={`explorer.html?v=1.0.1&platform=Hosted&authType=${AuthType.AAD}`}
|
||||||
></iframe>
|
></iframe>
|
||||||
)}
|
)} */}
|
||||||
{!isLoggedIn && !encryptedTokenMetadata && (
|
{!isLoggedIn && !encryptedTokenMetadata && (
|
||||||
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
|
<div id="connectExplorer" className="connectExplorerContainer" style={{ display: "flex" }}>
|
||||||
<div className="connectExplorerFormContainer">
|
<div className="connectExplorerFormContainer">
|
||||||
@@ -246,15 +223,42 @@ const App: React.FunctionComponent = () => {
|
|||||||
</p>
|
</p>
|
||||||
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
|
<p className="welcomeText">Welcome to Azure Cosmos DB</p>
|
||||||
{isConnectionStringVisible ? (
|
{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">
|
<p className="connectExplorerContent connectStringText">
|
||||||
Connect to your account with connection string
|
Connect to your account with connection string
|
||||||
</p>
|
</p>
|
||||||
<p className="connectExplorerContent">
|
<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" }}>
|
<span className="errorDetailsInfoTooltip" style={{ display: "none" }}>
|
||||||
<img className="errorImg" src="images/error.svg" alt="Error notification" />
|
<img className="errorImg" src="images/error.svg" alt="Error notification" />
|
||||||
<span className="errorDetails" />
|
<span className="errorDetails"></span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="connectExplorerContent">
|
<p className="connectExplorerContent">
|
||||||
@@ -285,35 +289,30 @@ const App: React.FunctionComponent = () => {
|
|||||||
onDismiss={dismissPanel}
|
onDismiss={dismissPanel}
|
||||||
closeButtonAriaLabel="Close"
|
closeButtonAriaLabel="Close"
|
||||||
>
|
>
|
||||||
<DetailsList
|
<ChoiceGroup
|
||||||
items={selection.getItems()}
|
options={directories.map(dir => ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))}
|
||||||
columns={[
|
selectedKey={tenantId}
|
||||||
{
|
onChange={async () => {
|
||||||
key: "name",
|
dismissPanel();
|
||||||
name: "Name",
|
// TODO!!! This does not work. Still not sure why. Tried lots of stuff.
|
||||||
minWidth: 200,
|
// const response = await msal.loginPopup({
|
||||||
maxWidth: 200,
|
// authority: `https://login.microsoftonline.com/${option.key}`
|
||||||
fieldName: "displayName"
|
// });
|
||||||
},
|
// // msal = new Msal.UserAgentApplication({
|
||||||
{
|
// // auth: {
|
||||||
key: "id",
|
// // authority: `https://login.microsoftonline.com/${option.key}`,
|
||||||
name: "ID",
|
// // clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||||
minWidth: 200,
|
// // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development
|
||||||
maxWidth: 200,
|
// // }
|
||||||
fieldName: "tenantId"
|
// // });
|
||||||
}
|
// setTenantId(option.key);
|
||||||
]}
|
// setAccount(response.account);
|
||||||
selectionMode={SelectionMode.single}
|
// console.log(account);
|
||||||
selection={selection}
|
}}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render(
|
render(<App />, document.body);
|
||||||
<AuthProvider>
|
|
||||||
<App />
|
|
||||||
</AuthProvider>,
|
|
||||||
document.body
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ import "url-polyfill/url-polyfill.min";
|
|||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as TelemetryProcessor from "./Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action, ActionModifiers } from "./Shared/Telemetry/TelemetryConstants";
|
|
||||||
|
|
||||||
import { BindingHandlersRegisterer } from "./Bindings/BindingHandlersRegisterer";
|
|
||||||
import * as Emulator from "./Platform/Emulator/Main";
|
import * as Emulator from "./Platform/Emulator/Main";
|
||||||
import Hosted from "./Platform/Hosted/Main";
|
import Hosted from "./Platform/Hosted/Main";
|
||||||
import * as Portal from "./Platform/Portal/Main";
|
import * as Portal from "./Platform/Portal/Main";
|
||||||
|
|||||||
@@ -107,14 +107,6 @@ export default class AuthHeadersUtil {
|
|||||||
const user = AuthHeadersUtil._authContext.getCachedUser();
|
const user = AuthHeadersUtil._authContext.getCachedUser();
|
||||||
return !!user;
|
return !!user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getCachedUser(): AuthenticationContext.UserInfo {
|
|
||||||
if (this.isUserSignedIn()) {
|
|
||||||
return AuthHeadersUtil._authContext.getCachedUser();
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static signIn() {
|
public static signIn() {
|
||||||
if (!AuthHeadersUtil.isUserSignedIn()) {
|
if (!AuthHeadersUtil.isUserSignedIn()) {
|
||||||
AuthHeadersUtil._authContext.login();
|
AuthHeadersUtil._authContext.login();
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import AuthHeadersUtil from "../Platform/Hosted/Authorization";
|
|
||||||
import * as UserUtils from "./UserUtils";
|
|
||||||
|
|
||||||
describe("UserUtils", () => {
|
|
||||||
it("getFullName works in regular data explorer (inside portal)", () => {
|
|
||||||
const user: AuthenticationContext.UserInfo = {
|
|
||||||
userName: "userName",
|
|
||||||
profile: {
|
|
||||||
name: "name"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AuthHeadersUtil.getCachedUser = jest.fn().mockReturnValue(user);
|
|
||||||
|
|
||||||
expect(UserUtils.getFullName()).toBe("name");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getFullName works in fullscreen data explorer (outside portal)", () => {
|
|
||||||
jest.mock("./AuthorizationUtils", () => {
|
|
||||||
(): { name: string } => ({ name: "name" });
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(UserUtils.getFullName()).toBe("name");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
import AuthHeadersUtil from "../Platform/Hosted/Authorization";
|
|
||||||
import { decryptJWTToken } from "./AuthorizationUtils";
|
import { decryptJWTToken } from "./AuthorizationUtils";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
export function getFullName(): string {
|
export function getFullName(): string {
|
||||||
let fullName: string;
|
|
||||||
const user = AuthHeadersUtil.getCachedUser();
|
|
||||||
if (user) {
|
|
||||||
fullName = user.profile.name;
|
|
||||||
} else {
|
|
||||||
const authToken = userContext.authorizationToken;
|
const authToken = userContext.authorizationToken;
|
||||||
const props = decryptJWTToken(authToken);
|
const props = decryptJWTToken(authToken);
|
||||||
fullName = props.name;
|
return props.name;
|
||||||
}
|
|
||||||
|
|
||||||
return fullName;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
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" // TODO! This should only be set development
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
interface AuthContext {
|
|
||||||
isLoggedIn: boolean;
|
|
||||||
account?: Msal.Account;
|
|
||||||
graphToken?: string;
|
|
||||||
armToken?: string;
|
|
||||||
tenantId?: string;
|
|
||||||
aadlogout: () => unknown;
|
|
||||||
aadlogin: () => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AuthContext = createContext<AuthContext>({
|
|
||||||
isLoggedIn: false,
|
|
||||||
aadlogin: () => {
|
|
||||||
throw Error(defaultError);
|
|
||||||
},
|
|
||||||
aadlogout: () => {
|
|
||||||
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 [tenantId, setTenantId] = useState<string>();
|
|
||||||
|
|
||||||
const aadlogin = useCallback(async () => {
|
|
||||||
const response = await msal.loginPopup();
|
|
||||||
setLoggedIn();
|
|
||||||
setAccount(response.account);
|
|
||||||
setTenantId(response.tenantId);
|
|
||||||
msal.authority = "https://login.microsoftonline.com/481f23b0-3fb3-4e76-812d-15513d11dbfc";
|
|
||||||
|
|
||||||
const [graphTokenResponse, armTokenResponse] = await Promise.all([
|
|
||||||
msal.acquireTokenSilent({
|
|
||||||
scopes: ["https://graph.windows.net//.default"]
|
|
||||||
}),
|
|
||||||
msal.acquireTokenSilent({
|
|
||||||
scopes: ["https://management.azure.com//.default"]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
setGraphToken(graphTokenResponse.accessToken);
|
|
||||||
setArmToken(armTokenResponse.accessToken);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const aadlogout = useCallback(() => {
|
|
||||||
msal.logout();
|
|
||||||
setLoggedOut();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken, tenantId }}>
|
|
||||||
{children}
|
|
||||||
</AuthContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { AuthContext } from "../contexts/authContext";
|
|
||||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||||
|
|
||||||
interface AccountListResult {
|
interface AccountListResult {
|
||||||
@@ -39,8 +38,7 @@ export async function fetchDatabaseAccounts(
|
|||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDatabaseAccounts(subscriptionId: string): DatabaseAccount[] {
|
export function useDatabaseAccounts(subscriptionId: string, armToken: string): DatabaseAccount[] {
|
||||||
const { armToken } = useContext(AuthContext);
|
|
||||||
const [state, setState] = useState<DatabaseAccount[]>();
|
const [state, setState] = useState<DatabaseAccount[]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { AuthContext } from "../contexts/authContext";
|
|
||||||
import { Tenant } from "../Contracts/DataModels";
|
import { Tenant } from "../Contracts/DataModels";
|
||||||
|
|
||||||
interface TenantListResult {
|
interface TenantListResult {
|
||||||
@@ -29,8 +28,7 @@ export async function fetchDirectories(accessToken: string): Promise<Tenant[]> {
|
|||||||
return tenents;
|
return tenents;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDirectories(): Tenant[] {
|
export function useDirectories(armToken: string): Tenant[] {
|
||||||
const { armToken } = useContext(AuthContext);
|
|
||||||
const [state, setState] = useState<Tenant[]>();
|
const [state, setState] = useState<Tenant[]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
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();
|
||||||
@@ -18,9 +17,8 @@ export async function fetchPhoto(accessToken: string): Promise<Blob | void> {
|
|||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGraphPhoto(): string {
|
export function useGraphPhoto(graphToken: string): string {
|
||||||
const [photo, setPhoto] = useState<string>();
|
const [photo, setPhoto] = useState<string>();
|
||||||
const { graphToken } = useContext(AuthContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (graphToken) {
|
if (graphToken) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { AuthContext } from "../contexts/authContext";
|
|
||||||
import { Subscription } from "../Contracts/DataModels";
|
import { Subscription } from "../Contracts/DataModels";
|
||||||
|
|
||||||
interface SubscriptionListResult {
|
interface SubscriptionListResult {
|
||||||
@@ -32,8 +31,7 @@ export async function fetchSubscriptions(accessToken: string): Promise<Subscript
|
|||||||
return subscriptions;
|
return subscriptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSubscriptions(): Subscription[] {
|
export function useSubscriptions(armToken: string): Subscription[] {
|
||||||
const { armToken } = useContext(AuthContext);
|
|
||||||
const [state, setState] = useState<Subscription[]>();
|
const [state, setState] = useState<Subscription[]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user