Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Large file upload task #94

Merged
merged 10 commits into from Aug 29, 2018
Merged

Support for Large file upload task #94

merged 10 commits into from Aug 29, 2018

Conversation

muthurathinam
Copy link
Contributor

@muthurathinam muthurathinam commented Aug 6, 2018

Large file upload task

In uncertain network connectivity areas uploading a large file would take multiple attempts to get it done. In case of larger files, it would take even more attempts to achieve or even impossible. Large file upload task helps in uploading large files effortlessly.

Implementation is done with the reference of this onedrive's resumable upload.

To create session and start upload:

let options =  {
    path: "/Documents",
    fileName: file.name,
    rangeSize: (1024 * 1024)
};

MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options)
.then((uploadTask) => {
    uploadTask.upload()
    .then((res) => {
        console.log(res);
        console.log("File Uploaded Successfully.!!");
    })
    .catch((err) => {
        console.log(err);
    });
}).catch((err) => {
    console.log(err);
});

With async/await syntactic sugar in ECMAScript 2017

async function uploadFile(client, file) {
    try {
        let options = {
            path: "/Documents",
            fileName: file.name,
            rangeSize: (1024 * 1024)
        };
        const uploadTask = await MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options);
        const response = await uploadTask.upload();
        console.log(response);
    } catch (err) {
        console.log(err);
    }
}

We can just resume the broken upload:

Lets consider some break down happens in the middle of the uploading, with the uploadTask object in hand resuming comes easy.

uploadTask.resume();

Even you can control the whole upload process:

Just create the upload task, and play with it by using sliceFile and uploadSlice methods

let range = uploadTask.getNextRange();
let slicedFile = uploadTask.sliceFile(range);
uploadTask.uploadSlice(slicedFile, range, uploadTask.file.size);

Copy link
Contributor

@MIchaelMainer MIchaelMainer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Customers will love this feature.

@@ -0,0 +1,47 @@
/// <reference types="node" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add jsdoc comments for interfaces, classes, and members. This should be generated from proper comments in the typescript.

@@ -0,0 +1 @@
export declare const getValidRangeSize: (rangeSize?: number) => number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add js doc comments. This applies to all .d.ts

}

export const getValidRangeSize = (rangeSize: number = DEFAULT_FILE_SIZE): number => {
const sixtyMB = 60 * 1024 * 1024;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the significance of 60MB? Please add a comment about why this size is not arbitrary.

// This method should be called to create an LargeFileUploadTask
// To create this uploadTask in node user has to provide the file in the NodeFile format

// retuns the instance of LargeFileUploadTask by making request to the sessionCreationUrl that is provided
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: typo 'retuns' becomes 'returns'

}
}

// Delets the upload current upload task in the server
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 'Delets' becomes 'Deletes'. Actually, this should be a proper doc comment so that it is picked up for .d.ts

return blob;
}

// Uploads the file by slicing and uploadin each slice in a sequential manner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 'uploadin' becomes 'uploading'

_fileObject: FileObject = <FileObject>{};
switch(file.constructor.name) {
case "Blob":
_fileObject.name = self.getRandomFileName();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should enable customers to name the file they are creating when they submit it as a blob. Perhaps add a member to LargeFileUploadTaskOptions named blobFileName and change line 59 to be = (options.blobFileName !== undefined) ? options.blobFileName : self.getRandomFileName();

rangeSize: 327680
};
let uploadTask = new LargeFileUploadTask(getClient(), fileObj, uploadSession, options);
it('Should return an exceptoion as trying to upload a slice after the session has expired', (done) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 'exceptoionbecomesexception`

done();
});

