11import { status as httpStatus } from 'http-status'
22
33import type { BeforeChangeHook , CollectionConfig } from '../../collections/config/types.js'
4- import type { Field } from '../../fields/config/types.js'
4+ import type { Config } from '../../config/types.js'
5+ import type { Field , TextField } from '../../fields/config/types.js'
56import type { Endpoint , PayloadHandler , SanitizedConfig } from '../types.js'
67
78import { executeAccess } from '../../auth/executeAccess.js'
89import { APIError } from '../../errors/index.js'
10+ import { sanitizeField } from '../../fields/config/sanitize.js'
911import { combineWhereConstraints } from '../../utilities/combineWhereConstraints.js'
1012import { commitTransaction } from '../../utilities/commitTransaction.js'
1113import { initTransaction } from '../../utilities/initTransaction.js'
1214import { killTransaction } from '../../utilities/killTransaction.js'
13- import { traverseFields } from '../../utilities/traverseFields.js'
1415import { generateKeyBetween , generateNKeysBetween } from './fractional-indexing.js'
1516import { getJoinScopeContext } from './utils/getJoinScopeContext.js'
1617import { getJoinScopeWhereFromDocData } from './utils/getJoinScopeWhereFromDocData.js'
1718import { resolvePendingTargetKey } from './utils/resolvePendingTargetKey.js'
1819
19- /**
20- * This function creates:
21- * - N fields per collection, named `_order` or `_<collection>_<joinField>_order`
22- * - 1 hook per collection
23- * - 1 endpoint per app
24- *
25- * Also, if collection.defaultSort or joinField.defaultSort is not set, it will be set to the orderable field.
26- */
27- export const setupOrderable = ( config : SanitizedConfig ) => {
28- const fieldsToAdd = new Map < CollectionConfig , string [ ] > ( )
29- const joinFieldPathsByCollection = new Map < string , Map < string , string > > ( )
30-
31- config . collections . forEach ( ( collection ) => {
32- if ( collection . orderable ) {
33- const currentFields = fieldsToAdd . get ( collection ) || [ ]
34- fieldsToAdd . set ( collection , [ ...currentFields , '_order' ] )
35- collection . defaultSort = collection . defaultSort ?? '_order'
36- }
37-
38- traverseFields ( {
39- callback : ( { field, parentRef, ref } ) => {
40- if ( field . type === 'array' || field . type === 'blocks' ) {
41- return false
42- }
43- if ( field . type === 'group' || field . type === 'tab' ) {
44- // @ts -expect-error ref is untyped
45- const parentPrefix = parentRef ?. prefix ? `${ parentRef . prefix } _` : ''
46- // @ts -expect-error ref is untyped
47- ref . prefix = `${ parentPrefix } ${ field . name } `
48- }
49- if ( field . type === 'join' && field . orderable === true ) {
50- if ( Array . isArray ( field . collection ) ) {
51- throw new APIError (
52- 'Orderable joins must target a single collection' ,
53- httpStatus . BAD_REQUEST ,
54- { } ,
55- true ,
56- )
57- }
58- const relationshipCollection = config . collections . find ( ( c ) => c . slug === field . collection )
59- if ( ! relationshipCollection ) {
60- return false
61- }
62- field . defaultSort = field . defaultSort ?? `_${ field . collection } _${ field . name } _order`
63- const currentFields = fieldsToAdd . get ( relationshipCollection ) || [ ]
64- // @ts -expect-error ref is untyped
65- const prefix = parentRef ?. prefix ? `${ parentRef . prefix } _` : ''
66- const joinOrderableFieldName = `_${ field . collection } _${ prefix } ${ field . name } _order`
67- fieldsToAdd . set ( relationshipCollection , [ ...currentFields , joinOrderableFieldName ] )
68-
69- const currentJoinFieldPaths =
70- joinFieldPathsByCollection . get ( relationshipCollection . slug ) || new Map < string , string > ( )
71- currentJoinFieldPaths . set ( joinOrderableFieldName , field . on )
72- joinFieldPathsByCollection . set ( relationshipCollection . slug , currentJoinFieldPaths )
73- }
74- } ,
75- fields : collection . fields ,
76- } )
77- } )
78-
79- Array . from ( fieldsToAdd . entries ( ) ) . forEach ( ( [ collection , orderableFields ] ) => {
80- addOrderableFieldsAndHook ( collection , orderableFields , joinFieldPathsByCollection )
81- } )
82-
83- if ( fieldsToAdd . size > 0 ) {
84- addOrderableEndpoint ( config , joinFieldPathsByCollection )
85- }
86- }
87-
88- export const addOrderableFieldsAndHook = (
20+ export const addOrderableFieldsAndHook = async (
8921 collection : CollectionConfig ,
22+ config : Config ,
9023 orderableFieldNames : string [ ] ,
9124 joinFieldPathsByCollection ?: Map < string , Map < string , string > > ,
9225) => {
93- // 1. Add field
94- orderableFieldNames . forEach ( ( orderableFieldName ) => {
95- const orderField : Field = {
26+ // 1. Add fields
27+ for ( const orderableFieldName of orderableFieldNames ) {
28+ const orderField : TextField = {
9629 name : orderableFieldName ,
9730 type : 'text' ,
9831 admin : {
@@ -114,8 +47,24 @@ export const addOrderableFieldsAndHook = (
11447 index : true ,
11548 }
11649
50+ // Sanitize the field using the standard sanitization logic
51+ await sanitizeField ( {
52+ collectionConfig : collection ,
53+ config,
54+ existingFieldNames : new Set ( ) ,
55+ field : orderField ,
56+ index : 0 ,
57+ isTopLevelField : true ,
58+ joinPath : '' ,
59+ parentIndexPath : '' ,
60+ parentIsLocalized : false ,
61+ parentSchemaPath : '' ,
62+ requireFieldLevelRichTextEditor : false ,
63+ validRelationships : null ,
64+ } )
65+
11766 collection . fields . unshift ( orderField )
118- } )
67+ }
11968
12069 // 2. Add hook
12170 if ( ! collection . hooks ) {
0 commit comments