UNPKG

@lightningkite/lightning-server-simplified

Version:
265 lines (218 loc) 8.2 kB
import { evaluateCondition, Condition } from "./Condition"; import { Modification, evaluateModification } from "./Modification"; import { Query, MassModification, EntryChange, GroupCountQuery, AggregateQuery, GroupAggregateQuery, Aggregate, DeepPartial, QueryPartial, } from "./otherModels"; import { HasId } from "./sessionRest"; export function mockRestEndpointFunctions<T extends HasId>( items: T[], label: string ) { return { default(userToken?: string): Promise<T> { return Promise.reject(new Error("Not implemented")); }, query(input: Query<T>, userToken?: string): Promise<Array<T>> { const { limit, skip = 0, orderBy, condition } = input; const filteredItems = condition ? items.filter((item) => evaluateCondition(condition, item)) : items; let sortedItems = filteredItems; if (orderBy?.length) { const sortModel: { key: keyof T; ascending: boolean }[] = orderBy.map( (orderItem) => { const ascending = !orderItem.toString().startsWith("-"); const key = ( ascending ? orderItem : orderItem.toString().substring(1) ) as keyof T; return { key, ascending }; } ); sortedItems = filteredItems.sort((a, b) => { for (const { key, ascending } of sortModel) { const aValue = a[key]; const bValue = b[key]; if (aValue < bValue) { return ascending ? -1 : 1; } else if (aValue > bValue) { return ascending ? 1 : -1; } } return 0; }); } const paginatedItems = limit ? sortedItems.slice(skip, skip + limit) : sortedItems.slice(skip); const result = paginatedItems; console.info(label, "query", { query: input, result }); return Promise.resolve(result); }, queryPartial(input: QueryPartial<T>, userToken?: string): Promise<Array<DeepPartial<T>>> { return this.query(input, userToken) }, detail(id: string, userToken?: string): Promise<T> { const result = items.find((item) => item._id === id); console.info(label, "detail", { id, result }); return new Promise((resolve, reject) => { if (result) resolve(result); else reject(); }); }, insertBulk(input: Array<T>, userToken?: string): Promise<Array<T>> { input.forEach((item) => items.push(item)); console.info(label, "insertBulk", { input }); return Promise.resolve(input); }, insert(input: T, userToken?: string): Promise<T> { items.push(input); console.info(label, "insert", { input }); return Promise.resolve(input); }, upsert(id: string, input: T, userToken?: string): Promise<T> { console.info(label, "upsert", { id, input }); const existingItemIndex = items.findIndex((item) => item._id === id); if (existingItemIndex >= 0) { items[existingItemIndex] = input; } else { items.push(input); } return Promise.resolve(input); }, bulkReplace(input: Array<T>, userToken?: string): Promise<Array<T>> { console.info(label, "bulkReplace", { input }); input.forEach((item) => this.replace(item._id, item, userToken)); return Promise.resolve(input); }, replace(id: string, input: T, userToken?: string): Promise<T> { console.info(label, "replace", { id, input }); const existingItemIndex = items.findIndex((item) => item._id === id); if (existingItemIndex >= 0) { items[existingItemIndex] = input; return Promise.resolve(input); } return Promise.reject(); }, async bulkModify( input: MassModification<T>, userToken?: string ): Promise<number> { console.info(label, "bulkModify", { input }); const filteredItems = items.filter((item) => evaluateCondition(input.condition, item) ); return filteredItems.length; }, modifyWithDiff( id: string, input: Modification<T>, userToken?: string ): Promise<EntryChange<T>> { return Promise.resolve({}); }, modify(id: string, input: Modification<T>, userToken?: string): Promise<T> { console.info(label, "modify", { id, input }); const existingItemIndex = items.findIndex((item) => item._id === id); if (existingItemIndex < 0) return Promise.reject(); const newItem = evaluateModification(input, items[existingItemIndex]); items[existingItemIndex] = newItem; return Promise.resolve(newItem); }, bulkDelete(input: Condition<T>, userToken?: string): Promise<number> { console.info(label, "bulkDelete", { input }); if (!items) return Promise.reject(); const previousLength = items.length; items = items.filter((item) => !evaluateCondition(input, item)); return Promise.resolve(previousLength - items.length); }, delete(id: string, userToken?: string): Promise<void> { console.info(label, "delete", { id }); const existingItemIndex = items.findIndex((item) => item._id === id); if (existingItemIndex >= 0) { items.splice(existingItemIndex, 1); return Promise.resolve(); } else { return Promise.reject(); } }, count(input: Condition<T>, userToken?: string): Promise<number> { console.info(label, "count", { input }); return this.query({ condition: input }, userToken).then( (it) => it.length ); }, groupCount( input: GroupCountQuery<T>, userToken?: string ): Promise<Record<string, number>> { const { condition, groupBy } = input; const filteredItems = condition ? items.filter((item) => evaluateCondition(condition, item)) : items; const result = filteredItems.reduce((result, item) => { const key = typeof item[groupBy] === "string" ? (item[groupBy] as unknown as string) : JSON.stringify(item[groupBy]); result[key] = (result[key] || 0) + 1; return result; }, {} as Record<string, number>); console.info(label, "groupCount", { input, result }); return Promise.resolve(result); }, aggregate(input: AggregateQuery<T>, userToken?: string): Promise<number> { const { condition, aggregate, property } = input; const filteredItems = condition ? items.filter((item) => evaluateCondition(condition, item)) : items; const result = performAggregate( filteredItems.map((item) => Number(item[property])), aggregate ); console.info(label, "aggregate", { input, result }); return Promise.resolve(result); }, groupAggregate( input: GroupAggregateQuery<T>, userToken?: string ): Promise<Record<string, number>> { const { aggregate, condition, property, groupBy } = input; const filteredItems = condition ? items.filter((item) => evaluateCondition(condition, item)) : items; const numberArrays = filteredItems.reduce((result, item) => { const key = typeof item[groupBy] === "string" ? (item[groupBy] as unknown as string) : JSON.stringify(item[groupBy]); result[key] = [...(result[key] || []), Number(item[property])]; return result; }, {} as Record<string, number[]>); const result = Object.keys(numberArrays).reduce((result, key) => { const array = numberArrays[key]; result[key] = performAggregate(array, aggregate); return result; }, {} as Record<string, number>); console.info(label, "groupAggregate", { input, result }); return Promise.resolve(result); }, }; } function performAggregate(array: number[], aggregate: Aggregate): number { switch (aggregate) { case Aggregate.Sum: return array.reduce((sum, value) => sum + value, 0); case Aggregate.Average: return array.reduce((sum, value) => sum + value, 0) / array.length; default: throw new Error(`Not implemented aggregate: ${aggregate}`); } }