Skip to content

Commit

Permalink
fix(document): types when multiple roots (#1128)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Sep 26, 2024
1 parent 9e362c4 commit 5f5549b
Show file tree
Hide file tree
Showing 22 changed files with 398 additions and 218 deletions.
36 changes: 29 additions & 7 deletions examples/55_generated/generated_document__document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This example shows how to use the TypeScript interface for GraphQL arguments.
* This example shows how to use the TypeScript interface for whole GraphQL documents.
*/

import { Pokemon } from '../$/generated-clients/pokemon/__.js'
Expand All @@ -8,15 +8,37 @@ import { showJson } from '../$/helpers.js'
const pokemon = Pokemon.create()

const pokemons = await pokemon.document({
query: {
pokemonsQuery: {
pokemons: [`pokemons2`, {
$: { filter: { name: { in: [`Pikachu`, `Charizard`] } } },
query: { // A root type.
pokemonsAndTrainers: { // A name of an the operation.
trainers: { // A root field.
name: true, // A field.
},
pokemons: {
$: { // A field's arguments
filter: { name: { in: [`Pikachu`, `Charizard`] } },
},
name: true,
trainer: { name: true },
}],
trainer: {
name: true,
},
},
},
},
mutation: {
makeSomeNewPokemons: {
addPokemon: [
['addAngryPikachu', {
$: { name: `AngryPikachu`, attack: 100, defense: 100, hp: 100 },
name: true,
}],
['addAngryCharizard', {
$: { name: `AngryCharizard`, attack: 100, defense: 100, hp: 100 },
name: true,
}],
],
},
},
})
.run('pokemonsAndTrainers')

showJson(pokemons)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
headers: Headers {
accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8',
'content-type': 'application/json',
'x-sent-at-time': '1727291929241'
'x-sent-at-time': '1727310087052'
},
signal: undefined,
method: 'post',
Expand Down
2 changes: 1 addition & 1 deletion examples/__outputs__/20_output/output_envelope.output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
headers: Headers {
'content-type': 'application/graphql-response+json; charset=utf-8',
'content-length': '104',
date: 'Wed, 25 Sep 2024 19:18:49 GMT',
date: 'Thu, 26 Sep 2024 00:21:27 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
---------------------------------------- SHOW ----------------------------------------
{}
{
"trainers": [
{
"name": "Ash"
},
{
"name": "Misty"
}
],
"pokemons": [
{
"name": "Pikachu",
"trainer": {
"name": "Ash"
}
},
{
"name": "Charizard",
"trainer": {
"name": "Ash"
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
parentId: '57916921f30d3744',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: '78959de15b116846',
traceState: undefined,
name: 'encode',
id: 'dcdd7c9ea7a24491',
id: 'e78df0ee2a54acb6',
kind: 0,
timestamp: 1727291930021000,
duration: 1392.459,
timestamp: 1727310087760000,
duration: 554.291,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -33,14 +33,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
parentId: '57916921f30d3744',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: '78959de15b116846',
traceState: undefined,
name: 'pack',
id: 'd1ca1155f050f30c',
id: '88b5c48ab9be7a36',
kind: 0,
timestamp: 1727291930024000,
duration: 16745.417,
timestamp: 1727310087845000,
duration: 17482.625,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -57,14 +57,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
parentId: '57916921f30d3744',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: '78959de15b116846',
traceState: undefined,
name: 'exchange',
id: 'c9978fd02d8f3228',
id: 'd4a611cb0daf3ac1',
kind: 0,
timestamp: 1727291930042000,
duration: 24516.084,
timestamp: 1727310087867000,
duration: 48095.333,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -81,14 +81,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
parentId: '57916921f30d3744',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: '78959de15b116846',
traceState: undefined,
name: 'unpack',
id: '663a79043ef1feea',
id: '20d213f3a3f86f1d',
kind: 0,
timestamp: 1727291930067000,
duration: 1665.125,
timestamp: 1727310087916000,
duration: 1125.833,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -105,14 +105,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
parentId: '57916921f30d3744',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: '78959de15b116846',
traceState: undefined,
name: 'decode',
id: '9fd8d38e892a7a50',
id: 'c35ae080bfb451be',
kind: 0,
timestamp: 1727291930068000,
duration: 184.333,
timestamp: 1727310087917000,
duration: 181.958,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -129,14 +129,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: '9d154073558dfe77a07840d133b4358c',
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
parentId: undefined,
traceState: undefined,
name: 'request',
id: '57916921f30d3744',
id: '78959de15b116846',
kind: 0,
timestamp: 1727291930020000,
duration: 48270.291,
timestamp: 1727310087759000,
duration: 158234.5,
attributes: {},
status: { code: 0 },
events: [],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"test:examples": "vitest --config vitest.examples.config.ts --dir tests/examples",
"test": "vitest",
"test:web": "vitest --environment jsdom",
"test:types": "vitest --typecheck src/**/**/*.test-d.ts",
"test:types": "vitest --typecheck --dir src --testNamePattern .*.test-d.ts",
"test:coverage": "pnpm test -- --coverage",
"release:stable": "dripip stable",
"release:preview": "dripip preview",
Expand Down
104 changes: 48 additions & 56 deletions src/layers/6_client/requestMethods/document.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expectTypeOf, test } from 'vitest'
import { expectTypeOf, test } from 'vitest'
import { Graffle } from '../../../../tests/_/schema/generated/__.js'
import * as Schema from '../../../../tests/_/schema/schema.js'
import { MutationOnly } from '../../../../tests/_/schemaMutationOnly/generated/__.js'
Expand All @@ -16,65 +16,57 @@ test(`requires input`, () => {
// graffle.document({})
})

describe(`input`, () => {
test(`document with one query`, () => {
const run = graffle.document({ query: { foo: { id: true } } }).run
expectTypeOf(run).toMatchTypeOf<(...params: ['foo'] | [] | [undefined]) => Promise<any>>()
})
test(`document with one query`, async () => {
const run = graffle.document({ query: { foo: { id: true } } }).run
type $Parameters = Parameters<typeof run>
expectTypeOf<$Parameters>().toEqualTypeOf<[]>()
const result = await run()
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
})

test(`document with two queries`, () => {
const run = graffle.document({
query: {
foo: { id: true },
bar: { id: true },
},
}).run
expectTypeOf(run).toMatchTypeOf<(name: 'foo' | 'bar') => Promise<any>>()
})
test(`document with two queries`, async () => {
const run = graffle.document({
query: {
foo: { id: true },
bar: { date: true },
},
}).run
type $Parameters = Parameters<typeof run>
expectTypeOf<$Parameters>().toEqualTypeOf<['foo' | 'bar']>()
const result = await run(`foo`)
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
})

test(`root operation not available if it is not in schema`, () => {
const queryOnly = QueryOnly.create({
schema: SchemaQueryOnly.schema,
})
queryOnly.document({
query: { foo: { id: true } },
// todo
// // @ts-expect-error mutation not in schema
// mutation: { foo: { id: true } },
})
const mutationOnly = MutationOnly.create({
schema: SchemaMutationOnly.schema,
})
mutationOnly.document({
// @ts-expect-error query not in schema
foo: { query: { id: true } },
bar: { mutation: { id: true } },
})
})
test(`document with two queries of different root types`, async () => {
const run = graffle.document({
query: {
foo: { id: true },
},
mutation: {
bar: { idNonNull: true },
},
}).run
type $Parameters = Parameters<typeof run>
expectTypeOf<$Parameters>().toEqualTypeOf<['foo' | 'bar']>()
const result = await run(`foo`)
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
})

describe(`document(...).run()`, () => {
test(`document with one query`, () => {
{
const result = graffle.document({ query: { x: { id: true } } }).run()
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
{
const result = graffle.document({ query: { x: { id: true } } }).run(`x`)
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
{
const result = graffle.document({ query: { x: { id: true } } }).run(undefined)
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
test(`root operation not available if it is not in schema`, () => {
const queryOnly = QueryOnly.create({
schema: SchemaQueryOnly.schema,
})
queryOnly.document({
query: { foo: { id: true } },
// @ts-expect-error mutation not in schema
mutation: { foo: { id: true } },
})
const mutationOnly = MutationOnly.create({
schema: SchemaMutationOnly.schema,
})
test(`document with two queries`, () => {
const result = graffle.document({
query: {
foo: { id: true },
bar: { id: true },
},
}).run(`foo`)
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
mutationOnly.document({
mutation: { bar: { id: true } },
// @ts-expect-error query not in schema
query: { foo: { id: true } },
})
})
4 changes: 0 additions & 4 deletions src/layers/6_client/requestMethods/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,12 @@ describe(`document with two queries`, () => {

test(`document with one query`, async () => {
const { run } = graffle.document({ query: { foo: { id: true } } })
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
await expect(run()).resolves.toEqual({ id: db.id1 })
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
})

test(`document with one mutation`, async () => {
const { run } = graffle.document({ mutation: { foo: { id: true } } })
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
await expect(run()).resolves.toEqual({ id: db.id1 })
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
})

test(`error`, async () => {
Expand Down
16 changes: 14 additions & 2 deletions src/layers/6_client/requestMethods/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,21 @@ export type DocumentRunner<
$$Name extends Document.GetOperationNames<$$Document> = Document.GetOperationNames<$$Document>
> = {
run: <
$Name extends $$Name,
$Params extends (IsTupleMultiple<UnionToTuple<$$Name>> extends true ? [name: $Name] : ([] | [name: $Name | undefined])),
$Params extends (IsTupleMultiple<UnionToTuple<$$Name>> extends true ? [name: $$Name] : []),
const $Name extends string = $Params extends [] ? $$Name : $Params[0],
>(...params: $Params) =>
// [$Name]
// ResultSet.Root<
// AddTypenameToSelectedRootTypeResultFields<
// $$Config,
// $$Index,
// Document.GetRootTypeNameOfOperation<$$Document, $Name>,
// Document.GetOperation<$$Document, $Name>
// >,
// $$Index,
// Document.GetRootTypeNameOfOperation<$$Document, $Name>
// >
//
Promise<
ResolveOutputReturnRootType<
$$Config,
Expand Down
2 changes: 1 addition & 1 deletion website/content/_snippets/example-links/raw.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<p class="ExampleLinks">Examples <span class="ExampleLinksTitleSeparator">-></span> <a href="../../examples/raw/raw-document-node">Raw Document Node</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node-typed">Raw Document Node Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string">Raw String</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string-typed">Raw String Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/headers">Headers</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/raw">Raw</a></p>
<p class="ExampleLinks">Examples <span class="ExampleLinksTitleSeparator">-></span> <a href="../../examples/transport-http/headers">Headers</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/raw">Raw</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node">Raw Document Node</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node-typed">Raw Document Node Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string">Raw String</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string-typed">Raw String Typed</a></p>
Loading

0 comments on commit 5f5549b

Please sign in to comment.