From 3f0923f70a8a89394673e083b4cb4b88efdd2865 Mon Sep 17 00:00:00 2001 From: Tyler Hall Date: Wed, 24 May 2023 00:26:06 -0400 Subject: [PATCH] fix(port-data): attempt to parse limit on listDocuments and queryDocuments --- packages/port-data/mod.test.ts | 182 ++++++++++++++++++++++++++++----- packages/port-data/port.ts | 23 ++++- 2 files changed, 179 insertions(+), 26 deletions(-) diff --git a/packages/port-data/mod.test.ts b/packages/port-data/mod.test.ts index cec0b103..4b5fa2ea 100644 --- a/packages/port-data/mod.test.ts +++ b/packages/port-data/mod.test.ts @@ -240,7 +240,7 @@ Deno.test('data', async (t) => { }) await t.step('listDocuments', async (t) => { - await t.step('should validate the inputs', async () => { + await t.step('should validate the inputs', async (t) => { assert( await adapter.listDocuments({ db: '123', @@ -264,17 +264,79 @@ Deno.test('data', async (t) => { }) ) - await assertRejects(() => - adapter.listDocuments({ - db: 'foo', - // @ts-ignore - limit: 'bar', - startkey: '123', - endkey: '456', - keys: '123,456', - descending: false, + await t.step('limit', async (t) => { + await t.step('should parse to a number, if provided', async () => { + assert( + await adapter.listDocuments({ + db: '123', + limit: 1000, + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }), + ) + + assert( + await adapter.listDocuments({ + db: '123', + limit: '456', + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }), + ) + + assert( + await adapter.listDocuments({ + db: '123', + limit: ' 456 ', + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }), + ) + + assert( + await adapter.listDocuments({ + db: '123', + limit: undefined, + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }), + ) }) - ) + + await t.step('should reject the unparseable value', async () => { + await assertRejects(() => + adapter.listDocuments({ + db: 'foo', + // @ts-ignore + limit: 'not parseable', + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }) + ) + + await assertRejects(() => + adapter.listDocuments({ + db: 'foo', + // @ts-ignore + limit: [], + startkey: '123', + endkey: '456', + keys: '123,456', + descending: false, + }) + ) + }) + }) await assertRejects(() => adapter.listDocuments({ @@ -339,7 +401,7 @@ Deno.test('data', async (t) => { }) await t.step('queryDocuments', async (t) => { - await t.step('should validate the inputs', async () => { + await t.step('should validate the inputs', async (t) => { assert( await adapter.queryDocuments({ db: 'foo', @@ -418,19 +480,91 @@ Deno.test('data', async (t) => { }) ) - await assertRejects(() => - adapter.queryDocuments({ - db: '123', - query: { - selector: { name: { $gt: 'mike' } }, - fields: ['name'], - sort: ['ASC' as const], - // @ts-ignore - limit: 'wut', - use_index: 'idx-name', - }, + await t.step('limit', async (t) => { + await t.step('should parse to a number, if provided', async () => { + assert( + await adapter.queryDocuments({ + db: 'foo', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + limit: 1000, + use_index: 'idx-name', + }, + }), + ) + + assert( + await adapter.queryDocuments({ + db: 'foo', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + limit: '1000', + use_index: 'idx-name', + }, + }), + ) + + assert( + await adapter.queryDocuments({ + db: 'foo', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + limit: ' 1000 ', + use_index: 'idx-name', + }, + }), + ) + + assert( + await adapter.queryDocuments({ + db: 'foo', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + limit: undefined, + use_index: 'idx-name', + }, + }), + ) }) - ) + + await t.step('should reject the unparseable value', async () => { + await assertRejects(() => + adapter.queryDocuments({ + db: '123', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + // @ts-ignore + limit: 'wut', + use_index: 'idx-name', + }, + }) + ) + + await assertRejects(() => + adapter.queryDocuments({ + db: '123', + query: { + selector: { name: { $gt: 'mike' } }, + fields: ['name'], + sort: ['ASC' as const], + // @ts-ignore + limit: [], + use_index: 'idx-name', + }, + }) + ) + }) + }) await assertRejects(() => adapter.queryDocuments({ diff --git a/packages/port-data/port.ts b/packages/port-data/port.ts index 97e49a21..82558638 100644 --- a/packages/port-data/port.ts +++ b/packages/port-data/port.ts @@ -42,6 +42,25 @@ const SortEnum = z.enum(['ASC', 'DESC']) const doc = z.record(z.any()) +const maybeNumber = z.preprocess( + (val) => { + if (typeof val === 'number') return val + /** + * Let zod do its job and reject the value + */ + if (typeof val !== 'string') return val + + const parsed = parseFloat(val) + /** + * The string could not be parsed into a number + * so let zod do its job and reject the value + */ + if (isNaN(parsed)) return val + return parsed + }, + z.number(), +) + export const port = z.object({ createDatabase: z .function() @@ -73,7 +92,7 @@ export const port = z.object({ .args( z.object({ db: z.string(), - limit: z.number().optional(), + limit: maybeNumber.optional(), startkey: z.string().optional(), endkey: z.string().optional(), // TODO: should we just make this an array? @@ -92,7 +111,7 @@ export const port = z.object({ selector: z.record(z.any()).optional(), fields: z.array(z.string()).optional(), sort: z.array(z.union([SortEnum, z.record(SortEnum)])).optional(), - limit: z.number().optional(), + limit: maybeNumber.optional(), use_index: z.string().optional(), }), }),