Compare commits

...

16 Commits

Author SHA1 Message Date
Bala Lakshmi Narayanasami
a1d3574af9 Reverting a snapshot change 2022-07-21 18:16:18 +05:30
Bala Lakshmi Narayanasami
b65b032d6d Fixing test 2022-07-20 21:06:21 +05:30
Bala Lakshmi Narayanasami
fac33509f5 Fixing test 2022-07-20 20:25:41 +05:30
Bala Lakshmi Narayanasami
3b27975895 Fixing test issue 2022-07-20 20:17:28 +05:30
Bala Lakshmi Narayanasami
667d92f050 Moving to client 2022-07-20 19:46:06 +05:30
Bala Lakshmi Narayanasami
6b689d3660 Few more build fixes 2022-07-20 15:34:35 +05:30
Bala Lakshmi Narayanasami
e3e05e832a Fixing test case 2022-07-20 02:02:14 +05:30
Bala Lakshmi Narayanasami
3e0694b8d2 Setting webpack compress true 2022-07-20 01:34:55 +05:30
Bala Lakshmi Narayanasami
d9c93c98c1 Fixing OOM in build 2022-07-20 00:12:11 +05:30
Bala Lakshmi Narayanasami
8e7a8e74a3 Fixing out of memory in build 2022-07-19 23:56:46 +05:30
Bala Lakshmi Narayanasami
ba338dd1b4 Merge branch 'master' of https://github.com/Azure/cosmos-explorer into user/balalakshmin/chatbotinit 2022-07-19 19:48:18 +05:30
Bala Lakshmi Narayanasami
8b4d4f5462 Adding snapshots 2022-07-19 19:43:33 +05:30
Bala Lakshmi Narayanasami
6af40b3a7d Merge branch 'master' of https://github.com/Azure/cosmos-explorer into user/balalakshmin/chatbotinit 2022-06-29 13:57:01 +05:30
Bala Lakshmi Narayanasami
1a5ff2845e Format fix 2022-06-29 11:16:39 +05:30
Bala Lakshmi Narayanasami
dba50edca2 Fixed formatting error 2022-06-28 21:21:25 +05:30
Bala Lakshmi Narayanasami
7364ac8628 Adding chatbot to data explorer 2022-06-27 21:48:14 +05:30
14 changed files with 7447 additions and 32189 deletions

39468
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,7 @@
"@types/node-fetch": "2.5.7",
"applicationinsights": "1.8.0",
"bootstrap": "3.4.1",
"botframework-webchat": "4.14.1",
"canvas": "file:./canvas",
"clean-webpack-plugin": "3.0.0",
"clipboard-copy": "4.0.1",
@@ -191,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",
@@ -232,4 +233,4 @@
"prettier": {
"printWidth": 120
}
}
}

View 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;
}
}

View File

@@ -1,6 +1,7 @@
/**
* React component for Command button component.
*/
import { Icon } from "@fluentui/react";
import * as React from "react";
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
import { KeyCodes } from "../../../Common/Constants";
@@ -259,7 +260,18 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
onClick={(e: React.MouseEvent<HTMLSpanElement>) => this.commandClickCallback(e)}
>
<div className={contentClassName}>
<img className="commandIcon" src={this.props.iconSrc} alt={this.props.iconAlt} />
if (this.props.iconName){" "}
{
<div>
<Icon
styles={{ root: { marginBottom: -3 } }}
className="panelInfoIcon"
iconName={this.props.iconName}
ariaLabel="ChatBot"
/>
</div>
}{" "}
else {<img className="commandIcon" src={this.props.iconSrc} alt={this.props.iconAlt} />}
{CommandButtonComponent.renderLabel(this.props)}
</div>
</span>

View File

@@ -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],

View File

@@ -0,0 +1,41 @@
import { Activity } from "botframework-directlinejs";
import ReactWebChat, { createDirectLine } from "botframework-webchat";
import * as React from "react";
import * as _ from "underscore";
export interface SupportPaneComponentProps {
directLineToken: string;
userToken: string;
subId: string;
rg: string;
accName: string;
}
export class SupportPaneComponent extends React.Component<SupportPaneComponentProps> {
private readonly userId: string = _.uniqueId();
constructor(props: SupportPaneComponentProps) {
super(props);
}
public render(): JSX.Element {
const styleOptions = {
bubbleBackground: "rgba(0, 0, 255, .1)",
bubbleFromUserBackground: "rgba(0, 255, 0, .1)",
};
const directLine = createDirectLine({ token: this.props.directLineToken });
const dl = {
...directLine,
postActivity: (activity: Activity) => {
activity.channelData.token = this.props.userToken;
activity.channelData.subId = this.props.subId;
activity.channelData.rg = this.props.rg;
activity.channelData.accName = this.props.accName;
return directLine.postActivity(activity);
},
};
return <ReactWebChat directLine={dl} userID={this.userId} styleOptions={styleOptions} />;
}
}

