Open books from Github (#10670)

* Add open book option in UI

* Add dropdowns option on dialog

* Add flow logic

* Fetch releases and validate URL

* Add class for github books and shared file books

* Change code structure

* Unblock local copy + stack overflows

* Download books from github

* Remove unused files

* Clean code and use the openNotebookFolder command to open remote book

* Checkpoint

* Refactor remote book dialog model to only hold data

* Remove ApiWrapper and refactor createlocalcopy method

* Use sinon js framework instead of typemoq for testing remotebookController

* Remove api wrapper

* Add some tests

* Add more tests and address pr comments

* Address PR comments

* Fix remotebook broken tests

* Add download location in output channel and use openBook command

* Address PR comments

* Fix typos, print error message and remove failing test

* Print error message

* Separate tests in different files

* Declare controller variable inside extension.tst

Co-authored-by: chlafreniere <hichise@gmail.com>
This commit is contained in:
Barbara Valdez
2020-07-24 19:39:03 -07:00
committed by GitHub
parent 56d1a1c1af
commit 6d9efbd603
17 changed files with 866 additions and 8 deletions

View File

@@ -6,7 +6,7 @@
import * as vscode from 'vscode';
import { NotebookUtils } from './notebookUtils';
import { BookTreeViewProvider } from '../book/bookTreeView';
import { NavigationProviders, BOOKS_VIEWID, PROVIDED_BOOKS_VIEWID } from './constants';
import { NavigationProviders, BOOKS_VIEWID, PROVIDED_BOOKS_VIEWID, extensionOutputChannelName } from './constants';
/**
* Global context for the application
@@ -16,6 +16,7 @@ export class AppContext {
public readonly notebookUtils: NotebookUtils;
public readonly bookTreeViewProvider: BookTreeViewProvider;
public readonly providedBookTreeViewProvider: BookTreeViewProvider;
public readonly outputChannel: vscode.OutputChannel;
constructor(public readonly extensionContext: vscode.ExtensionContext) {
this.notebookUtils = new NotebookUtils();
@@ -23,5 +24,6 @@ export class AppContext {
let workspaceFolders = vscode.workspace.workspaceFolders?.slice() ?? [];
this.bookTreeViewProvider = new BookTreeViewProvider(workspaceFolders, extensionContext, false, BOOKS_VIEWID, NavigationProviders.NotebooksNavigator);
this.providedBookTreeViewProvider = new BookTreeViewProvider([], extensionContext, true, PROVIDED_BOOKS_VIEWID, NavigationProviders.ProvidedBooksNavigator);
this.outputChannel = vscode.window.createOutputChannel(extensionOutputChannelName);
}
}

View File

@@ -8,7 +8,7 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
// CONFIG VALUES ///////////////////////////////////////////////////////////
export const extensionOutputChannel = 'Notebooks';
export const extensionOutputChannelName = 'Notebooks';
// JUPYTER CONFIG //////////////////////////////////////////////////////////
export const pythonBundleVersion = '0.0.1';
@@ -18,6 +18,7 @@ export const existingPythonConfigKey = 'useExistingPython';
export const notebookConfigKey = 'notebook';
export const trustedBooksConfigKey = 'trustedBooks';
export const maxBookSearchDepth = 'maxBookSearchDepth';
export const remoteBookDownloadTimeout = 'remoteBookDownloadTimeout';
export const winPlatform = 'win32';

View File

@@ -39,3 +39,35 @@ export function openMarkdownError(resource: string, error: string): string { ret
export function openUntitledNotebookError(resource: string, error: string): string { return localize('openUntitledNotebookError', "Open untitled notebook {0} as untitled failed: {1}", resource, error); }
export function openExternalLinkError(resource: string, error: string): string { return localize('openExternalLinkError', "Open link {0} failed: {1}", resource, error); }
export function closeBookError(resource: string, error: string): string { return localize('closeBookError', "Close book {0} failed: {1}", resource, error); }
// Remote Book dialog constants
export const url = localize('url', "URL");
export const repoUrl = localize('repoUrl', "Repository URL");
export const location = localize('location', "Location");
export const addRemoteBook = localize('addRemoteBook', "Add Remote Book");
export const onGitHub = localize('onGitHub', "GitHub");
export const onSharedFile = localize('onsharedFile', "Shared File");
export const releases = localize('releases', "Releases");
export const book = localize('book', "Book");
export const version = localize('version', "Version");
export const language = localize('language', "Language");
export const booksNotFound = localize('booksNotFound', "No books are currently available on the provided link");
export const urlGithubError = localize('urlGithubError', "The url provided is not a Github release url");
export const search = localize('search', "Search");
export const add = localize('add', "Add");
export const close = localize('close', "Close");
export const invalidTextPlaceholder = localize('invalidTextPlaceholder', "-");
// Remote Book Controller constants
export const msgRemoteBookDownloadProgress = localize('msgRemoteBookDownloadProgress', "Remote Book download is in progress");
export const msgRemoteBookDownloadComplete = localize('msgRemoteBookDownloadComplete', "Remote Book download is complete");
export const msgRemoteBookDownloadError = localize('msgRemoteBookDownloadError', "Error while downloading remote Book");
export const msgRemoteBookUnpackingError = localize('msgRemoteBookUnpackingError', "Error while decompressing remote Book");
export const msgRemoteBookDirectoryError = localize('msgRemoteBookDirectoryError', "Error while creating remote Book directory");
export const msgTaskName = localize('msgTaskName', "Downloading Remote Book");
export const msgResourceNotFound = localize('msgResourceNotFound', "Resource not Found");
export const msgBookNotFound = localize('msgBookNotFound', "Books not Found");
export const msgReleaseNotFound = localize('msgReleaseNotFound', "Releases not Found");
export const msgUndefinedAssetError = localize('msgUndefinedAssetError', "The selected book is not valid");
export function httpRequestError(code: number, message: string): string { return localize('httpRequestError', "Http Request failed with error: {0} {1}", code, message); }
export function msgDownloadLocation(downloadLocation: string): string { return localize('msgDownloadLocation', "Downloading to {0}", downloadLocation); }

View File

@@ -269,6 +269,25 @@ export function debounce(delay: number): Function {
});
}
export function generateGuid(): string {
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
let oct: string = '';
let tmp: number;
for (let a: number = 0; a < 4; a++) {
tmp = (4294967296 * Math.random()) | 0;
oct += hexValues[tmp & 0xF] +
hexValues[tmp >> 4 & 0xF] +
hexValues[tmp >> 8 & 0xF] +
hexValues[tmp >> 12 & 0xF] +
hexValues[tmp >> 16 & 0xF] +
hexValues[tmp >> 20 & 0xF] +
hexValues[tmp >> 24 & 0xF] +
hexValues[tmp >> 28 & 0xF];
}
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
}
// PRIVATE HELPERS /////////////////////////////////////////////////////////
function outputDataChunk(data: string | Buffer, outputChannel: vscode.OutputChannel, header: string): void {
data.toString().split(/\r?\n/)