1- import  {  ZModelCodeGenerator ,  isAuthInvocation  }  from  '@zenstackhq/sdk' ; 
1+ import  {  ZModelCodeGenerator ,  getRelationKeyPairs ,   isAuthInvocation ,   isDataModelFieldReference  }  from  '@zenstackhq/sdk' ; 
22import  { 
33    BinaryExpr , 
44    BooleanLiteral , 
5+     DataModelField , 
56    Expression , 
67    ExpressionType , 
78    LiteralExpr , 
@@ -160,46 +161,28 @@ export class ConstraintTransformer {
160161    } 
161162
162163    private  transformComparison ( expr : BinaryExpr )  { 
163-         if  ( this . isAuthEqualNull ( expr ) )  { 
164-             // `auth() == null` => `user === null` 
165-             return  this . value ( `${ this . options . authAccessor }  ,  'boolean' ) ; 
166-         } 
167- 
168-         if  ( this . isAuthNotEqualNull ( expr ) )  { 
169-             // `auth() != null` => `user !== null` 
170-             return  this . value ( `${ this . options . authAccessor }  ,  'boolean' ) ; 
164+         if  ( isAuthInvocation ( expr . left )  ||  isAuthInvocation ( expr . right ) )  { 
165+             // handle the case if any operand is `auth()` invocation 
166+             const  authComparison  =  this . transformAuthComparison ( expr ) ; 
167+             return  authComparison  ??  this . nextVar ( ) ; 
171168        } 
172169
173170        const  leftOperand  =  this . getComparisonOperand ( expr . left ) ; 
174171        const  rightOperand  =  this . getComparisonOperand ( expr . right ) ; 
175172
176-         const  op  =  match ( expr . operator ) 
177-             . with ( '==' ,  ( )  =>  'eq' ) 
178-             . with ( '!=' ,  ( )  =>  'eq' ) 
179-             . with ( '<' ,  ( )  =>  'lt' ) 
180-             . with ( '<=' ,  ( )  =>  'lte' ) 
181-             . with ( '>' ,  ( )  =>  'gt' ) 
182-             . with ( '>=' ,  ( )  =>  'gte' ) 
183-             . otherwise ( ( )  =>  { 
184-                 throw  new  Error ( `Unsupported operator: ${ expr . operator }  ) ; 
185-             } ) ; 
186- 
187-         let  result  =  `{ kind: '${ op } ${ leftOperand } ${ rightOperand }  ; 
188-         if  ( expr . operator  ===  '!=' )  { 
189-             // transform "!=" into "not eq" 
190-             result  =  this . not ( result ) ; 
191-         } 
173+         const  op  =  this . mapOperatorToConstraintKind ( expr . operator ) ; 
174+         const  result  =  `{ kind: '${ op } ${ leftOperand } ${ rightOperand }  ; 
192175
193-         // `auth()` access can be undefined, when that happens, we assume a false condition 
194-         // for the comparison, unless we're directly comparing `auth() != null`  
176+         // `auth()` member  access can be undefined, when that happens, we assume a false condition 
177+         // for the comparison 
195178
196179        const  leftAuthAccess  =  this . getAuthAccess ( expr . left ) ; 
197180        const  rightAuthAccess  =  this . getAuthAccess ( expr . right ) ; 
198181
199-         if  ( leftAuthAccess )  { 
182+         if  ( leftAuthAccess   &&   rightOperand )  { 
200183            // `auth().f op x` => `auth().f !== undefined && auth().f op x` 
201184            return  this . and ( this . value ( `${ this . normalizeToNull ( leftAuthAccess ) }  ,  'boolean' ) ,  result ) ; 
202-         }  else  if  ( rightAuthAccess )  { 
185+         }  else  if  ( rightAuthAccess   &&   leftOperand )  { 
203186            // `x op auth().f` => `auth().f !== undefined && x op auth().f` 
204187            return  this . and ( this . value ( `${ this . normalizeToNull ( rightAuthAccess ) }  ,  'boolean' ) ,  result ) ; 
205188        } 
@@ -212,6 +195,64 @@ export class ConstraintTransformer {
212195        return  result ; 
213196    } 
214197
198+     private  transformAuthComparison ( expr : BinaryExpr )  { 
199+         if  ( this . isAuthEqualNull ( expr ) )  { 
200+             // `auth() == null` => `user === null` 
201+             return  this . value ( `${ this . options . authAccessor }  ,  'boolean' ) ; 
202+         } 
203+ 
204+         if  ( this . isAuthNotEqualNull ( expr ) )  { 
205+             // `auth() != null` => `user !== null` 
206+             return  this . value ( `${ this . options . authAccessor }  ,  'boolean' ) ; 
207+         } 
208+ 
209+         // auth() equality check against a relation, translate to id-fk comparison 
210+         const  operand  =  isAuthInvocation ( expr . left )  ? expr . right  : expr . left ; 
211+         if  ( ! isDataModelFieldReference ( operand ) )  { 
212+             return  undefined ; 
213+         } 
214+ 
215+         // get id-fk field pairs from the relation field 
216+         const  relationField  =  operand . target . ref  as  DataModelField ; 
217+         const  idFkPairs  =  getRelationKeyPairs ( relationField ) ; 
218+ 
219+         // build id-fk field comparison constraints 
220+         const  fieldConstraints : string [ ]  =  [ ] ; 
221+ 
222+         idFkPairs . forEach ( ( {  id,  foreignKey } )  =>  { 
223+             const  idFieldType  =  this . mapType ( id . type . type  as  ExpressionType ) ; 
224+             if  ( ! idFieldType )  { 
225+                 return ; 
226+             } 
227+             const  fkFieldType  =  this . mapType ( foreignKey . type . type  as  ExpressionType ) ; 
228+             if  ( ! fkFieldType )  { 
229+                 return ; 
230+             } 
231+ 
232+             const  op  =  this . mapOperatorToConstraintKind ( expr . operator ) ; 
233+             const  authIdAccess  =  `${ this . options . authAccessor } ${ id . name }  ; 
234+ 
235+             fieldConstraints . push ( 
236+                 this . and ( 
237+                     // `auth()?.id != null` guard 
238+                     this . value ( `${ this . normalizeToNull ( authIdAccess ) }  ,  'boolean' ) , 
239+                     // `auth()?.id [op] fkField` 
240+                     `{ kind: '${ op } ${ this . value ( authIdAccess ,  idFieldType ) } ${ this . variable (  
241+                         foreignKey . name ,  
242+                         fkFieldType  
243+                     ) }   }`
244+                 ) 
245+             ) ; 
246+         } ) ; 
247+ 
248+         // combine field constraints 
249+         if  ( fieldConstraints . length  >  0 )  { 
250+             return  this . and ( ...fieldConstraints ) ; 
251+         } 
252+ 
253+         return  undefined ; 
254+     } 
255+ 
215256    // normalize `auth()` access undefined value to null 
216257    private  normalizeToNull ( expr : string )  { 
217258        return  `(${ expr }  ; 
@@ -241,7 +282,7 @@ export class ConstraintTransformer {
241282        const  fieldAccess  =  this . getFieldAccess ( expr ) ; 
242283        if  ( fieldAccess )  { 
243284            // model field access is transformed into a named variable 
244-             const  mappedType  =  this . mapType ( expr ) ; 
285+             const  mappedType  =  this . mapExpressionType ( expr ) ; 
245286            if  ( mappedType )  { 
246287                return  this . variable ( fieldAccess . name ,  mappedType ) ; 
247288            }  else  { 
@@ -251,7 +292,7 @@ export class ConstraintTransformer {
251292
252293        const  authAccess  =  this . getAuthAccess ( expr ) ; 
253294        if  ( authAccess )  { 
254-             const  mappedType  =  this . mapType ( expr ) ; 
295+             const  mappedType  =  this . mapExpressionType ( expr ) ; 
255296            if  ( mappedType )  { 
256297                return  `${ this . value ( authAccess ,  mappedType ) }  ; 
257298            }  else  { 
@@ -262,14 +303,31 @@ export class ConstraintTransformer {
262303        return  undefined ; 
263304    } 
264305
265-     private  mapType ( expression : Expression )  { 
266-         return  match ( expression . $resolvedType ?. decl  as  ExpressionType ) 
306+     private  mapExpressionType ( expression : Expression )  { 
307+         return  this . mapType ( expression . $resolvedType ?. decl  as  ExpressionType ) ; 
308+     } 
309+ 
310+     private  mapType ( type : ExpressionType )  { 
311+         return  match ( type ) 
267312            . with ( 'Boolean' ,  ( )  =>  'boolean' ) 
268313            . with ( 'Int' ,  ( )  =>  'number' ) 
269314            . with ( 'String' ,  ( )  =>  'string' ) 
270315            . otherwise ( ( )  =>  undefined ) ; 
271316    } 
272317
318+     private  mapOperatorToConstraintKind ( operator : BinaryExpr [ 'operator' ] )  { 
319+         return  match ( operator ) 
320+             . with ( '==' ,  ( )  =>  'eq' ) 
321+             . with ( '!=' ,  ( )  =>  'ne' ) 
322+             . with ( '<' ,  ( )  =>  'lt' ) 
323+             . with ( '<=' ,  ( )  =>  'lte' ) 
324+             . with ( '>' ,  ( )  =>  'gt' ) 
325+             . with ( '>=' ,  ( )  =>  'gte' ) 
326+             . otherwise ( ( )  =>  { 
327+                 throw  new  Error ( `Unsupported operator: ${ operator }  ) ; 
328+             } ) ; 
329+     } 
330+ 
273331    private  getFieldAccess ( expr : Expression )  { 
274332        if  ( isReferenceExpr ( expr ) )  { 
275333            return  isDataModelField ( expr . target . ref )  ? {  name : expr . target . $refText  }  : undefined ; 
0 commit comments