Skip to content

Commit 5de618b

Browse files
committed
feat(ts-client): root field methods
1 parent 9191249 commit 5de618b

7 files changed

Lines changed: 191 additions & 168 deletions

File tree

src/Schema/_.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './Args.js'
2+
export { RootTypeName } from './core/helpers.js'
23
export * from './core/Index.js'
34
export * from './core/Named/__.js'
45
export * from './Field.js'

src/Schema/core/helpers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Index } from './Index.js'
2+
13
export type MaybeThunk<$Type> = $Type | Thunk<$Type>
24

35
export type Thunk<$Type> = () => $Type
@@ -16,3 +18,5 @@ export namespace Base {
1618
type: $Type
1719
}
1820
}
21+
22+
export type RootTypeName = keyof Index['Root']

src/client/ResultSet/ResultSet.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,25 @@ import type { Schema, SomeField } from '../../Schema/__.js'
77
import type { PickScalarFields } from '../../Schema/Output/Output.js'
88
import type { SelectionSet } from '../SelectionSet/__.js'
99

10-
// dprint-ignore
11-
export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> =
12-
SimplifyDeep<Object$<$SelectionSetQuery, Exclude<$Index['Root']['Query'], null>, $Index>>
10+
type ExcludeNull<T> = Exclude<T, null>
11+
12+
export type Root<
13+
$SelectionSet extends object,
14+
$Index extends Schema.Index,
15+
$RootTypeName extends Schema.RootTypeName,
16+
> = SimplifyDeep<Object$<$SelectionSet, ExcludeNull<$Index['Root'][$RootTypeName]>, $Index>>
17+
18+
export type Query<$SelectionSet extends object, $Index extends Schema.Index> = Root<$SelectionSet, $Index, 'Query'>
1319

1420
// dprint-ignore
15-
export type Mutation<$SelectionSetMutation extends object, $Index extends Schema.Index> =
16-
SimplifyDeep<Object$<$SelectionSetMutation, Exclude<$Index['Root']['Mutation'], null>, $Index>>
21+
export type Mutation<$SelectionSet extends object, $Index extends Schema.Index> = Root<$SelectionSet, $Index, 'Mutation'>
1722

1823
// dprint-ignore
19-
export type Subscription<$SelectionSetSubscription extends object, $Index extends Schema.Index> =
20-
SimplifyDeep<Object$<$SelectionSetSubscription, Exclude<$Index['Root']['Subscription'], null>, $Index>>
24+
export type Subscription<$SelectionSet extends object, $Index extends Schema.Index> = Root<$SelectionSet, $Index, 'Subscription'>
2125

2226
// dprint-ignore
2327
export type Object$<$SelectionSet, $Node extends Schema.Output.Object$2, $Index extends Schema.Index> =
2428
SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true
25-
2629
/**
2730
* Handle Scalars Wildcard
2831
*/

src/client/SelectionSet/SelectionSet.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@ import type {
1111
SomeFields,
1212
} from '../../Schema/__.js'
1313

14-
export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object$2
15-
? Object<$Index['Root']['Query'], $Index>
16-
: never
14+
export type Query<$Index extends Schema.Index> = Root<$Index, 'Query'>
1715

18-
export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Schema.Object$2
19-
? Object<$Index['Root']['Mutation'], $Index>
20-
: never
16+
export type Mutation<$Index extends Schema.Index> = Root<$Index, 'Mutation'>
2117

22-
export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Schema.Object$2
23-
? Object<$Index['Root']['Subscription'], $Index>
24-
: never
18+
export type Subscription<$Index extends Schema.Index> = Root<$Index, 'Subscription'>
19+
20+
// dprint-ignore
21+
export type Root<$Index extends Schema.Index, Type extends keyof Schema.Index['Root']> =
22+
$Index['Root'][Type] extends Schema.Object$2 ? Object<$Index['Root'][Type], $Index> :
23+
never
2524

2625
// dprint-ignore
27-
type Object<$Object extends Schema.Object$2, $Index extends Schema.Index> =
26+
export type Object<$Object extends Schema.Object$2, $Index extends Schema.Index> =
2827
Fields<$Object['fields'], $Index>
2928

