feat: Prefer count header, procedures outside exposed schemas; 0.2.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@crudy/restloom",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"description": "A REST API generated from your database's catalog: discovery, a curl-friendly query grammar, relations of every kind, safe defaults",
|
||||
"keywords": ["rest", "api", "postgrest", "sql", "database", "crud", "generator"],
|
||||
|
||||
@@ -23,8 +23,11 @@ export interface Dialect {
|
||||
readonly name: string;
|
||||
readonly capabilities: Capabilities;
|
||||
readonly renderer: SqlRenderer;
|
||||
/** Reads the tables, views and functions of the given schemas and tables. */
|
||||
introspect(scope: { schemas: string[]; tables: string[] }): Promise<Catalog>;
|
||||
/**
|
||||
* Reads the tables and views of the given schemas and tables, and the
|
||||
* functions of the given schemas plus the named ones (schema.name).
|
||||
*/
|
||||
introspect(scope: { schemas: string[]; tables: string[]; functions: string[] }): Promise<Catalog>;
|
||||
/** Runs fn in one transaction with a statement timeout; read-only when asked. */
|
||||
transaction<T>(fn: (tx: Tx) => Promise<T>, options: { timeoutMs: number; readOnly: boolean }): Promise<T>;
|
||||
/** The JSON value of a statement's `r` column as a JavaScript value: drivers that parse JSON return it as is, others parse text. */
|
||||
|
||||
@@ -30,6 +30,8 @@ export async function readRows(db: Dialect, tx: Tx, resource: Resource, request:
|
||||
scope: (r: Resource) => Filter[], id?: string, extra: Filter[] = []): Promise<RestResponse> {
|
||||
const plan = parseRead(resource, request.query, limits);
|
||||
plan.where.push(...extra);
|
||||
const prefer = /\bcount=(exact|estimated)\b/.exec(request.headers.prefer ?? '');
|
||||
if (plan.count === 'none' && prefer) plan.count = prefer[1] as 'exact' | 'estimated';
|
||||
if (id !== undefined) {
|
||||
plan.where.unshift(keyFilter(resource, id));
|
||||
plan.limit = 1;
|
||||
|
||||
@@ -43,7 +43,8 @@ export function createREST<C = unknown>(options: RestOptions<C>): Rest<C> {
|
||||
const db = options.db;
|
||||
let loading: Promise<Api> | null = null;
|
||||
|
||||
const load = () => (loading ??= db.introspect({ schemas: options.expose.schemas ?? [], tables: options.expose.tables ?? [] })
|
||||
const functions = Object.values(options.procedures ?? {}).map((p) => p.function).filter((f) => f.includes('.'));
|
||||
const load = () => (loading ??= db.introspect({ schemas: options.expose.schemas ?? [], tables: options.expose.tables ?? [], functions })
|
||||
.then((catalog: Catalog) => ({
|
||||
resources: buildResources(catalog, options as RestOptions<unknown>),
|
||||
procedures: buildProcedures(catalog, options.procedures ?? {}),
|
||||
|
||||
@@ -18,3 +18,9 @@ test('the node listener serves under its prefix and refuses bad bodies', async (
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('Prefer: count=exact counts as count=exact does', async () => {
|
||||
const rest = createREST({ db: fakeDialect(), expose: { schemas: ['app'] } });
|
||||
const res = await rest.handle({ method: 'GET', path: '/items', query: new URLSearchParams(), headers: { prefer: 'count=exact' }, context: undefined });
|
||||
assert.equal(res.headers['content-range'], '0-0/1');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user