Skip to content

Commit

Permalink
feat(ts-client): root field methods
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt committed Apr 15, 2024
1 parent 9191249 commit 5de618b
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 168 deletions.
1 change: 1 addition & 0 deletions src/Schema/_.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './Args.js'
export { RootTypeName } from './core/helpers.js'
export * from './core/Index.js'
export * from './core/Named/__.js'
export * from './Field.js'
Expand Down
4 changes: 4 additions & 0 deletions src/Schema/core/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Index } from './Index.js'

export type MaybeThunk<$Type> = $Type | Thunk<$Type>

export type Thunk<$Type> = () => $Type
Expand All @@ -16,3 +18,5 @@ export namespace Base {
type: $Type
}
}

export type RootTypeName = keyof Index['Root']
19 changes: 11 additions & 8 deletions src/client/ResultSet/ResultSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@ import type { Schema, SomeField } from '../../Schema/__.js'
import type { PickScalarFields } from '../../Schema/Output/Output.js'
import type { SelectionSet } from '../SelectionSet/__.js'

// dprint-ignore
export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> =
SimplifyDeep<Object$<$SelectionSetQuery, Exclude<$Index['Root']['Query'], null>, $Index>>
type ExcludeNull<T> = Exclude<T, null>

export type Root<
$SelectionSet extends object,
$Index extends Schema.Index,
$RootTypeName extends Schema.RootTypeName,
> = SimplifyDeep<Object$<$SelectionSet, ExcludeNull<$Index['Root'][$RootTypeName]>, $Index>>

export type Query<$SelectionSet extends object, $Index extends Schema.Index> = Root<$SelectionSet, $Index, 'Query'>

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

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

// dprint-ignore
export type Object$<$SelectionSet, $Node extends Schema.Output.Object$2, $Index extends Schema.Index> =
SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true

/**
* Handle Scalars Wildcard
*/
Expand Down
19 changes: 9 additions & 10 deletions src/client/SelectionSet/SelectionSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@ import type {
SomeFields,
} from '../../Schema/__.js'

export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object$2
? Object<$Index['Root']['Query'], $Index>
: never
export type Query<$Index extends Schema.Index> = Root<$Index, 'Query'>

export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Schema.Object$2
? Object<$Index['Root']['Mutation'], $Index>
: never
export type Mutation<$Index extends Schema.Index> = Root<$Index, 'Mutation'>

export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Schema.Object$2
? Object<$Index['Root']['Subscription'], $Index>
: never
export type Subscription<$Index extends Schema.Index> = Root<$Index, 'Subscription'>

// dprint-ignore
export type Root<$Index extends Schema.Index, Type extends keyof Schema.Index['Root']> =
$Index['Root'][Type] extends Schema.Object$2 ? Object<$Index['Root'][Type], $Index> :
never

// dprint-ignore
type Object<$Object extends Schema.Object$2, $Index extends Schema.Index> =
export type Object<$Object extends Schema.Object$2, $Index extends Schema.Index> =
Fields<$Object['fields'], $Index>

// dprint-ignore
Expand Down
149 changes: 149 additions & 0 deletions src/client/client.customScalar.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* eslint-disable */
import { beforeEach, describe, expect, test } from 'vitest'
import { setupMockServer } from '../../tests/raw/__helpers.js'
import type { Index } from '../../tests/ts/_/schema/generated/Index.js'
import { $Index as schemaIndex } from '../../tests/ts/_/schema/generated/SchemaRuntime.js'
import { create } from './client.js'

const ctx = setupMockServer()
const data = { fooBarUnion: { int: 1 } }

// @ts-ignore infinite depth
const client = () => create<Index>({ url: ctx.url, schemaIndex })

