Ver código fonte

Handle nulls in contents

Steven Silvester 7 anos atrás
pai
commit
55eccb6483
1 arquivos alterados com 35 adições e 29 exclusões
  1. 35 29
      packages/services/src/contents/index.ts

+ 35 - 29
packages/services/src/contents/index.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/coreutils';
 
 import {
-  JSONExt, JSONObject
+  JSONObject
 } from '@phosphor/coreutils';
 
 import {
@@ -56,7 +56,7 @@ namespace Contents {
      * #### Notes
      *  Equivalent to the last part of the `path` field.
      */
-    readonly name?: string;
+    readonly name: string;
 
     /**
      * The full file path.
@@ -64,27 +64,27 @@ namespace Contents {
      * #### Notes
      * It will *not* start with `/`, and it will be `/`-delimited.
      */
-    readonly path?: string;
+    readonly path: string;
 
     /**
      * The type of file.
      */
-    readonly type?: ContentType;
+    readonly type: ContentType;
 
     /**
      * Whether the requester has permission to edit the file.
      */
-    readonly writable?: boolean;
+    readonly writable: boolean;
 
     /**
      * File creation timestamp.
      */
-    readonly created?: string;
+    readonly created: string;
 
     /**
      * Last modified timestamp.
      */
-    readonly last_modified?: string;
+    readonly last_modified: string;
 
     /**
      * Specify the mime-type of file contents.
@@ -92,12 +92,12 @@ namespace Contents {
      * #### Notes
      * Only non-`null` when `content` is present and `type` is `"file"`.
      */
-    readonly mimetype?: string;
+    readonly mimetype: string;
 
     /**
      * The optional file content.
      */
-    readonly content?: any;
+    readonly content: any;
 
     /**
      * The format of the file `content`.
@@ -105,7 +105,7 @@ namespace Contents {
      * #### Notes
      * Only relevant for type: 'file'
      */
-    readonly format?: FileFormat;
+    readonly format: FileFormat;
   }
 
   /**
@@ -125,7 +125,7 @@ namespace Contents {
    * The options used to fetch a file.
    */
   export
-  interface IFetchOptions extends JSONObject {
+  interface IFetchOptions {
     /**
      * The override file type for the request.
      */
@@ -148,7 +148,7 @@ namespace Contents {
    * The options used to create a file.
    */
   export
-  interface ICreateOptions extends JSONObject {
+  interface ICreateOptions {
     /**
      * The directory in which to create the file.
      */
@@ -197,12 +197,12 @@ namespace Contents {
     /**
      * The new contents.
      */
-    oldValue: IModel | null;
+    oldValue: Partial<IModel> | null;
 
     /**
      * The old contents.
      */
-    newValue: IModel | null;
+    newValue: Partial<IModel> | null;
   }
 
   /**
@@ -225,7 +225,7 @@ namespace Contents {
      * relevant backend. Returns `null` if the backend
      * does not provide one.
      */
-    getModelDBFactory(path: string): ModelDB.IFactory;
+    getModelDBFactory(path: string): ModelDB.IFactory | null;
 
     /**
      * Get a file or directory.
@@ -287,7 +287,7 @@ namespace Contents {
      * @returns A promise which resolves with the file content model when the
      *   file is saved.
      */
-    save(path: string, options?: IModel): Promise<IModel>;
+    save(path: string, options?: Partial<IModel>): Promise<IModel>;
 
     /**
      * Copy a file into a given directory.
@@ -432,7 +432,7 @@ namespace Contents {
      * @returns A promise which resolves with the file content model when the
      *   file is saved.
      */
-    save(localPath: string, options?: IModel): Promise<IModel>;
+    save(localPath: string, options?: Partial<IModel>): Promise<IModel>;
 
     /**
      * Copy a file into a given directory.
@@ -549,9 +549,9 @@ class ContentsManager implements Contents.IManager {
    * relevant backend. Returns `null` if the backend
    * does not provide one.
    */
-  getModelDBFactory(path: string): ModelDB.IFactory {
-    let [drive,] = this._driveForPath(path);
-    return drive.modelDBFactory || null;
+  getModelDBFactory(path: string): ModelDB.IFactory | null {
+    let [drive, ] = this._driveForPath(path);
+    return drive && drive.modelDBFactory || null;
   }
 
   /**
@@ -565,6 +565,9 @@ class ContentsManager implements Contents.IManager {
    */
   get(path: string, options?: Contents.IFetchOptions): Promise<Contents.IModel> {
     let [drive, localPath] = this._driveForPath(path);
+    if (!drive) {
+      return Promise.reject(`No valid drive for path: ${path}`);
+    }
     return drive.get(localPath, options).then( contentsModel => {
       let listing: Contents.IModel[] = [];
       if (contentsModel.type === 'directory') {
@@ -613,6 +616,9 @@ class ContentsManager implements Contents.IManager {
     if (options.path) {
       let globalPath = Private.normalize(options.path);
       let [drive, localPath] = this._driveForPath(globalPath);
+      if (!drive) {
+        return Promise.reject(`No valid drive for path: ${globalPath}`);
+      }
       return drive.newUntitled({ ...options, path: localPath }).then( contentsModel => {
         return {
           ...contentsModel,
@@ -673,7 +679,7 @@ class ContentsManager implements Contents.IManager {
    * #### Notes
    * Ensure that `model.content` is populated for the file.
    */
-  save(path: string, options: Contents.IModel = {}): Promise<Contents.IModel> {
+  save(path: string, options: Partial<Contents.IModel> = {}): Promise<Contents.IModel> {
     let [drive, localPath] = this._driveForPath(path);
     return drive.save(localPath, { ...options, path: localPath }).then(contentsModel => {
       return { ...contentsModel, path: Private.normalize(path) } as Contents.IModel;
@@ -814,15 +820,15 @@ class ContentsManager implements Contents.IManager {
     if (sender === this._defaultDrive) {
       this._fileChanged.emit(args);
     } else {
-      let newValue: Contents.IModel = null;
-      let oldValue: Contents.IModel = null;
-      if (args.newValue) {
+      let newValue: Partial<Contents.IModel> | null = null;
+      let oldValue: Partial<Contents.IModel> | null = null;
+      if (args.newValue && args.newValue.path) {
         newValue = {
           ...args.newValue,
           path: this._toGlobalPath(sender, args.newValue.path)
         };
       }
-      if (args.oldValue) {
+      if (args.oldValue && args.oldValue.path) {
         oldValue = {
           ...args.oldValue,
           path: this._toGlobalPath(sender, args.oldValue.path)
@@ -838,7 +844,7 @@ class ContentsManager implements Contents.IManager {
 
   private _isDisposed = false;
   private _additionalDrives = new Map<string, Contents.IDrive>();
-  private _defaultDrive: Contents.IDrive = null;
+  private _defaultDrive: Contents.IDrive;
   private _fileChanged = new Signal<this, Contents.IChangedArgs>(this);
 }
 
@@ -914,8 +920,8 @@ class Drive implements Contents.IDrive {
       if (options.type === 'notebook') {
         delete options['format'];
       }
-      let params: any = JSONExt.deepCopy(options);
-      params.content = options.content ? '1' : '0';
+      let content = options.content ? '1' : '0';
+      let params: JSONObject = { ...options, content };
       url += URLExt.objectToQueryString(params);
     }
 
@@ -1086,7 +1092,7 @@ class Drive implements Contents.IDrive {
    *
    * Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/contents) and validates the response model.
    */
-  save(localPath: string, options: Contents.IModel = {}): Promise<Contents.IModel> {
+  save(localPath: string, options: Partial<Contents.IModel> = {}): Promise<Contents.IModel> {
     let request = {
       url: this._getUrl(localPath),
       method: 'PUT',