import {ContextType, executionContext} from "N/runtime"; import { create, createColumn, createFilter, CreateSearchColumnOptions, CreateSearchFilterOptions, load, Result, Search, SearchCreateOptions, Type } from "N/search"; /** Search transform method */ export type SearchTransform = (result: Result) => IResponse; /** Constructor option to load a search instead of creating */ interface ISearchLoadOptions { /** Internal ID */ id: string; /** Search type */ type?: string | Type; } /** Transform response */ interface IResponse { continue: boolean; response: T; } /** * Search library file * * WARNING: * TypeScript generated file, do not edit directly * source files are located in the the repository * * @description: <%= description %> * * @copyright <%= date %> <%= company_name %> * @author <%= user_name %> <<%= user_email %>> * * @NApiVersion 2.x * @NModuleScope SameAccount */ /** Search Library */ class SearchLibrary { /** Default page size */ public pageSize: number; /** Search reference */ public search: Search; /** Search Client reference (available only on UI) */ public searchClient?: Promise; /** Transform method */ private transform: SearchTransform; /** Constructor */ constructor(options: SearchCreateOptions & ISearchLoadOptions) { this.pageSize = 50; this.transform = (): any => { return; }; const createOptions = SearchLibrary.setDefault(options); this.search = options.id ? load(options) : create(createOptions); if (executionContext === ContextType.USER_INTERFACE) { this.searchClient = options.id ? load.promise(options) : create.promise(createOptions); } } /** Compare if two objects have the same key and property */ private static hasSameKeyValue(f1: any, f2: any, key: string): boolean { return f1[key] && f2[key] && f1[key] === f2[key]; } /** Set default values on filters and columns */ private static setDefault(options: SearchCreateOptions): SearchCreateOptions { if (options.filters) { for (const f in options.filters) { options.filters[f] = createFilter(options.filters[f]); } } if (options.columns) { for (const j in options.columns) { options.columns[j] = createColumn(options.columns[j] as CreateSearchColumnOptions); } } return options; } /** Add a column to the current search */ public addColumn(column: CreateSearchColumnOptions): void { this.search.columns.push(column); } /** Add a filter to the current search */ public addFilter(filter: CreateSearchFilterOptions): void { this.search.columns.push(filter); } /** Get a set of records (up to 1000) */ public getRange(range: { end: number, start: number }): T[] { if (!this.transform) { throw new Error("A transform method needs to be set"); } const response: T[] = []; const resultSet = this.search.run(); if (resultSet.getRange({start: 0, end: 1}).length === 0) { return response; } resultSet.getRange(range).map((result) => { const transformResult = this.transform(result); response.push(transformResult.response); return transformResult.continue; }); return response; } /** Get all search results */ public getResults(): T[] { if (!this.transform) { throw new Error("A transform method needs to be set"); } const response: T[] = []; const pagedData = this.search.runPaged({ pageSize: this.pageSize }); if (pagedData.count === 0) { return response; } for (let i = 0; i < pagedData.pageRanges.length; i++) { const pageRange = pagedData.pageRanges[i]; const orderPage = pagedData.fetch({index: pageRange.index}); for (let j = 0; j < orderPage.data.length; j++) { const result = orderPage.data[j]; const transformResult = this.transform(result); response.push(transformResult.response); if (!transformResult.continue) { return response; } } } return response; } /** Async get all search results */ public getResultsClient(callback: (response?: T[], error?: Error) => void): void { if (!this.transform) { throw new Error("A transform method needs to be set"); } if (this.searchClient === undefined) { throw new Error("Method must be run on User Interface"); } const response: T[] = []; this.searchClient.then((search) => { const searchPromise = search.runPaged.promise({ pageSize: this.pageSize }); searchPromise.then((pagedData) => { if (pagedData.count === 0) { return callback(response); } for (let i = 0; i < pagedData.pageRanges.length; i++) { const pageRange = pagedData.pageRanges[i]; const orderPage = pagedData.fetch({index: pageRange.index}); for (let j = 0; j < orderPage.data.length; j++) { const result = orderPage.data[j]; const transformResult = this.transform(result); response.push(transformResult.response); if (!transformResult.continue || j === orderPage.data.length - 1) { return callback(response); } } } }); searchPromise.catch((error) => { callback(undefined, new Error(error)); }); }); } /** Remove a column to the current search */ public removeColumn(column: CreateSearchColumnOptions): void { this.search.columns = this.search.columns.filter((c) => { return !( SearchLibrary.hasSameKeyValue(column, c, "name") && SearchLibrary.hasSameKeyValue(column, c, "join)") && SearchLibrary.hasSameKeyValue(column, c, "summary") && SearchLibrary.hasSameKeyValue(column, c, "formula") && SearchLibrary.hasSameKeyValue(column, c, "function") && SearchLibrary.hasSameKeyValue(column, c, "label") && SearchLibrary.hasSameKeyValue(column, c, "sort") ); }); } /** Remove a filter to the current search */ public removeFilter(filter: CreateSearchFilterOptions): void { this.search.filters = this.search.filters.filter((f) => { return !( SearchLibrary.hasSameKeyValue(filter, f, "name") && SearchLibrary.hasSameKeyValue(filter, f, "formula") && SearchLibrary.hasSameKeyValue(filter, f, "join)") && SearchLibrary.hasSameKeyValue(filter, f, "summary") && SearchLibrary.hasSameKeyValue(filter, f, "operator") && SearchLibrary.hasSameKeyValue(filter, f, "values") ); }); } /** Set transformation method on search results */ public setTransform(transform: SearchTransform): void { this.transform = transform; } } export default SearchLibrary;