Skip to content
This repository has been archived by the owner on Sep 3, 2022. It is now read-only.

Commit

Permalink
List files using DriveFileManager (#1587)
Browse files Browse the repository at this point in the history
A significant part of this PR is also a refactor for the UnsupportedMethod error, and moving BigQuery-specific and Drive-specific methods in the GapiManager into their own nested classes.

* added drive file manager

* drive listing working

* refactor GapiManager to split drive and bigquery apis

* use object instead of constructor on UnsupportedMethod call

* cleanup

* rebase artifacts

* sort files on folder,modified,name

* pr comments

* add reverted grantScope call
  • Loading branch information
yebrahim authored Aug 18, 2017
1 parent 55a8b8c commit f0e94b2
Show file tree
Hide file tree
Showing 17 changed files with 381 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,30 +90,33 @@ class DataElement extends Polymer.Element {
// Make some calls to the BigQuery API and pass the results to the resultHandler
_callBigQuery(searchValue: string, resultHandler: (partialResults: Result[]) => void) {
const sampleProject = 'bigquery-public-data';
GapiManager.listBigQueryProjects()
GapiManager.bigquery.listProjects()
.then((response: HttpResponse<gapi.client.bigquery.ListProjectsResponse>) => {
Utils.log.verbose('== projects: ', response);
const projectResults: Result[] = response.result.projects.map(this._bqProjectToResult.bind(this)) as Result[];
const projectResults: Result[] =
response.result.projects.map(this._bqProjectToResult.bind(this)) as Result[];
resultHandler(projectResults);
})
.catch(() => {
// TODO: handle errors getting projects
});
// The filter arg when querying for datasets must be of the form labels.<name>[:<value>],
// see https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets/list
GapiManager.listBigQueryDatasets(sampleProject, searchValue /* label filter */)
GapiManager.bigquery.listDatasets(sampleProject, searchValue /* label filter */)
.then((response: HttpResponse<gapi.client.bigquery.ListDatasetsResponse>) => {
Utils.log.verbose('== datasets: ', response);
const datasetResults: Result[] = response.result.datasets.map(this._bqDatasetToResult.bind(this)) as Result[];
const datasetResults: Result[] =
response.result.datasets.map(this._bqDatasetToResult.bind(this)) as Result[];
resultHandler(datasetResults);
})
.catch(() => {
// TODO: handle errors getting datasets
});
GapiManager.listBigQueryTables(sampleProject, searchValue /* datasetId */)
GapiManager.bigquery.listTables(sampleProject, searchValue /* datasetId */)
.then((response: HttpResponse<gapi.client.bigquery.ListTablesResponse>) => {
Utils.log.verbose('== tables: ', response);
const tableResults: Result[] = response.result.tables.map(this._bqTableToResult.bind(this)) as Result[];
const tableResults: Result[] =
response.result.tables.map(this._bqTableToResult.bind(this)) as Result[];
resultHandler(tableResults);
})
.catch(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,7 @@ class SessionsElement extends Polymer.Element {
}

(this.$.sessions as ItemListElement).rows = this._sessionList.map((session) => {
return {
firstCol: session.notebook.path,
icon: 'editor:insert-drive-file',
secondCol: 'running',
selected: false
};
return new ItemListRow(session.notebook.path, '', 'editor:insert-drive-file');
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,8 @@ class FileBrowserElement extends Polymer.Element {
*/
_drawFileList() {
(this.$.files as ItemListElement).rows = this._fileList.map((file) => {
const row: ItemListRow = {
firstCol: file.name,
icon: file.icon,
secondCol: Utils.getFileStatusString(file.status || DatalabFileStatus.IDLE),
selected: false
};
return row;
return new ItemListRow(
file.name, Utils.getFileStatusString(file.status || DatalabFileStatus.IDLE) , file.icon);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
flex-grow: 1;
}
.first-col > iron-icon {
padding-right: 10px;
width: 17px;
height: 17px;
}
Expand Down Expand Up @@ -109,7 +110,7 @@
</paper-checkbox>
</div>
<div class="first-col">
<iron-icon hidden$="{{!row.icon}}" icon={{row.icon}}></iron-icon>
<iron-icon hidden$="{{!row._icon}}" icon="{{row.icon}}" src="{{row.src}}"></iron-icon>
<span>{{row.firstCol}}</span>
</div>
<div class="second-col" hidden$="{{disableSelection}}">
Expand Down
33 changes: 26 additions & 7 deletions sources/web/datalab/polymer/components/item-list/item-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,33 @@
*/

/**
* Interface representing a row in the item list
* Object representing a row in the item list
*/
interface ItemListRow {
firstCol: string;
secondCol: string;
icon: string;
selected: boolean;
class ItemListRow {
public firstCol: string;
public secondCol: string;
public selected: boolean;

private _icon: string;

constructor(firstCol: string, secondCol: string, icon: string, selected?: boolean) {
this.firstCol = firstCol;
this.secondCol = secondCol;
this.selected = selected || false;
this._icon = icon;
}

/**
* If the given icon is a link, its src attribute should be set to that link,
* and the icon attribute should be empty. If instead it's an icon name,
* these two attributes should be reversed.
*/
get icon() { return this._hasLinkIcon() ? '' : this._icon; }
get src() { return this._hasLinkIcon() ? this._icon : ''; }

private _hasLinkIcon() {
return this._icon.startsWith('http://') || this._icon.startsWith('https://');
}
}

/**
Expand Down Expand Up @@ -236,7 +256,6 @@ class ItemListElement extends Polymer.Element {
const ev = new ItemClickEvent('itemDoubleClick', { detail: {index} });
this.dispatchEvent(ev);
}

}

customElements.define(ItemListElement.is, ItemListElement);
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class TablePreviewElement extends Polymer.Element {
const datasetId = matches[2];
const tableId = matches[3];

GapiManager.getBigqueryTableDetails(projectId, datasetId, tableId)
GapiManager.bigquery.getTableDetails(projectId, datasetId, tableId)
.then((response: HttpResponse<gapi.client.bigquery.Table>) => {
this._table = response.result;
}, (errorResponse: any) =>
Expand Down
8 changes: 6 additions & 2 deletions sources/web/datalab/polymer/modules/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ class Utils {
}

class UnsupportedMethod extends Error {
constructor(methodName: string, objectType: any) {
super('Method ' + methodName + ' is not supported on type: ' + objectType.name);
constructor(methodName: string, object: {}) {
let type = 'Unknown';
if (object && object.constructor && object.constructor.name) {
type = object.constructor.name;
}
super('Method ' + methodName + ' is not supported on type ' + type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ class BigQueryFileManager implements FileManager {
if (fileId.path === '/') {
return Promise.resolve(this._bqRootDatalabFile());
}
throw new UnsupportedMethod('get', BigQueryFileManager);
throw new UnsupportedMethod('get', this);
}

public getContent(_fileId: DatalabFileId, _asText?: boolean): Promise<DatalabContent> {
throw new UnsupportedMethod('getContent', BigQueryFileManager);
throw new UnsupportedMethod('getContent', this);
}

public async getRootFile() {
return this.get(new DatalabFileId('/', FileManagerType.BIG_QUERY));
}

public saveText(_file: DatalabFile, _content: string): Promise<DatalabFile> {
throw new UnsupportedMethod('saveText', BigQueryFileManager);
throw new UnsupportedMethod('saveText', this);
}

public list(containerId: DatalabFileId): Promise<DatalabFile[]> {
Expand All @@ -57,37 +57,37 @@ class BigQueryFileManager implements FileManager {
if (pathParts.length === 2) {
return this._listTables(pathParts[0], pathParts[1]);
}
throw new UnsupportedMethod('listing datasets', BigQueryFileManager);
throw new UnsupportedMethod('listing datasets', this);
}

public create(_fileType: DatalabFileType, _containerId: DatalabFileId, _name: string):
Promise<DatalabFile> {
throw new UnsupportedMethod('create', BigQueryFileManager);
throw new UnsupportedMethod('create', this);
}

public rename(_oldFileId: DatalabFileId, _name: string, _newContainerId?: DatalabFileId):
Promise<DatalabFile> {
throw new UnsupportedMethod('rename', BigQueryFileManager);
throw new UnsupportedMethod('rename', this);
}

public delete(_fileId: DatalabFileId): Promise<boolean> {
throw new UnsupportedMethod('delete', BigQueryFileManager);
throw new UnsupportedMethod('delete', this);
}

public copy(_fileId: DatalabFileId, _destinationDirectoryId: DatalabFileId): Promise<DatalabFile> {
throw new UnsupportedMethod('copy', BigQueryFileManager);
throw new UnsupportedMethod('copy', this);
}

public getNotebookUrl(_fileId: DatalabFileId): Promise<string> {
throw new UnsupportedMethod('getNotebookUrl', BigQueryFileManager);
throw new UnsupportedMethod('getNotebookUrl', this);
}

public getEditorUrl(_fileId: DatalabFileId): Promise<string> {
throw new UnsupportedMethod('getEditorUrl', BigQueryFileManager);
throw new UnsupportedMethod('getEditorUrl', this);
}

private _listProjects(): Promise<DatalabFile[]> {
return GapiManager.listBigQueryProjects()
return GapiManager.bigquery.listProjects()
.then((response: HttpResponse<gapi.client.bigquery.ListProjectsResponse>) => {
const projects = response.result.projects || [];
return projects.map(
Expand All @@ -97,7 +97,7 @@ class BigQueryFileManager implements FileManager {
}

private _listDatasets(projectId: string): Promise<DatalabFile[]> {
return GapiManager.listBigQueryDatasets(projectId, '')
return GapiManager.bigquery.listDatasets(projectId, '')
.then((response: HttpResponse<gapi.client.bigquery.ListDatasetsResponse>) => {
const datasets = response.result.datasets || [];
return datasets.map(
Expand All @@ -107,7 +107,7 @@ class BigQueryFileManager implements FileManager {
}

private _listTables(projectId: string, datasetId: string): Promise<DatalabFile[]> {
return GapiManager.listBigQueryTables(projectId, datasetId)
return GapiManager.bigquery.listTables(projectId, datasetId)
.then((response: HttpResponse<gapi.client.bigquery.ListTablesResponse>) => {
const tables = response.result.tables || [];
return tables.map(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--
Copyright 2017 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->

<link rel="import" href="../api-manager-factory/api-manager-factory.html">
<link rel="import" href="../file-manager/file-manager.html">
<link rel="import" href="../gapi-manager/gapi-manager.html">

<script src="drive-file-manager.js"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

/**
* This file contains a collection of functions that call the Google Drive APIs, and are
* wrapped in the ApiManager class.
*/

class DriveFile extends DatalabFile {
}

/**
* An Google Drive specific file manager.
*/
class DriveFileManager implements FileManager {

private static readonly _directoryMimeType = 'application/vnd.google-apps.folder';

private static _upstreamToDriveFile(file: gapi.client.drive.File) {
const datalabFile: DriveFile = {
icon: file.iconLink,
id: new DatalabFileId(file.id, FileManagerType.DRIVE),
name: file.name,
status: DatalabFileStatus.IDLE,
type: file.mimeType === DriveFileManager._directoryMimeType ?
DatalabFileType.DIRECTORY :
DatalabFileType.FILE,
};
return datalabFile;
}
public get(_fileId: DatalabFileId): Promise<DatalabFile> {
throw new UnsupportedMethod('get', this);
}

public getContent(_fileId: DatalabFileId, _asText?: boolean): Promise<DatalabContent> {
throw new UnsupportedMethod('getContent', this);
}

public async getRootFile(): Promise<DatalabFile> {
const upstreamFile = await GapiManager.drive.getRoot();
return DriveFileManager._upstreamToDriveFile(upstreamFile);
}

public saveText(_file: DatalabFile): Promise<DatalabFile> {
throw new UnsupportedMethod('getRootFile', this);
}

public async list(fileId: DatalabFileId): Promise<DatalabFile[]> {
const queryPredicates = [
'"' + fileId.path + '" in parents',
'trashed = false',
];
const fileFields = [
'createdTime',
'iconLink',
'id',
'mimeType',
'modifiedTime',
'name',
'parents',
];
const orderModifiers = [
'folder',
'modifiedTime',
'name',
];
const upstreamFiles =
await GapiManager.drive.getFiles(fileFields, queryPredicates, orderModifiers);
// TODO: Check which files are running from the SessionsManager and modify
// their status accordingly.
return upstreamFiles.map((file) => DriveFileManager._upstreamToDriveFile(file));
}

public create(_fileType: DatalabFileType, _containerId?: DatalabFileId, _name?: string): Promise<DatalabFile> {
throw new UnsupportedMethod('create', this);
}

public rename(_oldFileId: DatalabFileId, _newName: string, _newContainerId?: DatalabFileId): Promise<DatalabFile> {
throw new UnsupportedMethod('rename', this);
}

public delete(_fileId: DatalabFileId): Promise<boolean> {
throw new UnsupportedMethod('delete', this);
}

public copy(_file: DatalabFileId, _destinationDirectoryId: DatalabFileId): Promise<DatalabFile> {
throw new UnsupportedMethod('copy', this);
}

public getEditorUrl(): Promise<string> {
throw new UnsupportedMethod('getEditorUrl', this);
}

public getNotebookUrl(): Promise<string> {
throw new UnsupportedMethod('getNotebookUrl', this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
-->

<link rel="import" href="../bigquery-file-manager/bigquery-file-manager.html">
<link rel="import" href="../drive-file-manager/drive-file-manager.html">
<link rel="import" href="../jupyter-file-manager/jupyter-file-manager.html">

<script src="file-manager-factory.js"></script>
Loading

0 comments on commit f0e94b2

Please sign in to comment.