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

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as request from 'request';
import * as loc from '../common/localizedConstants';
import * as utils from '../common/utils';
import * as vscode from 'vscode';
import { RemoteBookDialogModel } from '../dialog/remoteBookDialogModel';
import { GitHubRemoteBook } from '../book/githubRemoteBook';
import { SharedRemoteBook } from '../book/sharedRemoteBook';
const assetNameRE = /([a-zA-Z0-9]+)(?:-|_)([a-zA-Z0-9.]+)(?:-|_)([a-zA-Z0-9]+).(zip|tar.gz|tgz)/;
export class RemoteBookController {
constructor(public model: RemoteBookDialogModel, public outputChannel: vscode.OutputChannel) {
}
public async setRemoteBook(url: URL, remoteLocation: string, asset?: IAsset): Promise<void> {
if (remoteLocation === 'GitHub') {
this.model.remoteBook = new GitHubRemoteBook(url, this.outputChannel, asset);
} else {
this.model.remoteBook = new SharedRemoteBook(url, this.outputChannel);
}
return await this.model.remoteBook.createLocalCopy();
}
public async getReleases(url?: URL): Promise<IRelease[]> {
if (url) {
this.model.releases = [];
let options = {
headers: {
'User-Agent': 'request'
}
};
return new Promise<IRelease[]>((resolve, reject) => {
request.get(url.href, options, (error, response, body) => {
if (error) {
return reject(error);
}
if (response.statusCode !== 200) {
return reject(new Error(loc.httpRequestError(response.statusCode, response.statusMessage)));
}
let releases = JSON.parse(body);
let bookReleases: IRelease[] = [];
if (releases?.length > 0) {
let keys = Object.keys(releases);
keys.forEach(key => {
try {
bookReleases.push({ name: releases[key].name, assetsUrl: new URL(releases[key].assets_url) });
}
catch (error) {
return reject(error);
}
});
}
if (bookReleases.length > 0) {
this.model.releases = bookReleases;
resolve(bookReleases);
} else {
return reject(new Error(loc.msgReleaseNotFound));
}
});
});
} else {
return this.model.releases;
}
}
public async getAssets(release?: IRelease): Promise<IAsset[]> {
if (release) {
let format: string[] = [];
if (utils.getOSPlatform() === utils.Platform.Windows || utils.getOSPlatform() === utils.Platform.Mac) {
format = ['zip'];
} else {
format = ['tar.gz', 'tgz'];
}
let options = {
headers: {
'User-Agent': 'request'
}
};
return new Promise<IAsset[]>((resolve, reject) => {
request.get(release.assetsUrl.href, options, (error, response, body) => {
if (error) {
return reject(error);
}
if (response.statusCode !== 200) {
return reject(new Error(loc.httpRequestError(response.statusCode, response.statusMessage)));
}
let assets = JSON.parse(body);
let githubAssets: IAsset[] = [];
if (assets) {
let keys = Object.keys(assets);
keys.forEach(key => {
let asset = {} as IAsset;
asset.url = new URL(assets[key].url);
asset.name = assets[key].name;
asset.browserDownloadUrl = new URL(assets[key].browser_download_url);
let groupsRe = asset.name.match(assetNameRE);
if (groupsRe) {
asset.book = groupsRe[1];
asset.version = groupsRe[2];
asset.language = groupsRe[3];
asset.format = groupsRe[4];
if (format.includes(asset.format)) {
githubAssets.push(asset);
}
}
});
}
this.model.assets = githubAssets;
if (githubAssets.length > 0) {
resolve(githubAssets);
}
return reject(new Error(loc.msgBookNotFound));
});
});
} else {
return this.model.assets;
}
}
}
export interface IRelease {
name: string;
assetsUrl: URL;
}
export interface IAsset {
name: string;
book: string;
version: string;
language: string;
format: string;
url: URL;
browserDownloadUrl: URL;
}