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(input): allow request configuration #1059

Merged
merged 9 commits into from
Sep 3, 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
3 changes: 2 additions & 1 deletion examples/$generated-clients/SocialStudies/Global.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Index } from './Index.js'

declare global {
export namespace GraphQLRequestTypes {
export namespace GraffleGlobalTypes {
export interface Schemas {
SocialStudies: {
name: 'SocialStudies'
index: Index
customScalars: {}
featureOptions: {
Expand Down
11 changes: 11 additions & 0 deletions examples/transport-http_RequestInput.output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
url: 'https://countries.trevorblades.com/graphql',
body: '{"query":"{ languages { code } }"}',
method: 'POST',
headers: Headers {
authorization: 'Bearer MY_TOKEN',
accept: 'application/graphql-response+json',
'content-type': 'application/json'
},
mode: 'cors'
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import { publicGraphQLSchemaEndpoints } from './$helpers.js'
const graffle = Graffle
.create({
schema: publicGraphQLSchemaEndpoints.SocialStudies,
headers: { authorization: `Bearer MY_TOKEN` },
request: {
headers: {
authorization: `Bearer MY_TOKEN`,
},
mode: `cors`,
},
})
.use(async ({ exchange }) => {
show(exchange.input.request.headers)
show(exchange.input.request)
return exchange()
})

Expand Down
5 changes: 0 additions & 5 deletions examples/transport-http_headers.output.txt

This file was deleted.

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 { type Input } from '../layers/6_client/Settings/Input.js'
export { type InputStatic } from '../layers/6_client/Settings/Input.js'
4 changes: 4 additions & 0 deletions src/layers/1_Schema/core/Index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { GlobalRegistry } from '../../2_generator/globalRegistry.js'
import type { Output } from '../Output/__.js'

/**
* A generic schema index type. Any particular schema index will be a subtype of this, with
* additional specificity such as on objects where here `Record` is used.
*/
export interface Index {
name: GlobalRegistry.SchemaNames
Root: {
Expand Down
3 changes: 2 additions & 1 deletion src/layers/2_generator/code/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ export const { moduleName: moduleNameGlobal, generate: generateGlobal } = create

code.push(`
declare global {
export namespace GraphQLRequestTypes {
export namespace GraffleGlobalTypes {
export interface Schemas {
${config.name}: {
name: '${config.name}'
index: Index
customScalars: {
${
Expand Down
38 changes: 25 additions & 13 deletions src/layers/2_generator/globalRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import type { TSError } from '../../lib/TSError.js'
import type { Schema } from '../1_Schema/__.js'

declare global {
export namespace GraphQLRequestTypes {
export namespace GraffleGlobalTypes {
interface Schemas {}
// Use this is for manual internal type testing.
// interface SchemasAlwaysEmpty {}
}
}

type SomeSchema = {
name: string
index: Schema.Index
customScalars: Record<string, Schema.Scalar.Scalar>
featureOptions: {
Expand All @@ -23,6 +24,7 @@ type SomeSchema = {
}

type ZeroSchema = {
name: GlobalRegistry.DefaultSchemaName
index: { name: never }
featureOptions: {
schemaErrors: false
Expand All @@ -33,35 +35,45 @@ type ZeroSchema = {
export type GlobalRegistry = Record<string, SomeSchema>

export namespace GlobalRegistry {
export type Schemas = GraphQLRequestTypes.Schemas
export type DefaultSchemaName = 'default'

export type Schemas = GraffleGlobalTypes.Schemas

export type IsEmpty = keyof Schemas extends never ? true : false

export type SchemaList = IsEmpty extends true ? ZeroSchema : Values<Schemas>
export type SchemaUnion = IsEmpty extends true ? ZeroSchema : Values<Schemas>

export type DefaultSchemaName = 'default'

export type SchemaNames = keyof GraphQLRequestTypes.Schemas extends never
export type SchemaNames = keyof GraffleGlobalTypes.Schemas extends never
? TSError<'SchemaNames', 'No schemas have been registered. Did you run graffle generate?'>
: keyof GraphQLRequestTypes.Schemas
: keyof GraffleGlobalTypes.Schemas

// dprint-ignore
export type HasDefaultUrlForSchema<$Schema extends SchemaUnion> =
$Schema['defaultSchemaUrl'] extends null
? false
: true

export type HasSchemaErrors<$Schema extends SchemaList> = $Schema['featureOptions']['schemaErrors']
// dprint-ignore
export type HasSchemaErrors<$Schema extends SchemaUnion> =
$Schema['featureOptions']['schemaErrors']

export type HasSchemaErrorsViaName<$Name extends SchemaNames> =
// todo use conditional types?
// eslint-disable-next-line
// @ts-ignore passes after generation
GraphQLRequestTypes.Schemas[$Name]['featureOptions']['schemaErrors']
GraffleGlobalTypes.Schemas[$Name]['featureOptions']['schemaErrors']

// eslint-disable-next-line
// @ts-ignore passes after generation
export type GetSchemaIndex<$Name extends SchemaNames> = GraphQLRequestTypes.Schemas[$Name]['index']
export type GetSchemaIndex<$Name extends SchemaNames> = GraffleGlobalTypes.Schemas[$Name]['index']

// eslint-disable-next-line
// @ts-ignore passes after generation
export type SchemaIndexDefault = GetSchemaIndex<DefaultSchemaName>

export type GetSchemaIndexOrDefault<$Name extends SchemaNames | undefined> = $Name extends SchemaNames
? GetSchemaIndex<$Name>
: SchemaIndexDefault
// dprint-ignore
export type GetSchemaIndexOrDefault<$Name extends SchemaNames | undefined> =
$Name extends SchemaNames
? GetSchemaIndex<$Name>
: SchemaIndexDefault
}
12 changes: 11 additions & 1 deletion src/layers/3_SelectionSet/encode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ const testEachArgs = [
]
) => {
const [description, ss] = args.length === 1 ? [undefined, args[0]] : args
const context: Context = { schemaIndex, config: { output: outputConfigDefault, transport: `memory` } }
const context: Context = {
schemaIndex,
config: {
output: outputConfigDefault,
transport: `memory`,
name: schemaIndex[`name`],
// eslint-disable-next-line
initialInput: {} as any,
requestInputOptions: {},
},
}
const graphqlDocumentString = rootTypeSelectionSet(context, schemaIndex[`Root`][`Query`], ss as any)
// Should parse, ensures is syntactically valid graphql document.
const document = parse(graphqlDocumentString)
Expand Down
71 changes: 21 additions & 50 deletions src/layers/5_core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { GraphQLObjectSelection } from '../3_SelectionSet/encode.js'
import * as Result from '../4_ResultSet/customScalars.js'
import type { GraffleExecutionResultVar } from '../6_client/client.js'
import type { Config } from '../6_client/Settings/Config.js'
import { mergeRequestInputOptions, type RequestInput } from '../6_client/Settings/inputIncrementable/request.js'
import type {
ContextInterfaceRaw,
ContextInterfaceTyped,
Expand Down Expand Up @@ -48,9 +49,7 @@ type TransportInput<$Config extends Config, $HttpProperties = {}, $MemoryPropert
TransportHttp extends $Config['transport']
? ({
transport: TransportHttp
transportConstructorConfig: {
headers?: HeadersInit
}

} & $HttpProperties)
: never
)
Expand Down Expand Up @@ -94,29 +93,6 @@ export type HookDefPack<$Config extends Config> = {
}>
}

export type RequestInput = {
url: string | URL
method:
| 'get'
| 'post'
| 'put'
| 'delete'
| 'patch'
| 'head'
| 'options'
| 'trace'
| 'GET'
| 'POST'
| 'PUT'
| 'DELETE'
| 'PATCH'
| 'HEAD'
| 'OPTIONS'
| 'TRACE'
headers?: HeadersInit
body: BodyInit
}

export type HookDefExchange<$Config extends Config> = {
slots: {
fetch: typeof fetch
Expand Down Expand Up @@ -231,26 +207,26 @@ export const anyware = Anyware.create<HookSequence, HookMap, ExecutionResult>({
}
case `http`: {
// TODO thrown error here is swallowed in examples.
const headers = mergeHeadersInit(
input.transportConstructorConfig.headers ?? {},
input.headers ?? {},
)
// @see https://graphql.github.io/graphql-over-http/draft/#sec-Accept
headers.set(`accept`, CONTENT_TYPE_GQL)
// @see https://graphql.github.io/graphql-over-http/draft/#sec-POST
// todo if body is something else, say upload extension turns it into a FormData, then fetch will automatically set the content-type header.
// ... however we should not rely on that behavior, and instead error here if there is no content type header and we cannot infer it here?
if (typeof input.body === `string`) {
headers.set(`content-type`, CONTENT_TYPE_JSON)
const request: RequestInput = {
url: input.url,
body: input.body,
// @see https://graphql.github.io/graphql-over-http/draft/#sec-POST
method: `POST`,
...mergeRequestInputOptions(input.context.config.requestInputOptions, {
headers: mergeHeadersInit(input.headers, {
// @see https://graphql.github.io/graphql-over-http/draft/#sec-Accept
accept: CONTENT_TYPE_GQL,
// todo if body is something else, say upload extension turns it into a FormData, then fetch will automatically set the content-type header.
// ... however we should not rely on that behavior, and instead error here if there is no content type header and we cannot infer it here?
...(typeof input.body === `string`
? { 'content-type': CONTENT_TYPE_JSON }
: {}),
}),
}),
}
return {
...input,
request: {
url: input.url,
body: input.body,
method: `POST`,
headers,
},
request,
}
}
default:
Expand All @@ -266,13 +242,8 @@ export const anyware = Anyware.create<HookSequence, HookMap, ExecutionResult>({
run: async ({ input, slots }) => {
switch (input.transport) {
case `http`: {
const response = await slots.fetch(
new Request(input.request.url, {
method: input.request.method,
headers: input.request.headers,
body: input.request.body,
}),
)
const request = new Request(input.request.url, input.request)
const response = await slots.fetch(request)
return {
...input,
response,
Expand Down
8 changes: 8 additions & 0 deletions src/layers/6_client/Settings/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { Schema } from '../../1_Schema/__.js'
import type { GlobalRegistry } from '../../2_generator/globalRegistry.js'
import type { SelectionSet } from '../../3_SelectionSet/__.js'
import type { Transport } from '../../5_core/types.js'
import type { InputStatic } from './Input.js'
import type { RequestInputOptions } from './inputIncrementable/request.js'

export type OutputChannel = 'throw' | 'return'

Expand Down Expand Up @@ -102,8 +104,14 @@ export type OutputConfigDefault = {
}

export type Config = {
/**
* The initial input that was given to derive this config.
*/
initialInput: InputStatic<any> // InputStatic<GlobalRegistry.SchemaUnion>
name: GlobalRegistry.SchemaNames
output: OutputConfig
transport: Transport
requestInputOptions?: RequestInputOptions
}

// dprint-ignore
Expand Down
Loading