UNPKG

@adonis-agora/filter

Version:

Server-side query filtering/sorting/pagination for AdonisJS — Spatie-style input, a Lucid adapter, and field allow-listing. Part of the Agora ecosystem.

66 lines 3.67 kB
import { HttpContext } from '@adonisjs/core/http'; import { applyFilterFromRequest, } from './apply_from_request.js'; import { isFilterClass } from './filter_class.js'; import { readPagination, stashPagination } from './pagination_stash.js'; /** * O ctx da request para o macro: explícito, ou o `HttpContext` ativo lido do AsyncLocalStorage do * Adonis quando omitido. `getOrFail()` lança quando não há request em escopo (ex.: chamada dentro de * um job/command) — nesse caso passe o ctx explicitamente. Fica no macro (camada Adonis); o * `applyFilterFromRequest` livre continua framework-agnostic, exigindo o ctx. */ function resolveCtx(ctx) { return ctx ?? HttpContext.getOrFail(); } /** * Register the chainable filter macros onto a Lucid query-builder class (the * method-call form of {@link applyFilterFromRequest}). Call this from a provider's * `boot()` with `ModelQueryBuilder` (from `@adonisjs/lucid/orm`); the * `@adonis-agora/filter` provider does exactly that. * * Two macros are added: * * - `query.applyFilterFromRequest(spec, ctx, options?)` — applies the spec's * server scope + allow-listed filter/sort/search and returns the query so it * chains (`User.query().applyFilterFromRequest(spec, ctx).orderBy(...)`). The * resolved pagination is dropped; use `filterPaginate` (or the free function) * when you need it. * - `query.filterPaginate(spec, ctx, options?)` — applies the same and then * `paginate(page, size)` with the resolved pagination, returning Lucid's * paginator (`await User.query().filterPaginate(spec, ctx)`). * * Idempotent enough to call once at boot; calling twice re-defines the macros to * the same implementations. */ export function registerFilterMacros(ModelQueryBuilder) { ModelQueryBuilder.macro('applyFilterFromRequest', function (filter, ctx, options) { // A class is resolved through the container, so this leg is async — and it resolves to the // pagination rather than to the builder: a Lucid query builder is thenable, so a promise that // resolved to one would execute the query instead of handing it back. You already hold the // builder; `await query.applyFilterFromRequest(UserFilter)` gives you its `{ page, size }`. if (isFilterClass(filter)) { return applyFilterFromRequest(this, filter, resolveCtx(ctx), options).then((pagination) => { stashPagination(this, pagination); return pagination; }); } stashPagination(this, applyFilterFromRequest(this, filter, resolveCtx(ctx), options)); return this; }); ModelQueryBuilder.macro('filterPaginate', function (filter, ctx, options) { // No filter argument: this query has already been through one (the model's `filter()`, or the // `applyFilterFromRequest` macro), so page it with the pagination that call resolved. if (filter === undefined) { const stashed = readPagination(this); if (!stashed) { throw new Error('filterPaginate() was called with no filter on a query that has not been filtered. Pass the filter — filterPaginate(UserFilter) — or apply one first.'); } return this.paginate(stashed.page, stashed.size); } if (isFilterClass(filter)) { return applyFilterFromRequest(this, filter, resolveCtx(ctx), options).then(({ page, size }) => this.paginate(page, size)); } const { page, size } = applyFilterFromRequest(this, filter, resolveCtx(ctx), options); return this.paginate(page, size); }); } //# sourceMappingURL=lucid_macros.js.map