Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(document): no key plural for op types #1114

Merged
merged 3 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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