it('Should return next range as default(empty) range,this is for the upload task completed', (done) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: space needed 'range, this'

while (true) {
let nextRange = self.getNextRange();
if (nextRange.maxValue === -1) {
throw new Error("Invalid session: Uploading completed");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also aplies to line 181. Would it make sense to create the Error, and then give it a descriptive name so that customers can inspect the error.name and handle it? The error message should give some info on what happed advice on how the error can be handled.

@muthurathinam
Copy link
Contributor Author

@MIchaelMainer Made the changes that you are requested...


/**
* @async
* Resume the upload session and continue uploading the file from the place where it left
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resume the upload session and continue uploading the file from the last sent range.

}

/**
* To update the exipiration date and the next range
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: expiration

* @async
* Creates a LargeFilUploadTask
* @param client - The GraphClient instance
* @param file - File representated as Blob, File or NodeFile object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change 'NodeFile' to 'Buffer'

/**
* @static
* @async
* Creates a LargeFilUploadTask
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change LargeFilUploadTask to LargeFileUploadTask

case "File":
let _file = <File>file;
_fileObject.content = _file;
_fileObject.name = _fileObject.name = (options.fileName !== undefined) ? options.fileName : _file.name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra '_fileObject.name =' or is this something I don't understand?

Can _file.name ever be undefined? If so, we should use the random name as a last resort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is extra.!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can _file.name ever be undefined? If so, we should use the random name as a last resort.

_file.name will never be undefined.


/**
* User Options to create the upload task
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider this comment applicable to all of the files in this PR. Let's add doc comments to all of these types and members. These are all intended to be used by customers so let's make it easier for them to use them. Our .d.ts should be picking these up as well. I'm not sure how we go from .ts to .d.ts. Is there a tool for that?

/**

  • Interface used to define options when creating an upload task.
  • @interface
  • @Property {string} sessionRequestUrl - The default values for parties.
  • @Property {string} fileName - Optional. Specifies the file name of the file to upload. A file name will be created for you if you do not specify a value.
  • @Property {number} rangeSize - Optional. Specifies the range chunk size. The default value of ??? is used if a value is not specified.
  • @Property {number} maxTries - Optional. Specifies the ??? The default value of ??? is used if a value is not specified.
    */

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, will define interface in this format.

Typescript compiling does that, when we do compile .ts files using typescript compiler, it generates .js, .d.ts and .js.map files

will investigate and make the compiler take comments from .ts to .d.ts if possible.

2. Pull request changes

This reverts commit a72448b.
@muthurathinam
Copy link
Contributor Author

@MIchaelMainer
Updated the pull request with the changes:

  1. Changed compilation options to take comments from the .ts to .d.ts and .js files
  2. Corrected typos in the comments
  3. Made file name in options mandatory

MIchaelMainer
MIchaelMainer previously approved these changes Aug 10, 2018
deepak2016
deepak2016 previously approved these changes Aug 13, 2018
Copy link

@deepak2016 deepak2016 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please discuss the sesssionURL in Option and verify try catch mechanism while making upload call.
Changes look good.

.gitignore Outdated
spec/secrets.ts

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not required, remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

darrelmiller
darrelmiller previously approved these changes Aug 16, 2018
…OneDriveLargeFileUploadTask, write corresponding unit tests
@muthurathinam
Copy link
Contributor Author

@darrelmiller @deepak2016 Done changes related to extending LargeFileUploadTask to OneDriveLargeFileUploadTask.

darrelmiller
darrelmiller previously approved these changes Aug 23, 2018
@@ -0,0 +1 @@
export const AccessToken = "";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put a comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting done..!!

deepak2016
deepak2016 previously approved these changes Aug 28, 2018
import { assert } from "chai";
import { OneDriveLargeFileUploadTask } from "../../lib/src/OneDriveLargeFileUploadTask";

declare const describe, it;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed...

declare is used to tell TypeScript that the variable has been created elsewhere. If you use declare, nothing is added to the JavaScript that is generated - it is simply a hint to the compiler.

For example, if you use an external script that defines var externalModule, you would use declare var externalModule to hint to the TypeScript compiler that externalModule has already been set up

https://stackoverflow.com/a/35020317/1161237

import { assert } from "chai";
import * as OneDriveLargeFileUploadTaskUtil from "../../lib/src/OneDriveLargeFileUploadTaskUtil";

declare const describe, it;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

@@ -1,6 +1,6 @@
import { assert } from 'chai'

import { getClient, randomString } from "./test-helper"
import { getClient, randomString } from "../test-helper"
import { Notebook, OnenoteSection, OnenotePage } from '@microsoft/microsoft-graph-types-beta'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls check if we need to run test against beta or v1.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These models are now available in v1.0 so have changed it to use v1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants