src/api/listHelpers.ts (61 lines of code) (raw):

/* * Copyright 2020 Spotify AB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import SQL, { SQLStatement } from 'sql-template-strings'; import { Query } from 'express-serve-static-core'; import { DbConnectionType } from '../db'; import { InvalidRequestError } from '../errors'; export interface ListRequest { limit?: number; offset?: number; where?: SQLStatement; } export interface ListResponse<Item> extends ListRequest { items: Item[]; total: number; } export type ListItemsGetter<Item> = ( conn: DbConnectionType, options: ListRequest, ) => Promise<Item[]>; export type ListTotalGetter = (conn: DbConnectionType) => Promise<number>; export async function getListAsListResponse<Item>( conn: DbConnectionType, options: ListRequest, itemsGetter: ListItemsGetter<Item>, totalGetter: ListTotalGetter, ): Promise<ListResponse<Item>> { const [items, total] = await Promise.all([ itemsGetter(conn, options), totalGetter(conn), ]); const { limit, offset } = options; return { items, total, limit, offset }; } export function listResponseFactory<Item>( itemsGetter: ListItemsGetter<Item>, totalGetter: ListTotalGetter, ) { return async (conn: DbConnectionType, options: ListRequest) => await getListAsListResponse(conn, options, itemsGetter, totalGetter); } export function listOptionsFromQuery( query: Query, defaultLimit = 25, defaultOffset = 0, ): ListRequest { const { limit: limitStr = defaultLimit, offset: offsetStr = defaultOffset } = query; const limit = +limitStr; const offset = +offsetStr; if (isNaN(limit)) throw new InvalidRequestError(`limit must be a number.`); if (isNaN(offset)) throw new InvalidRequestError(`offset must be a number.`); return { limit, offset }; } export function addListRequestToQuery( query: SQLStatement, request: ListRequest, ): SQLStatement { if (typeof request.limit === 'number') query.append(SQL`\nLIMIT ${request.limit}`); if (typeof request.offset === 'number') query.append(SQL`\nOFFSET ${request.offset}`); return query; }