mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-06 03:00:23 +00:00
Compare commits
17 Commits
user/t-tom
...
user/balal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1d3574af9 | ||
|
|
b65b032d6d | ||
|
|
fac33509f5 | ||
|
|
3b27975895 | ||
|
|
667d92f050 | ||
|
|
6b689d3660 | ||
|
|
e3e05e832a | ||
|
|
3e0694b8d2 | ||
|
|
d9c93c98c1 | ||
|
|
8e7a8e74a3 | ||
|
|
ba338dd1b4 | ||
|
|
8b4d4f5462 | ||
|
|
139a9cb22c | ||
|
|
6af40b3a7d | ||
|
|
1a5ff2845e | ||
|
|
dba50edca2 | ||
|
|
7364ac8628 |
@@ -1,23 +0,0 @@
|
||||
@import "./Common/Constants";
|
||||
|
||||
.chat {
|
||||
display: grid;
|
||||
justify-content: right;
|
||||
padding: 10px;
|
||||
|
||||
.chatButton {
|
||||
margin: 0 45px 15px 0;
|
||||
border: 10px;
|
||||
min-height: 44px;
|
||||
|
||||
&:focus {
|
||||
outline: 0px;
|
||||
}
|
||||
|
||||
>span {
|
||||
font-size: 17px;
|
||||
font-family: @DataExplorerFont;
|
||||
color: @AccentLow;
|
||||
}
|
||||
}
|
||||
}
|
||||
51354
package-lock.json
generated
51354
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "3.16.2",
|
||||
"@azure/cosmos": "3.16.3",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
@@ -128,7 +128,6 @@
|
||||
"@types/react-notification-system": "0.2.39",
|
||||
"@types/react-redux": "7.1.7",
|
||||
"@types/react-splitter-layout": "3.0.1",
|
||||
"@types/react-youtube": "7.10.0",
|
||||
"@types/sanitize-html": "1.27.2",
|
||||
"@types/sinon": "2.3.3",
|
||||
"@types/styled-components": "5.1.1",
|
||||
@@ -193,7 +192,7 @@
|
||||
"start": "webpack serve --mode development",
|
||||
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
|
||||
"build:dataExplorer:ci": "npm run build:ci",
|
||||
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
|
||||
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod --max_old_space_size=4096 && npm run copyToConsumers",
|
||||
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
|
||||
"pack:prod": "webpack --mode production",
|
||||
"pack:fast": "webpack --mode development --progress",
|
||||
|
||||
26
src/Chatbot/ChatbotClient.ts
Normal file
26
src/Chatbot/ChatbotClient.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { HttpHeaders } from "../Common/Constants";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
|
||||
export class ChatbotClient {
|
||||
public async getConversationToken(): Promise<{ conversationId: string; token: string; expires_in: number }> {
|
||||
const url = `${configContext.JUNO_ENDPOINT}/api/chatbot/bot${userContext.databaseAccount.id}/conversationToken`;
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
const response = await window.fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
[authorizationHeader.header]: authorizationHeader.token,
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.json());
|
||||
}
|
||||
|
||||
const tokenResponse: { conversationId: string; token: string; expires_in: number } = await response.json();
|
||||
return tokenResponse;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
allowedJunoOrigins,
|
||||
allowedMongoBackendEndpoints,
|
||||
allowedMsalRedirectEndpoints,
|
||||
validateEndpoint
|
||||
validateEndpoint,
|
||||
} from "Utils/EndpointValidation";
|
||||
|
||||
export enum Platform {
|
||||
@@ -190,4 +190,3 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
}
|
||||
|
||||
export { configContext };
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { IIconProps } from '@fluentui/react';
|
||||
import { PrimaryButton } from '@fluentui/react/lib/Button';
|
||||
import { AuthType } from 'AuthType';
|
||||
import { SupportPaneComponent } from 'Explorer/Controls/SupportPaneComponent/SupportPaneComponent';
|
||||
import Explorer from 'Explorer/Explorer';
|
||||
import { useSidePanel } from 'hooks/useSidePanel';
|
||||
import * as React from 'react';
|
||||
import { userContext } from 'UserContext';
|
||||
|
||||
export interface ChatButtonProps {
|
||||
container: Explorer;
|
||||
}
|
||||
|
||||
const chatIcon: IIconProps = { iconName: 'ChatSolid', style: { marginRight: 10 } };
|
||||
|
||||
|
||||
|
||||
|
||||
export const ChatButtonAction: React.FunctionComponent<ChatButtonProps> = props => {
|
||||
const { container } = props;
|
||||
if (userContext.authType === AuthType.AAD && userContext.features.enableChatbot) {
|
||||
return (
|
||||
<PrimaryButton className={"chatButton"} iconProps={chatIcon} onClick={() => {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Chat Assistant (Beta)",
|
||||
<SupportPaneComponent
|
||||
directLineToken={container.conversationToken()}
|
||||
userToken={userContext.authorizationToken}
|
||||
subId={userContext.subscriptionId}
|
||||
rg={userContext.resourceGroup}
|
||||
accName={userContext.databaseAccount.name}
|
||||
/>
|
||||
);
|
||||
}}>
|
||||
<span> Help? </span>
|
||||
</PrimaryButton>
|
||||
);
|
||||
}
|
||||
return <div></div>
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* React component for Command button component.
|
||||
*/
|
||||
import { Icon, IIconStyles } from "@fluentui/react";
|
||||
import { Icon } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||
import { KeyCodes } from "../../../Common/Constants";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import * as StringUtils from "../../../Utils/StringUtils";
|
||||
|
||||
/**
|
||||
* Options for this component
|
||||
*/
|
||||
@@ -243,7 +244,6 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
||||
if (this.props.children && this.props.children.length > 0) {
|
||||
contentClassName += " hasHiddenItems";
|
||||
}
|
||||
const iconButtonStyles: Partial<IIconStyles> = { root: { marginBottom: -3 } };
|
||||
|
||||
return (
|
||||
<div className="commandButtonReact">
|
||||
@@ -264,7 +264,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
||||
{
|
||||
<div>
|
||||
<Icon
|
||||
styles={iconButtonStyles}
|
||||
styles={{ root: { marginBottom: -3 } }}
|
||||
className="panelInfoIcon"
|
||||
iconName={this.props.iconName}
|
||||
ariaLabel="ChatBot"
|
||||
|
||||
@@ -30,6 +30,8 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"chatbotClient": ChatbotClient {},
|
||||
"conversationToken": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
@@ -106,6 +108,8 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"chatbotClient": ChatbotClient {},
|
||||
"conversationToken": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Activity } from "botframework-directlinejs";
|
||||
import ReactWebChat from "botframework-webchat";
|
||||
import React from "react";
|
||||
import ReactWebChat, { createDirectLine } from "botframework-webchat";
|
||||
import * as React from "react";
|
||||
import * as _ from "underscore";
|
||||
|
||||
const BotFramework = require('botframework-webchat');
|
||||
|
||||
export interface SupportPaneComponentProps {
|
||||
directLineToken: string;
|
||||
userToken: string;
|
||||
@@ -24,11 +21,9 @@ export class SupportPaneComponent extends React.Component<SupportPaneComponentPr
|
||||
const styleOptions = {
|
||||
bubbleBackground: "rgba(0, 0, 255, .1)",
|
||||
bubbleFromUserBackground: "rgba(0, 255, 0, .1)",
|
||||
suggestedActionLayout: 'flow',
|
||||
markdownRespectCRLF: true,
|
||||
};
|
||||
|
||||
const directLine = BotFramework.createDirectLine({ token: this.props.directLineToken });
|
||||
const directLine = createDirectLine({ token: this.props.directLineToken });
|
||||
const dl = {
|
||||
...directLine,
|
||||
postActivity: (activity: Activity) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Link } from "@fluentui/react/lib/Link";
|
||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { IGalleryItem } from "Juno/JunoClient";
|
||||
import * as ko from "knockout";
|
||||
import React from "react";
|
||||
@@ -9,6 +8,7 @@ import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValid
|
||||
import shallow from "zustand/shallow";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||
import { ChatbotClient } from "../Chatbot/ChatbotClient";
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook, PoolIdType } from "../Common/Constants";
|
||||
import { readCollection } from "../Common/dataAccess/readCollection";
|
||||
@@ -35,7 +35,6 @@ import { userContext } from "../UserContext";
|
||||
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
||||
import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||
import { listByDatabaseAccount } from "../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
|
||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
import { stringToBlob } from "../Utils/BlobUtils";
|
||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||
@@ -101,12 +100,14 @@ export default class Explorer {
|
||||
|
||||
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
||||
private phoenixClient: PhoenixClient;
|
||||
private chatbotClient: ChatbotClient;
|
||||
constructor() {
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
});
|
||||
this._isInitializingNotebooks = false;
|
||||
this.phoenixClient = new PhoenixClient();
|
||||
this.chatbotClient = new ChatbotClient();
|
||||
useNotebook.subscribe(
|
||||
() => this.refreshCommandBarButtons(),
|
||||
(state) => state.isNotebooksEnabledForAccount
|
||||
@@ -116,8 +117,6 @@ export default class Explorer {
|
||||
|
||||
this.conversationToken = ko.observable<string>();
|
||||
|
||||
this.generateConversationToken();
|
||||
|
||||
useSelectedNode.subscribe(() => {
|
||||
// Make sure switching tabs restores tabs display
|
||||
this.isTabsContentExpanded(false);
|
||||
@@ -468,26 +467,18 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
private async generateConversationToken() {
|
||||
const url = `${configContext.JUNO_ENDPOINT}/api/chatbot/bot${userContext.databaseAccount.id}/conversationToken`;
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
[Constants.HttpHeaders.authorization]: authorizationHeader.token,
|
||||
Accept: "application/json",
|
||||
[Constants.HttpHeaders.contentType]: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.json());
|
||||
if (userContext.databaseAccount === undefined || userContext.databaseAccount.id === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenResponse: { conversationId: string; token: string; expires_in: number } = await response.json();
|
||||
this.conversationToken(tokenResponse?.token);
|
||||
if (tokenResponse?.expires_in) {
|
||||
setTimeout(() => this.generateConversationToken(), (tokenResponse?.expires_in - 1000) * 1000);
|
||||
try {
|
||||
const tokenResponse = await this.chatbotClient.getConversationToken();
|
||||
this.conversationToken(tokenResponse?.token);
|
||||
if (tokenResponse?.expires_in) {
|
||||
setTimeout(() => this.generateConversationToken(), (tokenResponse?.expires_in - 1000) * 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Exception while getting conversation token");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1306,5 +1297,8 @@ export default class Explorer {
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.initNotebooks(userContext.databaseAccount);
|
||||
}
|
||||
if (userContext.features.enableChatbot) {
|
||||
this.generateConversationToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
.fonticoncustom {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
color: @SelectionHigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
||||
"container": Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"chatbotClient": ChatbotClient {},
|
||||
"conversationToken": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
||||
@@ -9,6 +9,8 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
||||
Explorer {
|
||||
"_isInitializingNotebooks": false,
|
||||
"_resetNotebookWorkspace": [Function],
|
||||
"chatbotClient": ChatbotClient {},
|
||||
"conversationToken": [Function],
|
||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||
"isTabsContentExpanded": [Function],
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// CSS Dependencies
|
||||
import { initializeIcons } from "@fluentui/react";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import { ChatButtonAction } from "Explorer/Controls/ChatButton/ChatButtonComponent";
|
||||
import { QuickstartCarousel } from "Explorer/Tutorials/QuickstartCarousel";
|
||||
import { QuickstartTutorial } from "Explorer/Tutorials/QuickstartTutorial";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
@@ -18,7 +17,6 @@ import "../externals/jquery.typeahead.min.js";
|
||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||
import "../images/favicon.ico";
|
||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||
import "../less/chat.less";
|
||||
import "../less/documentDB.less";
|
||||
import "../less/forms.less";
|
||||
import "../less/infobox.less";
|
||||
@@ -105,9 +103,6 @@ const App: React.FunctionComponent = () => {
|
||||
<Tabs explorer={explorer} />
|
||||
</div>
|
||||
{/* Collections Tree and Tabs - End */}
|
||||
<div className="chat">
|
||||
<ChatButtonAction container={explorer} />
|
||||
</div>
|
||||
<div
|
||||
className="dataExplorerErrorConsoleContainer"
|
||||
role="contentinfo"
|
||||
@@ -116,7 +111,6 @@ const App: React.FunctionComponent = () => {
|
||||
>
|
||||
<NotificationConsole />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<SidePanel />
|
||||
<Dialog />
|
||||
|
||||
@@ -29,10 +29,8 @@ export type Features = {
|
||||
readonly mongoProxyEndpoint?: string;
|
||||
readonly mongoProxyAPIs?: string;
|
||||
readonly enableThroughputCap: boolean;
|
||||
readonly enableNewQuickstart: boolean;
|
||||
readonly enableChatbot?: boolean;
|
||||
|
||||
|
||||
// can be set via both flight and feature flag
|
||||
autoscaleDefault: boolean;
|
||||
partitionKeyDefault: boolean;
|
||||
@@ -93,7 +91,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
||||
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
||||
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
||||
enableThroughputCap: "true" === get("enablethroughputcap"),
|
||||
enableNewQuickstart: "true" === get("enablenewquickstart"),
|
||||
enableChatbot: "true" === get("enablechatbot"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"types": ["jest"],
|
||||
"baseUrl": "src"
|
||||
"baseUrl": "src",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"typedocOptions": {
|
||||
"entryPoints": [
|
||||
|
||||
@@ -244,10 +244,10 @@ module.exports = function (_env = {}, argv = {}) {
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
// These options increase our initial bundle size by ~5% but the builds are significantly faster and won't run out of memory
|
||||
compress: false,
|
||||
compress: true,
|
||||
mangle: {
|
||||
keep_fnames: true,
|
||||
keep_classnames: true,
|
||||
keep_fnames: false,
|
||||
keep_classnames: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user