3029
// dprint-ignore
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/* eslint-disable */
2+
import { beforeEach, describe, expect, test } from 'vitest'
3+
import { setupMockServer } from '../../tests/raw/__helpers.js'
4+
import type { Index } from '../../tests/ts/_/schema/generated/Index.js'
5+
import { $Index as schemaIndex } from '../../tests/ts/_/schema/generated/SchemaRuntime.js'
6+
import { create } from './client.js'
7+
8+
const ctx = setupMockServer()
9+
const data = { fooBarUnion: { int: 1 } }
10+
11+
// @ts-ignore infinite depth
12+
const client = () => create<Index>({ url: ctx.url, schemaIndex })
13+
14+
describe(`output`, () => {
15+
test(`query field`, async () => {
16+
ctx.res({ body: { data: { date: 0 } } })
17+
expect(await client().query.$batch({ date: true })).toEqual({ date: new Date(0) })
18+
})
19+
test(`query field in non-null`, async () => {
20+
ctx.res({ body: { data: { dateNonNull: 0 } } })
21+
expect(await client().query.$batch({ dateNonNull: true })).toEqual({ dateNonNull: new Date(0) })
22+
})
23+
test(`query field in list`, async () => {
24+
ctx.res({ body: { data: { dateList: [0, 1] } } })
25+
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
26+
})
27+
test(`query field in list non-null`, async () => {
28+
ctx.res({ body: { data: { dateList: [0, 1] } } })
29+
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
30+
})
31+
test(`object field`, async () => {
32+
ctx.res({ body: { data: { dateObject1: { date1: 0 } } } })
33+
expect(await client().query.$batch({ dateObject1: { date1: true } })).toEqual({
34+
dateObject1: { date1: new Date(0) },
35+
})
36+
})
37+
test(`object field in interface`, async () => {
38+
ctx.res({ body: { data: { dateInterface1: { date1: 0 } } } })
39+
expect(await client().query.$batch({ dateInterface1: { date1: true } })).toEqual({
40+
dateInterface1: { date1: new Date(0) },
41+
})
42+
})
43+
describe(`object field in union`, () => {
44+
test(`case 1 with __typename`, async () => {
45+
ctx.res({ body: { data: { dateUnion: { __typename: `DateObject1`, date1: 0 } } } })
46+
expect(await client().query.$batch({ dateUnion: { __typename: true, onDateObject1: { date1: true } } }))
47+
.toEqual({
48+
dateUnion: { __typename: `DateObject1`, date1: new Date(0) },
49+
})
50+
})
51+
test(`case 1 without __typename`, async () => {
52+
ctx.res({ body: { data: { dateUnion: { date1: 0 } } } })
53+
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
54+
dateUnion: { date1: new Date(0) },
55+
})
56+
})
57+
test(`case 2`, async () => {
58+
ctx.res({ body: { data: { dateUnion: { date2: 0 } } } })
59+
expect(
60+
await client().query.$batch({
61+
dateUnion: { onDateObject1: { date1: true }, onDateObject2: { date2: true } },
62+
}),
63+
)
64+
.toEqual({ dateUnion: { date2: new Date(0) } })
65+
})
66+
test(`case 2 miss`, async () => {
67+
ctx.res({ body: { data: { dateUnion: null } } })
68+
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
69+
dateUnion: null,
70+
}) // dprint-ignore
71+
})
72+
})
73+
})
74+
75+
describe(`input`, () => {
76+
// needed to avoid runtime error but data ignored because we only test input below.
77+
beforeEach(() => {
78+
ctx.res({ body: { data: {} } })
79+
})
80+
const clientExpected = (expectedDocument: (document: any) => void) => {
81+
const client = create<Index>({
82+
url: ctx.url,
83+
schemaIndex,
84+
hooks: {
85+
documentEncode: (input, run) => {
86+
const result = run(input)
87+
expectedDocument(result)
88+
return result
89+
},
90+
},
91+
})
92+
return client
93+
}
94+
95+
test(`arg field`, async () => {
96+
const client = clientExpected((doc) => expect(doc.dateArg.$.date).toEqual(new Date(0).getTime()))
97+
await client.query.$batch({ dateArg: { $: { date: new Date(0) } } })
98+
})
99+
test('arg field in non-null', async () => {
100+
const client = clientExpected((doc) => expect(doc.dateArgNonNull.$.date).toEqual(new Date(0).getTime()))
101+
await client.query.$batch({ dateArgNonNull: { $: { date: new Date(0) } } })
102+
})
103+
test('arg field in list', async () => {
104+
const client = clientExpected((doc) =>
105+
expect(doc.dateArgList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
106+
)
107+
await client.query.$batch({ dateArgList: { $: { date: [new Date(0), new Date(1)] } } })
108+
})
109+
test('arg field in list (null)', async () => {
110+
const client = clientExpected((doc) => expect(doc.dateArgList.$.date).toEqual(null))
111+
await client.query.$batch({ dateArgList: { $: { date: null } } })
112+
})
113+
test('arg field in non-null list (with list)', async () => {
114+
const client = clientExpected((doc) =>
115+
expect(doc.dateArgNonNullList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
116+
)
117+
await client.query.$batch({ dateArgNonNullList: { $: { date: [new Date(0), new Date(1)] } } })
118+
})
119+
test('arg field in non-null list (with null)', async () => {
120+
const client = clientExpected((doc) => expect(doc.dateArgNonNullList.$.date).toEqual([null, new Date(0).getTime()]))
121+
await client.query.$batch({ dateArgNonNullList: { $: { date: [null, new Date(0)] } } })
122+
})
123+
test('arg field in non-null list non-null', async () => {
124+
const client = clientExpected((doc) =>
125+
expect(doc.dateArgNonNullListNonNull.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
126+
)
127+
await client.query.$batch({ dateArgNonNullListNonNull: { $: { date: [new Date(0), new Date(1)] } } })
128+
})
129+
test(`input object field`, async () => {
130+
const client = clientExpected((doc) => {
131+
expect(doc.dateArgInputObject.$.input.dateRequired).toEqual(new Date(0).getTime())
132+
expect(doc.dateArgInputObject.$.input.date).toEqual(new Date(1).getTime())
133+
})
134+
await client.query.$batch({
135+
dateArgInputObject: { $: { input: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
136+
})
137+
})
138+
test(`nested input object field`, async () => {
139+
const client = clientExpected((doc) => {
140+
expect(doc.InputObjectNested.$.input.InputObject.dateRequired).toEqual(new Date(0).getTime())
141+
expect(doc.InputObjectNested.$.input.InputObject.date).toEqual(new Date(1).getTime())
142+
})
143+
await client.query.$batch({
144+
InputObjectNested: {
145+
$: { input: { InputObject: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
146+
},
147+
})
148+
})
149+
})

src/client/client.test.ts

Lines changed: 0 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -15,144 +15,3 @@ test.todo(`query`, async () => {
1515
const mockRes = ctx.res({ body: { data } }).spec.body!
1616
expect(await client().query.$batch({ fooBarUnion: { onBar: { int: true } } })).toEqual(mockRes.data)
1717
})
18-
19-
describe(`custom scalar`, () => {
20-
describe(`output`, () => {
21-
test(`query field`, async () => {
22-
ctx.res({ body: { data: { date: 0 } } })
23-
expect(await client().query.$batch({ date: true })).toEqual({ date: new Date(0) })
24-
})
25-
test(`query field in non-null`, async () => {
26-
ctx.res({ body: { data: { dateNonNull: 0 } } })
27-
expect(await client().query.$batch({ dateNonNull: true })).toEqual({ dateNonNull: new Date(0) })
28-
})
29-
test(`query field in list`, async () => {
30-
ctx.res({ body: { data: { dateList: [0, 1] } } })
31-
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
32-
})
33-
test(`query field in list non-null`, async () => {
34-
ctx.res({ body: { data: { dateList: [0, 1] } } })
35-
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
36-
})
37-
test(`object field`, async () => {
38-
ctx.res({ body: { data: { dateObject1: { date1: 0 } } } })
39-
expect(await client().query.$batch({ dateObject1: { date1: true } })).toEqual({
40-
dateObject1: { date1: new Date(0) },
41-
})
42-
})
43-
test(`object field in interface`, async () => {
44-
ctx.res({ body: { data: { dateInterface1: { date1: 0 } } } })
45-
expect(await client().query.$batch({ dateInterface1: { date1: true } })).toEqual({
46-
dateInterface1: { date1: new Date(0) },
47-
})
48-
})
49-
describe(`object field in union`, () => {
50-
test(`case 1 with __typename`, async () => {
51-
ctx.res({ body: { data: { dateUnion: { __typename: `DateObject1`, date1: 0 } } } })
52-
expect(await client().query.$batch({ dateUnion: { __typename: true, onDateObject1: { date1: true } } }))
53-
.toEqual({
54-
dateUnion: { __typename: `DateObject1`, date1: new Date(0) },
55-
})
56-
})
57-
test(`case 1 without __typename`, async () => {
58-
ctx.res({ body: { data: { dateUnion: { date1: 0 } } } })
59-
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
60-
dateUnion: { date1: new Date(0) },
61-
})
62-
})
63-
test(`case 2`, async () => {
64-
ctx.res({ body: { data: { dateUnion: { date2: 0 } } } })
65-
expect(
66-
await client().query.$batch({
67-
dateUnion: { onDateObject1: { date1: true }, onDateObject2: { date2: true } },
68-
}),
69-
)
70-
.toEqual({ dateUnion: { date2: new Date(0) } })
71-
})
72-
test(`case 2 miss`, async () => {
73-
ctx.res({ body: { data: { dateUnion: null } } })
74-
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
75-
dateUnion: null,
76-
}) // dprint-ignore
77-
})
78-
})
79-
})
80-
81-
describe(`input`, () => {
82-
// needed to avoid runtime error but data ignored because we only test input below.
83-
beforeEach(() => {
84-
ctx.res({ body: { data: {} } })
85-
})
86-
const clientExpected = (expectedDocument: (document: any) => void) => {
87-
const client = create<Index>({
88-
url: ctx.url,
89-
schemaIndex,
90-
hooks: {
91-
documentEncode: (input, run) => {
92-
const result = run(input)
93-
expectedDocument(result)
94-
return result
95-
},
96-
},
97-
})
98-
return client
99-
}
100-
101-
test(`arg field`, async () => {
102-
const client = clientExpected((doc) => expect(doc.dateArg.$.date).toEqual(new Date(0).getTime()))
103-
await client.query.$batch({ dateArg: { $: { date: new Date(0) } } })
104-
})
105-
test('arg field in non-null', async () => {
106-
const client = clientExpected((doc) => expect(doc.dateArgNonNull.$.date).toEqual(new Date(0).getTime()))
107-
await client.query.$batch({ dateArgNonNull: { $: { date: new Date(0) } } })
108-
})
109-
test('arg field in list', async () => {
110-
const client = clientExpected((doc) =>
111-
expect(doc.dateArgList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
112-
)
113-
await client.query.$batch({ dateArgList: { $: { date: [new Date(0), new Date(1)] } } })
114-
})
115-
test('arg field in list (null)', async () => {
116-
const client = clientExpected((doc) => expect(doc.dateArgList.$.date).toEqual(null))
117-
await client.query.$batch({ dateArgList: { $: { date: null } } })
118-
})
119-
test('arg field in non-null list (with list)', async () => {
120-
const client = clientExpected((doc) =>
121-
expect(doc.dateArgNonNullList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
122-
)
123-
await client.query.$batch({ dateArgNonNullList: { $: { date: [new Date(0), new Date(1)] } } })
124-
})
125-
test('arg field in non-null list (with null)', async () => {
126-
const client = clientExpected((doc) =>
127-
expect(doc.dateArgNonNullList.$.date).toEqual([null, new Date(0).getTime()])
128-
)
129-
await client.query.$batch({ dateArgNonNullList: { $: { date: [null, new Date(0)] } } })
130-
})
131-
test('arg field in non-null list non-null', async () => {
132-
const client = clientExpected((doc) =>
133-
expect(doc.dateArgNonNullListNonNull.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
134-
)
135-
await client.query.$batch({ dateArgNonNullListNonNull: { $: { date: [new Date(0), new Date(1)] } } })
136-
})
137-
test(`input object field`, async () => {
138-
const client = clientExpected((doc) => {
139-
expect(doc.dateArgInputObject.$.input.dateRequired).toEqual(new Date(0).getTime())
140-
expect(doc.dateArgInputObject.$.input.date).toEqual(new Date(1).getTime())
141-
})
142-
await client.query.$batch({
143-
dateArgInputObject: { $: { input: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
144-
})
145-
})
146-
test(`nested input object field`, async () => {
147-
const client = clientExpected((doc) => {
148-
expect(doc.InputObjectNested.$.input.InputObject.dateRequired).toEqual(new Date(0).getTime())
149-
expect(doc.InputObjectNested.$.input.InputObject.date).toEqual(new Date(1).getTime())
150-
})
151-
await client.query.$batch({
152-
InputObjectNested: {
153-
$: { input: { InputObject: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
154-
},
155-
})
156-
})
157-
})
158-
})

0 commit comments

Comments
 (0)