UNPKG

feathers-graph-populate

Version:

Add lightning fast, GraphQL-like populates to your FeathersJS API.

106 lines (86 loc) 3.46 kB
import _get from 'lodash/get.js' import _isEmpty from 'lodash/isEmpty.js' import _merge from 'lodash/merge.js' import { shallowPopulate as makeShallowPopulate } from './shallow-populate.hook' import type { HookContext, Query } from '@feathersjs/feathers' import type { GraphPopulateHookOptions, Method } from '../types' import type { GraphPopulateApplication } from '../app/graph-populate.class' const FILTERS = ['$limit', '$select', '$skip', '$sort'] /** * Sets up the deepPopulate hook using the provided options. * @param options * @property populates - an object whose properties are named populates to pass to feathers-shallow-populate. * * The deepPopulate hook uses `feathers-shallow-populate` along with a lightweight, * GraphQL-like syntax to populate data between services. It expects to find a query * object at `params.$populateParams.query`. */ export function graphPopulate( options: GraphPopulateHookOptions, ): (context: HookContext) => Promise<HookContext> { if (!options.populates) { throw new Error('options.populates must be provided to the feathers-graph-populate hook') } const { populates } = options return async function deepPopulateHook(context: HookContext): Promise<HookContext> { const populateQuery: Query | undefined = _get(context, 'params.$populateParams.query') if (!populateQuery) return context const { app } = context const graphPopulateApp: GraphPopulateApplication | undefined = (app as any).graphPopulate // Get the populate data based on the query keys const keys = Object.keys(populateQuery) const currentPopulates = keys.reduce((currentPopulates, key) => { if (!populates[key]) return currentPopulates const currentQuery = Object.assign({}, populateQuery[key]) const populate = populates[key] const service = app.service(populate.service) let params = [] if (populate.params) { if (Array.isArray(populate.params)) { params.push(...populate.params) } else { params.push(populate.params) } } if (!_isEmpty(currentQuery)) { const customKeysForQuery: string[] | undefined = _get( service, 'options.graphPopulate.whitelist', ) const extractKeys = [...FILTERS] if (customKeysForQuery) { extractKeys.push(...customKeysForQuery) } const paramsToAdd = Object.keys(currentQuery).reduce( (paramsToAdd, key) => { if (!extractKeys.includes(key)) return paramsToAdd const { query } = paramsToAdd _merge(query, { [key]: currentQuery[key] }) delete currentQuery[key] return paramsToAdd }, { query: {} }, ) params.push(paramsToAdd) } if (!_isEmpty(currentQuery)) { params.push({ $populateParams: { query: currentQuery, }, }) } if (graphPopulateApp) { params = graphPopulateApp.withAppParams(params, context.method as Method, service) } currentPopulates.push(Object.assign({}, populate, { params })) return currentPopulates }, []) if (!currentPopulates || !currentPopulates.length) { return context } const shallowPopulate = makeShallowPopulate({ include: currentPopulates }) const populatedContext = await shallowPopulate(context) return populatedContext } }