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: scalar method #1206

Merged
merged 6 commits into from
Oct 21, 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"serve:pokemon": "tsx tests/_/services/pokemonManual.ts",
"gen:graffle": "pnpm gen:graffle:tests && pnpm build && cd website && pnpm gen:graffle",
"gen:graffle:tests": "tsx tests/_/schemas/generate.ts && pnpm graffle --project src/extensions/SchemaErrors/tests/fixture",
"graffle": "tsx ./src/cli/generate.ts",
"graffle": "tsx ./src/generator/cli/generate.ts",
"gen:examples": "tsx scripts/generate-examples-derivatives/generate.ts && pnpm format",
"dev": "rm -rf dist && tsc --watch",
"format": "pnpm build:docs && dprint fmt",
Expand Down
2 changes: 1 addition & 1 deletion src/entrypoints/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { create as createSelect, select } from '../layers/5_select/select.js'
export { type Client, create } from '../layers/6_client/client.js'
export { createPrefilled, type InputPrefilled } from '../layers/6_client/prefilled.js'
export { createPrefilled } from '../layers/6_client/clientPrefilled.js'
export { type InputStatic } from '../layers/6_client/Settings/Input.js'
2 changes: 2 additions & 0 deletions src/entrypoints/utilities-for-generated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export { type Simplify } from 'type-fest'
export { type SchemaDrivenDataMap } from '../extensions/CustomScalars/schemaDrivenDataMap/__.js'
export { type Schema as SchemaIndexBase } from '../generator/generators/Schema.js'
export { SchemaKit } from '../layers/1_Schema/__.js'
export * from '../layers/2_Select/__.js'
export { type ClientContext } from '../layers/6_client/fluent.js'
export type {
ConfigGetOutputError,
HandleOutput,
Expand Down
4 changes: 3 additions & 1 deletion src/extensions/CustomScalars/CustomScalars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ export const CustomScalars = () =>
// documentNode: true,
onRequest: (async ({ pack }) => {
const sddm = pack.input.state.config.schemaMap
const scalars = pack.input.state.scalars
if (!sddm) return pack()

const request = normalizeRequestToNode(pack.input.request)

// We will mutate query. Assign it back to input for it to be carried forward.
pack.input.request.query = request.query

encodeRequestVariables({ sddm, request })
encodeRequestVariables({ sddm, scalars, request })

const { exchange } = await pack()
const { unpack } = await exchange()
Expand All @@ -33,6 +34,7 @@ export const CustomScalars = () =>
sddm,
request,
data: decode.input.result.data,
scalars,
})
}

Expand Down
47 changes: 23 additions & 24 deletions src/extensions/CustomScalars/decode.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { describe, expect } from 'vitest'
import { Date } from '../../../tests/_/fixtures/scalars.js'
import { createResponse, test } from '../../../tests/_/helpers.js'
import { db } from '../../../tests/_/schemas/db.js'
import type { Graffle } from '../../../tests/_/schemas/kitchen-sink/graffle/__.js'
import { Select } from '../../layers/2_Select/__.js'
import { SelectionSetGraphqlMapper } from '../../layers/3_SelectGraphQLMapper/__.js'
import { Grafaid } from '../../lib/grafaid/__.js'

const date0Encoded = db.date0.toISOString()

type TestCase = [
describe: string,
query: Graffle.SelectionSets.Query,
query: Graffle.SelectionSets.Query<{ Date: typeof Date }>,
responseData: object,
expectedData: object,
]
Expand All @@ -22,7 +21,7 @@ const withBatch: TestCaseWith = [
{},
async ([_, query, responseData, expectedData], { fetch, kitchenSinkHttp: kitchenSink }) => {
fetch.mockResolvedValueOnce(createResponse({ data: responseData }))
expect(await kitchenSink.query.$batch(query)).toEqual(expectedData)
expect(await kitchenSink.scalar(Date).query.$batch(query)).toEqual(expectedData)
},
]

Expand All @@ -34,7 +33,7 @@ const withGqlDocument: TestCaseWith = [
const { document } = SelectionSetGraphqlMapper.toGraphQL(
Select.Document.createDocumentNormalizedFromQuerySelection(query as any),
)
expect(await kitchenSink.gql(document).send()).toEqual(expectedData)
expect(await kitchenSink.scalar(Date).gql(document).send()).toEqual(expectedData)
},
]

