Skip to content

Commit

Permalink
Improve types (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
kibertoad authored Jul 8, 2024
1 parent 5c9e04b commit ce8ef63
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 124 deletions.
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { sendPost, sendGet, sendPut, sendDelete, sendPatch } from './src/client.js'
export { sendPost, sendGet, sendPut, sendDelete, sendPatch, UNKNOWN_SCHEMA } from './src/client.js'
16 changes: 10 additions & 6 deletions src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,16 +817,22 @@ describe('frontend-http-client', () => {

const response = await sendGet(client, {
path: '/',
responseBodySchema: z.null(),
responseBodySchema: z.object({
id: z.string(),
}),
isEmptyResponseExpected: true,
})

// This is for checking TS types, we are checking if it infers that the type is not WretchResponse correctly
if (response) {
// @ts-expect-error WretchResponse has this field, ResponseBody does not
// @ts-expect-error WretchResponse has this field, null does not
expect(response.ok).toBe(true)
}

// This is to test TS types: it should correctly infer that value is null or defined schema
if (response) {
expect(response.id).toBeDefined()
}

expect(response).toBe(null)
})

Expand Down Expand Up @@ -857,9 +863,7 @@ describe('frontend-http-client', () => {
})

// This is for checking TS types, we are checking if it infers the responseBody type as null | WretchResponse correctly
if (responseBody) {
expect(responseBody.ok).toBe(true)
}
expect(responseBody.ok).toBe(true)

expect(responseBody).containSubset({
status: 200,
Expand Down
146 changes: 47 additions & 99 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { stringify } from 'fast-querystring'
import type { WretchResponse } from 'wretch'
import { WretchError } from 'wretch/resolver'
import { z } from 'zod'

import type {
Expand All @@ -12,89 +9,31 @@ import type {
ResourceChangeParams,
WretchInstance,
} from './types.js'
import { tryToResolveJsonBody } from './utils/bodyUtils.js'
import { type Either, failure, success, isFailure } from './utils/either.js'

const UNKNOWN_SCHEMA = z.unknown()

function parseRequestBody<RequestBodySchema extends z.Schema>({
body,
requestBodySchema,
path,
}: {
body: unknown
requestBodySchema?: RequestBodySchema
path: string
}): Either<z.ZodError, z.input<RequestBodySchema>> {
if (!body) {
return success(body)
}

if (!requestBodySchema) {
return success(body)
}

const result = requestBodySchema.safeParse(body)

if (!result.success) {
console.error({
path,
body,
error: result.error,
})
return failure(result.error)
}

return success(body)
}

function parseQueryParams<RequestQuerySchema extends z.Schema>({
queryParams,
queryParamsSchema,
path,
}: {
queryParams: unknown
queryParamsSchema?: RequestQuerySchema
path: string
}): Either<z.ZodError, string> {
if (!queryParams) {
return success('')
}
import { parseRequestBody, tryToResolveJsonBody } from './utils/bodyUtils.js'
import { isFailure } from './utils/either.js'
import { buildWretchError } from './utils/errorUtils.js'
import { parseQueryParams } from './utils/queryUtils.js'

if (!queryParamsSchema) {
return success(`?${stringify(queryParams)}`)
}

const result = queryParamsSchema.safeParse(queryParams)

if (!result.success) {
console.error({
path,
queryParams,
error: result.error,
})
return failure(result.error)
}

return success(`?${stringify(queryParams)}`)
}
export const UNKNOWN_SCHEMA = z.unknown()

async function sendResourceChange<
T extends WretchInstance,
ResponseBody,
IsNonJSONResponseExpected extends boolean,
IsEmptyResponseExpected extends boolean,
RequestBodySchema extends z.Schema | undefined = undefined,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
>(
wretch: T,
method: 'post' | 'put' | 'patch',
params: ResourceChangeParams<
RequestBodySchema,
ResponseBody,
RequestQuerySchema,
IsNonJSONResponseExpected
IsNonJSONResponseExpected,
IsEmptyResponseExpected,
RequestQuerySchema
>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
const body = parseRequestBody({
body: params.body,
requestBodySchema: params.requestBodySchema,
Expand Down Expand Up @@ -154,16 +93,7 @@ async function sendResourceChange<

return bodyParseResult.result
},
) as Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>>
}

function buildWretchError(message: string, response: WretchResponse): WretchError {
const error = new WretchError(message)
error.response = response
error.status = response.status
error.url = response.url

return error
) as Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>>
}

/* METHODS */
Expand All @@ -175,12 +105,18 @@ export async function sendGet<
ResponseBody,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
IsEmptyResponseExpected extends boolean = false,
>(
wretch: T,
params: RequestQuerySchema extends z.Schema
? QueryParams<RequestQuerySchema, ResponseBody, IsNonJSONResponseExpected>
: FreeQueryParams<ResponseBody, IsNonJSONResponseExpected>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
? QueryParams<
RequestQuerySchema,
ResponseBody,
IsNonJSONResponseExpected,
IsEmptyResponseExpected
>
: FreeQueryParams<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
const queryParams = parseQueryParams({
queryParams: params.queryParams,
queryParamsSchema: params.queryParamsSchema,
Expand Down Expand Up @@ -227,7 +163,7 @@ export async function sendGet<
}

return bodyParseResult.result
}) as RequestResultType<ResponseBody, IsNonJSONResponseExpected>
}) as RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>
}

