mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 17:01:13 +00:00
Remove enableGallery feature flag (#68)
* Remove enableGallery feature flag * Fix bugs * Add tests to increase coverage * Move favorites functionality behind feature.enableGalleryPublish flag * Show code cells in NotebookViewer * Use cosmos db logo as persona image for sample notebook gallery cards * Update gallery card snapshot to fix test
This commit is contained in:
@@ -48,7 +48,6 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
||||
{ key: "feature.dataexplorerexecutesproc", label: "Execute stored procedure", value: "true" },
|
||||
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
|
||||
{ key: "feature.enablettl", label: "Enable TTL", value: "true" },
|
||||
{ key: "feature.enablegallery", label: "Enable Notebook Gallery", value: "true" },
|
||||
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
|
||||
{ key: "feature.canexceedmaximumvalue", label: "Can exceed max value", value: "true" },
|
||||
{
|
||||
|
||||
@@ -157,14 +157,14 @@ exports[`Feature panel renders all flags 1`] = `
|
||||
/>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
key="feature.enablegallery"
|
||||
label="Enable Notebook Gallery"
|
||||
key="feature.enablegallerypublish"
|
||||
label="Enable Notebook Gallery Publishing"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
key="feature.enablegallerypublish"
|
||||
label="Enable Notebook Gallery Publishing"
|
||||
key="feature.canexceedmaximumvalue"
|
||||
label="Can exceed max value"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -172,12 +172,6 @@ exports[`Feature panel renders all flags 1`] = `
|
||||
className="checkboxRow"
|
||||
horizontalAlign="space-between"
|
||||
>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
key="feature.canexceedmaximumvalue"
|
||||
label="Can exceed max value"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
<StyledCheckboxBase
|
||||
checked={false}
|
||||
key="feature.enablefixedcollectionwithsharedthroughput"
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import * as React from "react";
|
||||
import { IGalleryItem } from "../../../../Juno/JunoClient";
|
||||
import { FileSystemUtil } from "../../../Notebook/FileSystemUtil";
|
||||
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
|
||||
|
||||
export interface GalleryCardComponentProps {
|
||||
data: IGalleryItem;
|
||||
@@ -55,7 +56,11 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
return (
|
||||
<Card aria-label="Notebook Card" tokens={GalleryCardComponent.cardTokens} onClick={this.props.onClick}>
|
||||
<Card.Item>
|
||||
<Persona text={this.props.data.author} secondaryText={dateString} />
|
||||
<Persona
|
||||
imageUrl={this.props.data.isSample && CosmosDBLogo}
|
||||
text={this.props.data.author}
|
||||
secondaryText={dateString}
|
||||
/>
|
||||
</Card.Item>
|
||||
|
||||
<Card.Item fill>
|
||||
@@ -89,15 +94,9 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
</Card.Section>
|
||||
|
||||
<Card.Section horizontal styles={{ root: { alignItems: "flex-end" } }}>
|
||||
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
|
||||
<Icon iconName="RedEye" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.views}
|
||||
</Text>
|
||||
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
|
||||
<Icon iconName="Download" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.downloads}
|
||||
</Text>
|
||||
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
|
||||
<Icon iconName="Heart" styles={{ root: { verticalAlign: "middle" } }} /> {this.props.data.favorites}
|
||||
</Text>
|
||||
{this.generateIconText("RedEye", this.props.data.views.toString())}
|
||||
{this.generateIconText("Download", this.props.data.downloads.toString())}
|
||||
{this.props.isFavorite !== undefined && this.generateIconText("Heart", this.props.data.favorites.toString())}
|
||||
</Card.Section>
|
||||
|
||||
<Card.Item>
|
||||
@@ -105,17 +104,18 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
</Card.Item>
|
||||
|
||||
<Card.Section horizontal styles={{ root: { marginTop: 0 } }}>
|
||||
{this.generateIconButtonWithTooltip(
|
||||
this.props.isFavorite ? "HeartFill" : "Heart",
|
||||
this.props.isFavorite ? "Unlike" : "Like",
|
||||
this.props.isFavorite ? this.onUnfavoriteClick : this.onFavoriteClick
|
||||
)}
|
||||
{this.props.isFavorite !== undefined &&
|
||||
this.generateIconButtonWithTooltip(
|
||||
this.props.isFavorite ? "HeartFill" : "Heart",
|
||||
this.props.isFavorite ? "Unlike" : "Like",
|
||||
this.props.isFavorite ? this.onUnfavoriteClick : this.onFavoriteClick
|
||||
)}
|
||||
|
||||
{this.generateIconButtonWithTooltip("Download", "Download", this.onDownloadClick)}
|
||||
|
||||
{this.props.showDelete && (
|
||||
<div style={{ width: "100%", textAlign: "right" }}>
|
||||
{this.generateIconButtonWithTooltip("Delete", "Remove", this.props.onDeleteClick)}
|
||||
{this.generateIconButtonWithTooltip("Delete", "Remove", this.onDeleteClick)}
|
||||
</div>
|
||||
)}
|
||||
</Card.Section>
|
||||
@@ -123,6 +123,14 @@ export class GalleryCardComponent extends React.Component<GalleryCardComponentPr
|
||||
);
|
||||
}
|
||||
|
||||
private generateIconText = (iconName: string, text: string): JSX.Element => {
|
||||
return (
|
||||
<Text variant="tiny" styles={{ root: { color: "#ccc" } }}>
|
||||
<Icon iconName={iconName} styles={{ root: { verticalAlign: "middle" } }} /> {text}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* Fluent UI doesn't support tooltips on IconButtons out of the box. In the meantime the recommendation is
|
||||
* to do the following (from https://developer.microsoft.com/en-us/fluentui#/controls/web/button)
|
||||
|
||||
@@ -14,6 +14,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
>
|
||||
<CardItem>
|
||||
<StyledPersonaBase
|
||||
imageUrl={false}
|
||||
secondaryText="Invalid Date"
|
||||
text="author"
|
||||
/>
|
||||
@@ -256,6 +257,7 @@ exports[`GalleryCardComponent renders 1`] = `
|
||||
"iconName": "Delete",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
title="Remove"
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
|
||||
@@ -75,27 +75,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
|
||||
private static readonly mostViewedText = "Most viewed";
|
||||
private static readonly mostDownloadedText = "Most downloaded";
|
||||
private static readonly mostFavoritedText = "Most favorited";
|
||||
private static readonly mostFavoritedText = "Most liked";
|
||||
private static readonly mostRecentText = "Most recent";
|
||||
|
||||
private static readonly sortingOptions: IDropdownOption[] = [
|
||||
{
|
||||
key: SortBy.MostViewed,
|
||||
text: GalleryViewerComponent.mostViewedText
|
||||
},
|
||||
{
|
||||
key: SortBy.MostDownloaded,
|
||||
text: GalleryViewerComponent.mostDownloadedText
|
||||
},
|
||||
{
|
||||
key: SortBy.MostFavorited,
|
||||
text: GalleryViewerComponent.mostFavoritedText
|
||||
},
|
||||
{
|
||||
key: SortBy.MostRecent,
|
||||
text: GalleryViewerComponent.mostRecentText
|
||||
}
|
||||
];
|
||||
private readonly sortingOptions: IDropdownOption[];
|
||||
|
||||
private sampleNotebooks: IGalleryItem[];
|
||||
private publicNotebooks: IGalleryItem[];
|
||||
@@ -118,8 +101,29 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
dialogProps: undefined
|
||||
};
|
||||
|
||||
this.sortingOptions = [
|
||||
{
|
||||
key: SortBy.MostViewed,
|
||||
text: GalleryViewerComponent.mostViewedText
|
||||
},
|
||||
{
|
||||
key: SortBy.MostDownloaded,
|
||||
text: GalleryViewerComponent.mostDownloadedText
|
||||
},
|
||||
{
|
||||
key: SortBy.MostRecent,
|
||||
text: GalleryViewerComponent.mostRecentText
|
||||
}
|
||||
];
|
||||
if (this.props.container?.isGalleryPublishEnabled()) {
|
||||
this.sortingOptions.push({
|
||||
key: SortBy.MostFavorited,
|
||||
text: GalleryViewerComponent.mostFavoritedText
|
||||
});
|
||||
}
|
||||
|
||||
this.loadTabContent(this.state.selectedTab, this.state.searchText, this.state.sortBy, false);
|
||||
if (this.props.container) {
|
||||
if (this.props.container?.isGalleryPublishEnabled()) {
|
||||
this.loadFavoriteNotebooks(this.state.searchText, this.state.sortBy, false); // Need this to show correct favorite button state
|
||||
}
|
||||
}
|
||||
@@ -131,16 +135,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
public render(): JSX.Element {
|
||||
const tabs: GalleryTabInfo[] = [this.createTab(GalleryTab.OfficialSamples, this.state.sampleNotebooks)];
|
||||
|
||||
if (this.props.container) {
|
||||
if (this.props.container.isGalleryPublishEnabled()) {
|
||||
tabs.push(this.createTab(GalleryTab.PublicGallery, this.state.publicNotebooks));
|
||||
}
|
||||
|
||||
if (this.props.container?.isGalleryPublishEnabled()) {
|
||||
tabs.push(this.createTab(GalleryTab.PublicGallery, this.state.publicNotebooks));
|
||||
tabs.push(this.createTab(GalleryTab.Favorites, this.state.favoriteNotebooks));
|
||||
|
||||
if (this.props.container.isGalleryPublishEnabled()) {
|
||||
tabs.push(this.createTab(GalleryTab.Published, this.state.publishedNotebooks));
|
||||
}
|
||||
tabs.push(this.createTab(GalleryTab.Published, this.state.publishedNotebooks));
|
||||
}
|
||||
|
||||
const pivotProps: IPivotProps = {
|
||||
@@ -189,11 +187,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
<Label>Sort by</Label>
|
||||
</Stack.Item>
|
||||
<Stack.Item styles={{ root: { minWidth: 200 } }}>
|
||||
<Dropdown
|
||||
options={GalleryViewerComponent.sortingOptions}
|
||||
selectedKey={this.state.sortBy}
|
||||
onChange={this.onDropdownChange}
|
||||
/>
|
||||
<Dropdown options={this.sortingOptions} selectedKey={this.state.sortBy} onChange={this.onDropdownChange} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
@@ -405,7 +399,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
};
|
||||
|
||||
private onRenderCell = (data?: IGalleryItem): JSX.Element => {
|
||||
const isFavorite = this.favoriteNotebooks?.find(item => item.id === data.id) !== undefined;
|
||||
let isFavorite: boolean;
|
||||
if (this.props.container?.isGalleryPublishEnabled()) {
|
||||
isFavorite = this.favoriteNotebooks?.find(item => item.id === data.id) !== undefined;
|
||||
}
|
||||
const props: GalleryCardComponentProps = {
|
||||
data,
|
||||
isFavorite,
|
||||
@@ -434,7 +431,8 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
||||
[GalleryUtils.NotebookViewerParams.GalleryItemId]: data.id
|
||||
});
|
||||
|
||||
window.open(`/notebookViewer.html?${params.toString()}`);
|
||||
const location = new URL("./notebookViewer.html", window.location.href).href;
|
||||
window.open(`${location}?${params.toString()}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -67,10 +67,6 @@ exports[`GalleryViewerComponent renders 1`] = `
|
||||
"key": 1,
|
||||
"text": "Most downloaded",
|
||||
},
|
||||
Object {
|
||||
"key": 2,
|
||||
"text": "Most favorited",
|
||||
},
|
||||
Object {
|
||||
"key": 3,
|
||||
"text": "Most recent",
|
||||
|
||||
@@ -44,11 +44,15 @@ export class NotebookMetadataComponent extends React.Component<NotebookMetadataC
|
||||
{FileSystemUtil.stripExtension(this.props.data.name, "ipynb")}
|
||||
</Text>
|
||||
<Text>
|
||||
<IconButton
|
||||
iconProps={{ iconName: this.props.isFavorite ? "HeartFill" : "Heart" }}
|
||||
onClick={this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick}
|
||||
/>
|
||||
{this.props.data.favorites} likes
|
||||
{this.props.isFavorite !== undefined && (
|
||||
<>
|
||||
<IconButton
|
||||
iconProps={{ iconName: this.props.isFavorite ? "HeartFill" : "Heart" }}
|
||||
onClick={this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick}
|
||||
/>
|
||||
{this.props.data.favorites} likes
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
<PrimaryButton text={this.props.downloadButtonText} onClick={this.props.onDownloadClick} />
|
||||
</Stack>
|
||||
|
||||
@@ -129,7 +129,7 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
|
||||
<></>
|
||||
)}
|
||||
|
||||
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, { hideInputs: true })}
|
||||
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, { hideInputs: false })}
|
||||
|
||||
{this.state.dialogProps && <DialogComponent {...this.state.dialogProps} />}
|
||||
</div>
|
||||
|
||||
@@ -83,6 +83,7 @@ import { UploadFilePane } from "./Panes/UploadFilePane";
|
||||
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
||||
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
||||
import { toRawContentUri, fromContentUri } from "../Utils/GitHubUtils";
|
||||
|
||||
BindingHandlersRegisterer.registerBindingHandlers();
|
||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||
@@ -199,7 +200,6 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
public publishNotebookPaneAdapter: ReactAdapter;
|
||||
|
||||
// features
|
||||
public isGalleryEnabled: ko.Computed<boolean>;
|
||||
public isGalleryPublishEnabled: ko.Computed<boolean>;
|
||||
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||
public isPublishNotebookPaneEnabled: ko.Observable<boolean>;
|
||||
@@ -244,7 +244,10 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
private _isInitializingSparkConnectionInfo: boolean;
|
||||
private notebookBasePath: ko.Observable<string>;
|
||||
private _arcadiaManager: ViewModels.ArcadiaResourceManager;
|
||||
private _filePathToImportAndOpen: string;
|
||||
private notebookToImport: {
|
||||
name: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
// React adapters
|
||||
private commandBarComponentAdapter: CommandBarComponentAdapter;
|
||||
@@ -344,7 +347,7 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
await this.initNotebooks(this.databaseAccount());
|
||||
const workspaces = await this._getArcadiaWorkspaces();
|
||||
this.arcadiaWorkspaces(workspaces);
|
||||
} else if (this._filePathToImportAndOpen) {
|
||||
} else if (this.notebookToImport) {
|
||||
// if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
|
||||
this._openSetupNotebooksPaneForQuickstart();
|
||||
}
|
||||
@@ -405,7 +408,6 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
this.shouldShowShareDialogContents = ko.observable<boolean>(false);
|
||||
this.shouldShowDataAccessExpiryDialog = ko.observable<boolean>(false);
|
||||
this.shouldShowContextSwitchPrompt = ko.observable<boolean>(false);
|
||||
this.isGalleryEnabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableGallery));
|
||||
this.isGalleryPublishEnabled = ko.computed<boolean>(() =>
|
||||
this.isFeatureEnabled(Constants.Features.enableGalleryPublish)
|
||||
);
|
||||
@@ -2490,10 +2492,6 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||
|
||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
||||
if (this._filePathToImportAndOpen === path) {
|
||||
this._filePathToImportAndOpen = null; // we don't want to try opening this path again
|
||||
}
|
||||
|
||||
const existingItem = _.find(parent.children, node => node.name === name);
|
||||
if (existingItem) {
|
||||
return this.openNotebook(existingItem);
|
||||
@@ -2504,24 +2502,27 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
return this.openNotebook(uploadedItem);
|
||||
}
|
||||
|
||||
this._filePathToImportAndOpen = path; // we'll try opening this path later on
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
public async importAndOpenFromGallery(name: string, content: string): Promise<boolean> {
|
||||
public async importAndOpenContent(name: string, content: string): Promise<boolean> {
|
||||
const parent = this.resourceTree.myNotebooksContentRoot;
|
||||
|
||||
if (parent && parent.children && this.isNotebookEnabled() && this.notebookManager?.notebookClient) {
|
||||
if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) {
|
||||
this.notebookToImport = undefined; // we don't want to try opening this notebook again
|
||||
}
|
||||
|
||||
const existingItem = _.find(parent.children, node => node.name === name);
|
||||
if (existingItem) {
|
||||
this.showOkModalDialog("Download failed", "Notebook with the same name already exists.");
|
||||
return Promise.reject(false);
|
||||
return this.openNotebook(existingItem);
|
||||
}
|
||||
|
||||
const uploadedItem = await this.uploadFile(name, content, parent);
|
||||
return this.openNotebook(uploadedItem);
|
||||
}
|
||||
|
||||
this.notebookToImport = { name, content }; // we'll try opening this notebook later on
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
@@ -2927,8 +2928,8 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
}
|
||||
|
||||
await this.resourceTree.initialize();
|
||||
if (this._filePathToImportAndOpen) {
|
||||
this.importAndOpen(this._filePathToImportAndOpen);
|
||||
if (this.notebookToImport) {
|
||||
this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3351,6 +3352,21 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
this._openSetupNotebooksPaneForQuickstart();
|
||||
}
|
||||
|
||||
this.importAndOpen(path);
|
||||
// We still use github urls like https://github.com/Azure-Samples/cosmos-notebooks/blob/master/CSharp_quickstarts/GettingStarted_CSharp.ipynb
|
||||
// when launching a notebook quickstart from Portal. In future we should just use gallery id and use Juno to fetch instead of directly
|
||||
// calling GitHub. For now convert this url to a raw url and download content.
|
||||
const gitHubInfo = fromContentUri(path);
|
||||
if (gitHubInfo) {
|
||||
const rawUrl = toRawContentUri(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.branch, gitHubInfo.path);
|
||||
const response = await fetch(rawUrl);
|
||||
if (response.status === Constants.HttpStatusCodes.OK) {
|
||||
this.notebookToImport = {
|
||||
name: NotebookUtil.getName(path),
|
||||
content: await response.text()
|
||||
};
|
||||
|
||||
this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
mockExplorer.isGalleryEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
@@ -82,7 +81,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
mockExplorer.isGalleryEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
@@ -163,7 +161,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||
mockExplorer.isGalleryEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
@@ -250,7 +247,6 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||
mockExplorer.isRunningOnNationalCloud = ko.observable(false);
|
||||
mockExplorer.isGalleryEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||
mockExplorer.notebookManager = new NotebookManager();
|
||||
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { JunoClient } from "../../Juno/JunoClient";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
||||
import { GitHubClient } from "../../GitHub/GitHubClient";
|
||||
import { config } from "../../Config";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import { HttpStatusCodes, Areas } from "../../Common/Constants";
|
||||
import { GitHubReposPane } from "../Panes/GitHubReposPane";
|
||||
@@ -54,7 +53,7 @@ export default class NotebookManager {
|
||||
this.junoClient = new JunoClient(this.params.container.databaseAccount);
|
||||
|
||||
this.gitHubOAuthService = new GitHubOAuthService(this.junoClient);
|
||||
this.gitHubClient = new GitHubClient(config.AZURESAMPLESCOSMOSDBPAT, this.onGitHubClientError);
|
||||
this.gitHubClient = new GitHubClient(this.onGitHubClientError);
|
||||
this.gitHubReposPane = new GitHubReposPane({
|
||||
documentClientUtility: this.params.container.documentClientUtility,
|
||||
id: "gitHubReposPane",
|
||||
@@ -91,7 +90,7 @@ export default class NotebookManager {
|
||||
}
|
||||
|
||||
this.gitHubOAuthService.getTokenObservable().subscribe(token => {
|
||||
this.gitHubClient.setToken(token?.access_token ? token.access_token : config.AZURESAMPLESCOSMOSDBPAT);
|
||||
this.gitHubClient.setToken(token?.access_token);
|
||||
|
||||
if (this.gitHubReposPane.visible()) {
|
||||
this.gitHubReposPane.open();
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
import { IGitHubRepo, IGitHubBranch } from "../../GitHub/GitHubClient";
|
||||
|
||||
export const SamplesRepo: IGitHubRepo = {
|
||||
name: "cosmos-notebooks",
|
||||
owner: "Azure-Samples",
|
||||
private: false
|
||||
};
|
||||
|
||||
export const SamplesBranch: IGitHubBranch = {
|
||||
name: "master"
|
||||
};
|
||||
|
||||
export const isSamplesCall = (owner: string, repo: string, branch?: string): boolean => {
|
||||
return owner === SamplesRepo.owner && repo === SamplesRepo.name && (!branch || branch === SamplesBranch.name);
|
||||
};
|
||||
|
||||
// GitHub API calls have a rate limit of 5000 requests per hour. So if we get high traffic on Data Explorer
|
||||
// loading samples exceed that limit. Using this hard coded response for samples until we fix that.
|
||||
export const SamplesContentsQueryResponse = {
|
||||
repository: {
|
||||
owner: {
|
||||
login: "Azure-Samples"
|
||||
},
|
||||
name: "cosmos-notebooks",
|
||||
isPrivate: false,
|
||||
ref: {
|
||||
name: "master",
|
||||
target: {
|
||||
history: {
|
||||
nodes: [
|
||||
{
|
||||
oid: "cda7facb9e039b173f3376200c26c859896e7974",
|
||||
message:
|
||||
"Merge pull request #45 from Azure-Samples/users/deborahc/pythonSampleUpdates\n\nAdd bokeh version to notebook",
|
||||
committer: {
|
||||
date: "2020-05-28T11:28:01-07:00"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
object: {
|
||||
entries: [
|
||||
{
|
||||
name: ".github",
|
||||
type: "tree",
|
||||
object: {}
|
||||
},
|
||||
{
|
||||
name: ".gitignore",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "3e759b75bf455ac809d0987d369aab89137b5689",
|
||||
byteSize: 5582
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "1. GettingStarted.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "0732ff5366e4aefdc4c378c61cbd968664f0acec",
|
||||
byteSize: 3933
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "2. Visualization.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "6b16b0740a77afdd38a95bc6c3ebd0f2f17d9465",
|
||||
byteSize: 820317
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "3. RequestUnits.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "252b79a4adc81e9f2ffde453231b695d75e270e8",
|
||||
byteSize: 9490
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "4. Indexing.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "e10dd67bd1c55c345226769e4f80e43659ef9cd5",
|
||||
byteSize: 10394
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "5. StoredProcedures.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "949941949920de4d2d111149e2182e9657cc8134",
|
||||
byteSize: 11818
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "6. GlobalDistribution.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "b91c31dacacbc9e35750d9054063dda4a5309f3b",
|
||||
byteSize: 11375
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "7. IoTAnomalyDetection.ipynb",
|
||||
type: "blob",
|
||||
object: {
|
||||
oid: "82057ae52a67721a5966e2361317f5dfbd0ee595",
|
||||
byteSize: 377939
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "All_API_quickstarts",
|
||||
type: "tree",
|
||||
object: {}
|
||||
},
|
||||
{
|
||||
name: "CSharp_quickstarts",
|
||||
type: "tree",
|
||||
object: {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -96,7 +96,6 @@ export class ExplorerStub implements ViewModels.Explorer {
|
||||
public setupNotebooksPane: SetupNotebooksPane;
|
||||
public setupSparkClusterPane: ViewModels.ContextualPane;
|
||||
public manageSparkClusterPane: ViewModels.ContextualPane;
|
||||
public isGalleryEnabled: ko.Computed<boolean>;
|
||||
public isGalleryPublishEnabled: ko.Computed<boolean>;
|
||||
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||
public isPublishNotebookPaneEnabled: ko.Observable<boolean>;
|
||||
@@ -333,7 +332,7 @@ export class ExplorerStub implements ViewModels.Explorer {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
public importAndOpenFromGallery(name: string, content: string): Promise<boolean> {
|
||||
public importAndOpenContent(name: string, content: string): Promise<boolean> {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,11 @@ import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
||||
import { ArrayHashMap } from "../../Common/ArrayHashMap";
|
||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||
import _ from "underscore";
|
||||
import { StringUtils } from "../../Utils/StringUtils";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import { Areas } from "../../Common/Constants";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { SamplesRepo, SamplesBranch } from "../Notebook/NotebookSamples";
|
||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||
import { Callout, Text, Link, DirectionalHint, Stack, ICalloutProps, ILinkProps } from "office-ui-fabric-react";
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
@@ -37,7 +35,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
public parameters: ko.Observable<number>;
|
||||
|
||||
public galleryContentRoot: NotebookContentItem;
|
||||
public sampleNotebooksContentRoot: NotebookContentItem;
|
||||
public myNotebooksContentRoot: NotebookContentItem;
|
||||
public gitHubNotebooksContentRoot: NotebookContentItem;
|
||||
|
||||
@@ -95,26 +92,11 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
public async initialize(): Promise<void[]> {
|
||||
const refreshTasks: Promise<void>[] = [];
|
||||
|
||||
if (this.container.isGalleryEnabled()) {
|
||||
this.galleryContentRoot = {
|
||||
name: "Gallery",
|
||||
path: "Gallery",
|
||||
type: NotebookContentItemType.File
|
||||
};
|
||||
|
||||
this.sampleNotebooksContentRoot = undefined;
|
||||
} else {
|
||||
this.galleryContentRoot = undefined;
|
||||
|
||||
this.sampleNotebooksContentRoot = {
|
||||
name: "Sample Notebooks (View Only)",
|
||||
path: GitHubUtils.toContentUri(SamplesRepo.owner, SamplesRepo.name, SamplesBranch.name, ""),
|
||||
type: NotebookContentItemType.Directory
|
||||
};
|
||||
refreshTasks.push(
|
||||
this.container.refreshContentItem(this.sampleNotebooksContentRoot).then(() => this.triggerRender())
|
||||
);
|
||||
}
|
||||
this.galleryContentRoot = {
|
||||
name: "Gallery",
|
||||
path: "Gallery",
|
||||
type: NotebookContentItemType.File
|
||||
};
|
||||
|
||||
this.myNotebooksContentRoot = {
|
||||
name: "My Notebooks",
|
||||
@@ -361,10 +343,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
notebooksTree.children.push(this.buildGalleryNotebooksTree());
|
||||
}
|
||||
|
||||
if (this.sampleNotebooksContentRoot) {
|
||||
notebooksTree.children.push(this.buildSampleNotebooksTree());
|
||||
}
|
||||
|
||||
if (this.myNotebooksContentRoot) {
|
||||
notebooksTree.children.push(this.buildMyNotebooksTree());
|
||||
}
|
||||
@@ -437,57 +415,6 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
};
|
||||
}
|
||||
|
||||
private buildSampleNotebooksTree(): TreeNode {
|
||||
const sampleNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||
this.sampleNotebooksContentRoot,
|
||||
(item: NotebookContentItem) => {
|
||||
const databaseAccountName: string = this.container.databaseAccount() && this.container.databaseAccount().name;
|
||||
const defaultExperience: string = this.container.defaultExperience && this.container.defaultExperience();
|
||||
const dataExplorerArea: string = Areas.ResourceTree;
|
||||
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.OpenSampleNotebook, {
|
||||
databaseAccountName,
|
||||
defaultExperience,
|
||||
dataExplorerArea
|
||||
});
|
||||
|
||||
this.container.importAndOpen(item.path).then(hasOpened => {
|
||||
if (hasOpened) {
|
||||
this.pushItemToMostRecent(item);
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.OpenSampleNotebook,
|
||||
{
|
||||
databaseAccountName,
|
||||
defaultExperience,
|
||||
dataExplorerArea
|
||||
},
|
||||
startKey
|
||||
);
|
||||
} else {
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.OpenSampleNotebook,
|
||||
{
|
||||
databaseAccountName,
|
||||
defaultExperience,
|
||||
dataExplorerArea
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
sampleNotebooksTree.isExpanded = true;
|
||||
// Remove children starting with "."
|
||||
sampleNotebooksTree.children = sampleNotebooksTree.children.filter(
|
||||
node => !StringUtils.startsWith(node.label, ".")
|
||||
);
|
||||
return sampleNotebooksTree;
|
||||
}
|
||||
|
||||
private buildMyNotebooksTree(): TreeNode {
|
||||
const myNotebooksTree: TreeNode = this.buildNotebookDirectoryNode(
|
||||
this.myNotebooksContentRoot,
|
||||
|
||||
Reference in New Issue
Block a user