Expand All @@ -46,47 +45,47 @@ const withGqlString: TestCaseWith = [
const { document } = SelectionSetGraphqlMapper.toGraphQL(
Select.Document.normalizeOrThrow({ query: { foo: query as any } }),
)
expect(await kitchenSink.gql(Grafaid.Document.print(document)).send()).toEqual(expectedData)
expect(await kitchenSink.scalar(Date).gql(Grafaid.Document.print(document)).send()).toEqual(expectedData)
},
]

// dprint-ignore
const testGeneralCases = test.for<TestCase>([
[`nullable null`, { date: true }, { date: null }, { date: null }],
[`nullable value`, { date: true }, { date: date0Encoded }, { date: db.date0 }],
[`non-null`, { dateNonNull: true }, { dateNonNull: date0Encoded }, { dateNonNull: db.date0 }],
[`list`, { dateList: true }, { dateList: [0, 1] }, { dateList: [db.date0, new Date(1)] }],
[`list list`, { dateListList: true }, { dateListList: [[0, 1],[0,1]] }, { dateListList: [[db.date0, new Date(1)],[db.date0, new Date(1)]] }],
[`list non-null`, { dateListNonNull: true }, { dateListNonNull: [0, 1] }, { dateListNonNull: [db.date0, new Date(1)] }],
[`object field`, { dateObject1: { date1: true } }, { dateObject1: { date1: date0Encoded } }, { dateObject1: { date1: db.date0 } }],
[`interface field`, { dateInterface1: { date1: true } }, { dateInterface1: { date1: date0Encoded } }, { dateInterface1: { date1: db.date0 } }],
[`interface inline fragment`, { dateInterface1: { ___on_DateObject1: { date1: true } } }, { dateInterface1: { date1: date0Encoded } }, { dateInterface1: { date1: db.date0 } }],
[`nullable null`, { date: true }, { date: null }, { date: null }],
[`nullable value`, { date: true }, { date: db.date0Encoded }, { date: db.date0 }],
[`non-null`, { dateNonNull: true }, { dateNonNull: db.date0Encoded }, { dateNonNull: db.date0 }],
[`list`, { dateList: true }, { dateList: [0, 1] }, { dateList: [db.date0, db.date1] }],
[`list list`, { dateListList: true }, { dateListList: [[0, 1],[0,1]] }, { dateListList: [[db.date0, db.date1],[db.date0, db.date1]] }],
[`list non-null`, { dateListNonNull: true }, { dateListNonNull: [0, 1] }, { dateListNonNull: [db.date0, db.date1] }],
[`object field`, { dateObject1: { date1: true } }, { dateObject1: { date1: db.date0Encoded } }, { dateObject1: { date1: db.date0 } }],
[`interface field`, { dateInterface1: { date1: true } }, { dateInterface1: { date1: db.date0Encoded } }, { dateInterface1: { date1: db.date0 } }],
[`interface inline fragment`, { dateInterface1: { ___on_DateObject1: { date1: true } } }, { dateInterface1: { date1: db.date0Encoded } }, { dateInterface1: { date1: db.date0 } }],
])

