Notebooks Gallery (#59)

* Initial commit

* Address PR comments

* Move notebook related stuff to NotebookManager and dynamically load it

* Add New gallery callout and other UI tweaks

* Update test snapshot
This commit is contained in:
Tanuj Mittal
2020-06-30 11:47:21 -07:00
committed by GitHub
parent dd199e6565
commit 7512b3c1d5
41 changed files with 2801 additions and 1193 deletions

View File

@@ -2,10 +2,10 @@ import ko from "knockout";
import { HttpStatusCodes } from "../Common/Constants";
import { config } from "../Config";
import * as ViewModels from "../Contracts/ViewModels";
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
import { IGitHubResponse } from "../GitHub/GitHubClient";
import { IGitHubOAuthToken } from "../GitHub/GitHubOAuthService";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
export interface IJunoResponse<T> {
status: number;
@@ -23,10 +23,39 @@ export interface IPinnedBranch {
name: string;
}
export interface IGalleryItem {
id: string;
name: string;
description: string;
gitSha: string;
tags: string[];
author: string;
thumbnailUrl: string;
created: string;
isSample: boolean;
downloads: number;
favorites: number;
views: number;
}
export interface IUserGallery {
favorites: string[];
published: string[];
}
interface IPublishNotebookRequest {
name: string;
description: string;
tags: string[];
author: string;
thumbnailUrl: string;
content: any;
}
export class JunoClient {
private cachedPinnedRepos: ko.Observable<IPinnedRepo[]>;
constructor(public databaseAccount: ko.Observable<ViewModels.DatabaseAccount>) {
constructor(private databaseAccount?: ko.Observable<ViewModels.DatabaseAccount>) {
this.cachedPinnedRepos = ko.observable<IPinnedRepo[]>([]);
}
@@ -35,8 +64,8 @@ export class JunoClient {
}
public async getPinnedRepos(scope: string): Promise<IJunoResponse<IPinnedRepo[]>> {
const response = await window.fetch(`${this.getJunoGitHubUrl()}/pinnedrepos`, {
headers: this.getHeaders()
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/github/pinnedrepos`, {
headers: JunoClient.getHeaders()
});
let pinnedRepos: IPinnedRepo[];
@@ -58,10 +87,10 @@ export class JunoClient {
}
public async updatePinnedRepos(repos: IPinnedRepo[]): Promise<IJunoResponse<undefined>> {
const response = await window.fetch(`${this.getJunoGitHubUrl()}/pinnedrepos`, {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/github/pinnedrepos`, {
method: "PUT",
body: JSON.stringify(repos),
headers: this.getHeaders()
headers: JunoClient.getHeaders()
});
if (response.status === HttpStatusCodes.OK) {
@@ -75,9 +104,9 @@ export class JunoClient {
}
public async deleteGitHubInfo(): Promise<IJunoResponse<undefined>> {
const response = await window.fetch(this.getJunoGitHubUrl(), {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/github`, {
method: "DELETE",
headers: this.getHeaders()
headers: JunoClient.getHeaders()
});
return {
@@ -90,8 +119,8 @@ export class JunoClient {
const githubParams = JunoClient.getGitHubClientParams();
githubParams.append("code", code);
const response = await window.fetch(`${this.getJunoGitHubUrl()}/token?${githubParams.toString()}`, {
headers: this.getHeaders()
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/github/token?${githubParams.toString()}`, {
headers: JunoClient.getHeaders()
});
let data: IGitHubOAuthToken;
@@ -114,9 +143,9 @@ export class JunoClient {
const githubParams = JunoClient.getGitHubClientParams();
githubParams.append("access_token", token);
const response = await window.fetch(`${this.getJunoGitHubUrl()}/token?${githubParams.toString()}`, {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/github/token?${githubParams.toString()}`, {
method: "DELETE",
headers: this.getHeaders()
headers: JunoClient.getHeaders()
});
return {
@@ -125,11 +154,201 @@ export class JunoClient {
};
}
private getJunoGitHubUrl(): string {
return `${config.JUNO_ENDPOINT}/api/notebooks/${this.databaseAccount().name}/github`;
public async getSampleNotebooks(): Promise<IJunoResponse<IGalleryItem[]>> {
return this.getNotebooks(`${this.getNotebooksUrl()}/gallery/samples`);
}
private getHeaders(): HeadersInit {
public async getPublicNotebooks(): Promise<IJunoResponse<IGalleryItem[]>> {
return this.getNotebooks(`${this.getNotebooksUrl()}/gallery/public`);
}
public async getNotebook(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(this.getNotebookInfoUrl(id));
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async getNotebookContent(id: string): Promise<IJunoResponse<string>> {
const response = await window.fetch(this.getNotebookContentUrl(id));
let data: string;
if (response.status === HttpStatusCodes.OK) {
data = await response.text();
}
return {
status: response.status,
data
};
}
public async increaseNotebookViews(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksUrl()}/gallery/${id}/views`, {
method: "PATCH"
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async increaseNotebookDownloadCount(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/gallery/${id}/downloads`, {
method: "PATCH",
headers: JunoClient.getHeaders()
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async favoriteNotebook(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/gallery/${id}/favorite`, {
method: "PATCH",
headers: JunoClient.getHeaders()
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async unfavoriteNotebook(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksUrl()}/gallery/${id}/unfavorite`, {
method: "PATCH",
headers: JunoClient.getHeaders()
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async getFavoriteNotebooks(): Promise<IJunoResponse<IGalleryItem[]>> {
return await this.getNotebooks(`${this.getNotebooksUrl()}/gallery/favorites`, {
headers: JunoClient.getHeaders()
});
}
public async getPublishedNotebooks(): Promise<IJunoResponse<IGalleryItem[]>> {
return await this.getNotebooks(`${this.getNotebooksUrl()}/gallery/published`, {
headers: JunoClient.getHeaders()
});
}
public async deleteNotebook(id: string): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksUrl()}/gallery/${id}`, {
method: "DELETE",
headers: JunoClient.getHeaders()
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public async publishNotebook(
name: string,
description: string,
tags: string[],
author: string,
thumbnailUrl: string,
content: string
): Promise<IJunoResponse<IGalleryItem>> {
const response = await window.fetch(`${this.getNotebooksAccountUrl()}/gallery`, {
method: "PUT",
headers: JunoClient.getHeaders(),
body: JSON.stringify({
name,
description,
tags,
author,
thumbnailUrl,
content: JSON.parse(content)
} as IPublishNotebookRequest)
});
let data: IGalleryItem;
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
public getNotebookContentUrl(id: string): string {
return `${this.getNotebooksUrl()}/gallery/${id}/content`;
}
public getNotebookInfoUrl(id: string): string {
return `${this.getNotebooksUrl()}/gallery/${id}`;
}
private async getNotebooks(input: RequestInfo, init?: RequestInit): Promise<IJunoResponse<IGalleryItem[]>> {
const response = await window.fetch(input, init);
let data: IGalleryItem[];
if (response.status === HttpStatusCodes.OK) {
data = await response.json();
}
return {
status: response.status,
data
};
}
private getNotebooksUrl(): string {
return `${config.JUNO_ENDPOINT}/api/notebooks`;
}
private getNotebooksAccountUrl(): string {
return `${config.JUNO_ENDPOINT}/api/notebooks/${this.databaseAccount().name}`;
}
private static getHeaders(): HeadersInit {
const authorizationHeader = getAuthorizationHeader();
return {
[authorizationHeader.header]: authorizationHeader.token,
@@ -137,7 +356,7 @@ export class JunoClient {
};
}
public static getGitHubClientParams(): URLSearchParams {
private static getGitHubClientParams(): URLSearchParams {
const githubParams = new URLSearchParams({
client_id: config.GITHUB_CLIENT_ID
});