@microsoft/msgraph-sdk-core
Version:
Core functionalities for the Microsoft Graph JavaScript SDK
215 lines • 8.88 kB
JavaScript
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
/**
* @module PageIterator
*/
import { RequestInformation, HttpMethod, } from "@microsoft/kiota-abstractions";
/**
* Class representing a PageIterator to iterate over paginated collections.
* @template T - The type of the items in the collection.
*
* This class provides methods to iterate over a collection of items that are paginated.
* It handles fetching the next set of items when the current page is exhausted.
* The iteration can be paused and resumed, and the state of the iterator can be queried.
*
* The PageIterator uses a callback function to process each item in the collection.
* The callback function should return a boolean indicating whether to continue the iteration.
*
* The PageIterator also supports error handling through error mappings and can be configured
* with custom request options.
*/
export class PageIterator {
/**
* @public
* @constructor
* Creates new instance for PageIterator
* @returns An instance of a PageIterator
* @param requestAdapter - The request adapter
* @param pageResult - The page collection result of T
* @param callback - The callback function to be called on each item
* @param errorMappings - The error mappings
* @param parsableFactory - The factory to create the parsable object collection
* @param options - The request options to configure the request
*/
constructor(requestAdapter, pageResult, callback, parsableFactory, errorMappings, options) {
if (!requestAdapter) {
const error = new Error("Request adapter is undefined, Please provide a valid request adapter");
error.name = "Invalid Request Adapter Error";
throw error;
}
if (!pageResult) {
const error = new Error("Page result is undefined, Please provide a valid page result");
error.name = "Invalid Page Result Error";
throw error;
}
if (!callback) {
const error = new Error("Callback is undefined, Please provide a valid callback");
error.name = "Invalid Callback Error";
throw error;
}
if (!parsableFactory) {
const error = new Error("Parsable factory is undefined, Please provide a valid parsable factory");
error.name = "Invalid Parsable Factory Error";
throw error;
}
if (!errorMappings) {
const error = new Error("Error mappings is undefined, Please provide a valid error mappings");
error.name = "Invalid Error Mappings Error";
throw error;
}
this.requestAdapter = requestAdapter;
this.currentPage = pageResult;
this.cursor = 0;
this.errorMappings = errorMappings;
this.parsableFactory = parsableFactory;
this.callback = callback;
if (!options) {
options = {};
}
this.options = options;
this.pagingState = "NotStarted";
}
/**
* @public
* Getter to get the deltaLink in the current response
* @returns A deltaLink which is being used to make delta requests in future
*/
getOdataDeltaLink() {
var _a, _b, _c;
const deltaLink = (_a = this.currentPage) === null || _a === void 0 ? void 0 : _a["@odata.deltaLink"];
return (_c = (_b = this.currentPage) === null || _b === void 0 ? void 0 : _b.odataDeltaLink) !== null && _c !== void 0 ? _c : deltaLink;
}
/**
* @public
* Getter to get the nextLink in the current response
* @returns A nextLink which is being used to make requests in future
*/
getOdataNextLink() {
var _a, _b, _c;
const nextLink = (_a = this.currentPage) === null || _a === void 0 ? void 0 : _a["@odata.nextLink"];
return (_c = (_b = this.currentPage) === null || _b === void 0 ? void 0 : _b.odataNextLink) !== null && _c !== void 0 ? _c : nextLink;
}
/**
* @public
* @async
* Iterates over the collection and kicks callback for each item on iteration. Fetches next set of data through nextLink and iterates over again
* This happens until the nextLink is drained out or the user responds with a red flag to continue from callback
*/
async iterate() {
var _a, _b;
while (true) {
if (this.pagingState === "Complete") {
return;
}
if (this.pagingState === "Delta") {
const nextPage = await this.fetchNextPage();
if (!nextPage) {
this.pagingState = "Complete";
return;
}
this.currentPage = nextPage;
}
const advance = this.enumeratePage();
if (!advance) {
return;
}
const nextLink = this.getOdataNextLink();
const deltaLink = this.getOdataDeltaLink();
const hasNextPageLink = nextLink || deltaLink;
const pageSize = (_b = (_a = this.currentPage) === null || _a === void 0 ? void 0 : _a.value.length) !== null && _b !== void 0 ? _b : 0;
const isEndOfPage = !hasNextPageLink && this.cursor >= pageSize;
if (isEndOfPage) {
this.pagingState = "Complete";
return;
}
if (hasNextPageLink && this.cursor >= pageSize) {
this.cursor = 0;
if (deltaLink) {
this.pagingState = "Delta";
return;
}
const nextPage = await this.fetchNextPage();
if (!nextPage) {
this.pagingState = "Complete";
return;
}
this.currentPage = nextPage;
}
}
}
/**
* @public
* Getter to get the state of the iterator
*/
getPagingState() {
return this.pagingState;
}
/**
* @private
* @async
* Helper to make a get request to fetch next page with nextLink url and update the page iterator instance with the returned response
* @returns A promise that resolves to a response data with next page collection
*/
async fetchNextPage() {
this.pagingState = "InterpageIteration";
const nextLink = this.getOdataNextLink();
const deltaLink = this.getOdataDeltaLink();
if (!nextLink && !deltaLink) {
throw new Error("NextLink and DeltaLink are undefined, Please provide a valid nextLink or deltaLink");
}
const requestInformation = new RequestInformation();
requestInformation.httpMethod = HttpMethod.GET;
requestInformation.urlTemplate = nextLink !== null && nextLink !== void 0 ? nextLink : deltaLink;
if (this.options) {
if (this.options.headers) {
requestInformation.headers.addAll(this.options.headers);
}
if (this.options.requestOption) {
requestInformation.addRequestOptions(this.options.requestOption);
}
}
return await this.requestAdapter.send(requestInformation, this.parsableFactory, this.errorMappings);
}
/**
* @public
* @async
* To resume the iteration
* Note: This internally calls the iterate method, It's just for more readability.
*/
async resume() {
return this.iterate();
}
/**
* @private
* Iterates over a collection by enqueuing entries one by one and kicking the callback with the enqueued entry
* @returns A boolean indicating the continue flag to process next page
*/
enumeratePage() {
var _a;
this.pagingState = "IntrapageIteration";
let keepIterating = true;
const pageItems = (_a = this.currentPage) === null || _a === void 0 ? void 0 : _a.value;
// pageItems should never be undefined at this point
if (!pageItems) {
throw new Error("Page items are undefined, Please provide a valid page items");
}
if (pageItems.length === 0) {
return true;
}
// continue iterating from cursor
for (let i = this.cursor; i < pageItems.length; i++) {
keepIterating = this.callback(pageItems[i]);
this.cursor = i + 1;
if (!keepIterating) {
this.pagingState = "Paused";
break;
}
}
return keepIterating;
}
}
//# sourceMappingURL=PageIterator.js.map