1 | import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
|
2 | import type { Api, ApiContext } from '../apiTypes'
|
3 | import type {
|
4 | BaseQueryFn,
|
5 | BaseQueryError,
|
6 | QueryReturnValue,
|
7 | } from '../baseQueryTypes'
|
8 | import type { RootState, QueryKeys, QuerySubstateIdentifier } from './apiState'
|
9 | import { QueryStatus } from './apiState'
|
10 | import type {
|
11 | StartQueryActionCreatorOptions,
|
12 | QueryActionCreatorResult,
|
13 | } from './buildInitiate'
|
14 | import { forceQueryFnSymbol, isUpsertQuery } from './buildInitiate'
|
15 | import type {
|
16 | AssertTagTypes,
|
17 | EndpointDefinition,
|
18 | EndpointDefinitions,
|
19 | MutationDefinition,
|
20 | QueryArgFrom,
|
21 | QueryDefinition,
|
22 | ResultTypeFrom,
|
23 | FullTagDescription,
|
24 | } from '../endpointDefinitions'
|
25 | import { isQueryDefinition } from '../endpointDefinitions'
|
26 | import { calculateProvidedBy } from '../endpointDefinitions'
|
27 | import type {
|
28 | AsyncThunkPayloadCreator,
|
29 | Draft,
|
30 | UnknownAction,
|
31 | } from '@reduxjs/toolkit'
|
32 | import {
|
33 | isAllOf,
|
34 | isFulfilled,
|
35 | isPending,
|
36 | isRejected,
|
37 | isRejectedWithValue,
|
38 | createAsyncThunk,
|
39 | SHOULD_AUTOBATCH,
|
40 | } from './rtkImports'
|
41 | import type { Patch } from 'immer'
|
42 | import { isDraftable, produceWithPatches } from 'immer'
|
43 | import type { ThunkAction, ThunkDispatch, AsyncThunk } from '@reduxjs/toolkit'
|
44 |
|
45 | import { HandledError } from '../HandledError'
|
46 |
|
47 | import type { ApiEndpointQuery, PrefetchOptions } from './module'
|
48 | import type { UnwrapPromise } from '../tsHelpers'
|
49 |
|
50 | declare module './module' {
|
51 | export interface ApiEndpointQuery<
|
52 | Definition extends QueryDefinition<any, any, any, any, any>,
|
53 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
54 | Definitions extends EndpointDefinitions,
|
55 | > extends Matchers<QueryThunk, Definition> {}
|
56 |
|
57 | export interface ApiEndpointMutation<
|
58 | Definition extends MutationDefinition<any, any, any, any, any>,
|
59 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
60 | Definitions extends EndpointDefinitions,
|
61 | > extends Matchers<MutationThunk, Definition> {}
|
62 | }
|
63 |
|
64 | type EndpointThunk<
|
65 | Thunk extends QueryThunk | MutationThunk,
|
66 | Definition extends EndpointDefinition<any, any, any, any>,
|
67 | > =
|
68 | Definition extends EndpointDefinition<
|
69 | infer QueryArg,
|
70 | infer BaseQueryFn,
|
71 | any,
|
72 | infer ResultType
|
73 | >
|
74 | ? Thunk extends AsyncThunk<unknown, infer ATArg, infer ATConfig>
|
75 | ? AsyncThunk<
|
76 | ResultType,
|
77 | ATArg & { originalArgs: QueryArg },
|
78 | ATConfig & { rejectValue: BaseQueryError<BaseQueryFn> }
|
79 | >
|
80 | : never
|
81 | : never
|
82 |
|
83 | export type PendingAction<
|
84 | Thunk extends QueryThunk | MutationThunk,
|
85 | Definition extends EndpointDefinition<any, any, any, any>,
|
86 | > = ReturnType<EndpointThunk<Thunk, Definition>['pending']>
|
87 |
|
88 | export type FulfilledAction<
|
89 | Thunk extends QueryThunk | MutationThunk,
|
90 | Definition extends EndpointDefinition<any, any, any, any>,
|
91 | > = ReturnType<EndpointThunk<Thunk, Definition>['fulfilled']>
|
92 |
|
93 | export type RejectedAction<
|
94 | Thunk extends QueryThunk | MutationThunk,
|
95 | Definition extends EndpointDefinition<any, any, any, any>,
|
96 | > = ReturnType<EndpointThunk<Thunk, Definition>['rejected']>
|
97 |
|
98 | export type Matcher<M> = (value: any) => value is M
|
99 |
|
100 | export interface Matchers<
|
101 | Thunk extends QueryThunk | MutationThunk,
|
102 | Definition extends EndpointDefinition<any, any, any, any>,
|
103 | > {
|
104 | matchPending: Matcher<PendingAction<Thunk, Definition>>
|
105 | matchFulfilled: Matcher<FulfilledAction<Thunk, Definition>>
|
106 | matchRejected: Matcher<RejectedAction<Thunk, Definition>>
|
107 | }
|
108 |
|
109 | export interface QueryThunkArg
|
110 | extends QuerySubstateIdentifier,
|
111 | StartQueryActionCreatorOptions {
|
112 | type: 'query'
|
113 | originalArgs: unknown
|
114 | endpointName: string
|
115 | }
|
116 |
|
117 | export interface MutationThunkArg {
|
118 | type: 'mutation'
|
119 | originalArgs: unknown
|
120 | endpointName: string
|
121 | track?: boolean
|
122 | fixedCacheKey?: string
|
123 | }
|
124 |
|
125 | export type ThunkResult = unknown
|
126 |
|
127 | export type ThunkApiMetaConfig = {
|
128 | pendingMeta: {
|
129 | startedTimeStamp: number
|
130 | [SHOULD_AUTOBATCH]: true
|
131 | }
|
132 | fulfilledMeta: {
|
133 | fulfilledTimeStamp: number
|
134 | baseQueryMeta: unknown
|
135 | [SHOULD_AUTOBATCH]: true
|
136 | }
|
137 | rejectedMeta: {
|
138 | baseQueryMeta: unknown
|
139 | [SHOULD_AUTOBATCH]: true
|
140 | }
|
141 | }
|
142 | export type QueryThunk = AsyncThunk<
|
143 | ThunkResult,
|
144 | QueryThunkArg,
|
145 | ThunkApiMetaConfig
|
146 | >
|
147 | export type MutationThunk = AsyncThunk<
|
148 | ThunkResult,
|
149 | MutationThunkArg,
|
150 | ThunkApiMetaConfig
|
151 | >
|
152 |
|
153 | function defaultTransformResponse(baseQueryReturnValue: unknown) {
|
154 | return baseQueryReturnValue
|
155 | }
|
156 |
|
157 | export type MaybeDrafted<T> = T | Draft<T>
|
158 | export type Recipe<T> = (data: MaybeDrafted<T>) => void | MaybeDrafted<T>
|
159 | export type UpsertRecipe<T> = (
|
160 | data: MaybeDrafted<T> | undefined,
|
161 | ) => void | MaybeDrafted<T>
|
162 |
|
163 | export type PatchQueryDataThunk<
|
164 | Definitions extends EndpointDefinitions,
|
165 | PartialState,
|
166 | > = <EndpointName extends QueryKeys<Definitions>>(
|
167 | endpointName: EndpointName,
|
168 | args: QueryArgFrom<Definitions[EndpointName]>,
|
169 | patches: readonly Patch[],
|
170 | updateProvided?: boolean,
|
171 | ) => ThunkAction<void, PartialState, any, UnknownAction>
|
172 |
|
173 | export type UpdateQueryDataThunk<
|
174 | Definitions extends EndpointDefinitions,
|
175 | PartialState,
|
176 | > = <EndpointName extends QueryKeys<Definitions>>(
|
177 | endpointName: EndpointName,
|
178 | args: QueryArgFrom<Definitions[EndpointName]>,
|
179 | updateRecipe: Recipe<ResultTypeFrom<Definitions[EndpointName]>>,
|
180 | updateProvided?: boolean,
|
181 | ) => ThunkAction<PatchCollection, PartialState, any, UnknownAction>
|
182 |
|
183 | export type UpsertQueryDataThunk<
|
184 | Definitions extends EndpointDefinitions,
|
185 | PartialState,
|
186 | > = <EndpointName extends QueryKeys<Definitions>>(
|
187 | endpointName: EndpointName,
|
188 | args: QueryArgFrom<Definitions[EndpointName]>,
|
189 | value: ResultTypeFrom<Definitions[EndpointName]>,
|
190 | ) => ThunkAction<
|
191 | QueryActionCreatorResult<
|
192 | Definitions[EndpointName] extends QueryDefinition<any, any, any, any>
|
193 | ? Definitions[EndpointName]
|
194 | : never
|
195 | >,
|
196 | PartialState,
|
197 | any,
|
198 | UnknownAction
|
199 | >
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | export type PatchCollection = {
|
205 | |
206 |
|
207 |
|
208 | patches: Patch[]
|
209 | |
210 |
|
211 |
|
212 | inversePatches: Patch[]
|
213 | |
214 |
|
215 |
|
216 | undo: () => void
|
217 | }
|
218 |
|
219 | export function buildThunks<
|
220 | BaseQuery extends BaseQueryFn,
|
221 | ReducerPath extends string,
|
222 | Definitions extends EndpointDefinitions,
|
223 | >({
|
224 | reducerPath,
|
225 | baseQuery,
|
226 | context: { endpointDefinitions },
|
227 | serializeQueryArgs,
|
228 | api,
|
229 | assertTagType,
|
230 | }: {
|
231 | baseQuery: BaseQuery
|
232 | reducerPath: ReducerPath
|
233 | context: ApiContext<Definitions>
|
234 | serializeQueryArgs: InternalSerializeQueryArgs
|
235 | api: Api<BaseQuery, Definitions, ReducerPath, any>
|
236 | assertTagType: AssertTagTypes
|
237 | }) {
|
238 | type State = RootState<any, string, ReducerPath>
|
239 |
|
240 | const patchQueryData: PatchQueryDataThunk<EndpointDefinitions, State> =
|
241 | (endpointName, args, patches, updateProvided) => (dispatch, getState) => {
|
242 | const endpointDefinition = endpointDefinitions[endpointName]
|
243 |
|
244 | const queryCacheKey = serializeQueryArgs({
|
245 | queryArgs: args,
|
246 | endpointDefinition,
|
247 | endpointName,
|
248 | })
|
249 |
|
250 | dispatch(
|
251 | api.internalActions.queryResultPatched({ queryCacheKey, patches }),
|
252 | )
|
253 |
|
254 | if (!updateProvided) {
|
255 | return
|
256 | }
|
257 |
|
258 | const newValue = api.endpoints[endpointName].select(args)(
|
259 |
|
260 | getState() as RootState<any, any, any>,
|
261 | )
|
262 |
|
263 | const providedTags = calculateProvidedBy(
|
264 | endpointDefinition.providesTags,
|
265 | newValue.data,
|
266 | undefined,
|
267 | args,
|
268 | {},
|
269 | assertTagType,
|
270 | )
|
271 |
|
272 | dispatch(
|
273 | api.internalActions.updateProvidedBy({ queryCacheKey, providedTags }),
|
274 | )
|
275 | }
|
276 |
|
277 | const updateQueryData: UpdateQueryDataThunk<EndpointDefinitions, State> =
|
278 | (endpointName, args, updateRecipe, updateProvided = true) =>
|
279 | (dispatch, getState) => {
|
280 | const endpointDefinition = api.endpoints[endpointName]
|
281 |
|
282 | const currentState = endpointDefinition.select(args)(
|
283 |
|
284 | getState() as RootState<any, any, any>,
|
285 | )
|
286 |
|
287 | let ret: PatchCollection = {
|
288 | patches: [],
|
289 | inversePatches: [],
|
290 | undo: () =>
|
291 | dispatch(
|
292 | api.util.patchQueryData(
|
293 | endpointName,
|
294 | args,
|
295 | ret.inversePatches,
|
296 | updateProvided,
|
297 | ),
|
298 | ),
|
299 | }
|
300 | if (currentState.status === QueryStatus.uninitialized) {
|
301 | return ret
|
302 | }
|
303 | let newValue
|
304 | if ('data' in currentState) {
|
305 | if (isDraftable(currentState.data)) {
|
306 | const [value, patches, inversePatches] = produceWithPatches(
|
307 | currentState.data,
|
308 | updateRecipe,
|
309 | )
|
310 | ret.patches.push(...patches)
|
311 | ret.inversePatches.push(...inversePatches)
|
312 | newValue = value
|
313 | } else {
|
314 | newValue = updateRecipe(currentState.data)
|
315 | ret.patches.push({ op: 'replace', path: [], value: newValue })
|
316 | ret.inversePatches.push({
|
317 | op: 'replace',
|
318 | path: [],
|
319 | value: currentState.data,
|
320 | })
|
321 | }
|
322 | }
|
323 |
|
324 | if (ret.patches.length === 0) {
|
325 | return ret
|
326 | }
|
327 |
|
328 | dispatch(
|
329 | api.util.patchQueryData(
|
330 | endpointName,
|
331 | args,
|
332 | ret.patches,
|
333 | updateProvided,
|
334 | ),
|
335 | )
|
336 |
|
337 | return ret
|
338 | }
|
339 |
|
340 | const upsertQueryData: UpsertQueryDataThunk<Definitions, State> =
|
341 | (endpointName, args, value) => (dispatch) => {
|
342 | return dispatch(
|
343 | (
|
344 | api.endpoints[endpointName] as ApiEndpointQuery<
|
345 | QueryDefinition<any, any, any, any, any>,
|
346 | Definitions
|
347 | >
|
348 | ).initiate(args, {
|
349 | subscribe: false,
|
350 | forceRefetch: true,
|
351 | [forceQueryFnSymbol]: () => ({
|
352 | data: value,
|
353 | }),
|
354 | }),
|
355 | )
|
356 | }
|
357 |
|
358 | const executeEndpoint: AsyncThunkPayloadCreator<
|
359 | ThunkResult,
|
360 | QueryThunkArg | MutationThunkArg,
|
361 | ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
|
362 | > = async (
|
363 | arg,
|
364 | {
|
365 | signal,
|
366 | abort,
|
367 | rejectWithValue,
|
368 | fulfillWithValue,
|
369 | dispatch,
|
370 | getState,
|
371 | extra,
|
372 | },
|
373 | ) => {
|
374 | const endpointDefinition = endpointDefinitions[arg.endpointName]
|
375 |
|
376 | try {
|
377 | let transformResponse: (
|
378 | baseQueryReturnValue: any,
|
379 | meta: any,
|
380 | arg: any,
|
381 | ) => any = defaultTransformResponse
|
382 | let result: QueryReturnValue
|
383 | const baseQueryApi = {
|
384 | signal,
|
385 | abort,
|
386 | dispatch,
|
387 | getState,
|
388 | extra,
|
389 | endpoint: arg.endpointName,
|
390 | type: arg.type,
|
391 | forced:
|
392 | arg.type === 'query' ? isForcedQuery(arg, getState()) : undefined,
|
393 | }
|
394 |
|
395 | const forceQueryFn =
|
396 | arg.type === 'query' ? arg[forceQueryFnSymbol] : undefined
|
397 | if (forceQueryFn) {
|
398 | result = forceQueryFn()
|
399 | } else if (endpointDefinition.query) {
|
400 | result = await baseQuery(
|
401 | endpointDefinition.query(arg.originalArgs),
|
402 | baseQueryApi,
|
403 | endpointDefinition.extraOptions as any,
|
404 | )
|
405 |
|
406 | if (endpointDefinition.transformResponse) {
|
407 | transformResponse = endpointDefinition.transformResponse
|
408 | }
|
409 | } else {
|
410 | result = await endpointDefinition.queryFn(
|
411 | arg.originalArgs,
|
412 | baseQueryApi,
|
413 | endpointDefinition.extraOptions as any,
|
414 | (arg) =>
|
415 | baseQuery(
|
416 | arg,
|
417 | baseQueryApi,
|
418 | endpointDefinition.extraOptions as any,
|
419 | ),
|
420 | )
|
421 | }
|
422 | if (
|
423 | typeof process !== 'undefined' &&
|
424 | process.env.NODE_ENV === 'development'
|
425 | ) {
|
426 | const what = endpointDefinition.query ? '`baseQuery`' : '`queryFn`'
|
427 | let err: undefined | string
|
428 | if (!result) {
|
429 | err = `${what} did not return anything.`
|
430 | } else if (typeof result !== 'object') {
|
431 | err = `${what} did not return an object.`
|
432 | } else if (result.error && result.data) {
|
433 | err = `${what} returned an object containing both \`error\` and \`result\`.`
|
434 | } else if (result.error === undefined && result.data === undefined) {
|
435 | err = `${what} returned an object containing neither a valid \`error\` and \`result\`. At least one of them should not be \`undefined\``
|
436 | } else {
|
437 | for (const key of Object.keys(result)) {
|
438 | if (key !== 'error' && key !== 'data' && key !== 'meta') {
|
439 | err = `The object returned by ${what} has the unknown property ${key}.`
|
440 | break
|
441 | }
|
442 | }
|
443 | }
|
444 | if (err) {
|
445 | console.error(
|
446 | `Error encountered handling the endpoint ${arg.endpointName}.
|
447 | ${err}
|
448 | It needs to return an object with either the shape \`{ data: <value> }\` or \`{ error: <value> }\` that may contain an optional \`meta\` property.
|
449 | Object returned was:`,
|
450 | result,
|
451 | )
|
452 | }
|
453 | }
|
454 |
|
455 | if (result.error) throw new HandledError(result.error, result.meta)
|
456 |
|
457 | return fulfillWithValue(
|
458 | await transformResponse(result.data, result.meta, arg.originalArgs),
|
459 | {
|
460 | fulfilledTimeStamp: Date.now(),
|
461 | baseQueryMeta: result.meta,
|
462 | [SHOULD_AUTOBATCH]: true,
|
463 | },
|
464 | )
|
465 | } catch (error) {
|
466 | let catchedError = error
|
467 | if (catchedError instanceof HandledError) {
|
468 | let transformErrorResponse: (
|
469 | baseQueryReturnValue: any,
|
470 | meta: any,
|
471 | arg: any,
|
472 | ) => any = defaultTransformResponse
|
473 |
|
474 | if (
|
475 | endpointDefinition.query &&
|
476 | endpointDefinition.transformErrorResponse
|
477 | ) {
|
478 | transformErrorResponse = endpointDefinition.transformErrorResponse
|
479 | }
|
480 | try {
|
481 | return rejectWithValue(
|
482 | await transformErrorResponse(
|
483 | catchedError.value,
|
484 | catchedError.meta,
|
485 | arg.originalArgs,
|
486 | ),
|
487 | { baseQueryMeta: catchedError.meta, [SHOULD_AUTOBATCH]: true },
|
488 | )
|
489 | } catch (e) {
|
490 | catchedError = e
|
491 | }
|
492 | }
|
493 | if (
|
494 | typeof process !== 'undefined' &&
|
495 | process.env.NODE_ENV !== 'production'
|
496 | ) {
|
497 | console.error(
|
498 | `An unhandled error occurred processing a request for the endpoint "${arg.endpointName}".
|
499 | In the case of an unhandled error, no tags will be "provided" or "invalidated".`,
|
500 | catchedError,
|
501 | )
|
502 | } else {
|
503 | console.error(catchedError)
|
504 | }
|
505 | throw catchedError
|
506 | }
|
507 | }
|
508 |
|
509 | function isForcedQuery(
|
510 | arg: QueryThunkArg,
|
511 | state: RootState<any, string, ReducerPath>,
|
512 | ) {
|
513 | const requestState = state[reducerPath]?.queries?.[arg.queryCacheKey]
|
514 | const baseFetchOnMountOrArgChange =
|
515 | state[reducerPath]?.config.refetchOnMountOrArgChange
|
516 |
|
517 | const fulfilledVal = requestState?.fulfilledTimeStamp
|
518 | const refetchVal =
|
519 | arg.forceRefetch ?? (arg.subscribe && baseFetchOnMountOrArgChange)
|
520 |
|
521 | if (refetchVal) {
|
522 |
|
523 | return (
|
524 | refetchVal === true ||
|
525 | (Number(new Date()) - Number(fulfilledVal)) / 1000 >= refetchVal
|
526 | )
|
527 | }
|
528 | return false
|
529 | }
|
530 |
|
531 | const queryThunk = createAsyncThunk<
|
532 | ThunkResult,
|
533 | QueryThunkArg,
|
534 | ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
|
535 | >(`${reducerPath}/executeQuery`, executeEndpoint, {
|
536 | getPendingMeta() {
|
537 | return { startedTimeStamp: Date.now(), [SHOULD_AUTOBATCH]: true }
|
538 | },
|
539 | condition(queryThunkArgs, { getState }) {
|
540 | const state = getState()
|
541 |
|
542 | const requestState =
|
543 | state[reducerPath]?.queries?.[queryThunkArgs.queryCacheKey]
|
544 | const fulfilledVal = requestState?.fulfilledTimeStamp
|
545 | const currentArg = queryThunkArgs.originalArgs
|
546 | const previousArg = requestState?.originalArgs
|
547 | const endpointDefinition =
|
548 | endpointDefinitions[queryThunkArgs.endpointName]
|
549 |
|
550 |
|
551 |
|
552 |
|
553 | if (isUpsertQuery(queryThunkArgs)) {
|
554 | return true
|
555 | }
|
556 |
|
557 |
|
558 | if (requestState?.status === 'pending') {
|
559 | return false
|
560 | }
|
561 |
|
562 |
|
563 | if (isForcedQuery(queryThunkArgs, state)) {
|
564 | return true
|
565 | }
|
566 |
|
567 | if (
|
568 | isQueryDefinition(endpointDefinition) &&
|
569 | endpointDefinition?.forceRefetch?.({
|
570 | currentArg,
|
571 | previousArg,
|
572 | endpointState: requestState,
|
573 | state,
|
574 | })
|
575 | ) {
|
576 | return true
|
577 | }
|
578 |
|
579 |
|
580 | if (fulfilledVal) {
|
581 |
|
582 | return false
|
583 | }
|
584 |
|
585 | return true
|
586 | },
|
587 | dispatchConditionRejection: true,
|
588 | })
|
589 |
|
590 | const mutationThunk = createAsyncThunk<
|
591 | ThunkResult,
|
592 | MutationThunkArg,
|
593 | ThunkApiMetaConfig & { state: RootState<any, string, ReducerPath> }
|
594 | >(`${reducerPath}/executeMutation`, executeEndpoint, {
|
595 | getPendingMeta() {
|
596 | return { startedTimeStamp: Date.now(), [SHOULD_AUTOBATCH]: true }
|
597 | },
|
598 | })
|
599 |
|
600 | const hasTheForce = (options: any): options is { force: boolean } =>
|
601 | 'force' in options
|
602 | const hasMaxAge = (
|
603 | options: any,
|
604 | ): options is { ifOlderThan: false | number } => 'ifOlderThan' in options
|
605 |
|
606 | const prefetch =
|
607 | <EndpointName extends QueryKeys<Definitions>>(
|
608 | endpointName: EndpointName,
|
609 | arg: any,
|
610 | options: PrefetchOptions,
|
611 | ): ThunkAction<void, any, any, UnknownAction> =>
|
612 | (dispatch: ThunkDispatch<any, any, any>, getState: () => any) => {
|
613 | const force = hasTheForce(options) && options.force
|
614 | const maxAge = hasMaxAge(options) && options.ifOlderThan
|
615 |
|
616 | const queryAction = (force: boolean = true) => {
|
617 | const options = { forceRefetch: force, isPrefetch: true }
|
618 | return (
|
619 | api.endpoints[endpointName] as ApiEndpointQuery<any, any>
|
620 | ).initiate(arg, options)
|
621 | }
|
622 | const latestStateValue = (
|
623 | api.endpoints[endpointName] as ApiEndpointQuery<any, any>
|
624 | ).select(arg)(getState())
|
625 |
|
626 | if (force) {
|
627 | dispatch(queryAction())
|
628 | } else if (maxAge) {
|
629 | const lastFulfilledTs = latestStateValue?.fulfilledTimeStamp
|
630 | if (!lastFulfilledTs) {
|
631 | dispatch(queryAction())
|
632 | return
|
633 | }
|
634 | const shouldRetrigger =
|
635 | (Number(new Date()) - Number(new Date(lastFulfilledTs))) / 1000 >=
|
636 | maxAge
|
637 | if (shouldRetrigger) {
|
638 | dispatch(queryAction())
|
639 | }
|
640 | } else {
|
641 |
|
642 | dispatch(queryAction(false))
|
643 | }
|
644 | }
|
645 |
|
646 | function matchesEndpoint(endpointName: string) {
|
647 | return (action: any): action is UnknownAction =>
|
648 | action?.meta?.arg?.endpointName === endpointName
|
649 | }
|
650 |
|
651 | function buildMatchThunkActions<
|
652 | Thunk extends
|
653 | | AsyncThunk<any, QueryThunkArg, ThunkApiMetaConfig>
|
654 | | AsyncThunk<any, MutationThunkArg, ThunkApiMetaConfig>,
|
655 | >(thunk: Thunk, endpointName: string) {
|
656 | return {
|
657 | matchPending: isAllOf(isPending(thunk), matchesEndpoint(endpointName)),
|
658 | matchFulfilled: isAllOf(
|
659 | isFulfilled(thunk),
|
660 | matchesEndpoint(endpointName),
|
661 | ),
|
662 | matchRejected: isAllOf(isRejected(thunk), matchesEndpoint(endpointName)),
|
663 | } as Matchers<Thunk, any>
|
664 | }
|
665 |
|
666 | return {
|
667 | queryThunk,
|
668 | mutationThunk,
|
669 | prefetch,
|
670 | updateQueryData,
|
671 | upsertQueryData,
|
672 | patchQueryData,
|
673 | buildMatchThunkActions,
|
674 | }
|
675 | }
|
676 |
|
677 | export function calculateProvidedByThunk(
|
678 | action: UnwrapPromise<
|
679 | ReturnType<ReturnType<QueryThunk>> | ReturnType<ReturnType<MutationThunk>>
|
680 | >,
|
681 | type: 'providesTags' | 'invalidatesTags',
|
682 | endpointDefinitions: EndpointDefinitions,
|
683 | assertTagType: AssertTagTypes,
|
684 | ) {
|
685 | return calculateProvidedBy(
|
686 | endpointDefinitions[action.meta.arg.endpointName][type],
|
687 | isFulfilled(action) ? action.payload : undefined,
|
688 | isRejectedWithValue(action) ? action.payload : undefined,
|
689 | action.meta.arg.originalArgs,
|
690 | 'baseQueryMeta' in action.meta ? action.meta.baseQueryMeta : undefined,
|
691 | assertTagType,
|
692 | )
|
693 | }
|