Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split enhanceEndpoints into addTagTypes and enhanceEndpoint #3485

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
53d0626
create separate addTagTypes function
EskiMojo14 May 28, 2023
93f8b4e
add enhanceEndpoint
EskiMojo14 May 28, 2023
60b2efc
omit base api methods from module requirements
EskiMojo14 May 29, 2023
ee02202
update codegen and type tests
EskiMojo14 Jun 1, 2023
8d2b42e
update codemod deps
EskiMojo14 Jun 1, 2023
16ab2b8
add codemod
EskiMojo14 Jun 2, 2023
cc406db
Merge branch 'v2.0-integration' into enhance-endpoint
EskiMojo14 Jun 2, 2023
36187be
fix APIProvider
EskiMojo14 Jun 2, 2023
12d8720
simplify typing using ApiModules instead of Omit<Api, keyof BaseApiMe…
EskiMojo14 Jun 4, 2023
81f187e
fix apiprovider type
EskiMojo14 Jun 4, 2023
b65f6b5
update docs and remove enhanceEndpoints from typing (runtime fallback…
EskiMojo14 Jun 4, 2023
8c38f92
use promiseWithResolvers for RTKQ lifecycle management
Jun 6, 2023
319c772
require reason to ensure onrejected callback type is accurate
EskiMojo14 Jun 6, 2023
8833de4
export promiseWithResolvers from utils index
EskiMojo14 Jun 6, 2023
7e3f4a5
use utils index
EskiMojo14 Jun 6, 2023
bb7451c
add promiseWithResolvers test
EskiMojo14 Jun 6, 2023
47eeb8c
Merge branch 'v2.0-integration' into enhance-endpoint
Oct 2, 2023
fb8a1aa
lockfile
Oct 2, 2023
b683ccb
Merge branch 'master' into enhance-endpoint
EskiMojo14 Jan 29, 2024
948f6d9
run prettier
EskiMojo14 Jan 29, 2024
c5da068
fix tests to avoid enhanceEndpoints
EskiMojo14 Jan 29, 2024
31707eb
fix fixedCacheKey test
EskiMojo14 Jan 29, 2024
49698cd
add enhanceEndpoints back as deprecated
EskiMojo14 Jan 29, 2024
fa4f0e4
add the types as well
EskiMojo14 Jan 29, 2024
7e53c8e
try something
EskiMojo14 Feb 2, 2024
273139c
Merge branch 'master' into enhance-endpoint
EskiMojo14 Feb 4, 2024
119abc9
Merge branch 'master' into enhance-endpoint
EskiMojo14 Feb 5, 2024
ceff137
Merge branch 'master' into enhance-endpoint
EskiMojo14 Feb 5, 2024
386e24b
Merge branch 'master' into enhance-endpoint
EskiMojo14 Feb 21, 2024
41cded6
Merge branch 'master' into enhance-endpoint
EskiMojo14 Jun 19, 2024
dc08e93
update README version
EskiMojo14 Jun 19, 2024
675f261
put code on one line, to account for error in different versions
EskiMojo14 Jun 19, 2024
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
64 changes: 38 additions & 26 deletions docs/rtk-query/api/created-api/code-splitting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Each API slice allows [additional endpoint definitions to be injected at runtime

The individual API slice endpoint definitions can also be split across multiple files. This is primarily useful for working with API slices that were [code-generated from an API schema file](../../usage/code-generation.mdx), allowing you to add additional custom behavior and configuration to a set of automatically-generated endpoint definitions.

Each API slice object has `injectEndpoints` and `enhanceEndpoints` functions to support these use cases.
Each API slice object has `injectEndpoints`, `addTagTypes` and `enhanceEndpoint` functions to support these use cases.

## `injectEndpoints`

Expand Down Expand Up @@ -46,29 +46,44 @@ Endpoints will not be overridden unless `overrideExisting` is set to `true`. If

This method is primarily useful for code splitting and hot reloading.

## `enhanceEndpoints`
## `addTagTypes`

#### Signature

```ts no-transpile
const enhanceEndpoints = (endpointOptions: EnhanceEndpointsOptions) =>
EnhancedApiSlice
const addTagTypes = (...newTags: readonly string[]) => EnhancedApiSlice
```

interface EnhanceEndpointsOptions {
addTagTypes?: readonly string[]
endpoints?: Record<string, Partial<EndpointDefinition>>
}
#### Description

Accepts a number of new tags to add to the API slice.

Returns an updated and enhanced version of the API slice object, with the new tags added.

This is primarily useful for chaining before `injectEndpoints` or `enhanceEndpoint`, to add tags which can then be used by new/enhanced endpoints.

## `enhanceEndpoint`

#### Signature

```ts no-transpile
const enhanceEndpoint = (
endpointName: string,
partialDefinition:
| Partial<EndpointDefinition>
| ((definition: EndpointDefinition) => void)
) => EnhancedApiSlice
```

#### Description

Any provided tag types or endpoint definitions will be merged into the existing endpoint definitions for this API slice. Unlike `injectEndpoints`, the partial endpoint definitions will not _replace_ existing definitions, but are rather merged together on a per-definition basis (ie, `Object.assign(existingEndpoint, newPartialEndpoint)`).
Provided partial definition will be merged into the existing endpoint definition for this API slice. Unlike `injectEndpoints`, the partial endpoint definition will not _replace_ the existing definition, but is rather merged together (i.e. `Object.assign(existingEndpoint, newPartialEndpoint)`).

Returns an updated and enhanced version of the API slice object, containing the combined endpoint definitions.

This is primarily useful for taking an API slice object that was code-generated from an API schema file like OpenAPI, and adding additional specific hand-written configuration for cache invalidation management on top of the generated endpoint definitions.

For example, `enhanceEndpoints` can be used to modify caching behavior by changing the values of `providesTags`, `invalidatesTags`, and `keepUnusedDataFor`:
For example, `enhanceEndpoint` can be used to modify caching behavior by changing the values of `providesTags`, `invalidatesTags`, and `keepUnusedDataFor`:

```ts
// file: api.ts noEmit
Expand Down Expand Up @@ -98,20 +113,17 @@ export const api = createApi({
// file: enhanceEndpoints.ts
import { api } from './api'

const enhancedApi = api.enhanceEndpoints({
addTagTypes: ['User'],
endpoints: {
getUserByUserId: {
providesTags: ['User'],
},
patchUserByUserId: {
invalidatesTags: ['User'],
},
// alternatively, define a function which is called with the endpoint definition as an argument
getUsers(endpoint) {
endpoint.providesTags = ['User']
endpoint.keepUnusedDataFor = 120
},
},
})
const enhancedApi = api
.addTagTypes('User')
.enhanceEndpoint('getUserByUserId', {
providesTags: ['User'],
})
.enhanceEndpoint('patchUserByUserId', {
invalidatesTags: ['User'],
})
// alternatively, define a function which is called with the endpoint definition as an argument
.enhanceEndpoint('getUsers', (endpoint) => {
endpoint.providesTags = ['User']
endpoint.keepUnusedDataFor = 120
})
```
10 changes: 8 additions & 2 deletions docs/rtk-query/api/created-api/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ type Api = {

// Code splitting and generation
injectEndpoints: (options: InjectEndpointsOptions) => UpdatedApi
enhanceEndpoints: (options: EnhanceEndpointsOptions) => UpdatedApi
addTagTypes: (...newTags: readonly string[]) => UpdatedApi
enhanceEndpoint: (
endpointName: string,
partialDefinition:
| Partial<EndpointDefinition>
| ((definition: EndpointDefinition) => void)
) => UpdatedApi

// Utilities
utils: {
Expand Down Expand Up @@ -118,7 +124,7 @@ Each API slice allows [additional endpoint definitions to be injected at runtime

The individual API slice endpoint definitions can also be split across multiple files. This is primarily useful for working with API slices that were [code-generated from an API schema file](../../usage/code-generation.mdx), allowing you to add additional custom behavior and configuration to a set of automatically-generated endpoint definitions.

Each API slice object has `injectEndpoints` and `enhanceEndpoints` functions to support these use cases.
Each API slice object has `injectEndpoints`, `addTagTypes` and `enhanceEndpoint` functions to support these use cases.

:::info API Reference

Expand Down
2 changes: 1 addition & 1 deletion docs/rtk-query/usage/code-generation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ That will result in all generated endpoints having `providesTags`/`invalidatesTa

Note that this will only result in string tags with no ids, so it might lead to scenarios where too much is invalidated and unneccessary requests are made on mutation.

In that case it is still recommended to manually specify tags by using [`enhanceEndpoints`](../api/created-api/code-splitting.mdx) on top of the generated api and manually declare `providesTags`/`invalidatesTags`.
In that case it is still recommended to manually specify tags by using [`addTagTypes` and `enhanceEndpoint`](../api/created-api/code-splitting.mdx) on top of the generated api and manually declare `providesTags`/`invalidatesTags`.

### Programmatic usage

Expand Down
6 changes: 0 additions & 6 deletions examples/query/react/kitchen-sink/src/app/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,3 @@ export const api = createApi({
*/
endpoints: () => ({}),
})

export const enhancedApi = api.enhanceEndpoints({
endpoints: () => ({
getPost: () => 'test',
}),
})
28 changes: 28 additions & 0 deletions packages/rtk-codemods/transforms/enhanceEndpoint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# enhanceEndpoint

Transforms enhanceEndpoints calls into addTagType and enhanceEndpoint calls, in preparation for RTK 3.0.

## Usage

```
npx @reduxjs/rtk-codemods enhanceEndpoint path/of/files/ or/some**/*glob.js

# or

yarn global add @reduxjs/rtk-codemods
@reduxjs/rtk-codemods enhanceEndpoint path/of/files/ or/some**/*glob.js
```

## Local Usage

```
node ./bin/cli.js enhanceEndpoint path/of/files/ or/some**/*glob.js
```

## Input / Output

<!--FIXTURES_TOC_START-->
<!--FIXTURES_TOC_END-->

<!--FIXTURES_CONTENT_START-->
<!--FIXTURES_CONTENT_END-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const withTags = api.enhanceEndpoints({
addTagTypes: ['tag1']
});

const mutation2 = 'mutation2';

const withPartials = withTags.enhanceEndpoints({
endpoints: {
query1: {
providesTags: ['tag1']
},
mutation1(definition) {
definition.invalidatesTags = ['tag1']
},
[mutation2]: (definition) => {}
}
})

const tags = ['tag1']

const withBoth = api.enhanceEndpoints({
addTagTypes: tags,
endpoints: {
["query1"]: {
providesTags: ['tag1']
},
}
})

const addTagTypes = tags

const chained = api
.enhanceEndpoints({
addTagTypes
})
.injectEndpoints({
endpoints: () => {}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const withTags = api.addTagTypes('tag1');

const mutation2 = 'mutation2';

const withPartials = withTags.enhanceEndpoint("query1", {
providesTags: ['tag1']
}).enhanceEndpoint("mutation1", (definition) => {
definition.invalidatesTags = ['tag1']
}).enhanceEndpoint(mutation2, (definition) => {})

const tags = ['tag1']

const withBoth = api.addTagTypes(...tags).enhanceEndpoint("query1", {
providesTags: ['tag1']
})

const addTagTypes = tags

const chained = api.addTagTypes(...addTagTypes)
.injectEndpoints({
endpoints: () => {}
})
126 changes: 126 additions & 0 deletions packages/rtk-codemods/transforms/enhanceEndpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type {
Transform,
MemberExpression,
ObjectExpression,
RestElement,
JSCodeshift
} from 'jscodeshift'

type Prop = Extract<
ObjectExpression['properties'][number],
{ value: unknown }
>['value']

const createAddTagTypesCall = (
j: JSCodeshift,
object: MemberExpression['object'],
addTagTypes: Prop
) => {
const newCall = j.callExpression(
j.memberExpression(object, j.identifier('addTagTypes')),
[]
)
if (addTagTypes.type === 'Identifier') {
newCall.arguments.push(j.spreadElement(addTagTypes))
} else if (addTagTypes.type === 'ArrayExpression') {
newCall.arguments = addTagTypes.elements.filter(
(el): el is Exclude<typeof el, RestElement | null> =>
!!(el && el.type !== 'RestElement')
)
}
return newCall
}

const transform: Transform = (file, api) => {
const j = api.jscodeshift

const root = j(file.source)

return root
.find(j.CallExpression, {
callee: {
property: {
name: 'enhanceEndpoints'
}
}
})
.forEach((path) => {
const callee = path.value.callee as MemberExpression
const [config] = path.value.arguments
if (config.type === 'ObjectExpression') {
let addTagTypes: Prop | undefined = undefined
let endpoints: Prop | undefined = undefined
for (const property of config.properties) {
if (
(property.type === 'ObjectProperty' ||
property.type === 'Property') &&
property.key.type === 'Identifier'
) {
switch (property.key.name) {
case 'addTagTypes':
addTagTypes = property.value
break
case 'endpoints':
endpoints = property.value
break
}
}
}
if (!endpoints) {
if (!addTagTypes) {
return
}
// no endpoints - we can go ahead and replace
path.replace(createAddTagTypesCall(j, callee.object, addTagTypes))
} else {
let calleeObject = addTagTypes
? createAddTagTypesCall(j, callee.object, addTagTypes)
: callee.object
if (endpoints.type === 'ObjectExpression') {
for (const endpointProp of endpoints.properties) {
if (endpointProp.type === 'ObjectProperty') {
const endpointName =
endpointProp.key.type === 'Identifier' &&
!endpointProp.computed
? j.stringLiteral(endpointProp.key.name)
: endpointProp.key
calleeObject = j.callExpression(
j.memberExpression(
calleeObject,
j.identifier('enhanceEndpoint')
),
[endpointName, endpointProp.value as any]
)
} else if (endpointProp.type === 'ObjectMethod') {
const endpointName =
endpointProp.key.type === 'Identifier'
? j.stringLiteral(endpointProp.key.name)
: endpointProp.key
calleeObject = j.callExpression(
j.memberExpression(
calleeObject,
j.identifier('enhanceEndpoint')
),
[
endpointName,
j.arrowFunctionExpression(
endpointProp.params,
endpointProp.body
)
]
)
}
}
}
path.replace(calleeObject)
}
}
})
.toSource({
arrowParensAlways: true
})
}

export const parser = 'tsx'

export default transform
9 changes: 9 additions & 0 deletions packages/rtk-codemods/transforms/enhanceEndpoint/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

const { runTransformTest } = require('codemod-cli')

runTransformTest({
name: 'enhanceEndpoint',
path: require.resolve('./index.ts'),
fixtureDir: `${__dirname}/__testfixtures__/`
})
8 changes: 2 additions & 6 deletions packages/rtk-query-codegen-openapi/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ export function generateCreateApiCall({
true
);
if (tag) {
const enhanceEndpointsObjectLiteralExpression = factory.createObjectLiteralExpression(
[factory.createShorthandPropertyAssignment(factory.createIdentifier('addTagTypes'), undefined)],
true
);
return factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
Expand All @@ -70,10 +66,10 @@ export function generateCreateApiCall({
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('api'),
factory.createIdentifier('enhanceEndpoints')
factory.createIdentifier('addTagTypes')
),
undefined,
[enhanceEndpointsObjectLiteralExpression]
[factory.createSpreadElement(factory.createIdentifier('addTagTypes'))]
),
factory.createIdentifier('injectEndpoints')
),
Expand Down
Loading