@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
JavaScript
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