Skip to content

Commit b45dc60

Browse files
committed
feat(ts-client): use graphql return mode & named schema
1 parent 547dfe5 commit b45dc60

32 files changed

+549
-224
lines changed

src/Schema/Hybrid/types/Scalar/Scalar.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ export const Scalars = {
5757
Boolean,
5858
}
5959

60-
export type Any = String | Int | Boolean | ID | Float | SchemaCustomScalars[keyof SchemaCustomScalars]
60+
// todo this mixes scalars from different schemas
61+
export type Any = String | Int | Boolean | ID | Float | Values<NamedSchemas[keyof NamedSchemas]['customScalars']>
6162

62-
declare global {
63-
interface SchemaCustomScalars {}
64-
}
63+
type Values<T> = T extends any ? keyof T extends never ? never : T[keyof T] : never
64+
// type x = Values<NamedSchemas[keyof NamedSchemas]['customScalars']>

src/client/SelectionSet/toGraphQLDocumentString.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { parse, print } from 'graphql'
22
import { describe, expect, test } from 'vitest'
33
import type { Index } from '../../../tests/ts/_/schema/generated/Index.js'
44
import type { SelectionSet } from './__.js'
5-
import { toGraphQLDocumentString } from './toGraphQLDocumentString.js'
5+
import { toGraphQLDocumentSelectionSet } from './toGraphQLDocumentString.js'
66

