Skip to content

Commit

Permalink
feat(ts-client): use graphql return mode & named schema
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt committed Apr 20, 2024
1 parent 547dfe5 commit b45dc60
Show file tree
Hide file tree
Showing 32 changed files with 549 additions and 224 deletions.
8 changes: 4 additions & 4 deletions src/Schema/Hybrid/types/Scalar/Scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export const Scalars = {
Boolean,
}

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

declare global {
interface SchemaCustomScalars {}
}
type Values<T> = T extends any ? keyof T extends never ? never : T[keyof T] : never
// type x = Values<NamedSchemas[keyof NamedSchemas]['customScalars']>
4 changes: 2 additions & 2 deletions src/client/SelectionSet/toGraphQLDocumentString.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { parse, print } from 'graphql'
import { describe, expect, test } from 'vitest'
import type { Index } from '../../../tests/ts/_/schema/generated/Index.js'
import type { SelectionSet } from './__.js'
import { toGraphQLDocumentString } from './toGraphQLDocumentString.js'
import { toGraphQLDocumentSelectionSet } from './toGraphQLDocumentString.js'

// eslint-disable-next-line
// @ts-ignore
type Q = SelectionSet.Query<Index>
const s = (selectionSet: Q) => selectionSet
const prepareResult = (ss: Q) => {
const graphqlDocumentString = toGraphQLDocumentString(ss as any)
const graphqlDocumentString = toGraphQLDocumentSelectionSet(ss as any)
// Should parse, ensures is syntactically valid graphql document.
const document = parse(graphqlDocumentString)
const graphqlDocumentStringFormatted = print(document)
Expand Down
18 changes: 11 additions & 7 deletions src/client/SelectionSet/toGraphQLDocumentString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@ type Args_ = string | boolean | null | number | Args

type Indicator = 0 | 1 | boolean

export type GraphQLDocumentObject = Record<string, Indicator | SS>
export type DocumentObject = Record<string, GraphQLRootSelection>

type SS = {
export type GraphQLRootSelection = { query: GraphQLObjectSelection } | { mutation: GraphQLObjectSelection }

export type GraphQLObjectSelection = Record<string, Indicator | SS>

export type SS = {
[k: string]: Indicator | SS
} & SpecialFields

export const toGraphQLDocumentString = (ss: GraphQLDocumentObject) => {
return `query ${toGraphQLDocumentSelectionSet(ss)}`
}
// export const toGraphQLDocumentString = (ss: GraphQLDocumentObject) => {
// return `query ${toGraphQLDocumentSelectionSet(ss)}`
// }

export const toGraphQLDocumentSelectionSet = (ss: GraphQLDocumentObject) => {
export const toGraphQLDocumentSelectionSet = (ss: GraphQLObjectSelection) => {
return `{
${selectionSet(ss)}
}`
Expand Down Expand Up @@ -93,7 +97,7 @@ const indicatorOrSelectionSet = (ss: null | Indicator | SS): string => {
}`
}

const selectionSet = (ss: GraphQLDocumentObject) => {
export const selectionSet = (ss: GraphQLObjectSelection) => {
return Object.entries(ss).filter(([_, v]) => {
return isPositiveIndicator(v)
}).map(([field, ss]) => {
Expand Down
9 changes: 5 additions & 4 deletions src/client/client.customScalar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
import { beforeEach, describe, expect, test } from 'vitest'
import { db } from '../../tests/_/db.js'
import { setupMockServer } from '../../tests/raw/__helpers.js'
import type { Index } from '../../tests/ts/_/schema/generated/Index.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 date0Encoded = db.date0.toISOString()
const date1Encoded = db.date1.toISOString()

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

describe(`output`, () => {
test(`query field`, async () => {
Expand Down Expand Up @@ -79,13 +79,14 @@ describe(`input`, () => {
ctx.res({ body: { data: {} } })
})
const clientExpected = (expectedDocument: (document: any) => void) => {
const client = create<Index>({
const client = create({
name: 'MigrateMe',
schema: ctx.url,
schemaIndex,
hooks: {
documentEncode: (input, run) => {
const result = run(input)
expectedDocument(result)
expectedDocument(result['query'])
return result
},
},
Expand Down
8 changes: 5 additions & 3 deletions src/client/client.document.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as SchemaMutationOnly from '../../tests/_/schemaMutationOnly/schema.js'
import * as SchemaQueryOnly from '../../tests/_/schemaQueryOnly/schema.js'
import { create } from './client.js'

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

test(`requires input`, () => {
// @ts-expect-error missing input
Expand All @@ -28,7 +28,8 @@ describe(`input`, () => {
})

test(`root operation not available if it is not in schema`, () => {
const clientQueryOnly = create<SchemaQueryOnly.Index>({
const clientQueryOnly = create({
name: `QueryOnly`,
schema: SchemaQueryOnly.schema,
schemaIndex: SchemaQueryOnly.$Index,
})
Expand All @@ -37,7 +38,8 @@ describe(`input`, () => {
// @ts-expect-error mutation not in schema
bar: { mutation: { id: true } },
})
const clientMutationOnly = create<SchemaMutationOnly.Index>({
const clientMutationOnly = create({
name: `MutationOnly`,
schema: SchemaMutationOnly.schema,
schemaIndex: SchemaMutationOnly.$Index,
})
Expand Down
37 changes: 16 additions & 21 deletions src/client/client.document.test.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,55 @@
import { describe, expect, test } from 'vitest'
import { db } from '../../tests/_/db.js'
import type { Index } from '../../tests/_/schema/generated/Index.js'
import { $Index } from '../../tests/_/schema/generated/SchemaRuntime.js'
import { schema } from '../../tests/_/schema/schema.js'
import { create } from './client.js'

const client = create<Index>({ schema, schemaIndex: $Index })
const client = create({ schema, schemaIndex: $Index })

describe(`document with two queries`, () => {
const withTwo = client.document({
foo: {
query: { id: true },
},
bar: {
query: { idNonNull: true },
},
foo: { query: { id: true } },
bar: { query: { idNonNull: true } },
})

test(`works`, async () => {
const { run } = withTwo
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
await expect(run(`bar`)).resolves.toEqual({ data: { idNonNull: db.id1 } })
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
await expect(run(`bar`)).resolves.toEqual({ idNonNull: db.id1 })
})
test(`error if no operation name is provided`, async () => {
const { run } = withTwo
// @ts-expect-error
await expect(run()).resolves.toMatchObject({
await expect(run()).rejects.toMatchObject({
errors: [{ message: `Must provide operation name if query contains multiple operations.` }],
})
})
test(`error if wrong operation name is provided`, async () => {
const { run } = withTwo
// @ts-expect-error
await expect(run(`boo`)).resolves.toMatchObject({ errors: [{ message: `Unknown operation named "boo".` }] })
await expect(run(`boo`)).rejects.toMatchObject({ errors: [{ message: `Unknown operation named "boo".` }] })
})
test(`error if invalid name in document`, async () => {
// @ts-expect-error
const { run } = client.document({ foo$: { query: { id: true } } })
await expect(run(`foo$`)).resolves.toMatchObject({
await expect(run(`foo$`)).rejects.toMatchObject({
errors: [{ message: `Syntax Error: Expected "{", found "$".` }],
})
})
})

test(`document with one query`, async () => {
const { run } = client.document({ foo: { query: { id: true } } })
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
await expect(run()).resolves.toEqual({ data: { id: db.id1 } })
await expect(run(undefined)).resolves.toEqual({ data: { id: db.id1 } })
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 } = client.document({ foo: { mutation: { id: true } } })
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
await expect(run()).resolves.toEqual({ data: { id: db.id1 } })
await expect(run(undefined)).resolves.toEqual({ data: { id: db.id1 } })
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 and one query`, async () => {
Expand All @@ -66,6 +61,6 @@ test(`document with one mutation and one query`, async () => {
query: { idNonNull: true },
},
})
await expect(run(`foo`)).resolves.toEqual({ data: { id: db.id1 } })
await expect(run(`bar`)).resolves.toEqual({ data: { idNonNull: db.id1 } })
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
await expect(run(`bar`)).resolves.toEqual({ idNonNull: db.id1 })
})
2 changes: 1 addition & 1 deletion src/client/client.error.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isError } from '../../tests/_/schema/generated/Error.js'
import * as Schema from '../../tests/_/schema/schema.js'
import { create } from './client.js'

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

test('isError utility function narrows for error objects', async () => {
const result = await client.query.result({ $: { case: 'Object1' }, __typename: true })
Expand Down
59 changes: 59 additions & 0 deletions src/client/client.returnMode.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable */
import { ExecutionResult } from 'graphql'
import { ObjMap } from 'graphql/jsutils/ObjMap.js'
import { describe } from 'node:test'
import { expectTypeOf, test } from 'vitest'
import { schema } from '../../tests/_/schema/schema.js'
import { $Index as schemaIndex } from '../../tests/ts/_/schema/generated/SchemaRuntime.js'
import { create } from './client.js'

// dprint-ignore
describe('default', () => {
const client = create({ schema, schemaIndex })
test(`document`, async () => {
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
})
test(`raw`, async () => {
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
})
test('query field method', async () => {
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<'Query'>()
})
test('query $batch', async () => {
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<{ __typename: 'Query', id: string|null }>()
})
})

// dprint-ignore
describe('data', () => {
const client = create({ schema, schemaIndex, returnMode: 'data' })
test(`document`, async () => {
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
})
test(`raw`, async () => {
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
})
test('query field method', async () => {
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<'Query'>()
})
test('query $batch', async () => {
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<{ __typename: 'Query', id: string|null }>()
})
})

// dprint-ignore
describe('graphql', () => {
const client = create({ schema, schemaIndex, returnMode: 'graphql' })
test(`document`, async () => {
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<ExecutionResult<{ id: string | null }, ObjMap<unknown>>>()
})
test(`raw`, async () => {
expectTypeOf(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqualTypeOf<ExecutionResult>()
})
test('query field method', async () => {
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<ExecutionResult<{ __typename: 'Query' }>>()
})
test('query $batch', async () => {
await expectTypeOf(client.query.$batch({ __typename: true, id: true })).resolves.toEqualTypeOf<ExecutionResult<{ __typename: 'Query', id: string|null }>>()
})
})
76 changes: 76 additions & 0 deletions src/client/client.returnMode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* eslint-disable */
import { describe, expect, test } from 'vitest'
import { db } from '../../tests/_/db.js'
import { $Index as schemaIndex } from '../../tests/_/schema/generated/SchemaRuntime.js'
import { schema } from '../../tests/_/schema/schema.js'
import { __typename } from '../Schema/_.js'
import { create } from './client.js'

// dprint-ignore
describe('default', () => {
const client = create({ schema, schemaIndex })
test(`document`, async () => {
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ id: db.id })
})
test('raw', async () => {
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
})
test('query field method', async () => {
await expect(client.query.__typename()).resolves.toEqual('Query')
})
test('query $batch', async () => {
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Query', id: db.id })
})
test('mutation field method', async () => {
await expect(client.mutation.__typename()).resolves.toEqual('Mutation')
})
test('mutation $batch', async () => {
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Mutation', id: db.id })
})
})

// dprint-ignore
describe('data', () => {
const client = create({ schema, schemaIndex, returnMode: 'data' })
test(`document`, async () => {
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ id: db.id })
})
test('raw', async () => {
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
})
test('query field method', async () => {
await expect(client.query.__typename()).resolves.toEqual('Query')
})
test('query $batch', async () => {
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Query', id: db.id })
})
test('mutation field method', async () => {
await expect(client.mutation.__typename()).resolves.toEqual('Mutation')
})
test('mutation $batch', async () => {
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ __typename: 'Mutation', id: db.id })
})
})