/* POST */
Expand All @@ -238,15 +174,17 @@ export function sendPost<
RequestBodySchema extends z.Schema | undefined = undefined,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
IsEmptyResponseExpected extends boolean = false,
>(
wretch: T,
params: ResourceChangeParams<
RequestBodySchema,
ResponseBody,
RequestQuerySchema,
IsNonJSONResponseExpected
IsNonJSONResponseExpected,
IsEmptyResponseExpected,
RequestQuerySchema
>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
return sendResourceChange(wretch, 'post', params)
}

Expand All @@ -258,15 +196,17 @@ export function sendPut<
RequestBodySchema extends z.Schema | undefined = undefined,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
IsEmptyResponseExpected extends boolean = false,
>(
wretch: T,
params: ResourceChangeParams<
RequestBodySchema,
ResponseBody,
RequestQuerySchema,
IsNonJSONResponseExpected
IsNonJSONResponseExpected,
IsEmptyResponseExpected,
RequestQuerySchema
>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
return sendResourceChange(wretch, 'put', params)
}

Expand All @@ -278,15 +218,17 @@ export function sendPatch<
RequestBodySchema extends z.Schema | undefined = undefined,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
IsEmptyResponseExpected extends boolean = false,
>(
wretch: T,
params: ResourceChangeParams<
RequestBodySchema,
ResponseBody,
RequestQuerySchema,
IsNonJSONResponseExpected
IsNonJSONResponseExpected,
IsEmptyResponseExpected,
RequestQuerySchema
>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
return sendResourceChange(wretch, 'patch', params)
}

Expand All @@ -297,12 +239,18 @@ export function sendDelete<
ResponseBody,
RequestQuerySchema extends z.Schema | undefined = undefined,
IsNonJSONResponseExpected extends boolean = false,
IsEmptyResponseExpected extends boolean = true,
>(
wretch: T,
params: RequestQuerySchema extends z.Schema
? DeleteParams<RequestQuerySchema, ResponseBody, IsNonJSONResponseExpected>
: FreeDeleteParams<ResponseBody, IsNonJSONResponseExpected>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>> {
? DeleteParams<
RequestQuerySchema,
ResponseBody,
IsNonJSONResponseExpected,
IsEmptyResponseExpected
>
: FreeDeleteParams<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>,
): Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>> {
const queryParams = parseQueryParams({
queryParams: params.queryParams,
queryParamsSchema: params.queryParamsSchema,
Expand Down Expand Up @@ -351,5 +299,5 @@ export function sendDelete<
}

return bodyParseResult.result
}) as Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected>>
}) as Promise<RequestResultType<ResponseBody, IsNonJSONResponseExpected, IsEmptyResponseExpected>>
}
Loading

0 comments on commit ce8ef63

Please sign in to comment.