77
// eslint-disable-next-line
88
// @ts-ignore
99
type Q = SelectionSet.Query<Index>
1010
const s = (selectionSet: Q) => selectionSet
1111
const prepareResult = (ss: Q) => {
12-
const graphqlDocumentString = toGraphQLDocumentString(ss as any)
12+
const graphqlDocumentString = toGraphQLDocumentSelectionSet(ss as any)
1313
// Should parse, ensures is syntactically valid graphql document.
1414
const document = parse(graphqlDocumentString)
1515
const graphqlDocumentStringFormatted = print(document)

src/client/SelectionSet/toGraphQLDocumentString.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ type Args_ = string | boolean | null | number | Args
1717

1818
type Indicator = 0 | 1 | boolean
1919

20-
export type GraphQLDocumentObject = Record<string, Indicator | SS>
20+
export type DocumentObject = Record<string, GraphQLRootSelection>
2121

22-
type SS = {
22+
export type GraphQLRootSelection = { query: GraphQLObjectSelection } | { mutation: GraphQLObjectSelection }
23+
24+
export type GraphQLObjectSelection = Record<string, Indicator | SS>
25+
26+
export type SS = {
2327
[k: string]: Indicator | SS
2428
} & SpecialFields
2529

26-
export const toGraphQLDocumentString = (ss: GraphQLDocumentObject) => {
27-
return `query ${toGraphQLDocumentSelectionSet(ss)}`
28-
}
30+
// export const toGraphQLDocumentString = (ss: GraphQLDocumentObject) => {
31+
// return `query ${toGraphQLDocumentSelectionSet(ss)}`
32+
// }
2933

30-
export const toGraphQLDocumentSelectionSet = (ss: GraphQLDocumentObject) => {
34+
export const toGraphQLDocumentSelectionSet = (ss: GraphQLObjectSelection) => {
3135
return `{
3236
${selectionSet(ss)}
3337
}`
@@ -93,7 +97,7 @@ const indicatorOrSelectionSet = (ss: null | Indicator | SS): string => {
9397
}`
9498
}
9599

96-
const selectionSet = (ss: GraphQLDocumentObject) => {
100+
export const selectionSet = (ss: GraphQLObjectSelection) => {
97101
return Object.entries(ss).filter(([_, v]) => {
98102
return isPositiveIndicator(v)
99103
}).map(([field, ss]) => {

src/client/client.customScalar.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
import { beforeEach, describe, expect, test } from 'vitest'
33
import { db } from '../../tests/_/db.js'
44
import { setupMockServer } from '../../tests/raw/__helpers.js'
5-
import type { Index } from '../../tests/ts/_/schema/generated/Index.js'
5+
// import type { Index } from '../../tests/ts/_/schema/generated/Index.js'
66
import { $Index as schemaIndex } from '../../tests/ts/_/schema/generated/SchemaRuntime.js'
77
import { create } from './client.js'
88

99
const ctx = setupMockServer()
1010
const date0Encoded = db.date0.toISOString()
1111
const date1Encoded = db.date1.toISOString()
1212

13-
const client = () => create<Index>({ schema: ctx.url, schemaIndex })
13+
const client = () => create({ name: 'MigrateMe', schema: ctx.url, schemaIndex })
1414

1515
describe(`output`, () => {
1616
test(`query field`, async () => {
@@ -79,13 +79,14 @@ describe(`input`, () => {
7979
ctx.res({ body: { data: {} } })
8080
})
8181
const clientExpected = (expectedDocument: (document: any) => void) => {
82-
const client = create<Index>({
82+
const client = create({
83+
name: 'MigrateMe',
8384
schema: ctx.url,
8485
schemaIndex,
8586
hooks: {
8687
documentEncode: (input, run) => {
8788
const result = run(input)
88-
expectedDocument(result)
89+
expectedDocument(result['query'])
8990
return result
9091
},
9192
},

src/client/client.document.test-d.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as SchemaMutationOnly from '../../tests/_/schemaMutationOnly/schema.js'
44
import * as SchemaQueryOnly from '../../tests/_/schemaQueryOnly/schema.js'
55
import { create } from './client.js'
66

7-
const client = create<Schema.Index>({ schema: Schema.schema, schemaIndex: Schema.$Index })
7+
const client = create({ schema: Schema.schema, schemaIndex: Schema.$Index })
88

99
test(`requires input`, () => {
1010
// @ts-expect-error missing input
@@ -28,7 +28,8 @@ describe(`input`, () => {
2828
})
2929

3030
test(`root operation not available if it is not in schema`, () => {
31-
const clientQueryOnly = create<SchemaQueryOnly.Index>({
31+
const clientQueryOnly = create({
32+
name: `QueryOnly`,
3233
schema: SchemaQueryOnly.schema,
3334
schemaIndex: SchemaQueryOnly.$Index,
3435
})
@@ -37,7 +38,8 @@ describe(`input`, () => {
3738
// @ts-expect-error mutation not in schema
3839
bar: { mutation: { id: true } },
3940
})
40-
const clientMutationOnly = create<SchemaMutationOnly.Index>({
41+
const clientMutationOnly = create({
42+
name: `MutationOnly`,
4143
schema: SchemaMutationOnly.schema,
4244
schemaIndex: SchemaMutationOnly.$Index,
4345
})

src/client/client.document.test.ts

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,55 @@
11
import { describe, expect, test } from 'vitest'
22
import { db } from '../../tests/_/db.js'
3-
import type { Index } from '../../tests/_/schema/generated/Index.js'
43
import { $Index } from '../../tests/_/schema/generated/SchemaRuntime.js'
54
import { schema } from '../../tests/_/schema/schema.js'
65
import { create } from './client.js'
76

8-
const client = create<Index>({ schema, schemaIndex: $Index })
7+
const client = create({ schema, schemaIndex: $Index })
98

109
describe(`document with two queries`, () => {
1110
const withTwo = client.document({
12-
foo: {
13-
query: { id: true },
14-
},
15-
bar: {
16-
query: { idNonNull: true },
17-
},
11+
foo: { query: { id: true } },
12+
bar: { query: { idNonNull: true } },
1813
})
1914

2015
test(`works`, async () => {
2116
const { run } = withTwo
22-
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
23-
await expect(run(`bar`)).resolves.toEqual({ data: { idNonNull: db.id1 } })
17+
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
18+
await expect(run(`bar`)).resolves.toEqual({ idNonNull: db.id1 })
2419
})
2520
test(`error if no operation name is provided`, async () => {
2621
const { run } = withTwo
2722
// @ts-expect-error
28-
await expect(run()).resolves.toMatchObject({
23+
await expect(run()).rejects.toMatchObject({
2924
errors: [{ message: `Must provide operation name if query contains multiple operations.` }],
3025
})
3126
})
3227
test(`error if wrong operation name is provided`, async () => {
3328
const { run } = withTwo
3429
// @ts-expect-error
35-
await expect(run(`boo`)).resolves.toMatchObject({ errors: [{ message: `Unknown operation named "boo".` }] })
30+
await expect(run(`boo`)).rejects.toMatchObject({ errors: [{ message: `Unknown operation named "boo".` }] })
3631
})
3732
test(`error if invalid name in document`, async () => {
3833
// @ts-expect-error
3934
const { run } = client.document({ foo$: { query: { id: true } } })
40-
await expect(run(`foo$`)).resolves.toMatchObject({
35+
await expect(run(`foo$`)).rejects.toMatchObject({
4136
errors: [{ message: `Syntax Error: Expected "{", found "$".` }],
4237
})
4338
})
4439
})
4540

4641
test(`document with one query`, async () => {
4742
const { run } = client.document({ foo: { query: { id: true } } })
48-
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
49-
await expect(run()).resolves.toEqual({ data: { id: db.id1 } })
50-
await expect(run(undefined)).resolves.toEqual({ data: { id: db.id1 } })
43+
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
44+
await expect(run()).resolves.toEqual({ id: db.id1 })
45+
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
5146
})
5247

5348
test(`document with one mutation`, async () => {
5449
const { run } = client.document({ foo: { mutation: { id: true } } })
55-
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
56-
await expect(run()).resolves.toEqual({ data: { id: db.id1 } })
57-
await expect(run(undefined)).resolves.toEqual({ data: { id: db.id1 } })
50+
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
51+
await expect(run()).resolves.toEqual({ id: db.id1 })
52+
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
5853
})
5954

6055
test(`document with one mutation and one query`, async () => {
@@ -66,6 +61,6 @@ test(`document with one mutation and one query`, async () => {
6661
query: { idNonNull: true },
6762
},
6863
})
69-
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
70-
await expect(run(`bar`)).resolves.toEqual({ data: { idNonNull: db.id1 } })
64+
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
65+
await expect(run(`bar`)).resolves.toEqual({ idNonNull: db.id1 })
7166
})

src/client/client.error.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isError } from '../../tests/_/schema/generated/Error.js'
44
import * as Schema from '../../tests/_/schema/schema.js'
55
import { create } from './client.js'
66

7-
const client = create<Schema.Index>({ schema: Schema.schema, schemaIndex: Schema.$Index })
7+
const client = create({ schema: Schema.schema, schemaIndex: Schema.$Index })
88

99
test('isError utility function narrows for error objects', async () => {
1010
const result = await client.query.result({ $: { case: 'Object1' }, __typename: true })
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* eslint-disable */
2+
import { ExecutionResult } from 'graphql'
3+
import { ObjMap } from 'graphql/jsutils/ObjMap.js'
4+
import { describe } from 'node:test'
5+
import { expectTypeOf, test } from 'vitest'
6+
import { schema } from '../../tests/_/schema/schema.js'
7+
import { $Index as schemaIndex } from '../../tests/ts/_/schema/generated/SchemaRuntime.js'
8+
import { create } from './client.js'
9+
10+
// dprint-ignore
11+
describe('default', () => {
12+
const client = create({ schema, schemaIndex })
13+
test(`document`, async () => {
14+
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
15+
})
16+
test(`raw`, async () => {
17+
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
18+
})
19+
test('query field method', async () => {
20+
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<'Query'>()
21+
})
22+
test('query $batch', async () => {
23+
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<{ __typename: 'Query', id: string|null }>()
24+
})
25+
})
26+
27+
// dprint-ignore
28+
describe('data', () => {
29+
const client = create({ schema, schemaIndex, returnMode: 'data' })
30+
test(`document`, async () => {
31+
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
32+
})
33+
test(`raw`, async () => {
34+
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
35+
})
36+
test('query field method', async () => {
37+
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<'Query'>()
38+
})
39+
test('query $batch', async () => {
40+
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<{ __typename: 'Query', id: string|null }>()
41+
})
42+
})
43+
44+
// dprint-ignore
45+
describe('graphql', () => {
46+
const client = create({ schema, schemaIndex, returnMode: 'graphql' })
47+
test(`document`, async () => {
48+
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<ExecutionResult<{ id: string | null }, ObjMap<unknown>>>()
49+
})
50+
test(`raw`, async () => {
51+
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
52+
})
53+
test('query field method', async () => {
54+
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<ExecutionResult<{ __typename: 'Query' }>>()
55+
})
56+
test('query $batch', async () => {
57+
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<ExecutionResult<{ __typename: 'Query', id: string|null }>>()
58+
})
59+
})

src/client/client.returnMode.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* eslint-disable */
2+
import { describe, expect, test } from 'vitest'
3+
import { db } from '../../tests/_/db.js'
4+
import { $Index as schemaIndex } from '../../tests/_/schema/generated/SchemaRuntime.js'
5+
import { schema } from '../../tests/_/schema/schema.js'
6+
import { __typename } from '../Schema/_.js'
7+
import { create } from './client.js'
8+
9+
// dprint-ignore
10+
describe('default', () => {
11+
const client = create({ schema, schemaIndex })
12+
test(`document`, async () => {
13+
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ id: db.id })
14+
})
15+
test('raw', async () => {
16+
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
17+
})
18+
test('query field method', async () => {
19+
await expect(client.query.__typename()).resolves.toEqual('Query')
20+
})
21+
test('query $batch', async () => {
22+
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Query', id: db.id })
23+
})
24+
test('mutation field method', async () => {
25+
await expect(client.mutation.__typename()).resolves.toEqual('Mutation')
26+
})
27+
test('mutation $batch', async () => {
28+
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Mutation', id: db.id })
29+
})
30+
})
31+
32+
// dprint-ignore
33+
describe('data', () => {
34+
const client = create({ schema, schemaIndex, returnMode: 'data' })
35+
test(`document`, async () => {
36+
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ id: db.id })
37+
})
38+
test('raw', async () => {
39+
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
40+
})
41+
test('query field method', async () => {
42+
await expect(client.query.__typename()).resolves.toEqual('Query')
43+
})
44+
test('query $batch', async () => {
45+
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Query', id: db.id })
46+
})
47+
test('mutation field method', async () => {
48+
await expect(client.mutation.__typename()).resolves.toEqual('Mutation')
49+
})
50+
test('mutation $batch', async () => {
51+
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Mutation', id: db.id })
52+
})
53+
})
54+
55+
// dprint-ignore
56+
describe('graphql', () => {
57+
const client = create({ schema, schemaIndex, returnMode: 'graphql' })
58+
test(`document`, async () => {
59+
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ data: { id: db.id } }) // dprint-ignore
60+
})
61+
test('raw', async () => {
62+
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
63+
})
64+
test('query field method', async () => {
65+
await expect(client.query.__typename()).resolves.toEqual({ data: { __typename: 'Query' } })
66+
})
67+
test('query $batch', async () => {
68+
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ data: { __typename: 'Query', id: db.id } })
69+
})
70+
test('mutation field method', async () => {
71+
await expect(client.mutation.__typename()).resolves.toEqual({ data: { __typename: 'Mutation' } })
72+
})
73+
test('mutation $batch', async () => {
74+
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ data: { __typename: 'Mutation', id: db.id } })
75+
})
76+
})

src/client/client.rootTypeMethods.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { expectTypeOf, test } from 'vitest'
33
import * as Schema from '../../tests/_/schema/schema.js'
44
import { create } from './client.js'
55

6-
const client = create<Schema.Index>({ schema: Schema.schema, schemaIndex: Schema.$Index })
6+
const client = create({ schema: Schema.schema, schemaIndex: Schema.$Index })
77

88
// dprint-ignore
99
test(`query`, () => {

0 commit comments

Comments
 (0)