// dprint-ignore
describe('graphql', () => {
const client = create({ schema, schemaIndex, returnMode: 'graphql' })
test(`document`, async () => {
await expect(client.document({ main: { query: { id: true } } }).run()).resolves.toEqual({ data: { id: db.id } }) // dprint-ignore
})
test('raw', async () => {
await expect(client.raw('query main {\nid\n}', {}, 'main')).resolves.toEqual({ data: { id: db.id } })
})
test('query field method', async () => {
await expect(client.query.__typename()).resolves.toEqual({ data: { __typename: 'Query' } })
})
test('query $batch', async () => {
await expect(client.query.$batch({ __typename: true, id: true })).resolves.toEqual({ data: { __typename: 'Query', id: db.id } })
})
test('mutation field method', async () => {
await expect(client.mutation.__typename()).resolves.toEqual({ data: { __typename: 'Mutation' } })
})
test('mutation $batch', async () => {
await expect(client.mutation.$batch({ __typename: true, id: true })).resolves.toEqual({ data: { __typename: 'Mutation', id: db.id } })
})
})
2 changes: 1 addition & 1 deletion src/client/client.rootTypeMethods.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expectTypeOf, test } from 'vitest'
import * as Schema from '../../tests/_/schema/schema.js'
import { create } from './client.js'

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

// dprint-ignore
test(`query`, () => {
Expand Down
2 changes: 1 addition & 1 deletion src/client/client.rootTypeMethods.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { db } from '../../tests/_/db.js'
import * as Schema from '../../tests/_/schema/schema.js'
import { create } from './client.js'

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

describe(`query`, () => {
test(`scalar`, async () => {
Expand Down
4 changes: 1 addition & 3 deletions src/client/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
/* eslint-disable */
import { 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 })
const client = () => create({ name: 'MigrateMe', schema: ctx.url, schemaIndex })

test.todo(`query`, async () => {
const mockRes = ctx.res({ body: { data } }).spec.body!
Expand Down
Loading

0 comments on commit b45dc60

Please sign in to comment.