( options: ParseFieldsOptions, context: ParseFieldsContext, )
| 31 | } |
| 32 | |
| 33 | export async function parseFields( |
| 34 | options: ParseFieldsOptions, |
| 35 | context: ParseFieldsContext, |
| 36 | ): Promise<[] | (NestedCollectionNode | FieldNode | FunctionFieldNode)[]> { |
| 37 | let { fields } = options; |
| 38 | if (!fields) return []; |
| 39 | |
| 40 | fields = await convertWildcards( |
| 41 | { |
| 42 | fields, |
| 43 | collection: options.parentCollection, |
| 44 | alias: options.query.alias, |
| 45 | accountability: options.accountability, |
| 46 | backlink: options.query.backlink, |
| 47 | }, |
| 48 | context, |
| 49 | ); |
| 50 | |
| 51 | if (!fields || !Array.isArray(fields)) return []; |
| 52 | |
| 53 | const children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[] = []; |
| 54 | |
| 55 | const policies = |
| 56 | options.accountability && options.accountability.admin === false |
| 57 | ? await fetchPolicies(options.accountability, context) |
| 58 | : null; |
| 59 | |
| 60 | const relationalStructure: Record<string, string[] | CollectionScope> = Object.create(null); |
| 61 | |
| 62 | for (const fieldKey of fields) { |
| 63 | let alias = false; |
| 64 | let name = fieldKey; |
| 65 | |
| 66 | if (options.query.alias) { |
| 67 | // check for field alias (is one of the key) |
| 68 | if (name in options.query.alias) { |
| 69 | alias = true; |
| 70 | name = options.query.alias[fieldKey]!; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // Normalize function calls to move relational prefixes outside the function. |
| 75 | // e.g., json(m2m.data, color) → m2m.json(data, color) |
| 76 | // This allows the standard relational handling below to process the traversal |
| 77 | // uniformly for all functions (json, count, year, etc.). |
| 78 | name = parseFilterFunctionPath(name); |
| 79 | |
| 80 | const isFunctionCall = name.includes('(') && name.includes(')'); |
| 81 | |
| 82 | if (isFunctionCall) { |
| 83 | const functionName = name.split('(')[0]!; |
| 84 | const columnName = name.match(REGEX_BETWEEN_PARENS)![1]!; |
| 85 | |
| 86 | const foundField = context.schema.collections[options.parentCollection]!.fields[columnName]; |
| 87 | |
| 88 | // Create a FunctionFieldNode for relational count functions (count(related_items)) |
| 89 | if (functionName === 'count' && foundField && foundField.type === 'alias') { |
| 90 | const foundRelation = context.schema.relations.find( |
no test coverage detected