mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-24 11:21:23 +00:00
Compare commits
1 Commits
make_accou
...
force-enab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a5ccbb51b |
@@ -48,18 +48,32 @@ export function sendCachedDataMessage<TResponseDataModel>(
|
||||
}
|
||||
|
||||
export function sendMessage(data: any): void {
|
||||
_sendMessage({
|
||||
signature: "pcIframe",
|
||||
data: data,
|
||||
});
|
||||
if (canSendMessage()) {
|
||||
// We try to find data explorer window first, then fallback to current window
|
||||
const portalChildWindow = getDataExplorerWindow(window) || window;
|
||||
portalChildWindow.parent.postMessage(
|
||||
{
|
||||
signature: "pcIframe",
|
||||
data: data,
|
||||
},
|
||||
portalChildWindow.document.referrer || "*"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function sendReadyMessage(): void {
|
||||
_sendMessage({
|
||||
signature: "pcIframe",
|
||||
kind: "ready",
|
||||
data: "ready",
|
||||
});
|
||||
if (canSendMessage()) {
|
||||
// We try to find data explorer window first, then fallback to current window
|
||||
const portalChildWindow = getDataExplorerWindow(window) || window;
|
||||
portalChildWindow.parent.postMessage(
|
||||
{
|
||||
signature: "pcIframe",
|
||||
kind: "ready",
|
||||
data: "ready",
|
||||
},
|
||||
portalChildWindow.document.referrer || "*"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function canSendMessage(): boolean {
|
||||
@@ -75,17 +89,3 @@ export function runGarbageCollector() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const _sendMessage = (message: any): void => {
|
||||
if (canSendMessage()) {
|
||||
// Portal window can receive messages from only child windows
|
||||
const portalChildWindow = getDataExplorerWindow(window) || window;
|
||||
if (portalChildWindow === window) {
|
||||
// Current window is a child of portal, send message to portal window
|
||||
portalChildWindow.parent.postMessage(message, portalChildWindow.document.referrer || "*");
|
||||
} else {
|
||||
// Current window is not a child of portal, send message to the child window instead (which is data explorer)
|
||||
portalChildWindow.postMessage(message, portalChildWindow.location.origin || "*");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -300,11 +300,7 @@ export default class Explorer {
|
||||
);
|
||||
Promise.all([this._refreshNotebooksEnabledStateForAccount(), this._refreshSparkEnabledStateForAccount()]).then(
|
||||
async () => {
|
||||
this.isNotebookEnabled(
|
||||
userContext.authType !== AuthType.ResourceToken &&
|
||||
((await this._containsDefaultNotebookWorkspace(this.databaseAccount())) ||
|
||||
userContext.features.enableNotebooks)
|
||||
);
|
||||
this.isNotebookEnabled(false);
|
||||
|
||||
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
|
||||
isNotebookEnabled: this.isNotebookEnabled(),
|
||||
@@ -1810,11 +1806,7 @@ export default class Explorer {
|
||||
|
||||
private async _refreshNotebooksEnabledStateForAccount(): Promise<void> {
|
||||
const authType = userContext.authType;
|
||||
if (
|
||||
authType === AuthType.EncryptedToken ||
|
||||
authType === AuthType.ResourceToken ||
|
||||
authType === AuthType.MasterKey
|
||||
) {
|
||||
if (true) {
|
||||
this.isNotebooksEnabledForAccount(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export function createFetchEdgePairQuery(
|
||||
export function trimGraph(
|
||||
currentRoot: GraphData.GremlinVertex,
|
||||
graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>
|
||||
): void {
|
||||
) {
|
||||
const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId);
|
||||
graphData.unloadAllVertices(importantNodes);
|
||||
|
||||
@@ -150,7 +150,7 @@ export function addRootChildToGraph(
|
||||
root: GraphData.GremlinVertex,
|
||||
child: GraphData.GremlinVertex,
|
||||
graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>
|
||||
): void {
|
||||
) {
|
||||
child._ancestorsId = (root._ancestorsId || []).concat([root.id]);
|
||||
graphData.addVertex(child);
|
||||
createEdgesfromNode(child, graphData);
|
||||
|
||||
@@ -456,7 +456,7 @@ function createEnableNotebooksButton(container: Explorer): CommandButtonComponen
|
||||
onCommandClick: () => container.setupNotebooksPane.openWithTitleAndDescription(label, description),
|
||||
commandButtonLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: !container.isNotebooksEnabledForAccount(),
|
||||
disabled: false,
|
||||
ariaLabel: label,
|
||||
tooltipText: container.isNotebooksEnabledForAccount() ? "" : tooltip,
|
||||
};
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Callout, DefaultButton, DirectionalHint, Stack, TextField } from "office-ui-fabric-react";
|
||||
import React from "react";
|
||||
|
||||
export interface DropdownItem {
|
||||
key: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface SearchableDropdownProps {
|
||||
items: DropdownItem[];
|
||||
onItemSelected: (selectedItem: DropdownItem) => void;
|
||||
defaultSelectedItem?: DropdownItem;
|
||||
placeholder?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface SearchableDropdownState {
|
||||
isDropdownExpanded: boolean;
|
||||
selectedItem: DropdownItem;
|
||||
filteredItems: DropdownItem[];
|
||||
}
|
||||
|
||||
export class SearchableDropdown extends React.Component<SearchableDropdownProps, SearchableDropdownState> {
|
||||
constructor(props: SearchableDropdownProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isDropdownExpanded: false,
|
||||
selectedItem: props.defaultSelectedItem,
|
||||
filteredItems: props.items,
|
||||
};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return this.state.isDropdownExpanded ? (
|
||||
<Stack>
|
||||
<TextField
|
||||
className="dropdownTextField"
|
||||
title={this.props.title}
|
||||
onChange={(event, newInput?: string) => this.onSearchInputChange(newInput)}
|
||||
placeholder={this.props.placeholder}
|
||||
autoFocus
|
||||
/>
|
||||
<Callout
|
||||
isBeakVisible={false}
|
||||
target=".dropdownTextField"
|
||||
directionalHint={DirectionalHint.rightTopEdge}
|
||||
onDismiss={() => this.setState({ isDropdownExpanded: false })}
|
||||
gapSpace={0}
|
||||
>
|
||||
<Stack>
|
||||
{this.state.filteredItems?.map((item) => (
|
||||
<DefaultButton
|
||||
key={item.key}
|
||||
text={item.text}
|
||||
style={{ border: "none", textAlign: "left" }}
|
||||
styles={{ label: { fontWeight: "normal" } }}
|
||||
onClick={() => this.onItemSelected(item)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Callout>
|
||||
</Stack>
|
||||
) : (
|
||||
<TextField
|
||||
className="dropdownTextField"
|
||||
title={this.props.title}
|
||||
onClick={() => this.setState({ isDropdownExpanded: true, filteredItems: this.props.items })}
|
||||
value={this.state.selectedItem?.text || ""}
|
||||
placeholder={this.props.placeholder}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onSearchInputChange(newInput: string): void {
|
||||
const filteredItems = this.props.items.filter((item: DropdownItem) =>
|
||||
item.text.toLocaleLowerCase().includes(newInput.toLocaleLowerCase())
|
||||
);
|
||||
this.setState({ filteredItems });
|
||||
}
|
||||
|
||||
private onItemSelected(item: DropdownItem): void {
|
||||
this.setState({ selectedItem: item, isDropdownExpanded: false });
|
||||
this.props.onItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { Dropdown } from "office-ui-fabric-react/lib/Dropdown";
|
||||
import * as React from "react";
|
||||
import { FunctionComponent } from "react";
|
||||
import { DatabaseAccount } from "../../../Contracts/DataModels";
|
||||
import { DropdownItem, SearchableDropdown } from "./SearchableDropdown";
|
||||
|
||||
interface Props {
|
||||
accounts: DatabaseAccount[];
|
||||
@@ -9,32 +10,30 @@ interface Props {
|
||||
dismissMenu: () => void;
|
||||
}
|
||||
|
||||
export const SwitchAccount: React.FunctionComponent<Props> = ({
|
||||
export const SwitchAccount: FunctionComponent<Props> = ({
|
||||
accounts,
|
||||
setSelectedAccountName,
|
||||
selectedAccount,
|
||||
dismissMenu,
|
||||
}: Props) => {
|
||||
const accountItems = accounts?.map((account) => ({
|
||||
key: account.name,
|
||||
text: account.name,
|
||||
}));
|
||||
|
||||
const defaultAccount = selectedAccount && {
|
||||
key: selectedAccount.name,
|
||||
text: selectedAccount.name,
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchableDropdown
|
||||
items={accountItems}
|
||||
title="Cosmos DB Account Name"
|
||||
defaultSelectedItem={defaultAccount}
|
||||
placeholder={accounts?.length === 0 ? "No Accounts Found" : "Select an Account"}
|
||||
onItemSelected={(accountItem: DropdownItem) => {
|
||||
setSelectedAccountName(accountItem.key);
|
||||
<Dropdown
|
||||
label="Cosmos DB Account Name"
|
||||
className="accountSwitchAccountDropdown"
|
||||
options={accounts?.map((account) => ({
|
||||
key: account.name,
|
||||
text: account.name,
|
||||
data: account,
|
||||
}))}
|
||||
onChange={(_, option) => {
|
||||
setSelectedAccountName(String(option.key));
|
||||
dismissMenu();
|
||||
}}
|
||||
defaultSelectedKey={selectedAccount?.name}
|
||||
placeholder={accounts && accounts.length === 0 ? "No Accounts Found" : "Select an Account"}
|
||||
styles={{
|
||||
callout: "accountSwitchAccountDropdownMenu",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { Dropdown } from "office-ui-fabric-react/lib/Dropdown";
|
||||
import * as React from "react";
|
||||
import { FunctionComponent } from "react";
|
||||
import { Subscription } from "../../../Contracts/DataModels";
|
||||
import { DropdownItem, SearchableDropdown } from "./SearchableDropdown";
|
||||
|
||||
interface Props {
|
||||
subscriptions: Subscription[];
|
||||
@@ -8,28 +9,30 @@ interface Props {
|
||||
setSelectedSubscriptionId: (id: string) => void;
|
||||
}
|
||||
|
||||
export const SwitchSubscription: React.FunctionComponent<Props> = ({
|
||||
export const SwitchSubscription: FunctionComponent<Props> = ({
|
||||
subscriptions,
|
||||
setSelectedSubscriptionId,
|
||||
selectedSubscription,
|
||||
}: Props) => {
|
||||
const subscriptionItems = subscriptions?.map((sub) => ({
|
||||
key: sub.subscriptionId,
|
||||
text: sub.displayName,
|
||||
}));
|
||||
|
||||
const defaultSubscription = selectedSubscription && {
|
||||
key: selectedSubscription.subscriptionId,
|
||||
text: selectedSubscription.displayName,
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchableDropdown
|
||||
items={subscriptionItems}
|
||||
title="Subscription"
|
||||
defaultSelectedItem={defaultSubscription}
|
||||
placeholder={subscriptions?.length === 0 ? "No Subscriptions Found" : "Select a Subscription"}
|
||||
onItemSelected={(subscriptionItem: DropdownItem) => setSelectedSubscriptionId(subscriptionItem.key)}
|
||||
<Dropdown
|
||||
label="Subscription"
|
||||
className="accountSwitchSubscriptionDropdown"
|
||||
options={subscriptions?.map((sub) => {
|
||||
return {
|
||||
key: sub.subscriptionId,
|
||||
text: sub.displayName,
|
||||
data: sub,
|
||||
};
|
||||
})}
|
||||
onChange={(_, option) => {
|
||||
setSelectedSubscriptionId(String(option.key));
|
||||
}}
|
||||
defaultSelectedKey={selectedSubscription?.subscriptionId}
|
||||
placeholder={subscriptions && subscriptions.length === 0 ? "No Subscriptions Found" : "Select a Subscription"}
|
||||
styles={{
|
||||
callout: "accountSwitchSubscriptionDropdownMenu",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ export function getAuthorizationHeader(): ViewModels.AuthorizationTokenHeaderMet
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function decryptJWTToken(token: string) {
|
||||
if (!token) {
|
||||
Logger.logError("Cannot decrypt token: No JWT token found", "AuthorizationUtils/decryptJWTToken");
|
||||
|
||||
@@ -3,7 +3,7 @@ import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { AccountKind, DefaultAccountExperience } from "../Common/Constants";
|
||||
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
||||
import { sendMessage, sendReadyMessage } from "../Common/MessageHandler";
|
||||
import { sendReadyMessage } from "../Common/MessageHandler";
|
||||
import { configContext, Platform, updateConfigContext } from "../ConfigContext";
|
||||
import { ActionType, DataExplorerAction } from "../Contracts/ActionContracts";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
@@ -266,8 +266,6 @@ async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer
|
||||
if (openAction) {
|
||||
handleOpenAction(openAction, explorer.databases(), explorer);
|
||||
}
|
||||
} else if (shouldForwardMessage(message, event.origin)) {
|
||||
sendMessage(message);
|
||||
}
|
||||
},
|
||||
false
|
||||
@@ -277,11 +275,6 @@ async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer
|
||||
});
|
||||
}
|
||||
|
||||
function shouldForwardMessage(message: PortalMessage, messageOrigin: string) {
|
||||
// Only allow forwarding messages from the same origin
|
||||
return messageOrigin === window.document.location.origin && message.type === MessageTypes.TelemetryInfo;
|
||||
}
|
||||
|
||||
function shouldProcessMessage(event: MessageEvent): boolean {
|
||||
if (typeof event.data !== "object") {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user