From 24c1c95969d56f65b8f993c182b794c1de287a6e Mon Sep 17 00:00:00 2001 From: "leto.w" Date: Thu, 12 Sep 2024 14:03:08 +0800 Subject: [PATCH 1/3] feat: query support array --- README.md | 2 +- packages/core/src/core.ts | 24 +++++++++++++------ packages/core/src/types.ts | 5 +++- packages/test/server/main.go | 5 ++++ packages/test/tests/basic.spec.ts | 38 ++++++++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7a26bbb..485bf25 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ type LylaRequestOptions = { * `body`. */ json?: any - query?: Record + query?: Record baseUrl?: string /** * Abort signal of the request. diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 299300f..6e78cd8 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -38,9 +38,15 @@ function isOkStatus(status: number): boolean { declare const setTimeout: (callback: () => void, timeout?: number) => number +type URLSearchParamsLike = { + toString: () => string + append: (key: string, value: string) => void +} + // It exists both in node, browser, miniprogram environment declare const URLSearchParams: { - new (params: Record): { toString: () => string } + new (params: Record): URLSearchParamsLike + new (): URLSearchParamsLike } export function createLyla( @@ -164,13 +170,17 @@ export function createLyla( // Resolve query string, patch it to URL if (_options.query) { - const resolvedQuery: Record = {} - for (const key in _options.query) { - const v = _options.query[key] - if (v === undefined || v === null) continue - resolvedQuery[key] = v.toString() + const urlSearchParams = new URLSearchParams() + for (const [key, value] of Object.entries(_options.query)) { + if (Array.isArray(value)) { + for (const v of value) { + urlSearchParams.append(key, v.toString()) + } + } else if (value !== undefined && value !== null) { + urlSearchParams.append(key, value.toString()) + } } - const urlSearchParams = new URLSearchParams(resolvedQuery) + const queryString = urlSearchParams.toString() if (_options.url.includes('?')) { const badRequestError = defineLylaError< diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 2c01436..f80b5b7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -53,7 +53,10 @@ export type LylaRequestOptions< * `body`. */ json?: any - query?: Record + query?: Record< + string, + string | number | boolean | undefined | null | string[] | number[] + > baseUrl?: string /** * Abort signal of the request. diff --git a/packages/test/server/main.go b/packages/test/server/main.go index 49911ef..90e3383 100644 --- a/packages/test/server/main.go +++ b/packages/test/server/main.go @@ -42,6 +42,11 @@ func GetTestRoutes(r *gin.Engine) { c.Header("X-UPPER", "X-UPPER") c.Header("x-lower", "x-lower") }) + r.GET("/api/get-query", func(c *gin.Context) { + queryParams := c.Request.URL.Query() + // return {"key1": ["value1"]} + c.JSON(200, queryParams) + }) } func PostTestRoutes(r *gin.Engine) { diff --git a/packages/test/tests/basic.spec.ts b/packages/test/tests/basic.spec.ts index 0c6e401..5b4591f 100644 --- a/packages/test/tests/basic.spec.ts +++ b/packages/test/tests/basic.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from '@playwright/test' import { beforeEach } from './utils' -import "./types" +import './types' beforeEach(test) ;(['get', 'post', 'delete', 'put', 'patch'] as const).forEach((method) => { @@ -71,3 +71,39 @@ beforeEach(test) }) } }) + +test('parse normal query', async ({ page }) => { + const res = await page.evaluate(async () => { + const res = await window.lyla.get('/api/get-query', { + query: { + key1: 'value1' + } + }) + return res.json + }) + expect(res).toMatchObject({ key1: ['value1'] }) +}) + +test('parse array query', async ({ page }) => { + const res = await page.evaluate(async () => { + const res = await window.lyla.get('/api/get-query', { + query: { + key1: ['value1', 'value2'] + } + }) + return res.json + }) + expect(res).toMatchObject({ key1: ['value1', 'value2'] }) +}) + +test('parse empty query', async ({ page }) => { + const res = await page.evaluate(async () => { + const res = await window.lyla.get('/api/get-query', { + query: { + key1: null + } + }) + return res.json + }) + expect(res).toMatchObject({}) +}) From 51545c058301e7930f8b9a845d17293c18494135 Mon Sep 17 00:00:00 2001 From: "leto.w" Date: Thu, 12 Sep 2024 14:06:53 +0800 Subject: [PATCH 2/3] feat: improve type declaration --- README.md | 2 +- packages/core/src/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 485bf25..0123b38 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ type LylaRequestOptions = { * `body`. */ json?: any - query?: Record + query?: Record> baseUrl?: string /** * Abort signal of the request. diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f80b5b7..936e87f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -55,7 +55,7 @@ export type LylaRequestOptions< json?: any query?: Record< string, - string | number | boolean | undefined | null | string[] | number[] + string | number | boolean | undefined | null | Array > baseUrl?: string /** From 11308f36ccb4fb58dda7a215feeef5c994a9b01b Mon Sep 17 00:00:00 2001 From: "leto.w" Date: Thu, 12 Sep 2024 14:22:23 +0800 Subject: [PATCH 3/3] docs: update readme about query --- README.md | 16 +++++++++++++++- README.zh_CN.md | 16 +++++++++++++++- packages/core/src/types.ts | 7 ++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0123b38..acc26d3 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,21 @@ type LylaRequestOptions = { * `body`. */ json?: any - query?: Record> + /** + * Query object, also known as search params. + * Note, if you want to set `null` or `undefined` as value in query, + * use object like `query: { key: "undefined" }` instead of `query: { key: undefined }`. + * Otherwise, the k-v pair will be ignored. + */ + query?: Record< + string, + | string + | number + | boolean + | Array + | null + | undefined + > baseUrl?: string /** * Abort signal of the request. diff --git a/README.zh_CN.md b/README.zh_CN.md index 0248d26..7139c91 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -133,7 +133,21 @@ type LylaRequestOptions = { * 需要被写入请求主体的 JSON 值,不可以同时和 body 使用 */ json?: any - query?: Record + /** + * Query 对象,用于构建 URL 里的搜索参数 search params。 + * 注意,如果你想在查询中设置 `null` 或 `undefined` 作为值, + * 请使用字符串作为值,如 `query: { key: "undefined" }` ,而非 `query: { key: undefined }`。 + * 否则,该键值对将被忽略。 + */ + query?: Record< + string, + | string + | number + | boolean + | Array + | null + | undefined + > baseUrl?: string /** * 请求使用的 Abort signal diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 936e87f..d2ee57c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -55,7 +55,12 @@ export type LylaRequestOptions< json?: any query?: Record< string, - string | number | boolean | undefined | null | Array + | string + | number + | boolean + | undefined + | null + | Array > baseUrl?: string /**