Skip to content

Commit

Permalink
only dedupe pagination queries if the variables match
Browse files Browse the repository at this point in the history
  • Loading branch information
AlecAivazis committed Oct 24, 2024
1 parent 03ede8e commit 4892fdf
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 45 deletions.
24 changes: 18 additions & 6 deletions packages/houdini/src/codegen/generators/artifacts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as recast from 'recast'
import type {
CachePolicies,
Config,
DedupeMatchModes,
Document,
DocumentArtifact,
MutationArtifact,
Expand All @@ -12,6 +13,7 @@ import type {
} from '../../../lib'
import {
ArtifactKind,
DedupeMatchMode,
HoudiniError,
cleanupFiles,
fs,
Expand Down Expand Up @@ -233,12 +235,22 @@ export default function artifactGenerator(stats: {
(arg) => arg.name.value === 'cancelFirst'
)

dedupe =
cancelFirstArg &&
cancelFirstArg.value.kind === 'BooleanValue' &&
cancelFirstArg.value
? 'first'
: 'last'
const matchArg = dedupeDirective.arguments?.find(
(arg) => arg.name.value === 'match'
)

dedupe = {
cancel:
cancelFirstArg &&
cancelFirstArg.value.kind === 'BooleanValue' &&
cancelFirstArg.value
? 'first'
: 'last',
match:
matchArg && matchArg.value.kind === 'StringValue'
? (matchArg.value.value as DedupeMatchModes)
: DedupeMatchMode.Operation,
}
}

// use this selection set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,11 @@ test('paginate over unions', async function () {
},
"pluginData": {},
"dedupe": "last",
"dedupe": {
"cancel": "last",
"match": "Operation"
},
"input": {
"fields": {
Expand Down Expand Up @@ -4932,7 +4936,11 @@ describe('mutation artifacts', function () {
},
"pluginData": {},
"dedupe": "last",
"dedupe": {
"cancel": "last",
"match": "Operation"
},
"input": {
"fields": {
Expand Down Expand Up @@ -5185,7 +5193,11 @@ describe('mutation artifacts', function () {
},
"pluginData": {},
"dedupe": "last",
"dedupe": {
"cancel": "last",
"match": "Operation"
},
"input": {
"fields": {
Expand Down Expand Up @@ -7774,7 +7786,12 @@ test('persists dedupe which', async function () {
},
"pluginData": {},
"dedupe": "last",
"dedupe": {
"cancel": "last",
"match": "Operation"
},
"policy": "CacheOrNetwork",
"partial": false
};
Expand Down Expand Up @@ -7846,7 +7863,12 @@ test('persists dedupe first', async function () {
},
"pluginData": {},
"dedupe": "first",
"dedupe": {
"cancel": "first",
"match": "Operation"
},
"policy": "CacheOrNetwork",
"partial": false
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,11 @@ test('cursor as scalar gets the right pagination query argument types', async fu
},
"pluginData": {},
"dedupe": "last",
"dedupe": {
"cancel": "last",
"match": "Operation"
},
"input": {
"fields": {
Expand Down
14 changes: 14 additions & 0 deletions packages/houdini/src/codegen/generators/definitions/enums.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ test('generates runtime definitions for each enum', async function () {
expect(parsedQuery).toMatchInlineSnapshot(`
type ValuesOf<T> = T[keyof T]
export declare const DedupeMatchMode: {
readonly Variables: "Variables";
readonly Operation: "Operation";
readonly None: "None";
}
export type DedupeMatchMode$options = ValuesOf<typeof DedupeMatchMode>
/** Documentation of testenum1 */
export declare const TestEnum1: {
/** Documentation of Value1 */
Expand Down Expand Up @@ -72,5 +80,11 @@ test('generates runtime definitions for each enum', async function () {
"Value3": "Value3",
"Value2": "Value2"
};
export const DedupeMatchMode = {
"Variables": "Variables",
"Operation": "Operation",
"None": "None"
};
`)
})
32 changes: 28 additions & 4 deletions packages/houdini/src/codegen/generators/definitions/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ test('adds internal documents to schema', async function () {
@dedupe is used to prevent an operation from running more than once at the same time.
If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
"""
directive @dedupe(cancelFirst: Boolean) on QUERY | MUTATION
directive @dedupe(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
"""@optimisticKey is used to identify a field as an optimistic key"""
directive @optimisticKey on FIELD
Expand Down Expand Up @@ -101,6 +101,12 @@ test('adds internal documents to schema', async function () {
SinglePage
}
enum DedupeMatchMode {
Variables
Operation
None
}
scalar ViewerIDFromSession
`)
})
Expand Down Expand Up @@ -138,7 +144,7 @@ test('list operations are included', async function () {
@dedupe is used to prevent an operation from running more than once at the same time.
If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
"""
directive @dedupe(cancelFirst: Boolean) on QUERY | MUTATION
directive @dedupe(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
"""@optimisticKey is used to identify a field as an optimistic key"""
directive @optimisticKey on FIELD
Expand Down Expand Up @@ -199,6 +205,12 @@ test('list operations are included', async function () {
SinglePage
}
enum DedupeMatchMode {
Variables
Operation
None
}
scalar ViewerIDFromSession
directive @User_delete repeatable on FIELD
Expand Down Expand Up @@ -255,7 +267,7 @@ test('list operations are included but delete directive should not be in when we
@dedupe is used to prevent an operation from running more than once at the same time.
If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
"""
directive @dedupe(cancelFirst: Boolean) on QUERY | MUTATION
directive @dedupe(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
"""@optimisticKey is used to identify a field as an optimistic key"""
directive @optimisticKey on FIELD
Expand Down Expand Up @@ -316,6 +328,12 @@ test('list operations are included but delete directive should not be in when we
SinglePage
}
enum DedupeMatchMode {
Variables
Operation
None
}
scalar ViewerIDFromSession
directive @User_delete repeatable on FIELD
Expand Down Expand Up @@ -385,7 +403,7 @@ test("writing twice doesn't duplicate definitions", async function () {
@dedupe is used to prevent an operation from running more than once at the same time.
If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
"""
directive @dedupe(cancelFirst: Boolean) on QUERY | MUTATION
directive @dedupe(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
"""@optimisticKey is used to identify a field as an optimistic key"""
directive @optimisticKey on FIELD
Expand Down Expand Up @@ -446,6 +464,12 @@ test("writing twice doesn't duplicate definitions", async function () {
SinglePage
}
enum DedupeMatchMode {
Variables
Operation
None
}
scalar ViewerIDFromSession
`)
})
20 changes: 10 additions & 10 deletions packages/houdini/src/codegen/transforms/paginate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ test('query with forwards cursor paginate', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($first: Int = 10, $after: String) @dedupe {
query Users($first: Int = 10, $after: String) @dedupe(match: Variables) {
usersByForwardsCursor(first: $first, after: $after) @paginate {
edges {
node {
Expand Down Expand Up @@ -1108,7 +1108,7 @@ test('query with custom first args', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($limit: Int!, $after: String) @dedupe {
query Users($limit: Int!, $after: String) @dedupe(match: Variables) {
usersByForwardsCursor(first: $limit, after: $after) @paginate {
edges {
node {
Expand Down Expand Up @@ -1155,7 +1155,7 @@ test('query with backwards cursor paginate', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users @dedupe {
query Users @dedupe(match: Variables) {
usersByBackwardsCursor(last: 10) @paginate {
edges {
node {
Expand Down Expand Up @@ -1198,7 +1198,7 @@ test('query with offset paginate', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($limit: Int = 10, $offset: Int) @dedupe {
query Users($limit: Int = 10, $offset: Int) @dedupe(match: Variables) {
usersByOffset(limit: $limit, offset: $offset) @paginate {
id
}
Expand Down Expand Up @@ -1229,7 +1229,7 @@ test('query with backwards cursor on full paginate', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($first: Int, $after: String, $last: Int = 10, $before: String) @dedupe {
query Users($first: Int, $after: String, $last: Int = 10, $before: String) @dedupe(match: Variables) {
usersByCursor(last: $last, first: $first, after: $after, before: $before) @paginate {
edges {
node {
Expand Down Expand Up @@ -1276,7 +1276,7 @@ test('query with forwards cursor on full paginate', async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($first: Int = 10, $after: String, $last: Int, $before: String) @dedupe {
query Users($first: Int = 10, $after: String, $last: Int, $before: String) @dedupe(match: Variables) {
usersByCursor(first: $first, after: $after, last: $last, before: $before) @paginate {
edges {
node {
Expand Down Expand Up @@ -1323,7 +1323,7 @@ test("don't generate unsupported directions", async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($first: Int = 10, $after: String) @dedupe {
query Users($first: Int = 10, $after: String) @dedupe(match: Variables) {
usersByForwardsCursor(first: $first, after: $after) @paginate {
edges {
node {
Expand Down Expand Up @@ -1370,7 +1370,7 @@ test("forwards cursor paginated query doesn't overlap variables", async function

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($first: Int!, $after: String, $last: Int, $before: String) @dedupe {
query Users($first: Int!, $after: String, $last: Int, $before: String) @dedupe(match: Variables) {
usersByCursor(first: $first, after: $after, last: $last, before: $before) @paginate {
edges {
node {
Expand Down Expand Up @@ -1417,7 +1417,7 @@ test("backwards cursor paginated query doesn't overlap variables", async functio

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($last: Int!, $first: Int, $after: String, $before: String) @dedupe {
query Users($last: Int!, $first: Int, $after: String, $before: String) @dedupe(match: Variables) {
usersByCursor(last: $last, first: $first, after: $after, before: $before) @paginate {
edges {
node {
Expand Down Expand Up @@ -1460,7 +1460,7 @@ test("offset paginated query doesn't overlap variables", async function () {

// load the contents of the file
expect(docs[0]?.document).toMatchInlineSnapshot(`
query Users($limit: Int! = 10, $offset: Int) @dedupe {
query Users($limit: Int! = 10, $offset: Int) @dedupe(match: Variables) {
usersByOffset(limit: $limit, offset: $offset) @paginate {
id
}
Expand Down
15 changes: 14 additions & 1 deletion packages/houdini/src/codegen/transforms/paginate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as graphql from 'graphql'

import type { Config, Document } from '../../lib'
import { HoudiniError, parentTypeFromAncestors, unwrapType, wrapType } from '../../lib'
import { ArtifactKind, type PaginateModes } from '../../runtime/lib/types'
import { ArtifactKind, DedupeMatchMode, type PaginateModes } from '../../runtime/lib/types'
import {
fragmentArguments as collectFragmentArguments,
type FragmentArgument,
Expand Down Expand Up @@ -233,6 +233,19 @@ export default async function paginate(config: Config, documents: Document[]): P
kind: graphql.Kind.NAME,
value: config.dedupeDirective,
},
arguments: [
{
kind: 'Argument',
name: {
kind: 'Name',
value: 'match',
},
value: {
kind: 'EnumValue',
value: DedupeMatchMode.Variables,
},
},
],
},
],
} as graphql.OperationDefinitionNode
Expand Down
12 changes: 10 additions & 2 deletions packages/houdini/src/codegen/transforms/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as graphql from 'graphql'

import type { Config, Document } from '../../lib'
import { siteURL } from '../../lib'
import { CachePolicy, PaginateMode } from '../../runtime/lib/types'
import { CachePolicy, DedupeMatchMode, PaginateMode } from '../../runtime/lib/types'
import { fragmentArguments } from './fragmentVariables'

// graphqlExtensions adds a few different things to the graphql schema
Expand Down Expand Up @@ -52,13 +52,21 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
"""
directive @${config.listPrependDirective} on FRAGMENT_SPREAD
enum DedupeMatchMode {
${DedupeMatchMode.Variables}
${DedupeMatchMode.Operation}
${DedupeMatchMode.None}
}
"""
@${
config.dedupeDirective
} is used to prevent an operation from running more than once at the same time.
If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
"""
directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
directive @${
config.dedupeDirective
}(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
"""
@${config.optimisticKeyDirective} is used to identify a field as an optimistic key
Expand Down
Loading

0 comments on commit 4892fdf

Please sign in to comment.