UNPKG

@kwiz/common

Version:

KWIZ common utilities and helpers for M365 platform

276 lines (236 loc) 10.7 kB
import { chunkArray } from "../../../helpers/collections.base"; import { getGlobal, jsonClone } from "../../../helpers/objects"; import { isBoolean, isNotEmptyArray, isNullOrEmptyArray, isNullOrUndefined, isNumber } from "../../../helpers/typecheckers"; import { jsonTypes } from "../../../types/rest.types"; import { IRestItem } from "../../../types/sharepoint.utils.types"; import { GetJson, GetJsonSync } from "../../rest"; import { GetListRestUrl } from "../list"; function _getGlobalCache() { let _cache = getGlobal<{ getItemsByIdCache: { [cachekey: string]: IRestItem[]; }; }>("SharePoint_Rest_List_Cache", { getItemsByIdCache: {} }); return _cache; } /** return array will use the item ID as indexer, not a real array */ export async function GetItemsById<T extends IRestItem>(siteUrl: string, listIdOrTitle: string, itemIds: number[], options?: { expand?: string[]; select?: string[]; refreshCache?: boolean; jsonMetadata?: jsonTypes; }) { try { let baseParams = _parseItemsByIdParams<T>(siteUrl, listIdOrTitle, itemIds, { ...options, batchRequests: itemIds.length > 1 }); const { results, allowCache, queue } = baseParams; let cacheKey = baseParams.cacheKey; if (options && !isNullOrUndefined(options.jsonMetadata)) { cacheKey += "|jsonMetadata=" + options.jsonMetadata } if (queue.length > 0) { let promises = queue.map(batchRequestUrl => { return GetJson<tGetItemsByIdResult<T>>(batchRequestUrl, null, { //do not allow cache. modern forms have apply option which will need this to reload the item //it is cached in _restListItems anyways so this is not needed. allowCache: allowCache === true, includeDigestInGet: true, jsonMetadata: options && options.jsonMetadata }).then(obj => { if (!isNullOrUndefined(obj)) { //no-metadata will return a value, as a single result or array //otherwise, it'll go into "d" let items: T[] = []; if (isNoMetaDataResult(obj)) { items = isNotEmptyArray(obj.value) ? obj.value : [obj.value]; } else if (isVerboseResult(obj)) { items = Array.isArray(obj.d.results) ? obj.d.results : [obj.d]; } else if (isSingleResult(obj)) { // Issue 1471: If only single item returns it not an array and just object items.push(obj); } items.forEach((restItem) => { results[Number(restItem.Id)] = restItem; _addCacheItem(cacheKey, restItem); }); } }); }); await Promise.all(promises); } return results; } catch (e) { throw new Error("Could not retrieve rest item from list"); } } export function GetItemsByIdSync<T extends IRestItem>(siteUrl: string, listIdOrTitle: string, itemIds: number[], options?: { expand?: string[]; select?: string[]; refreshCache?: boolean; jsonMetadata?: jsonTypes; }) { try { let baseParams = _parseItemsByIdParams<T>(siteUrl, listIdOrTitle, itemIds, { ...options, batchRequests: itemIds.length > 1 }); const { results, allowCache, queue } = baseParams; let cacheKey = baseParams.cacheKey; if (options && !isNullOrUndefined(options.jsonMetadata)) { cacheKey += "|jsonMetadata=" + options.jsonMetadata } if (queue.length > 0) { queue.forEach((batchRequestUrl) => { let response = GetJsonSync<tGetItemsByIdResult<T>>(batchRequestUrl, null, { //do not allow cache. modern forms have apply option which will need this to reload the item //it is cached in _restListItems anyways so this is not needed. allowCache: allowCache === true, includeDigestInGet: true, jsonMetadata: options && options.jsonMetadata }); if (response && response.success && response.result) { //no-metadata will return a value, as a single result or array //otherwise, it'll go into "d" let items: T[] = []; if (isNoMetaDataResult(response.result)) { items = isNotEmptyArray(response.result.value) ? response.result.value : [response.result.value]; } else if (isVerboseResult(response.result)) { items = Array.isArray(response.result.d.results) ? response.result.d.results : [response.result.d]; } else if (isSingleResult(response.result)) { // Issue 1471: If only single item returns it not an array and just object items.push(response.result); } items.forEach((restItem) => { results[Number(restItem.Id)] = restItem; _addCacheItem(cacheKey, restItem); }); } }); } return results; } catch (e) { throw new Error("Could not retrieve rest item from list"); } } function _addCacheItem<T extends IRestItem>(cacheKey: string, item: T) { let g_cache = _getGlobalCache(); if (isNullOrUndefined(g_cache.getItemsByIdCache[cacheKey])) { g_cache.getItemsByIdCache[cacheKey] = []; } g_cache.getItemsByIdCache[cacheKey][item.Id] = jsonClone(item); } function _getCacheItem<T extends IRestItem>(cacheKey: string, itemId: number) { let g_cache = _getGlobalCache(); if (isNullOrUndefined(g_cache.getItemsByIdCache[cacheKey])) { g_cache.getItemsByIdCache[cacheKey] = []; } if (!isNullOrUndefined(g_cache.getItemsByIdCache[cacheKey][Number(itemId)])) { return jsonClone(g_cache.getItemsByIdCache[cacheKey][Number(itemId)]) as T; } return null; } function _refreshCache(cacheKey: string) { let g_cache = _getGlobalCache(); g_cache.getItemsByIdCache[cacheKey] = []; } function _getItemsByIdBaseUrl(siteUrl: string, listIdOrTitle: string) { return `${GetListRestUrl(siteUrl, listIdOrTitle)}/items`; } function _parseItemsByIdParams<T extends IRestItem>(siteUrl: string, listIdOrTitle: string, itemIds: number[], options?: { expand?: string[]; select?: string[]; refreshCache?: boolean; batchRequests?: boolean; }) { let baseUrl = _getItemsByIdBaseUrl(siteUrl, listIdOrTitle); let expand: string[] = []; let select: string[] = []; let allowCache = true; let results: T[] = []; let queue: string[] = []; if (!isNullOrUndefined(options)) { if (!isNullOrEmptyArray(options.select)) { select = options.select.sort(); } if (!isNullOrEmptyArray(options.expand)) { expand = options.expand.sort(); } if (isBoolean(options.refreshCache)) { allowCache = options.refreshCache !== true; } } let cacheKey = [baseUrl, select.join(",").toLowerCase(), expand.join(",").toLowerCase()].join("|"); if (allowCache === true) { itemIds.forEach((itemId) => { let cachedItem = _getCacheItem<T>(cacheKey, itemId); if (!isNullOrUndefined(cachedItem)) { results[itemId] = cachedItem; } }); } else { _refreshCache(cacheKey); } //remove item ids that were retrieved from cache itemIds = itemIds.filter((itemId) => { return !results.some((result) => { return Number(itemId) === Number(result.Id); }); }); if (itemIds.length !== 0) { let selectExpand: string[] = []; if (select.length) { selectExpand.push(`$select=${select.join(",")}`); } if (expand.length) { selectExpand.push(`$expand=${expand.join(",")}`); } let selectExpandQS = selectExpand.join("&"); if (options.batchRequests === false) { itemIds.forEach((itemId) => { let getItemsRequestUrl = `${baseUrl}(${itemId})?${selectExpandQS}`; queue.push(getItemsRequestUrl); }); } else { let chunks = chunkArray(itemIds, 60); for (var chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) { let chunk = chunks[chunkIndex]; let filter = chunk.map((id) => { return `(ID eq ${id})`; }).join("or"); let getItemsRequestUrl = `${baseUrl}?${[`$filter=${filter}`, selectExpandQS].join("&")}`; queue.push(getItemsRequestUrl); } } } return { results: results, allowCache: allowCache, queue: queue, cacheKey: cacheKey }; } type tGetItemsByIdResult_Single<T> = T; type tGetItemsByIdResult_Verbose<T> = { d: T | { results: T[]; } } type tGetItemsByIdResult_NoMetadata<T> = { value: T | T[] } type tGetItemsByIdResult<T> = tGetItemsByIdResult_Single<T> | tGetItemsByIdResult_Verbose<T> | tGetItemsByIdResult_NoMetadata<T>; function isVerboseResult<T extends IRestItem>(result: tGetItemsByIdResult<T>): result is tGetItemsByIdResult_Verbose<T> { return !isNullOrUndefined((result as tGetItemsByIdResult_Verbose<T>).d); } function isNoMetaDataResult<T extends IRestItem>(result: tGetItemsByIdResult<T>): result is tGetItemsByIdResult_NoMetadata<T> { return !isNullOrUndefined((result as tGetItemsByIdResult_NoMetadata<T>).value); } function isSingleResult<T extends IRestItem>(result: tGetItemsByIdResult<T>): result is tGetItemsByIdResult_Single<T> { return isNumber((result as tGetItemsByIdResult_Single<T>).Id); }