View File

@@ -8,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";
@@ -85,6 +86,12 @@ export default class Explorer {
// Notebooks
public notebookManager?: NotebookManager;
public conversationToken: ko.Observable<string>;
public userToken: ko.Observable<string>;
public subId: ko.Observable<string>;
public rg: ko.Observable<string>;
public accName: ko.Observable<string>;
private _isInitializingNotebooks: boolean;
private notebookToImport: {
name: string;
@@ -93,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
@@ -106,6 +115,8 @@ export default class Explorer {
this.queriesClient = new QueriesClient(this);
this.conversationToken = ko.observable<string>();
useSelectedNode.subscribe(() => {
// Make sure switching tabs restores tabs display
this.isTabsContentExpanded(false);
@@ -455,6 +466,22 @@ export default class Explorer {
useDialog.getState().openDialog(resetConfirmationDialogProps);
}
private async generateConversationToken() {
if (userContext.databaseAccount === undefined || userContext.databaseAccount.id === undefined) {
return;
}
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);
}
}
private async _containsDefaultNotebookWorkspace(databaseAccount: DataModels.DatabaseAccount): Promise<boolean> {
if (!databaseAccount) {
return false;
@@ -1270,5 +1297,8 @@ export default class Explorer {
if (useNotebook.getState().isPhoenixNotebooks) {
await this.initNotebooks(userContext.databaseAccount);
}
if (userContext.features.enableChatbot) {
this.generateConversationToken();
}
}
}

View File

@@ -26,6 +26,7 @@ import { userContext } from "../../../UserContext";
import { getCollectionName, getDatabaseName } from "../../../Utils/APITypeUtils";
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import { SupportPaneComponent } from "../../Controls/SupportPaneComponent/SupportPaneComponent";
import Explorer from "../../Explorer";
import { useNotebook } from "../../Notebook/useNotebook";
import { OpenFullScreen } from "../../OpenFullScreen";
@@ -195,6 +196,35 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
const showOpenFullScreen =
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
if (userContext.authType === AuthType.AAD && userContext.features.enableChatbot) {
const label = "Chat Assistant";
const supportPaneButton: CommandButtonComponentProps = {
iconName: "ChatBot",
iconAlt: label,
onCommandClick: () => {
useSidePanel
.getState()
.openSidePanel(
"Chat Assistant (Beta)",
<SupportPaneComponent
directLineToken={container.conversationToken()}
userToken={userContext.authorizationToken}
subId={userContext.subscriptionId}
rg={userContext.resourceGroup}
accName={userContext.databaseAccount.name}
/>
);
},
commandButtonLabel: null,
ariaLabel: label,
tooltipText: label,
hasPopup: true,
disabled: false,
className: "fonticoncustom",
};
buttons.push(supportPaneButton);
}
if (showOpenFullScreen) {
const label = "Open Full Screen";
const fullScreenButton: CommandButtonComponentProps = {

View File

@@ -41,6 +41,9 @@
height: 18px;
width: 18px;
color: rgb(0, 120, 212);
}
.fonticoncustom {
padding-top: 12px;
}
.status {
position: relative;

View File

@@ -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],

View File

@@ -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],

View File

@@ -29,6 +29,7 @@ export type Features = {
readonly mongoProxyEndpoint?: string;
readonly mongoProxyAPIs?: string;
readonly enableThroughputCap: boolean;
readonly enableChatbot?: boolean;
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -90,6 +91,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
notebooksDownBanner: "true" === get("notebooksDownBanner"),
enableThroughputCap: "true" === get("enablethroughputcap"),
enableChatbot: "true" === get("enablechatbot"),
};
}

View File

@@ -21,7 +21,8 @@
"resolveJsonModule": true,
"noEmit": true,
"types": ["jest"],
"baseUrl": "src"
"baseUrl": "src",
"skipLibCheck": true
},
"typedocOptions": {
"entryPoints": [

View File

@@ -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,
},
},
}),