describe(`output`, () => {
test(`query field`, async () => {
ctx.res({ body: { data: { date: 0 } } })
expect(await client().query.$batch({ date: true })).toEqual({ date: new Date(0) })
})
test(`query field in non-null`, async () => {
ctx.res({ body: { data: { dateNonNull: 0 } } })
expect(await client().query.$batch({ dateNonNull: true })).toEqual({ dateNonNull: new Date(0) })
})
test(`query field in list`, async () => {
ctx.res({ body: { data: { dateList: [0, 1] } } })
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
})
test(`query field in list non-null`, async () => {
ctx.res({ body: { data: { dateList: [0, 1] } } })
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
})
test(`object field`, async () => {
ctx.res({ body: { data: { dateObject1: { date1: 0 } } } })
expect(await client().query.$batch({ dateObject1: { date1: true } })).toEqual({
dateObject1: { date1: new Date(0) },
})
})
test(`object field in interface`, async () => {
ctx.res({ body: { data: { dateInterface1: { date1: 0 } } } })
expect(await client().query.$batch({ dateInterface1: { date1: true } })).toEqual({
dateInterface1: { date1: new Date(0) },
})
})
describe(`object field in union`, () => {
test(`case 1 with __typename`, async () => {
ctx.res({ body: { data: { dateUnion: { __typename: `DateObject1`, date1: 0 } } } })
expect(await client().query.$batch({ dateUnion: { __typename: true, onDateObject1: { date1: true } } }))
.toEqual({
dateUnion: { __typename: `DateObject1`, date1: new Date(0) },
})
})
test(`case 1 without __typename`, async () => {
ctx.res({ body: { data: { dateUnion: { date1: 0 } } } })
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
dateUnion: { date1: new Date(0) },
})
})
test(`case 2`, async () => {
ctx.res({ body: { data: { dateUnion: { date2: 0 } } } })
expect(
await client().query.$batch({
dateUnion: { onDateObject1: { date1: true }, onDateObject2: { date2: true } },
}),
)
.toEqual({ dateUnion: { date2: new Date(0) } })
})
test(`case 2 miss`, async () => {
ctx.res({ body: { data: { dateUnion: null } } })
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
dateUnion: null,
}) // dprint-ignore
})
})
})

