Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions packages/toolkit/src/query/core/buildThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type {
SchemaFailureConverter,
SchemaFailureHandler,
SchemaFailureInfo,
SchemaType,
} from '../endpointDefinitions'
import {
calculateProvidedBy,
Expand Down Expand Up @@ -68,7 +69,11 @@ import {
isRejectedWithValue,
SHOULD_AUTOBATCH,
} from './rtkImports'
import { parseWithSchema, NamedSchemaError } from '../standardSchema'
import {
parseWithSchema,
NamedSchemaError,
shouldSkip,
} from '../standardSchema'

export type BuildThunksApiEndpointQuery<
Definition extends QueryDefinition<any, any, any, any, any>,
Expand Down Expand Up @@ -346,7 +351,7 @@ export function buildThunks<
selectors: AllSelectors
onSchemaFailure: SchemaFailureHandler | undefined
catchSchemaFailure: SchemaFailureConverter<BaseQuery> | undefined
skipSchemaValidation: boolean | undefined
skipSchemaValidation: boolean | SchemaType[] | undefined
}) {
type State = RootState<any, string, ReducerPath>

Expand Down Expand Up @@ -569,7 +574,7 @@ export function buildThunks<
const { extraOptions, argSchema, rawResponseSchema, responseSchema } =
endpointDefinition

if (argSchema && !skipSchemaValidation) {
if (argSchema && !shouldSkip(skipSchemaValidation, 'arg')) {
finalQueryArg = await parseWithSchema(
argSchema,
finalQueryArg,
Expand Down Expand Up @@ -633,7 +638,10 @@ export function buildThunks<

let { data } = result

if (rawResponseSchema && !skipSchemaValidation) {
if (
rawResponseSchema &&
!shouldSkip(skipSchemaValidation, 'rawResponse')
) {
data = await parseWithSchema(
rawResponseSchema,
result.data,
Expand All @@ -648,7 +656,7 @@ export function buildThunks<
finalQueryArg,
)

if (responseSchema && !skipSchemaValidation) {
if (responseSchema && !shouldSkip(skipSchemaValidation, 'response')) {
transformedResponse = await parseWithSchema(
responseSchema,
transformedResponse,
Expand Down Expand Up @@ -751,7 +759,11 @@ export function buildThunks<
finalQueryReturnValue = await executeRequest(arg.originalArgs)
}

if (metaSchema && !skipSchemaValidation && finalQueryReturnValue.meta) {
if (
metaSchema &&
!shouldSkip(skipSchemaValidation, 'meta') &&
finalQueryReturnValue.meta
) {
finalQueryReturnValue.meta = await parseWithSchema(
metaSchema,
finalQueryReturnValue.meta,
Expand Down Expand Up @@ -781,7 +793,10 @@ export function buildThunks<
let { value, meta } = caughtError

try {
if (rawErrorResponseSchema && !skipSchemaValidation) {
if (
rawErrorResponseSchema &&
!shouldSkip(skipSchemaValidation, 'rawErrorResponse')
) {
value = await parseWithSchema(
rawErrorResponseSchema,
value,
Expand All @@ -790,15 +805,18 @@ export function buildThunks<
)
}

if (metaSchema && !skipSchemaValidation) {
if (metaSchema && !shouldSkip(skipSchemaValidation, 'meta')) {
meta = await parseWithSchema(metaSchema, meta, 'metaSchema', meta)
}
let transformedErrorResponse = await transformErrorResponse(
value,
meta,
arg.originalArgs,
)
if (errorResponseSchema && !skipSchemaValidation) {
if (
errorResponseSchema &&
!shouldSkip(skipSchemaValidation, 'errorResponse')
) {
transformedErrorResponse = await parseWithSchema(
errorResponseSchema,
transformedErrorResponse,
Expand Down
7 changes: 5 additions & 2 deletions packages/toolkit/src/query/createApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
EndpointDefinitions,
SchemaFailureConverter,
SchemaFailureHandler,
SchemaType,
} from './endpointDefinitions'
import {
DefinitionType,
Expand Down Expand Up @@ -280,6 +281,8 @@ export interface CreateApiOptions<
*
* If set to `true`, will skip schema validation for all endpoints, unless overridden by the endpoint.
*
* Can be overridden for specific schemas by passing an array of schema types to skip.
*
* @example
* ```ts
* // codeblock-meta no-transpile
Expand All @@ -288,7 +291,7 @@ export interface CreateApiOptions<
*
* const api = createApi({
* baseQuery: fetchBaseQuery({ baseUrl: '/' }),
* skipSchemaValidation: process.env.NODE_ENV === "test", // skip schema validation in tests, since we'll be mocking the response
* skipSchemaValidation: process.env.NODE_ENV === "test" ? ["response"] : false, // skip schema validation for response in tests, since we'll be mocking the response
* endpoints: (build) => ({
* getPost: build.query<Post, { id: number }>({
* query: ({ id }) => `/post/${id}`,
Expand All @@ -298,7 +301,7 @@ export interface CreateApiOptions<
* })
* ```
*/
skipSchemaValidation?: boolean
skipSchemaValidation?: boolean | SchemaType[]
}

export type CreateApi<Modules extends ModuleName> = {
Expand Down
14 changes: 12 additions & 2 deletions packages/toolkit/src/query/endpointDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,14 @@ type BaseEndpointTypes<
RawResultType: RawResultType
}

export type SchemaType =
| 'arg'
| 'rawResponse'
| 'response'
| 'rawErrorResponse'
| 'errorResponse'
| 'meta'

interface CommonEndpointDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
Expand Down Expand Up @@ -427,6 +435,8 @@ interface CommonEndpointDefinition<
* If set to `true`, will skip schema validation for this endpoint.
* Overrides the global setting.
*
* Can be overridden for specific schemas by passing an array of schema types to skip.
*
* @example
* ```ts
* // codeblock-meta no-transpile
Expand All @@ -439,13 +449,13 @@ interface CommonEndpointDefinition<
* getPost: build.query<Post, { id: number }>({
* query: ({ id }) => `/post/${id}`,
* responseSchema: v.object({ id: v.number(), name: v.string() }),
* skipSchemaValidation: process.env.NODE_ENV === "test", // skip schema validation in tests, since we'll be mocking the response
* skipSchemaValidation: process.env.NODE_ENV === "test" ? ["response"] : false, // skip schema validation for response in tests, since we'll be mocking the response
* }),
* })
* })
* ```
*/
skipSchemaValidation?: boolean
skipSchemaValidation?: boolean | SchemaType[]
}

export type BaseEndpointDefinition<
Expand Down
1 change: 1 addition & 0 deletions packages/toolkit/src/query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type {
SchemaFailureHandler,
SchemaFailureConverter,
SchemaFailureInfo,
SchemaType,
} from './endpointDefinitions'
export { fetchBaseQuery } from './fetchBaseQuery'
export type {
Expand Down
13 changes: 11 additions & 2 deletions packages/toolkit/src/query/standardSchema.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import type { StandardSchemaV1 } from '@standard-schema/spec'
import { SchemaError } from '@standard-schema/utils'
import type { SchemaType } from './endpointDefinitions'

export class NamedSchemaError extends SchemaError {
constructor(
issues: readonly StandardSchemaV1.Issue[],
public readonly value: any,
public readonly schemaName: string,
public readonly schemaName: `${SchemaType}Schema`,
public readonly _bqMeta: any,
) {
super(issues)
}
}

export const shouldSkip = (
skipSchemaValidation: boolean | SchemaType[] | undefined,
schemaName: SchemaType,
) =>
Array.isArray(skipSchemaValidation)
? skipSchemaValidation.includes(schemaName)
: !!skipSchemaValidation

export async function parseWithSchema<Schema extends StandardSchemaV1>(
schema: Schema,
data: unknown,
schemaName: string,
schemaName: `${SchemaType}Schema`,
bqMeta: any,
): Promise<StandardSchemaV1.InferOutput<Schema>> {
const result = await schema['~standard'].validate(data)
Expand Down
Loading
Loading