Skip to content

Commit

Permalink
feat(document): no key plural for op types (#1114)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Sep 24, 2024
1 parent 714c78c commit 5c62024
Show file tree
Hide file tree
Showing 24 changed files with 196 additions and 86 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm check:types
# While we do not deploy website for PRs we still want to know that it can be built.
# This catches issues before merging such as broken twoslash examples.
website:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm build
- name: Install Website Dependencies
working-directory: website
run: pnpm install
- name: Build Website
working-directory: website
run: pnpm build
test:
runs-on: ubuntu-latest
strategy:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Index } from './SchemaIndex.js'
import type * as SelectionSets from './SelectionSets.js'

interface DocumentInput {
queries?: Record<string, SelectionSets.Query>
query?: Record<string, SelectionSets.Query>
}

export interface Document<$Config extends Utilities.Config> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { Index } from './SchemaIndex.js'
import type * as SelectionSets from './SelectionSets.js'

interface DocumentInput {
queries?: Record<string, SelectionSets.Query>
mutations?: Record<string, SelectionSets.Mutation>
query?: Record<string, SelectionSets.Query>
mutation?: Record<string, SelectionSets.Mutation>
}

export interface Document<$Config extends Utilities.Config> {
Expand Down
2 changes: 1 addition & 1 deletion examples/55_generated/generated_document__document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { showJson } from '../$/helpers.js'
const atlas = Atlas.create()

const countries = await atlas.document({
queries: {
query: {
countriesQuery: {
countries: [`countries2`, {
$: { filter: { name: { in: [`Canada`, `Germany`, `Japan`] } } },
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
},
"homepage": "https://github.com/jasonkuhrt/graffle",
"scripts": {
"gen:graffle": "pnpm gen:test:schema && pnpm gen:examples:clients && pnpm build && cd website && pnpm gen:graffle",
"graffle": "tsx ./src/cli/generate.ts",
"gen:test:schema": "tsx tests/_/schemaGenerate.ts",
"gen:examples": "tsx scripts/generate-examples-derivatives/generate.ts && pnpm format",
Expand Down
4 changes: 2 additions & 2 deletions src/layers/2_generator/code/MethodsDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const { generate: generateMethodsDocument, moduleName: moduleNameMethodsD

code.push(
`interface DocumentInput {`,
hasQuery(config.typeMapByKind) ? `queries?: Record<string, SelectionSets.Query>` : null,
hasMutation(config.typeMapByKind) ? `mutations?: Record<string, SelectionSets.Mutation>` : null,
hasQuery(config.typeMapByKind) ? `query?: Record<string, SelectionSets.Query>` : null,
hasMutation(config.typeMapByKind) ? `mutation?: Record<string, SelectionSets.Mutation>` : null,
`}`,
)
code.push(``)
Expand Down
4 changes: 2 additions & 2 deletions src/layers/3_SelectionSet/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ type Args = { [k: string]: ArgValue }
type ArgValue = string | boolean | null | number | Args

export type DocumentObject = {
queries?: Record<string, GraphQLObjectSelection>
mutations?: Record<string, GraphQLObjectSelection>
query?: Record<string, GraphQLObjectSelection>
mutation?: Record<string, GraphQLObjectSelection>
}

export type GraphQLRootSelection = { query: GraphQLObjectSelection } | { mutation: GraphQLObjectSelection }
Expand Down
8 changes: 4 additions & 4 deletions src/layers/6_client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Anyware } from '../../lib/anyware/__.js'
import type { Errors } from '../../lib/errors/__.js'
import type { Fluent } from '../../lib/fluent/__.js'
import type { FnProperty, ToFnPropertyObject } from '../../lib/fluent/Fluent.js'
import { type RootTypeName } from '../../lib/graphql.js'
import { type RootTypeName, RootTypeNameToOperationName } from '../../lib/graphql.js'
import type { HKT } from '../../lib/hkt/__.js'
import { mergeHeadersInit, mergeRequestInit } from '../../lib/http.js'
import { proxyGet, type SimplifyExceptError } from '../../lib/prelude.js'
Expand Down Expand Up @@ -423,8 +423,8 @@ const createWithState = (

Object.assign(clientDirect, {
document: (documentObject: DocumentObject) => {
const queryOperationNames = Object.keys(documentObject.queries ?? {})
const mutationOperationNames = Object.keys(documentObject.mutations ?? {})
const queryOperationNames = Object.keys(documentObject.query ?? {})
const mutationOperationNames = Object.keys(documentObject.mutation ?? {})
const operationNames = [
...queryOperationNames,
...mutationOperationNames,
Expand Down Expand Up @@ -466,7 +466,7 @@ const createWithState = (
}
const operationName = maybeOperationName ? maybeOperationName : defaultOperationName
const rootTypeName = queryOperationNames.includes(operationName) ? `Query` : `Mutation`
const selection = documentObject[rootTypeName === `Query` ? `queries` : `mutations`]![operationName]!
const selection = documentObject[RootTypeNameToOperationName[rootTypeName]]![operationName]!
return {
rootTypeName,
selection,
Expand Down
16 changes: 8 additions & 8 deletions src/layers/6_client/document.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ test(`requires input`, () => {

describe(`input`, () => {
test(`document with one query`, () => {
const run = graffle.document({ queries: { foo: { id: true } } }).run
const run = graffle.document({ query: { foo: { id: true } } }).run
expectTypeOf(run).toMatchTypeOf<(...params: ['foo'] | [] | [undefined]) => Promise<any>>()
})

test(`document with two queries`, () => {
const run = graffle.document({
queries: {
query: {
foo: { id: true },
bar: { id: true },
},
Expand All @@ -37,10 +37,10 @@ describe(`input`, () => {
schema: SchemaQueryOnly.schema,
})
queryOnly.document({
queries: { foo: { id: true } },
query: { foo: { id: true } },
// todo
// // @ts-expect-error mutation not in schema
// mutations: { foo: { id: true } },
// mutation: { foo: { id: true } },
})
const mutationOnly = MutationOnly.create({
schema: SchemaMutationOnly.schema,
Expand All @@ -56,21 +56,21 @@ describe(`input`, () => {
describe(`document(...).run()`, () => {
test(`document with one query`, () => {
{
const result = graffle.document({ queries: { x: { id: true } } }).run()
const result = graffle.document({ query: { x: { id: true } } }).run()
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
{
const result = graffle.document({ queries: { x: { id: true } } }).run(`x`)
const result = graffle.document({ query: { x: { id: true } } }).run(`x`)
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
{
const result = graffle.document({ queries: { x: { id: true } } }).run(undefined)
const result = graffle.document({ query: { x: { id: true } } }).run(undefined)
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
}
})
test(`document with two queries`, () => {
const result = graffle.document({
queries: {
query: {
foo: { id: true },
bar: { id: true },
},
Expand Down
14 changes: 7 additions & 7 deletions src/layers/6_client/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const graffle = Graffle.create({ schema })

describe(`document with two queries`, () => {
const withTwo = graffle.document({
queries: {
query: {
foo: { id: true },
bar: { idNonNull: true },
},
Expand Down Expand Up @@ -64,38 +64,38 @@ describe(`document with two queries`, () => {
test.skip(`error if invalid name in document`, async () => {
// // todo
// // @ts-expect-error
// const { run } = graffle.document({ queries: { foo$: { id: true } } })
// const { run } = graffle.document({ query: { foo$: { id: true } } })
// await expect(run(`foo$`)).rejects.toMatchObject({
// errors: [{ message: `Syntax Error: Expected "{", found "$".` }],
// })
})
})

test(`document with one query`, async () => {
const { run } = graffle.document({ queries: { foo: { id: true } } })
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({ mutations: { foo: { id: true } } })
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 () => {
const { run } = graffle.document({ queries: { foo: { error: true } } })
const { run } = graffle.document({ query: { foo: { error: true } } })
await expect(run()).rejects.toMatchObject({ errors: [{ message: `Something went wrong.` }] })
})

test(`document with one mutation and one query`, async () => {
const { run } = graffle.document({
mutations: {
mutation: {
foo: { id: true },
},
queries: {
query: {
bar: { idNonNull: true },
},
})
Expand Down
83 changes: 52 additions & 31 deletions src/layers/6_client/document.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import type { UnionToTuple } from 'type-fest'
import {
type OperationType,
operationTypeNameToRootTypeName,
OperationTypes,
type RootTypeNameMutation,
type RootTypeNameQuery,
} from '../../lib/graphql.js'
import type { IsMultipleKeys, Values } from '../../lib/prelude.js'
import type { FirstNonUnknownNever, IsKeyInObjectOptional, IsTupleMultiple, Values } from '../../lib/prelude.js'
import type { Schema } from '../1_Schema/__.js'
import { SelectionSet } from '../3_SelectionSet/__.js'
import type { Context, DocumentObject } from '../3_SelectionSet/encode.js'
import type { ResultSet } from '../4_ResultSet/__.js'
import type { ResolveOutputReturnRootType } from './handleOutput.js'
import type { AddTypenameToSelectedRootTypeResultFields, Config } from './Settings/Config.js'

type OperationName = string

interface SomeDocumentOperation {
[k: string]: object
}

interface SomeDocument {
mutations?: Record<string, object>
queries?: Record<string, object>
mutation?: SomeDocumentOperation
query?: SomeDocumentOperation
}

// dprint-ignore
type HasMultipleOperations<$Document extends SomeDocument> =
IsMultipleKeys<
& ('mutations' extends keyof $Document ? $Document['mutations'] : {})
& ('queries' extends keyof $Document ? $Document['queries'] : {})
>
// --- Utility Types To Work With Documents ---

// // dprint-ignore
// type IsHasMultipleOperations<$Document extends SomeDocument> =
// All<[
// IsHasMultipleKeys<$Document[OperationType.Query]>,
// IsHasMultipleKeys<$Document[OperationType.Mutation]>,
// ]>

// dprint-ignore
type GetOperationNames<$Document extends SomeDocument> = Values<
Expand All @@ -31,36 +42,46 @@ type GetOperationNames<$Document extends SomeDocument> = Values<
>

// dprint-ignore
type GetRootTypeName<$Document extends SomeDocument, $Name extends string> =
$Name extends keyof $Document['mutations'] ? RootTypeNameMutation :
$Name extends keyof $Document['queries'] ? RootTypeNameQuery :
never
type GetRootTypeNameOfOperation<$Document extends SomeDocument, $Name extends OperationName> =
IsKeyInObjectOptional<$Document[OperationType.Mutation], $Name> extends true ? RootTypeNameMutation :
IsKeyInObjectOptional<$Document[OperationType.Query], $Name> extends true ? RootTypeNameQuery :
never

// dprint-ignore
type GetOperation<$Document extends SomeDocument, $Name extends string> =
$Name extends keyof $Document['mutations'] ? $Document['mutations'][$Name] :
$Name extends keyof $Document['queries'] ? $Document['queries'][$Name] :
never
FirstNonUnknownNever<[
// @ts-expect-error could be unknown
$Document[OperationType.Mutation][$Name],
// @ts-expect-error could be unknown
$Document[OperationType.Query][$Name]
]>

// -- Interface --
//
// dprint-ignore
export type DocumentRunner<$Config extends Config, $Index extends Schema.Index, $Document extends SomeDocument> = {
export type DocumentRunner<
$$Config extends Config,
$$Index extends Schema.Index,
$$Document extends SomeDocument,
$$Name extends GetOperationNames<$$Document> = GetOperationNames<$$Document>
> = {
run: <
$Name extends GetOperationNames<$Document>,
$Params extends (HasMultipleOperations<$Document> extends true ? [name: $Name] : ([] | [name: $Name | undefined])),
$Name extends $$Name,
$Params extends (IsTupleMultiple<UnionToTuple<$$Name>> extends true ? [name: $Name] : ([] | [name: $Name | undefined])),
>(...params: $Params) =>
Promise<
ResolveOutputReturnRootType<
$Config,
$Index,
$$Config,
$$Index,
ResultSet.Root<
AddTypenameToSelectedRootTypeResultFields<
$Config,
$Index,
GetRootTypeName<$Document, $Name>,
GetOperation<$Document, $Name>
$$Config,
$$Index,
GetRootTypeNameOfOperation<$$Document, $Name>,
GetOperation<$$Document, $Name>
>,
$Index,
GetRootTypeName<$Document, $Name>
$$Index,
GetRootTypeNameOfOperation<$$Document, $Name>
>
>
>
Expand All @@ -71,15 +92,15 @@ export const toDocumentString = (
document: DocumentObject,
) => {
const operations = [
...(Object.entries(document.queries || {}).map(([operationName, selectionSet]) => ({
...(Object.entries(document.query || {}).map(([operationName, selectionSet]) => ({
operationName,
selectionSet,
operationType: `query` as const,
operationType: OperationTypes.query,
}))),
...(Object.entries(document.mutations || {}).map(([operationName, selectionSet]) => ({
...(Object.entries(document.mutation || {}).map(([operationName, selectionSet]) => ({
operationName,
selectionSet,
operationType: `mutation` as const,
operationType: OperationTypes.mutation,
}))),
]

Expand Down
6 changes: 3 additions & 3 deletions src/layers/7_extensions/Throws/Throws.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ const graffle = Graffle.create({ schema }).use(Throws())
describe(`document`, () => {
describe(`query result field`, () => {
test(`with __typename`, async () => {
const result = await graffle.throws().document({ queries: { x: { resultNonNull: { __typename: true } } } }).run()
const result = await graffle.throws().document({ query: { x: { resultNonNull: { __typename: true } } } }).run()
expectTypeOf(result).toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>()
})
test(`without __typename`, async () => {
const result = await graffle.throws().document({ queries: { x: { resultNonNull: {} } } }).run()
const result = await graffle.throws().document({ query: { x: { resultNonNull: {} } } }).run()
expectTypeOf(result).toEqualTypeOf<{ resultNonNull: { __typename: 'Object1' } }>()
})
test(`multiple via alias`, async () => {
const result = await graffle.throws().document({
queries: { x: { resultNonNull: [[`resultNonNull`, {}], [`x`, {}]] } },
query: { x: { resultNonNull: [[`resultNonNull`, {}], [`x`, {}]] } },
})
.run()
expectTypeOf(result).toEqualTypeOf<
Expand Down
6 changes: 3 additions & 3 deletions src/layers/7_extensions/Throws/Throws.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ describe(`document`, () => {
describe(`query result field`, () => {
test(`with __typename`, async () => {
const result = graffle.throws().document({
queries: { x: { resultNonNull: { $: { case: `ErrorOne` }, __typename: true } } },
query: { x: { resultNonNull: { $: { case: `ErrorOne` }, __typename: true } } },
})
.run()
await expect(result).rejects.toMatchInlineSnapshot(`[Error: Failure on field resultNonNull: ErrorOne]`)
})
test(`without __typename`, async () => {
const result = graffle.throws().document({ queries: { x: { resultNonNull: { $: { case: `ErrorOne` } } } } }).run()
const result = graffle.throws().document({ query: { x: { resultNonNull: { $: { case: `ErrorOne` } } } } }).run()
await expect(result).rejects.toMatchInlineSnapshot(
`[Error: Failure on field resultNonNull: ErrorOne]`,
)
})
test.todo(`multiple via alias`, async () => {
const result = graffle.throws().document({
queries: {
query: {
x: {
resultNonNull: [
[`resultNonNull`, { $: { case: `ErrorOne` } }],
Expand Down
Loading

0 comments on commit 5c62024

Please sign in to comment.