describe(`input`, () => {
// needed to avoid runtime error but data ignored because we only test input below.
beforeEach(() => {
ctx.res({ body: { data: {} } })
})
const clientExpected = (expectedDocument: (document: any) => void) => {
const client = create<Index>({
url: ctx.url,
schemaIndex,
hooks: {
documentEncode: (input, run) => {
const result = run(input)
expectedDocument(result)
return result
},
},
})
return client
}

test(`arg field`, async () => {
const client = clientExpected((doc) => expect(doc.dateArg.$.date).toEqual(new Date(0).getTime()))
await client.query.$batch({ dateArg: { $: { date: new Date(0) } } })
})
test('arg field in non-null', async () => {
const client = clientExpected((doc) => expect(doc.dateArgNonNull.$.date).toEqual(new Date(0).getTime()))
await client.query.$batch({ dateArgNonNull: { $: { date: new Date(0) } } })
})
test('arg field in list', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgList: { $: { date: [new Date(0), new Date(1)] } } })
})
test('arg field in list (null)', async () => {
const client = clientExpected((doc) => expect(doc.dateArgList.$.date).toEqual(null))
await client.query.$batch({ dateArgList: { $: { date: null } } })
})
test('arg field in non-null list (with list)', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgNonNullList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgNonNullList: { $: { date: [new Date(0), new Date(1)] } } })
})
test('arg field in non-null list (with null)', async () => {
const client = clientExpected((doc) => expect(doc.dateArgNonNullList.$.date).toEqual([null, new Date(0).getTime()]))
await client.query.$batch({ dateArgNonNullList: { $: { date: [null, new Date(0)] } } })
})
test('arg field in non-null list non-null', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgNonNullListNonNull.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgNonNullListNonNull: { $: { date: [new Date(0), new Date(1)] } } })
})
test(`input object field`, async () => {
const client = clientExpected((doc) => {
expect(doc.dateArgInputObject.$.input.dateRequired).toEqual(new Date(0).getTime())
expect(doc.dateArgInputObject.$.input.date).toEqual(new Date(1).getTime())
})
await client.query.$batch({
dateArgInputObject: { $: { input: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
})
})
test(`nested input object field`, async () => {
const client = clientExpected((doc) => {
expect(doc.InputObjectNested.$.input.InputObject.dateRequired).toEqual(new Date(0).getTime())
expect(doc.InputObjectNested.$.input.InputObject.date).toEqual(new Date(1).getTime())
})
await client.query.$batch({
InputObjectNested: {
$: { input: { InputObject: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
},
})
})
})
141 changes: 0 additions & 141 deletions src/client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,144 +15,3 @@ test.todo(`query`, async () => {
const mockRes = ctx.res({ body: { data } }).spec.body!
expect(await client().query.$batch({ fooBarUnion: { onBar: { int: true } } })).toEqual(mockRes.data)
})

describe(`custom scalar`, () => {
describe(`output`, () => {
test(`query field`, async () => {
ctx.res({ body: { data: { date: 0 } } })
expect(await client().query.$batch({ date: true })).toEqual({ date: new Date(0) })
})
test(`query field in non-null`, async () => {
ctx.res({ body: { data: { dateNonNull: 0 } } })
expect(await client().query.$batch({ dateNonNull: true })).toEqual({ dateNonNull: new Date(0) })
})
test(`query field in list`, async () => {
ctx.res({ body: { data: { dateList: [0, 1] } } })
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
})
test(`query field in list non-null`, async () => {
ctx.res({ body: { data: { dateList: [0, 1] } } })
expect(await client().query.$batch({ dateList: true })).toEqual({ dateList: [new Date(0), new Date(1)] })
})
test(`object field`, async () => {
ctx.res({ body: { data: { dateObject1: { date1: 0 } } } })
expect(await client().query.$batch({ dateObject1: { date1: true } })).toEqual({
dateObject1: { date1: new Date(0) },
})
})
test(`object field in interface`, async () => {
ctx.res({ body: { data: { dateInterface1: { date1: 0 } } } })
expect(await client().query.$batch({ dateInterface1: { date1: true } })).toEqual({
dateInterface1: { date1: new Date(0) },
})
})
describe(`object field in union`, () => {
test(`case 1 with __typename`, async () => {
ctx.res({ body: { data: { dateUnion: { __typename: `DateObject1`, date1: 0 } } } })
expect(await client().query.$batch({ dateUnion: { __typename: true, onDateObject1: { date1: true } } }))
.toEqual({
dateUnion: { __typename: `DateObject1`, date1: new Date(0) },
})
})
test(`case 1 without __typename`, async () => {
ctx.res({ body: { data: { dateUnion: { date1: 0 } } } })
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
dateUnion: { date1: new Date(0) },
})
})
test(`case 2`, async () => {
ctx.res({ body: { data: { dateUnion: { date2: 0 } } } })
expect(
await client().query.$batch({
dateUnion: { onDateObject1: { date1: true }, onDateObject2: { date2: true } },
}),
)
.toEqual({ dateUnion: { date2: new Date(0) } })
})
test(`case 2 miss`, async () => {
ctx.res({ body: { data: { dateUnion: null } } })
expect(await client().query.$batch({ dateUnion: { onDateObject1: { date1: true } } })).toEqual({
dateUnion: null,
}) // dprint-ignore
})
})
})

describe(`input`, () => {
// needed to avoid runtime error but data ignored because we only test input below.
beforeEach(() => {
ctx.res({ body: { data: {} } })
})
const clientExpected = (expectedDocument: (document: any) => void) => {
const client = create<Index>({
url: ctx.url,
schemaIndex,
hooks: {
documentEncode: (input, run) => {
const result = run(input)
expectedDocument(result)
return result
},
},
})
return client
}

test(`arg field`, async () => {
const client = clientExpected((doc) => expect(doc.dateArg.$.date).toEqual(new Date(0).getTime()))
await client.query.$batch({ dateArg: { $: { date: new Date(0) } } })
})
test('arg field in non-null', async () => {
const client = clientExpected((doc) => expect(doc.dateArgNonNull.$.date).toEqual(new Date(0).getTime()))
await client.query.$batch({ dateArgNonNull: { $: { date: new Date(0) } } })
})
test('arg field in list', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgList: { $: { date: [new Date(0), new Date(1)] } } })
})
test('arg field in list (null)', async () => {
const client = clientExpected((doc) => expect(doc.dateArgList.$.date).toEqual(null))
await client.query.$batch({ dateArgList: { $: { date: null } } })
})
test('arg field in non-null list (with list)', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgNonNullList.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgNonNullList: { $: { date: [new Date(0), new Date(1)] } } })
})
test('arg field in non-null list (with null)', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgNonNullList.$.date).toEqual([null, new Date(0).getTime()])
)
await client.query.$batch({ dateArgNonNullList: { $: { date: [null, new Date(0)] } } })
})
test('arg field in non-null list non-null', async () => {
const client = clientExpected((doc) =>
expect(doc.dateArgNonNullListNonNull.$.date).toEqual([new Date(0).getTime(), new Date(1).getTime()])
)
await client.query.$batch({ dateArgNonNullListNonNull: { $: { date: [new Date(0), new Date(1)] } } })
})
test(`input object field`, async () => {
const client = clientExpected((doc) => {
expect(doc.dateArgInputObject.$.input.dateRequired).toEqual(new Date(0).getTime())
expect(doc.dateArgInputObject.$.input.date).toEqual(new Date(1).getTime())
})
await client.query.$batch({
dateArgInputObject: { $: { input: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
})
})
test(`nested input object field`, async () => {
const client = clientExpected((doc) => {
expect(doc.InputObjectNested.$.input.InputObject.dateRequired).toEqual(new Date(0).getTime())
expect(doc.InputObjectNested.$.input.InputObject.date).toEqual(new Date(1).getTime())
})
await client.query.$batch({
InputObjectNested: {
$: { input: { InputObject: { idRequired: '', dateRequired: new Date(0), date: new Date(1) } } },
},
})
})
})
})

0 comments on commit 5de618b

Please sign in to comment.