// dprint-ignore
const testAliasCases = test.for<TestCase>([
[`alias`, { date: [`x`, true] }, { x: date0Encoded }, { x: db.date0 }],
[`interface inline fragment alias`, { dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } }, { dateInterface1: { x: date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`interface inline fragment nested alias`, { dateInterface1: { ___on_DateObject1: { ___: { date1: [`x`, true] } } } }, { dateInterface1: { x: date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`inline fragment interface alias`, { ___: { dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } } }, { dateInterface1: { x: date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`inline fragment x2 interface alias & nullable value`, { ___: [{ dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } }, {date: [`y`,true]}] }, { dateInterface1: { x: date0Encoded }, y: date0Encoded }, { dateInterface1: { x: db.date0 }, y: db.date0 }],
[`alias`, { date: [`x`, true] }, { x: db.date0Encoded }, { x: db.date0 }],
[`interface inline fragment alias`, { dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } }, { dateInterface1: { x: db.date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`interface inline fragment nested alias`, { dateInterface1: { ___on_DateObject1: { ___: { date1: [`x`, true] } } } }, { dateInterface1: { x: db.date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`inline fragment interface alias`, { ___: { dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } } }, { dateInterface1: { x: db.date0Encoded }}, { dateInterface1: { x: db.date0 }}],
[`inline fragment x2 interface alias & nullable value`, { ___: [{ dateInterface1: { ___on_DateObject1: { date1: [`x`, true] } } }, {date: [`y`,true]}] }, { dateInterface1: { x: db.date0Encoded }, y: db.date0Encoded }, { dateInterface1: { x: db.date0 }, y: db.date0 }],
])

// dprint-ignore
const testUnionCases = test.for<TestCase>([
[`case 1with __typename`,
{ dateUnion: { __typename: true, ___on_DateObject1: { date1: true } } },
{ dateUnion: { __typename: `DateObject1`, date1: date0Encoded } },
{ dateUnion: { __typename: `DateObject1`, date1: db.date0Encoded } },
{ dateUnion: { __typename: `DateObject1`, date1: db.date0 }}
],
[`case 1 without __typename`,
{ dateUnion: { ___on_DateObject1: { date1: true } } },
{ dateUnion: { date1: date0Encoded } },
{ dateUnion: { date1: db.date0Encoded } },
{ dateUnion: { date1: db.date0 } }
],
[`case 2`,
{ dateUnion: { ___on_DateObject1: { date1: true }, ___on_DateObject2: { date2: true } } },
{ dateUnion: { date2: date0Encoded } },
{ dateUnion: { date2: db.date0Encoded } },
{ dateUnion: { date2: db.date0 } }
],
[`case 2 miss`,
Expand All @@ -103,7 +102,7 @@ describe(`$batch`, () => {
describe(`object field in union`, () => {
testUnionCases(`%s`, async ([_, query, responseData, expectedData], { fetch, kitchenSinkHttp: kitchenSink }) => {
fetch.mockResolvedValueOnce(createResponse({ data: responseData }))
expect(await kitchenSink.query.$batch(query)).toEqual(expectedData)
expect(await kitchenSink.scalar(Date).query.$batch(query)).toEqual(expectedData)
})
})
})
Expand Down
17 changes: 14 additions & 3 deletions src/extensions/CustomScalars/decode.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Kind } from 'graphql'
import { SchemaKit } from '../../entrypoints/schema.js'
import { applyCodec } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'
import type { RegisteredScalars } from '../../layers/6_client/fluent.js'
import type { Grafaid } from '../../lib/grafaid/__.js'
import { SchemaDrivenDataMap } from './schemaDrivenDataMap/__.js'

/**
* If a document is given then aliases will be decoded as well.
*/
export const decodeResultData = ({ request, data, sddm }: {
export const decodeResultData = ({ request, data, sddm, scalars }: {
/**
* Result data to decode.
*/
Expand All @@ -19,6 +21,10 @@ export const decodeResultData = ({ request, data, sddm }: {
* Request is used to traverse aliases if any were used.
*/
request: Grafaid.RequestAnalyzedDocumentNodeInput
/**
* Registered custom scalars.
*/
scalars: RegisteredScalars
}) => {
const sddmOutputObject = sddm.roots[request.rootType]
if (!sddmOutputObject) return
Expand All @@ -27,15 +33,17 @@ export const decodeResultData = ({ request, data, sddm }: {
data,
sddmOutputObject,
documentPart: request.operation.selectionSet,
scalars,
})
}

const decodeResultData_ = (input: {
data: Grafaid.SomeObjectData | null | undefined
sddmOutputObject: SchemaDrivenDataMap.OutputObject
documentPart: null | Grafaid.Document.SelectionSetNode
scalars: RegisteredScalars
}): void => {
const { data, sddmOutputObject, documentPart } = input
const { data, sddmOutputObject, documentPart, scalars } = input
if (!data) return

for (const [k, v] of Object.entries(data)) {
Expand All @@ -51,14 +59,17 @@ const decodeResultData_ = (input: {

const sddmNode = sddmOutputField.nt

// console.log(sddmNode)
if (SchemaDrivenDataMap.isScalar(sddmNode)) {
data[k] = applyCodec(sddmNode.codec.decode, v)
} else if (SchemaDrivenDataMap.isCustomScalarName(sddmNode)) {
const scalar = SchemaKit.Scalar.lookupCustomScalarOrFallbackToString(scalars, sddmNode)
data[k] = applyCodec(scalar.codec.decode, v)
} else if (SchemaDrivenDataMap.isOutputObject(sddmNode)) {
decodeResultData_({
data: v,
sddmOutputObject: sddmNode,
documentPart: documentField?.selectionSet ?? null,
scalars,
})
} else {
// enums not decoded.
Expand Down
25 changes: 12 additions & 13 deletions src/extensions/CustomScalars/encode.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'vitest'
import { Date } from '../../../tests/_/fixtures/scalars.js'
import { test } from '../../../tests/_/helpers.js'
import { db } from '../../../tests/_/schemas/db.js'
import type { Graffle } from '../../../tests/_/schemas/kitchen-sink/graffle/__.js'
Expand All @@ -8,31 +9,29 @@ import { Select } from '../../layers/2_Select/__.js'
import { SelectionSetGraphqlMapper } from '../../layers/3_SelectGraphQLMapper/__.js'
import { Grafaid } from '../../lib/grafaid/__.js'

const date0Encoded = db.date0.toISOString()
const date1Encoded = db.date1.toISOString()

type TestCase = [
description: string,
query: Graffle.SelectionSets.Query,
query: Graffle.SelectionSets.Query<{ Date: typeof Date }>,
expectedVariables: object,
]

// todo test variable to a directive.
// dprint-ignore
const testCases = test.for<TestCase>([
[`arg enum` , { stringWithArgEnum: { $: { $ABCEnum: `A` } } } , { ABCEnum: `A` }],
[`arg field` , { dateArg: { $: { date: db.date0 } } } , { date: date0Encoded }],
[`arg field in non-null` , { dateArgNonNull: { $: { date: db.date0 } } } , { date: date0Encoded }],
[`arg field in list` , { dateArgList: { $: { date: [db.date0, db.date1] } } } , { date: [date0Encoded, date1Encoded] }],
[`arg field` , { dateArg: { $: { date: db.date0 } } } , { date: db.date0Encoded }],
[`arg field in non-null` , { dateArgNonNull: { $: { date: db.date0 } } } , { date: db.date0Encoded }],
[`arg field in list` , { dateArgList: { $: { date: [db.date0, db.date1] } } } , { date: [db.date0Encoded, db.date1Encoded] }],
[`arg field in list (null)` , { dateArgList: { $: { date: null } } } , { date: null } ],
[`arg field in non-null list (with list)` , { dateArgNonNullList: { $: { date: [db.date0, db.date1] } } } , { date: [date0Encoded, date1Encoded] }],
[`arg field in non-null list (with null)` , { dateArgNonNullList: { $: { date: [null, db.date0] } } } , { date: [null, date0Encoded] }],
[`arg field in non-null list non-null` , { dateArgNonNullListNonNull: { $: { date: [db.date0, db.date1] } } } , { date: [date0Encoded, date1Encoded] }],
[`input object field` , { dateArgInputObject: { $: { input: { idRequired: ``, dateRequired: db.date0, date: db.date1 } } } } , { input: { idRequired: ``, dateRequired: date0Encoded, date: date1Encoded } }],
[`nested input object field` , { InputObjectNested: { $: { input: { InputObject: { idRequired: ``, dateRequired: db.date0, date: db.date1 }}}}} , { input: { InputObject: { idRequired: ``, dateRequired: date0Encoded, date: date1Encoded } } }],
[`arg field in non-null list (with list)` , { dateArgNonNullList: { $: { date: [db.date0, db.date1] } } } , { date: [db.date0Encoded, db.date1Encoded] }],
[`arg field in non-null list (with null)` , { dateArgNonNullList: { $: { date: [null, db.date0] } } } , { date: [null, db.date0Encoded] }],
[`arg field in non-null list non-null` , { dateArgNonNullListNonNull: { $: { date: [db.date0, db.date1] } } } , { date: [db.date0Encoded, db.date1Encoded] }],
[`input object field` , { dateArgInputObject: { $: { input: { idRequired: ``, dateRequired: db.date0, date: db.date1 } } } } , { input: { idRequired: ``, dateRequired: db.date0Encoded, date: db.date1Encoded } }],
[`nested input object field` , { InputObjectNested: { $: { input: { InputObject: { idRequired: ``, dateRequired: db.date0, date: db.date1 }}}}} , { input: { InputObject: { idRequired: ``, dateRequired: db.date0Encoded, date: db.date1Encoded } } }],
])

testCases(`%s`, async ([_, query, expectedVariables], { kitchenSink }) => {
const g = kitchenSink.use(Spy()).scalar(Date)
const { document, operationsVariables } = SelectionSetGraphqlMapper.toGraphQL(
Select.Document.createDocumentNormalizedFromQuerySelection(query as any),
{
Expand All @@ -41,6 +40,6 @@ testCases(`%s`, async ([_, query, expectedVariables], { kitchenSink }) => {
},
)
const documentString = Grafaid.Document.print(document)
await kitchenSink.use(Spy()).gql(documentString).send(operationsVariables[`$default`])
await g.gql(documentString).send(operationsVariables[`$default`])
expect(Spy.data.pack.input?.request.variables).toEqual(expectedVariables)
})
Loading