Skip to content

Commit 587d400

Browse files
committed
breakout GraphQL omit functionality for fields
1 parent 7639410 commit 587d400

File tree

1 file changed

+64
-37
lines changed

1 file changed

+64
-37
lines changed

packages/core/src/lib/core/initialise-lists.ts

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CacheHint } from '@apollo/cache-control-types'
1+
import { type CacheHint } from '@apollo/cache-control-types'
22
import { GraphQLString, isInputObjectType } from 'graphql'
33
import { type getGqlNames, QueryMode } from '../../types'
44
import {
@@ -12,11 +12,21 @@ import {
1212
type KeystoneConfig,
1313
type MaybePromise,
1414
type NextFieldType,
15+
type FieldTypeFunc,
1516
} from '../../types'
1617
import { graphql } from '../..'
17-
import type { FieldHooks, ResolvedListHooks, ResolvedFieldHooks } from '../../types/config/hooks'
18-
import type { FilterOrderArgs } from '../../types/config/fields'
19-
import type { MaybeItemFunction, MaybeSessionFunction } from '../../types/config/lists'
18+
import {
19+
type FieldHooks,
20+
type ResolvedListHooks,
21+
type ResolvedFieldHooks
22+
} from '../../types/config/hooks'
23+
import {
24+
type FilterOrderArgs
25+
} from '../../types/config/fields'
26+
import {
27+
type MaybeItemFunction,
28+
type MaybeSessionFunction
29+
} from '../../types/config/lists'
2030
import {
2131
type ResolvedFieldAccessControl,
2232
type ResolvedListAccessControl,
@@ -133,16 +143,19 @@ function throwIfNotAFilter (x: unknown, listKey: string, fieldKey: string) {
133143
)
134144
}
135145

136-
function getIsEnabled (listKey: string, listConfig: KeystoneConfig['lists'][string]) {
146+
type ListConfigType = KeystoneConfig['lists'][string]
147+
type FieldConfigType = ReturnType<FieldTypeFunc<any>>
148+
type PartiallyInitialisedList1 = { graphql: { isEnabled: IsEnabled } }
149+
type PartiallyInitialisedList2 = Omit<InitialisedList, 'lists' | 'resolvedDbFields'>
150+
151+
function getIsEnabled (listKey: string, listConfig: ListConfigType) {
137152
const omit = listConfig.graphql?.omit ?? false
138153
const {
139154
defaultIsFilterable = true,
140155
defaultIsOrderable = true
141156
} = listConfig
142157

143-
// We explicity check for boolean/function values here to ensure the dev hasn't made a mistake
144-
// when defining these values. We avoid duck-typing here as this is security related
145-
// and we want to make it hard to write incorrect code.
158+
// TODO: check types in initConfig
146159
throwIfNotAFilter(defaultIsFilterable, listKey, 'defaultIsFilterable')
147160
throwIfNotAFilter(defaultIsOrderable, listKey, 'defaultIsOrderable')
148161

@@ -170,6 +183,39 @@ function getIsEnabled (listKey: string, listConfig: KeystoneConfig['lists'][stri
170183
}
171184
}
172185

186+
function getIsEnabledField (f: FieldConfigType, listKey: string, listConfig: PartiallyInitialisedList1) {
187+
const omit = f.graphql?.omit ?? false
188+
const {
189+
isFilterable = listConfig.graphql.isEnabled.filter,
190+
isOrderable = listConfig.graphql.isEnabled.orderBy,
191+
} = f
192+
193+
// TODO: check types in initConfig
194+
throwIfNotAFilter(isFilterable, listKey, 'isFilterable')
195+
throwIfNotAFilter(isOrderable, listKey, 'isOrderable')
196+
197+
if (typeof omit === 'boolean') {
198+
const notOmit = !omit
199+
return {
200+
type: notOmit,
201+
read: notOmit,
202+
create: notOmit,
203+
update: notOmit,
204+
filter: notOmit ? isFilterable : false,
205+
orderBy: notOmit ? isOrderable : false,
206+
}
207+
}
208+
209+
return {
210+
type: true,
211+
read: !omit.read,
212+
create: !omit.create,
213+
update: !omit.update,
214+
filter: !omit.read ? isFilterable : false, // prevent filtering if read is false
215+
orderBy: !omit.read ? isOrderable : false, // prevent ordering if read is false
216+
}
217+
}
218+
173219
function defaultOperationHook () {}
174220
function defaultListHooksResolveInput ({ resolvedData }: { resolvedData: any }) {
175221
return resolvedData
@@ -264,14 +310,12 @@ function parseFieldHooks (
264310
}
265311
}
266312

267-
type PartiallyInitialisedList = Omit<InitialisedList, 'lists' | 'resolvedDbFields'>
268-
269313
function getListsWithInitialisedFields (
270314
{ storage: configStorage, lists: listsConfig, db: { provider } }: KeystoneConfig,
271315
listGraphqlTypes: Record<string, ListGraphQLTypes>,
272-
intermediateLists: Record<string, { graphql: { isEnabled: IsEnabled } }>
316+
intermediateLists: Record<string, PartiallyInitialisedList1>
273317
) {
274-
const result: Record<string, PartiallyInitialisedList> = {}
318+
const result: Record<string, PartiallyInitialisedList2> = {}
275319

276320
for (const [listKey, list] of Object.entries(listsConfig)) {
277321
const intermediateList = intermediateLists[listKey]
@@ -308,16 +352,7 @@ function getListsWithInitialisedFields (
308352
getStorage: storage => configStorage?.[storage],
309353
})
310354

311-
// We explicity check for boolean values here to ensure the dev hasn't made a mistake
312-
// when defining these values. We avoid duck-typing here as this is security related
313-
// and we want to make it hard to write incorrect code.
314-
throwIfNotAFilter(f.isFilterable, listKey, 'isFilterable')
315-
throwIfNotAFilter(f.isOrderable, listKey, 'isOrderable')
316-
317-
const omit = f.graphql?.omit ?? false
318-
const read = typeof omit === 'boolean' ? !omit : !omit.read
319-
const create = typeof omit === 'boolean' ? !omit : !omit.create
320-
const update = typeof omit === 'boolean' ? !omit : !omit.update
355+
const isEnabledField = getIsEnabledField(f, listKey, intermediateList)
321356
const fieldModes = {
322357
create: f.ui?.createView?.fieldMode ?? list.ui?.createView?.defaultFieldMode ?? 'edit',
323358
item: f.ui?.itemView?.fieldMode ?? list.ui?.itemView?.defaultFieldMode ?? 'edit',
@@ -330,15 +365,7 @@ function getListsWithInitialisedFields (
330365
hooks: parseFieldHooks(f.hooks ?? {}),
331366
graphql: {
332367
cacheHint: f.graphql?.cacheHint,
333-
isEnabled: {
334-
read,
335-
create,
336-
update,
337-
// Filter and orderBy can be defaulted at the list level, otherwise they
338-
// default to `false` if no value was set at the list level.
339-
filter: read && (f.isFilterable ?? intermediateList.graphql.isEnabled.filter),
340-
orderBy: read && (f.isOrderable ?? intermediateList.graphql.isEnabled.orderBy),
341-
},
368+
isEnabled: isEnabledField,
342369
isNonNull: {
343370
read: f.graphql?.isNonNull?.read ?? false,
344371
create: f.graphql?.isNonNull?.create ?? false,
@@ -350,20 +377,20 @@ function getListsWithInitialisedFields (
350377
description: f.ui?.description ?? null,
351378
views: f.ui?.views ?? null,
352379
createView: {
353-
fieldMode: create ? fieldModes.create : 'hidden',
380+
fieldMode: isEnabledField.create ? fieldModes.create : 'hidden',
354381
},
355382

356383
itemView: {
357384
fieldPosition: f.ui?.itemView?.fieldPosition ?? 'form',
358-
fieldMode: update
385+
fieldMode: isEnabledField.update
359386
? fieldModes.item
360-
: read && fieldModes.item !== 'hidden'
361-
? 'read'
362-
: 'hidden',
387+
: isEnabledField.read && fieldModes.item !== 'hidden'
388+
? 'read'
389+
: 'hidden',
363390
},
364391

365392
listView: {
366-
fieldMode: read ? fieldModes.list : 'hidden',
393+
fieldMode: isEnabledField.read ? fieldModes.list : 'hidden',
367394
},
368395
},
369396

0 commit comments

Comments
 (0)