Skip to content

Commit 04a4e88

Browse files
author
Jason Kuhrt
authored
refactor: settings lib (#1275)
1 parent 95ac7ae commit 04a4e88

File tree

13 files changed

+355
-364
lines changed

13 files changed

+355
-364
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"prismjs": "^1.20.0",
6262
"prompts": "^2.3.0",
6363
"rxjs": "^6.5.4",
64+
"setset": "^0.0.3",
6465
"simple-git": "^2.0.0",
6566
"slash": "^3.0.0",
6667
"source-map-support": "^0.5.19",
@@ -121,7 +122,7 @@
121122
"postpublish": "yarn clean",
122123
"postinstall": "node scripts/postinstall",
123124
"prepublishOnly": "yarn build",
124-
"test": "cross-env FORCE_COLOR=3 LOG_PRETTY=true DEBUG=true jest --verbose --testTimeout 1200000 --forceExit",
125+
"test": "cross-env FORCE_COLOR=3 LOG_PRETTY=true DEBUG=true jest --testTimeout 1200000 --forceExit",
125126
"test:unit": "yarn test src",
126127
"dev:test": "yarn test --watch src",
127128
"dev": "yarn clean && node scripts/build-module-facades && tsc -b tsconfig.cjs.json -w"

src/lib/utils/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export type Param3<F> = F extends (p1: any, p2: any, p3: infer P3, ...args: any[
2121

2222
/**
2323
* Represents a POJO. Prevents from allowing arrays and functions
24+
*
25+
* @remarks
26+
*
27+
* TypeScript interfaces will not be considered sub-types.
2428
*/
2529
export type PlainObject = {
2630
[x: string]: Primitive | object
@@ -92,12 +96,13 @@ export declare type DeepRequired<T> = T extends (...args: any[]) => any
9296
? DeepRequiredObject<T>
9397
: T
9498

95-
export interface DeepRequiredArray<T> extends Array<DeepRequired<NonUndefined<T>>> {}
99+
export type ExcludeUndefined<A> = A extends undefined ? never : A
100+
101+
export interface DeepRequiredArray<T> extends Array<DeepRequired<ExcludeUndefined<T>>> {}
96102

97103
export declare type DeepRequiredObject<T> = {
98-
[P in keyof T]-?: DeepRequired<NonUndefined<T[P]>>
104+
[K in keyof T]-?: DeepRequired<ExcludeUndefined<T[K]>>
99105
}
100-
export declare type NonUndefined<A> = A extends undefined ? never : A
101106

102107
/**
103108
* Guarantee the length of a given string, padding before or after with the

src/runtime/schema/settings-mapper.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function mapSettingsAndPluginsToNexusSchemaConfig(
3232

3333
baseConfig.plugins!.push(...mapConnectionsSettingsToNexusSchemaConfig(settings))
3434

35-
if (settings.authorization !== false) {
35+
if (settings.authorization.enabled) {
3636
baseConfig.plugins!.push(NexusSchema.fieldAuthorizePlugin(settings.authorization))
3737
}
3838

@@ -51,24 +51,7 @@ export function mapSettingsAndPluginsToNexusSchemaConfig(
5151
* Specialized mapping for the complexity of relay connections plugins.
5252
*/
5353
function mapConnectionsSettingsToNexusSchemaConfig(settings: SettingsData): NexusSchema.core.NexusPlugin[] {
54-
const instances: NexusSchema.core.NexusPlugin[] = []
55-
56-
const { default: defaultTypeConfig, ...customTypesConfig } = settings.connections
57-
58-
for (const [name, config] of Object.entries(customTypesConfig)) {
59-
if (config) {
60-
instances.push(
61-
NexusSchema.connectionPlugin({
62-
nexusFieldName: name,
63-
...config,
64-
})
65-
)
66-
}
67-
}
68-
69-
if (defaultTypeConfig) {
70-
instances.push(NexusSchema.connectionPlugin(defaultTypeConfig))
71-
}
72-
73-
return instances
54+
return Object.values(settings.connections).map((config) => {
55+
return NexusSchema.connectionPlugin(config)
56+
})
7457
}

src/runtime/schema/settings.spec.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ it('has defaults', () => {
1111
expect(sm.data).toMatchInlineSnapshot(`
1212
Object {
1313
"authorization": Object {
14+
"enabled": true,
1415
"formatError": [Function],
1516
},
1617
"connections": Object {
1718
"default": Object {
19+
"enabled": true,
1820
"nexusFieldName": "connection",
1921
"nexusSchemaImportId": "nexus/components/schema",
2022
},
@@ -40,23 +42,26 @@ describe('connctions', () => {
4042
expect(sm.data.connections).toMatchInlineSnapshot(`
4143
Object {
4244
"a": Object {
43-
"nexusFieldName": "connection",
45+
"enabled": true,
46+
"nexusFieldName": "a",
4447
"nexusSchemaImportId": "nexus/components/schema",
4548
},
4649
"default": Object {
50+
"enabled": true,
4751
"nexusFieldName": "connection",
4852
"nexusSchemaImportId": "nexus/components/schema",
4953
},
5054
}
5155
`)
5256
})
5357
it('existing custom types can be changed, preserving its core settings', () => {
54-
sm.change({ connections: { a: {} } }) // test above says this has the core settings
58+
// sm.change({ connections: { a: {} } }) // test above says this has the core settings
5559
sm.change({ connections: { a: { disableForwardPagination: true } } })
5660
expect(sm.data.connections.a).toMatchInlineSnapshot(`
5761
Object {
5862
"disableForwardPagination": true,
59-
"nexusFieldName": "connection",
63+
"enabled": true,
64+
"nexusFieldName": "a",
6065
"nexusSchemaImportId": "nexus/components/schema",
6166
}
6267
`)
@@ -66,6 +71,7 @@ describe('connctions', () => {
6671
expect(sm.data.connections.default).toMatchInlineSnapshot(`
6772
Object {
6873
"disableForwardPagination": true,
74+
"enabled": true,
6975
"nexusFieldName": "connection",
7076
"nexusSchemaImportId": "nexus/components/schema",
7177
}

src/runtime/schema/settings.ts

Lines changed: 86 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
import * as NexusSchema from '@nexus/schema'
2-
import * as Lo from 'lodash'
2+
import * as Setset from 'setset'
33
import { Param1 } from '../../lib/utils'
44

55
// todo export type from @nexus/schema
66
type ConnectionPluginConfig = NonNullable<Param1<typeof NexusSchema.connectionPlugin>>
77

88
type ConnectionPluginConfigPropsManagedByNexus = 'nexusFieldName' | 'nexusSchemaImportId'
99

10-
const connectionPluginConfigManagedByNexus: Pick<
11-
ConnectionPluginConfig,
12-
ConnectionPluginConfigPropsManagedByNexus
13-
> = {
14-
nexusSchemaImportId: 'nexus/components/schema',
15-
/**
16-
* The name of the relay connection field builder. This is not configurable by users.
17-
*/
18-
nexusFieldName: 'connection',
19-
}
10+
type ConnectionPluginConfigManagedByNexus = Required<
11+
Pick<ConnectionPluginConfig, ConnectionPluginConfigPropsManagedByNexus>
12+
>
2013

2114
/**
2215
* Relay connection field builder settings for users.
2316
*/
24-
export type ConnectionSettings = Omit<ConnectionPluginConfig, ConnectionPluginConfigPropsManagedByNexus>
17+
export type ConnectionSettings = Omit<ConnectionPluginConfig, ConnectionPluginConfigPropsManagedByNexus> & {
18+
enabled?: boolean
19+
}
20+
21+
export type ConnectionSettingsData = ConnectionSettings &
22+
ConnectionPluginConfigManagedByNexus & {
23+
enabled: boolean
24+
}
2525

2626
/**
2727
* The schema settings users can control.
@@ -51,16 +51,16 @@ export type SettingsInput = {
5151
/**
5252
* todo
5353
*/
54-
default?: ConnectionSettings | false
54+
default?: false | ConnectionSettings
5555
// Extra undefined below is forced by it being above, forced via `?:`.
5656
// This is a TS limitation, cannot express void vs missing semantics,
5757
// being tracked here: https://github.com/microsoft/TypeScript/issues/13195
58-
[connectionTypeName: string]: ConnectionSettings | undefined | false
58+
[connectionTypeName: string]: false | undefined | ConnectionSettings
5959
}
6060
/**
6161
* Disable or configure the authorization plugin
6262
*/
63-
authorization?: false | NexusSchema.core.FieldAuthorizePluginConfig
63+
authorization?: false | (NexusSchema.core.FieldAuthorizePluginConfig & { enabled?: boolean })
6464
/**
6565
* Should a [GraphQL SDL file](https://www.prisma.io/blog/graphql-sdl-schema-definition-language-6755bcb9ce51) be generated when the app is built and to where?
6666
*
@@ -118,99 +118,86 @@ export type SettingsInput = {
118118
/**
119119
* Internal representation of settings data.
120120
*/
121-
export type SettingsData = {
122-
nullable: NonNullable<Required<SettingsInput['nullable']>>
121+
export type SettingsData = Omit<Setset.InferDataFromInput<SettingsInput>, 'connections'> & {
123122
connections: {
124-
default: false | ConnectionPluginConfig
125-
[connectionTypeName: string]: false | ConnectionPluginConfig
126-
}
127-
generateGraphQLSDLFile: NonNullable<SettingsInput['generateGraphQLSDLFile']>
128-
rootTypingsGlobPattern: NonNullable<SettingsInput['rootTypingsGlobPattern']>
129-
authorization: NonNullable<SettingsInput['authorization']>
130-
}
131-
132-
/**
133-
* Mutate the settings data with new settings input.
134-
*/
135-
export function changeSettings(state: SettingsData, newSettings: SettingsInput): void {
136-
if (newSettings.nullable !== undefined) {
137-
Lo.merge(state.nullable, newSettings.nullable)
123+
default: ConnectionSettingsData
124+
[connectionTypeName: string]: ConnectionSettingsData
138125
}
139-
140-
if (newSettings.generateGraphQLSDLFile !== undefined) {
141-
state.generateGraphQLSDLFile = newSettings.generateGraphQLSDLFile
142-
}
143-
144-
if (newSettings.rootTypingsGlobPattern !== undefined) {
145-
state.rootTypingsGlobPattern = newSettings.rootTypingsGlobPattern
146-
}
147-
148-
if (newSettings.authorization !== undefined) {
149-
state.authorization = newSettings.authorization
150-
}
151-
152-
if (newSettings.connections !== undefined) {
153-
Object.keys(newSettings.connections)
154-
// must already have the defaults
155-
.filter((key) => state.connections[key] === undefined)
156-
.forEach((key) => {
157-
state.connections[key] = Lo.merge(state.connections[key], connectionPluginConfigManagedByNexus)
158-
})
159-
Lo.merge(state.connections, newSettings.connections)
160-
}
161-
}
162-
163-
function defaultAuthorizationErrorFormatter(config: NexusSchema.core.FieldAuthorizePluginErrorConfig) {
164-
return config.error
165126
}
166127

167128
/**
168-
* Get the default settings.
129+
* Create a schema settings manager.
169130
*/
170-
function defaultSettings(): SettingsData {
171-
const data: SettingsData = {
172-
nullable: {
173-
inputs: true,
174-
outputs: true,
175-
},
176-
generateGraphQLSDLFile: 'api.graphql',
177-
rootTypingsGlobPattern: './**/*.ts',
178-
connections: {
179-
// there is another level of defaults that will be applied by Nexus Schema Relay Connections plugin
180-
default: {
181-
...connectionPluginConfigManagedByNexus,
131+
export const createSchemaSettingsManager = () =>
132+
Setset.create<SettingsInput, SettingsData>({
133+
fields: {
134+
nullable: {
135+
fields: {
136+
inputs: {
137+
initial() {
138+
return true
139+
},
140+
},
141+
outputs: {
142+
initial() {
143+
return true
144+
},
145+
},
146+
},
147+
},
148+
generateGraphQLSDLFile: {
149+
initial() {
150+
return 'api.graphql'
151+
},
152+
},
153+
rootTypingsGlobPattern: {
154+
initial() {
155+
return './**/*.ts'
156+
},
157+
},
158+
authorization: {
159+
shorthand(enabled) {
160+
return { enabled }
161+
},
162+
fields: {
163+
enabled: {
164+
initial: () => true,
165+
},
166+
formatError: {
167+
initial: () => defaultAuthorizationErrorFormatter,
168+
},
169+
},
170+
},
171+
connections: {
172+
initial() {
173+
return {
174+
default: {},
175+
}
176+
},
177+
entry: {
178+
map(input, ctx) {
179+
return {
180+
nexusSchemaImportId: 'nexus/components/schema',
181+
nexusFieldName: ctx.key === 'default' ? 'connection' : ctx.key,
182+
}
183+
},
184+
shorthand(disabled) {
185+
return { enabled: disabled }
186+
},
187+
fields: {
188+
enabled: {
189+
initial() {
190+
return true
191+
},
192+
},
193+
}, // all data is optional, see type
194+
},
182195
},
183196
},
184-
authorization: {
185-
formatError: defaultAuthorizationErrorFormatter,
186-
},
187-
}
188-
189-
return data
190-
}
197+
})
191198

192-
/**
193-
* Create a schema settings manager.
194-
*/
195-
export function createSchemaSettingsManager() {
196-
const data = defaultSettings()
197-
198-
function change(newSettings: SettingsInput) {
199-
return changeSettings(data, newSettings)
200-
}
201-
202-
function reset() {
203-
for (const k of Object.keys(data)) {
204-
delete (data as any)[k]
205-
}
206-
Object.assign(data, defaultSettings())
207-
}
208-
209-
return {
210-
change,
211-
reset,
212-
data,
213-
}
199+
function defaultAuthorizationErrorFormatter(config: NexusSchema.core.FieldAuthorizePluginErrorConfig) {
200+
return config.error
214201
}
215202

216203
export type SchemaSettingsManager = ReturnType<typeof createSchemaSettingsManager>

src/runtime/server/handler-graphql.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { ContextAdder } from '../schema'
33
import { ApolloServerless } from './apollo-server'
44
import { log } from './logger'
55
import { NexusRequestHandler } from './server'
6-
import { PlaygroundInput } from './settings'
6+
import { PlaygroundLonghandInput } from './settings'
77

88
type Settings = {
99
introspection: boolean
10-
playground: false | PlaygroundInput
10+
playground: false | PlaygroundLonghandInput // todo why not use data? why input?
1111
path: string
1212
errorFormatterFn(graphqlError: GraphQLError): GraphQLFormattedError
1313
}

0 commit comments

Comments
 (0)