@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
63 lines (61 loc) • 2.86 kB
JavaScript
import { parseArgs } from "../schema/parse-args.js";
import { getQuery } from "../schema/parse-query.js";
import { replaceFragmentsInSelections } from "../utils/replace-fragments.js";
import { getAggregateQuery } from "../utils/aggregate-query.js";
import { parseFilterFunctionPath } from "@directus/utils";
import { omit } from "lodash-es";
//#region src/services/graphql/resolvers/query.ts
/**
* Generic resolver that's used for every "regular" items/system query. Converts the incoming GraphQL AST / fragments into
* Directus' query structure which is then executed by the services.
*/
async function resolveQuery(gql, info) {
let collection = info.fieldName;
if (gql.scope === "system") collection = `directus_${collection}`;
const selections = replaceFragmentsInSelections(info.fieldNodes[0]?.selectionSet?.selections, info.fragments);
if (!selections) return null;
const args = parseArgs(info.fieldNodes[0].arguments || [], info.variableValues);
let query;
if (collection.endsWith("_aggregated") && collection in gql.schema.collections === false) {
collection = collection.slice(0, -11);
query = await getAggregateQuery(args, selections, gql.schema, gql.accountability, collection);
} else {
if (collection.endsWith("_by_id") && collection in gql.schema.collections === false) collection = collection.slice(0, -6);
query = await getQuery(args, gql.schema, selections, info.variableValues, gql.accountability, collection);
if (collection.endsWith("_by_version") && collection in gql.schema.collections === false) {
collection = collection.slice(0, -11);
query.versionRaw = true;
}
}
if (query.fields?.length) for (let fieldIndex = 0; fieldIndex < query.fields.length; fieldIndex++) query.fields[fieldIndex] = parseFilterFunctionPath(query.fields[fieldIndex]);
const result = await gql.read(collection, query, args["id"]);
if (args["id"]) return result;
/**
* Since grouped fields are returned at the top level, we duplicate those fields
* into the expected `group` property on the payload, excluding any non-group
* fields (i.e. aggregate keys).
*
* The payload can only contain grouped fields and aggregate keys, as the
* aggregate selection is restricted to those fields. Therefore, the original
* grouped fields can safely remain at the top level.
*
* We cannot iterate over `query.group`, because grouped fields that use
* functions are normalized (e.g. function(field) → function_field), which would
* result in them being skipped.
*
* Before:
* { year_date: 2023, count: { id: 42 } }
*
* After:
* { year_date: 2023, count: { id: 42 }, group: { year_date: 2023 } }
*/
if (query.group) {
const aggregateKeys = Object.keys(query.aggregate ?? {});
result["map"]((payload) => {
payload["group"] = omit(payload, aggregateKeys);
});
}
return result;
}
//#endregion
export { resolveQuery };