@@ -19,6 +19,7 @@ import { getFields, resolveField } from '../model-meta';
19
19
import { NestedWriteVisitorContext } from '../nested-write-vistor' ;
20
20
import type { InputCheckFunc , ModelMeta , PolicyDef , ReadFieldCheckFunc , ZodSchemas } from '../types' ;
21
21
import {
22
+ enumerate ,
22
23
formatObject ,
23
24
getIdFields ,
24
25
getModelFields ,
@@ -54,14 +55,28 @@ export class PolicyUtil {
54
55
* Creates a conjunction of a list of query conditions.
55
56
*/
56
57
and ( ...conditions : ( boolean | object | undefined ) [ ] ) : object {
57
- return this . reduce ( { AND : conditions } ) ;
58
+ const filtered = conditions . filter ( ( c ) => c !== undefined ) ;
59
+ if ( filtered . length === 0 ) {
60
+ return this . makeTrue ( ) ;
61
+ } else if ( filtered . length === 1 ) {
62
+ return this . reduce ( filtered [ 0 ] ) ;
63
+ } else {
64
+ return this . reduce ( { AND : filtered } ) ;
65
+ }
58
66
}
59
67
60
68
/**
61
69
* Creates a disjunction of a list of query conditions.
62
70
*/
63
71
or ( ...conditions : ( boolean | object | undefined ) [ ] ) : object {
64
- return this . reduce ( { OR : conditions } ) ;
72
+ const filtered = conditions . filter ( ( c ) => c !== undefined ) ;
73
+ if ( filtered . length === 0 ) {
74
+ return this . makeFalse ( ) ;
75
+ } else if ( filtered . length === 1 ) {
76
+ return this . reduce ( filtered [ 0 ] ) ;
77
+ } else {
78
+ return this . reduce ( { OR : filtered } ) ;
79
+ }
65
80
}
66
81
67
82
/**
@@ -116,48 +131,75 @@ export class PolicyUtil {
116
131
return this . makeFalse ( ) ;
117
132
}
118
133
119
- if ( 'AND' in condition && Array . isArray ( condition . AND ) ) {
120
- const children = condition . AND . map ( ( c : any ) => this . reduce ( c ) ) . filter (
121
- ( c ) => c !== undefined && ! this . isTrue ( c )
122
- ) ;
123
- if ( children . length === 0 ) {
124
- return this . makeTrue ( ) ;
125
- } else if ( children . some ( ( c ) => this . isFalse ( c ) ) ) {
126
- return this . makeFalse ( ) ;
127
- } else if ( children . length === 1 ) {
128
- return children [ 0 ] ;
129
- } else {
130
- return { AND : children } ;
131
- }
134
+ if ( condition === null ) {
135
+ return condition ;
132
136
}
133
137
134
- if ( 'OR' in condition && Array . isArray ( condition . OR ) ) {
135
- const children = condition . OR . map ( ( c : any ) => this . reduce ( c ) ) . filter (
136
- ( c ) => c !== undefined && ! this . isFalse ( c )
137
- ) ;
138
- if ( children . length === 0 ) {
139
- return this . makeFalse ( ) ;
140
- } else if ( children . some ( ( c ) => this . isTrue ( c ) ) ) {
141
- return this . makeTrue ( ) ;
142
- } else if ( children . length === 1 ) {
143
- return children [ 0 ] ;
144
- } else {
145
- return { OR : children } ;
138
+ const result : any = { } ;
139
+ for ( const [ key , value ] of Object . entries < any > ( condition ) ) {
140
+ if ( value === null || value === undefined ) {
141
+ result [ key ] = value ;
142
+ continue ;
146
143
}
147
- }
148
144
149
- if ( 'NOT' in condition && condition . NOT !== null && typeof condition . NOT === 'object' ) {
150
- const child = this . reduce ( condition . NOT ) ;
151
- if ( this . isTrue ( child ) ) {
152
- return this . makeFalse ( ) ;
153
- } else if ( this . isFalse ( child ) ) {
154
- return this . makeTrue ( ) ;
155
- } else {
156
- return { NOT : child } ;
145
+ switch ( key ) {
146
+ case 'AND' : {
147
+ const children = enumerate ( value )
148
+ . map ( ( c : any ) => this . reduce ( c ) )
149
+ . filter ( ( c ) => c !== undefined && ! this . isTrue ( c ) ) ;
150
+ if ( children . length === 0 ) {
151
+ result [ key ] = [ ] ; // true
152
+ } else if ( children . some ( ( c ) => this . isFalse ( c ) ) ) {
153
+ result [ 'OR' ] = [ ] ; // false
154
+ } else {
155
+ if ( ! this . isTrue ( { AND : result [ key ] } ) ) {
156
+ // use AND only if it's not already true
157
+ result [ key ] = ! Array . isArray ( value ) && children . length === 1 ? children [ 0 ] : children ;
158
+ }
159
+ }
160
+ break ;
161
+ }
162
+
163
+ case 'OR' : {
164
+ const children = enumerate ( value )
165
+ . map ( ( c : any ) => this . reduce ( c ) )
166
+ . filter ( ( c ) => c !== undefined && ! this . isFalse ( c ) ) ;
167
+ if ( children . length === 0 ) {
168
+ result [ key ] = [ ] ; // false
169
+ } else if ( children . some ( ( c ) => this . isTrue ( c ) ) ) {
170
+ result [ 'AND' ] = [ ] ; // true
171
+ } else {
172
+ if ( ! this . isFalse ( { OR : result [ key ] } ) ) {
173
+ // use OR only if it's not already false
174
+ result [ key ] = ! Array . isArray ( value ) && children . length === 1 ? children [ 0 ] : children ;
175
+ }
176
+ }
177
+ break ;
178
+ }
179
+
180
+ case 'NOT' : {
181
+ result [ key ] = this . reduce ( value ) ;
182
+ break ;
183
+ }
184
+
185
+ default : {
186
+ const booleanKeys = [ 'AND' , 'OR' , 'NOT' , 'is' , 'isNot' , 'none' , 'every' , 'some' ] ;
187
+ if (
188
+ typeof value === 'object' &&
189
+ value &&
190
+ // recurse only if the value has at least one boolean key
191
+ Object . keys ( value ) . some ( ( k ) => booleanKeys . includes ( k ) )
192
+ ) {
193
+ result [ key ] = this . reduce ( value ) ;
194
+ } else {
195
+ result [ key ] = value ;
196
+ }
197
+ break ;
198
+ }
157
199
}
158
200
}
159
201
160
- return condition ;
202
+ return result ;
161
203
}
162
204
163
205
//#endregion
@@ -349,18 +391,18 @@ export class PolicyUtil {
349
391
operation : PolicyOperationKind
350
392
) {
351
393
const guard = this . getAuthGuard ( db , fieldInfo . type , operation ) ;
394
+
395
+ // is|isNot and flat fields conditions are mutually exclusive
396
+
352
397
if ( payload . is || payload . isNot ) {
353
398
if ( payload . is ) {
354
399
this . injectGuardForRelationFields ( db , fieldInfo . type , payload . is , operation ) ;
355
- // turn "is" into: { is: { AND: [ originalIs, guard ] }
356
- payload . is = this . and ( payload . is , guard ) ;
357
400
}
358
401
if ( payload . isNot ) {
359
402
this . injectGuardForRelationFields ( db , fieldInfo . type , payload . isNot , operation ) ;
360
- // turn "isNot" into: { isNot: { AND: [ originalIsNot, { NOT: guard } ] } }
361
- payload . isNot = this . and ( payload . isNot , this . not ( guard ) ) ;
362
- delete payload . isNot ;
363
403
}
404
+ // merge guard with existing "is": { is: [originalIs, guard] }
405
+ payload . is = this . and ( payload . is , guard ) ;
364
406
} else {
365
407
this . injectGuardForRelationFields ( db , fieldInfo . type , payload , operation ) ;
366
408
// turn direct conditions into: { is: { AND: [ originalConditions, guard ] } }
@@ -1062,7 +1104,6 @@ export class PolicyUtil {
1062
1104
throw new Error ( 'invalid where clause' ) ;
1063
1105
}
1064
1106
1065
- extra = this . reduce ( extra ) ;
1066
1107
if ( this . isTrue ( extra ) ) {
1067
1108
return ;
1068
1109
}
0 commit comments