(
eb: ExpressionBuilder<any, any>,
args: Expression<any>[],
{ client, model, modelAlias, operation }: ZModelFunctionContext<any>,
)
| 8 | * Relation checker implementation. |
| 9 | */ |
| 10 | export const check: ZModelFunction<any> = ( |
| 11 | eb: ExpressionBuilder<any, any>, |
| 12 | args: Expression<any>[], |
| 13 | { client, model, modelAlias, operation }: ZModelFunctionContext<any>, |
| 14 | ) => { |
| 15 | invariant(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments'); |
| 16 | |
| 17 | const arg1Node = args[0]!.toOperationNode(); |
| 18 | |
| 19 | const arg2Node = args.length === 2 ? args[1]!.toOperationNode() : undefined; |
| 20 | if (arg2Node) { |
| 21 | invariant( |
| 22 | ValueNode.is(arg2Node) && typeof arg2Node.value === 'string', |
| 23 | '"operation" parameter must be a string literal when provided', |
| 24 | ); |
| 25 | invariant( |
| 26 | CRUD.includes(arg2Node.value as CRUD), |
| 27 | '"operation" parameter must be one of "create", "read", "update", "delete"', |
| 28 | ); |
| 29 | } |
| 30 | |
| 31 | // first argument must be a field reference |
| 32 | const fieldName = QueryUtils.extractFieldName(arg1Node); |
| 33 | invariant(fieldName, 'Failed to extract field name from the first argument of "check" function'); |
| 34 | const fieldDef = QueryUtils.requireField(client.$schema, model, fieldName); |
| 35 | invariant(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`); |
| 36 | invariant(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`); |
| 37 | const relationModel = fieldDef.type; |
| 38 | |
| 39 | // build the join condition between the current model and the related model |
| 40 | const joinConditions: Expression<any>[] = []; |
| 41 | const fkInfo = QueryUtils.getRelationForeignKeyFieldPairs(client.$schema, model, fieldName); |
| 42 | const idFields = QueryUtils.requireIdFields(client.$schema, model); |
| 43 | |
| 44 | // helper to build a base model select for delegate models |
| 45 | const buildBaseSelect = (baseModel: string, field: string): Expression<any> => { |
| 46 | return eb |
| 47 | .selectFrom(baseModel) |
| 48 | .select(field) |
| 49 | .where( |
| 50 | eb.and( |
| 51 | idFields.map((idField) => |
| 52 | eb(eb.ref(`${fieldDef.originModel}.${idField}`), '=', eb.ref(`${modelAlias}.${idField}`)), |
| 53 | ), |
| 54 | ), |
| 55 | ); |
| 56 | }; |
| 57 | |
| 58 | if (fkInfo.ownedByModel) { |
| 59 | // model owns the relation |
| 60 | joinConditions.push( |
| 61 | ...fkInfo.keyPairs.map(({ fk, pk }) => { |
| 62 | let fkRef: Expression<any>; |
| 63 | if (fieldDef.originModel && fieldDef.originModel !== model) { |
| 64 | // relation is actually defined in a delegate base model, select from there |
| 65 | fkRef = buildBaseSelect(fieldDef.originModel, fk); |
| 66 | } else { |
| 67 | fkRef = eb.ref(`${modelAlias}.${fk}`); |
nothing calls this directly
no test coverage detected