Skip to content

Commit 3f9c22f

Browse files
authored
Merge pull request #151 from OneNoteDev/feature/parallelize-patch-wait-and-dataurl-fetching
Parallelize the PATCH interval and fetching of dataUrls
2 parents 35424a9 + 812bedc commit 3f9c22f

File tree

1 file changed

+114
-79
lines changed

1 file changed

+114
-79
lines changed

src/scripts/clipperUI/saveToOneNote.ts

Lines changed: 114 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {Settings} from "../settings";
66
import {StringUtils} from "../stringUtils";
77
import {Utils} from "../utils";
88

9+
import {SmartValue} from "../communicator/smartValue";
10+
911
import {AugmentationModel} from "../contentCapture/augmentationHelper";
1012
import {PdfScreenshotResult} from "../contentCapture/pdfScreenshotHelper";
1113

@@ -37,7 +39,7 @@ export class SaveToOneNote {
3739
// Used by PDF mode
3840
private static maxImagesPerPatchRequest = 15;
3941
private static timeBeforeFirstPatch = 1000;
40-
private static timeBetweenPatchRequests = 5000;
42+
private static timeBetweenPatchRequests = 7000;
4143

4244
/**
4345
* Checks for the 1) data result from creating a new page on clip, and 2) completion of the show ratings prompt calculation
@@ -287,13 +289,6 @@ export class SaveToOneNote {
287289
}
288290
}
289291

290-
private static getAllPdfPageIndexesToBeSent(): number[] {
291-
if (this.clipperState.pdfPreviewInfo.allPages) {
292-
return _.range(this.clipperState.pdfResult.data.get().viewportDimensions.length);
293-
}
294-
return StringUtils.parsePageRange(this.clipperState.pdfPreviewInfo.selectedPageRange).map((indexFromOne) => indexFromOne - 1);
295-
}
296-
297292
// Note this is called only after we finish waiting on the pdf request
298293
private static logPdfOptions() {
299294
let clipPdfEvent = new Log.Event.BaseEvent(Log.Event.Label.ClipPdfOptions);
@@ -313,64 +308,44 @@ export class SaveToOneNote {
313308
/**
314309
* Executes the request(s) needed to save the clip to OneNote
315310
*/
316-
private static executeApiRequest(page: OneNoteApi.OneNotePage, clipMode: ClipMode): Promise<any> {
311+
private static executeApiRequest(page: OneNoteApi.OneNotePage, clipMode: ClipMode): Promise<OneNoteApi.ResponsePackage<any>> {
317312
if (clipMode === ClipMode.Pdf) {
318-
return SaveToOneNote.patchPdfPages(page);
313+
return SaveToOneNote.createAndPatchPdfPage(page, SaveToOneNote.getAllPdfPageIndexesToBeSent());
319314
}
320315

321316
let saveLocation = SaveToOneNote.clipperState.saveLocation;
322317
return SaveToOneNote.getApiInstance().createPage(page, saveLocation);
323318
}
324319

325-
/**
326-
* Starting from the 31st PDF page, divides the remaining PDF pages as evenly as possible, where each
327-
* group has <= 30 pages, and then chains them all to be sent to OneNote as PATCH requests. Simply
328-
* resolves if there are no PDF pages to PATCH.
329-
*/
330-
private static patchPdfPages(page: OneNoteApi.OneNotePage): Promise<any> {
331-
let clipperState = SaveToOneNote.clipperState;
332-
let previewOptions = clipperState.pdfPreviewInfo;
333-
let pdfDocumentProxy = clipperState.pdfResult.data.get().pdf;
334-
335-
let indexesToBePatched = SaveToOneNote.getAllPdfPageIndexesToBeSent();
336-
if (indexesToBePatched.length === 0) {
337-
return new Promise<any>((resolve) => { resolve(); });
320+
private static getAllPdfPageIndexesToBeSent(): number[] {
321+
if (this.clipperState.pdfPreviewInfo.allPages) {
322+
return _.range(this.clipperState.pdfResult.data.get().viewportDimensions.length);
338323
}
324+
return StringUtils.parsePageRange(this.clipperState.pdfPreviewInfo.selectedPageRange).map((indexFromOne) => indexFromOne - 1);
325+
}
339326

340-
let indexesToBePatchedRanges = ArrayUtils.partition(indexesToBePatched, SaveToOneNote.maxImagesPerPatchRequest);
341-
342-
return SaveToOneNote.checkIfUserHasPermissionToPatch().then(() => {
343-
return SaveToOneNote.pdfCreatePage(page).then((postPageResponse /* should also be a onenote response */) => {
344-
let pageId = postPageResponse.parsedResponse.id;
345-
346-
// As of 10/27/16, the page is not always ready when the 200 is returned, so we wait a bit, and then getPageContent with retries
347-
// When the getPageContent returns a 200, we start PATCHing the page.
348-
let timeBetweenPatchRequests = SaveToOneNote.timeBeforeFirstPatch;
349-
return Promise.all([postPageResponse,
350-
indexesToBePatchedRanges.reduce((chainedPromise, currentIndexesRange) => {
351-
return chainedPromise = chainedPromise.then((returnValueOfPreviousPromise /* should be a onenote response */) => {
352-
return new Promise((resolve, reject) => {
353-
// OneNote API returns 204 on a PATCH request when it receives it, but we have no way of telling when it actually
354-
// completes processing, so we add an artificial timeout before the next PATCH to try and ensure that they get
355-
// processed in the order that they were sent.
356-
setTimeout(() => {
357-
SaveToOneNote.createOneNotePagePatchRequest(pageId, currentIndexesRange).then(() => {
358-
timeBetweenPatchRequests = SaveToOneNote.timeBetweenPatchRequests;
359-
resolve();
360-
}).catch((error) => {
361-
reject(error);
362-
});
363-
}, timeBetweenPatchRequests);
364-
});
365-
});
366-
}, SaveToOneNote.getOneNotePageContentWithRetries(pageId, 3))
367-
]);
327+
/**
328+
* Creates a page for the PDF clipping, then sends PATCH requests to it. Resolves with the createPage
329+
* response package when all operations are complete; rejects otherwise.
330+
*/
331+
private static createAndPatchPdfPage(page: OneNoteApi.OneNotePage, pageIndexes: number[]): Promise<OneNoteApi.ResponsePackage<any>> {
332+
return new Promise<OneNoteApi.ResponsePackage<any>>((resolve, reject) => {
333+
SaveToOneNote.checkIfUserHasPermissionToPatch().then(() => {
334+
SaveToOneNote.pdfCreatePage(page).then((postPageResponse /* should also be a onenote response */) => {
335+
let pageId = postPageResponse.parsedResponse.id;
336+
SaveToOneNote.sendPagesAsPatchRequests(pageId, pageIndexes).then(() => {
337+
resolve(postPageResponse);
338+
});
339+
});
340+
}).catch((error) => {
341+
return Promise.reject(error);
368342
});
369-
}).catch((error) => {
370-
return Promise.reject(error);
371343
});
372344
}
373345

346+
/**
347+
* Resolves if true; false otherwise
348+
*/
374349
private static checkIfUserHasPermissionToPatch(): Promise<void> {
375350
return new Promise<any>((resolve, reject) => {
376351
Clipper.getStoredValue(ClipperStorageKeys.hasPatchPermissions, (hasPermissions) => {
@@ -397,7 +372,10 @@ export class SaveToOneNote {
397372
});
398373
}
399374

400-
private static pdfCreatePage(page: OneNoteApi.OneNotePage): Promise<any> {
375+
/**
376+
* Creates the initial page for the PDF clipping
377+
*/
378+
private static pdfCreatePage(page: OneNoteApi.OneNotePage): Promise<OneNoteApi.ResponsePackage<any>> {
401379
let pdfCreatePageEvent = new Log.Event.PromiseEvent(Log.Event.Label.PdfCreatePage);
402380
return new Promise<any>((resolve, reject) => {
403381
SaveToOneNote.createNewPage(page, ClipMode.Pdf).then((postPageResponse) => {
@@ -412,6 +390,10 @@ export class SaveToOneNote {
412390
});
413391
}
414392

393+
/**
394+
* Checks to see if a given page exists with a certain number of retries. There is a delay
395+
* between each retry.
396+
*/
415397
private static getOneNotePageContentWithRetries(pageId: string, numRetries: number): Promise<any> {
416398
return SaveToOneNote.getPageContent(pageId).catch((error1) => {
417399
// If the first call fails, we need to wait a couple of seconds before trying again
@@ -431,46 +413,99 @@ export class SaveToOneNote {
431413
});
432414
}
433415

434-
private static sendOneNotePagePatchRequestWithRetries(pageId: string, dataUrls: string[], numRetries: number): Promise<any> {
435-
return SaveToOneNote.sendOneNotePagePatchRequest(pageId, dataUrls).catch((error) => {
436-
if (numRetries >= 1) {
437-
return SaveToOneNote.sendOneNotePagePatchRequestWithRetries(pageId, dataUrls, numRetries - 1);
438-
} else {
439-
return Promise.reject(error);
440-
}
441-
});
416+
/**
417+
* Given a page and a list of page indexes, buckets them into PATCH request and sends them sequentially.
418+
*/
419+
private static sendPagesAsPatchRequests(pageId: string, indexesToBePatched: number[]): Promise<any> {
420+
let indexesToBePatchedRanges: number[][] = ArrayUtils.partition(indexesToBePatched, SaveToOneNote.maxImagesPerPatchRequest);
421+
422+
// As of 10/27/16, the page is not always ready when the 200 is returned, so we wait a bit, and then getPageContent with retries
423+
// When the getPageContent returns a 200, we start PATCHing the page.
424+
let timeBetweenPatchRequests = SaveToOneNote.timeBeforeFirstPatch;
425+
return Promise.all([
426+
indexesToBePatchedRanges.reduce((chainedPromise, currentIndexesRange) => {
427+
return chainedPromise = chainedPromise.then((returnValueOfPreviousPromise /* should be a onenote response */) => {
428+
return new Promise((resolve, reject) => {
429+
// OneNote API returns 204 on a PATCH request when it receives it, but we have no way of telling when it actually
430+
// completes processing, so we add an artificial timeout before the next PATCH to try and ensure that they get
431+
// processed in the order that they were sent.
432+
let dataUrlsSv = new SmartValue<string[]>();
433+
let createPatchRequestCb = () => {
434+
SaveToOneNote.createOneNotePagePatchRequest(pageId, dataUrlsSv.get()).then(() => {
435+
timeBetweenPatchRequests = SaveToOneNote.timeBetweenPatchRequests;
436+
resolve();
437+
}).catch((error) => {
438+
reject(error);
439+
});
440+
};
441+
442+
// Parallelize the PATCH request intervals with the fetching of the next set of dataUrls
443+
setTimeout(() => {
444+
if (dataUrlsSv.get()) {
445+
createPatchRequestCb();
446+
} else {
447+
dataUrlsSv.subscribe((value) => {
448+
createPatchRequestCb();
449+
}, { times: 1, callOnSubscribe: false });
450+
}
451+
}, timeBetweenPatchRequests);
452+
453+
SaveToOneNote.getDataUrls(currentIndexesRange).then((dataUrls) => {
454+
dataUrlsSv.set(dataUrls);
455+
});
456+
});
457+
});
458+
}, SaveToOneNote.getOneNotePageContentWithRetries(pageId, 3))
459+
]);
442460
}
443461

444462
/**
445-
* Given a list of page indexes, creates and sends the request to append the pages to the specified
446-
* page with the images
463+
* Given a list of page indexes, queries the PDF file for a list of corresponding data urls representing
464+
* images of the pages
447465
*/
448-
private static createOneNotePagePatchRequest(pageId: string, pageIndices: number[]): Promise<any> {
449-
let pdf = this.clipperState.pdfResult.data.get().pdf;
450-
466+
private static getDataUrls(pageIndices: number[]): Promise<string[]> {
451467
let getPageListAsDataUrlsEvent = new Log.Event.PromiseEvent(Log.Event.Label.ProcessPdfIntoDataUrls);
452468
getPageListAsDataUrlsEvent.setCustomProperty(Log.PropertyName.Custom.NumPages, pageIndices.length);
453469

454-
return pdf.getPageListAsDataUrls(pageIndices).then((dataUrls) => {
470+
let pdf = this.clipperState.pdfResult.data.get().pdf;
471+
return pdf.getPageListAsDataUrls(pageIndices).then((dataUrls: string[]) => {
455472
getPageListAsDataUrlsEvent.stopTimer();
456473
getPageListAsDataUrlsEvent.setCustomProperty(Log.PropertyName.Custom.AverageProcessingDurationPerPage, getPageListAsDataUrlsEvent.getDuration() / pageIndices.length);
457474
Clipper.logger.logEvent(getPageListAsDataUrlsEvent);
458475

459-
let patchRequestEvent = new Log.Event.PromiseEvent(Log.Event.Label.PatchRequest);
460-
return new Promise<any>((resolve, reject) => {
461-
SaveToOneNote.sendOneNotePagePatchRequestWithRetries(pageId, dataUrls, Constants.Settings.numRetriesPerPatchRequest).then((data) => {
462-
resolve(data);
463-
}, (err) => {
464-
patchRequestEvent.setStatus(Log.Status.Failed);
465-
patchRequestEvent.setFailureInfo({ error: err });
466-
reject(err);
467-
}).then(() => {
468-
Clipper.logger.logEvent(patchRequestEvent);
469-
});
476+
return Promise.resolve(dataUrls);
477+
});
478+
}
479+
480+
/**
481+
* Given a list of page indexes, creates and sends the request to append the pages to the specified
482+
* page with the images
483+
*/
484+
private static createOneNotePagePatchRequest(pageId: string, dataUrls: string[]): Promise<any> {
485+
let patchRequestEvent = new Log.Event.PromiseEvent(Log.Event.Label.PatchRequest);
486+
return new Promise<any>((resolve, reject) => {
487+
SaveToOneNote.sendOneNotePagePatchRequestWithRetries(pageId, dataUrls, Constants.Settings.numRetriesPerPatchRequest).then((data) => {
488+
resolve(data);
489+
}, (err) => {
490+
patchRequestEvent.setStatus(Log.Status.Failed);
491+
patchRequestEvent.setFailureInfo({ error: err });
492+
reject(err);
493+
}).then(() => {
494+
Clipper.logger.logEvent(patchRequestEvent);
470495
});
471496
});
472497
}
473498

499+
private static sendOneNotePagePatchRequestWithRetries(pageId: string, dataUrls: string[], numRetries: number): Promise<any> {
500+
return SaveToOneNote.sendOneNotePagePatchRequest(pageId, dataUrls).catch((error) => {
501+
if (numRetries >= 1) {
502+
return SaveToOneNote.sendOneNotePagePatchRequestWithRetries(pageId, dataUrls, numRetries - 1);
503+
} else {
504+
return Promise.reject(error);
505+
}
506+
});
507+
}
508+
474509
/**
475510
* Given a pageId and an array of dataUrls, sends a PATCH request with those dataUrls as image to update
476511
* the page with pageId

0 commit comments

Comments
 (0)