@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
173 lines (171 loc) • 7.19 kB
JavaScript
import { applyFunctionToColumnName } from "../../../database/run-ast/utils/apply-function-to-column-name.js";
import { getGraphQLType } from "../../../utils/get-graphql-type.js";
import { SYSTEM_DENY_LIST } from "./index.js";
import { mapKeys, pick } from "lodash-es";
import { GENERATE_SPECIAL } from "@directus/constants";
import { GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLScalarType, GraphQLString, GraphQLUnionType } from "graphql";
import { GraphQLJSON } from "graphql-compose";
//#region src/services/graphql/schema/get-types.ts
/**
* Construct an object of types for every collection, using the permitted fields per action type
* as it's fields.
*/
function getTypes(schemaComposer, scope, schema, inconsistentFields, action) {
const CollectionTypes = {};
const VersionTypes = {};
const CountFunctions = schemaComposer.createObjectTC({
name: "count_functions",
fields: { count: { type: GraphQLInt } }
});
const DateFunctions = schemaComposer.createObjectTC({
name: "date_functions",
fields: {
year: { type: GraphQLInt },
month: { type: GraphQLInt },
week: { type: GraphQLInt },
day: { type: GraphQLInt },
weekday: { type: GraphQLInt }
}
});
const TimeFunctions = schemaComposer.createObjectTC({
name: "time_functions",
fields: {
hour: { type: GraphQLInt },
minute: { type: GraphQLInt },
second: { type: GraphQLInt }
}
});
const DateTimeFunctions = schemaComposer.createObjectTC({
name: "datetime_functions",
fields: {
...DateFunctions.getFields(),
...TimeFunctions.getFields()
}
});
for (const collection of Object.values(schema[action].collections)) {
if (Object.keys(collection.fields).length === 0) continue;
if (SYSTEM_DENY_LIST.includes(collection.collection)) continue;
CollectionTypes[collection.collection] = schemaComposer.createObjectTC({
name: action === "read" ? collection.collection : `${action}_${collection.collection}`,
fields: Object.values(collection.fields).reduce((acc, field) => {
let type = getGraphQLType(field.type, field.special);
const fieldIsInconsistent = inconsistentFields[action][collection.collection]?.includes(field.field);
if (field.nullable === false && !field.defaultValue && !GENERATE_SPECIAL.some((flag) => field.special.includes(flag)) && fieldIsInconsistent === false && action !== "update") type = new GraphQLNonNull(type);
if (collection.primary === field.field && fieldIsInconsistent === false) if (collection.collection === "directus_permissions") type = GraphQLID;
else if (!field.defaultValue && !field.special.includes("uuid") && action === "create") type = new GraphQLNonNull(GraphQLID);
else if (["create", "update"].includes(action)) type = GraphQLID;
else type = new GraphQLNonNull(GraphQLID);
acc[field.field] = {
type,
description: field.note,
resolve: (obj) => {
return obj[field.field];
}
};
if (action === "read") {
if (field.type === "date") acc[`${field.field}_func`] = {
type: DateFunctions,
resolve: (obj) => {
return mapKeys(pick(obj, Object.keys(DateFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
}
};
if (field.type === "time") acc[`${field.field}_func`] = {
type: TimeFunctions,
resolve: (obj) => {
return mapKeys(pick(obj, Object.keys(TimeFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
}
};
if (field.type === "dateTime" || field.type === "timestamp") acc[`${field.field}_func`] = {
type: DateTimeFunctions,
resolve: (obj) => {
return mapKeys(pick(obj, Object.keys(DateTimeFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
}
};
if (field.type === "alias") acc[`${field.field}_func`] = {
type: CountFunctions,
resolve: (obj) => {
return mapKeys(pick(obj, Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
}
};
if (field.type === "json") {
const JsonFieldFunctions = schemaComposer.createObjectTC({
name: `${collection.collection}_${field.field}_func`,
fields: {
count: {
type: GraphQLInt,
resolve: (obj) => obj[`${field.field}_count`]
},
json: {
type: GraphQLJSON,
args: { path: { type: new GraphQLNonNull(GraphQLString) } },
resolve: (obj, { path }) => obj[applyFunctionToColumnName(`json(${field.field}, ${path})`)]
}
}
});
acc[`${field.field}_func`] = {
type: JsonFieldFunctions,
resolve: (obj) => obj
};
}
}
return acc;
}, {})
});
if (scope === "items") VersionTypes[collection.collection] = CollectionTypes[collection.collection].clone(`version_${collection.collection}`);
}
for (const relation of schema[action].relations) if (relation.related_collection) {
if (SYSTEM_DENY_LIST.includes(relation.related_collection)) continue;
CollectionTypes[relation.collection]?.addFields({ [relation.field]: {
type: CollectionTypes[relation.related_collection],
resolve: (obj, _, __, info) => {
return obj[info?.path?.key] ?? obj[info?.fieldName ?? relation.field];
}
} });
VersionTypes[relation.collection]?.addFields({ [relation.field]: {
type: GraphQLJSON,
resolve: (obj, _, __, info) => {
return obj[info?.path?.key] ?? obj[info?.fieldName ?? relation.field];
}
} });
if (relation.meta?.one_field) {
CollectionTypes[relation.related_collection]?.addFields({ [relation.meta.one_field]: {
type: [CollectionTypes[relation.collection]],
resolve: (obj, _, __, info) => {
return obj[info?.path?.key] ?? obj[info?.fieldName ?? relation.meta.one_field];
}
} });
if (scope === "items") VersionTypes[relation.related_collection]?.addFields({ [relation.meta.one_field]: {
type: GraphQLJSON,
resolve: (obj, _, __, info) => {
return obj[info?.path?.key] ?? obj[info?.fieldName ?? relation.meta.one_field];
}
} });
}
} else if (relation.meta?.one_allowed_collections && action === "read") CollectionTypes[relation.collection]?.addFields({ [relation.field]: {
type: new GraphQLUnionType({
name: `${relation.collection}_${relation.field}_union`,
types: relation.meta.one_allowed_collections.map((collection) => CollectionTypes[collection].getType()),
resolveType(_value, context, info) {
let path = [];
let currentPath = info.path;
while (currentPath.prev) {
path.push(currentPath.key);
currentPath = currentPath.prev;
}
path = path.reverse().slice(0, -1);
let parent = context["data"];
for (const pathPart of path) parent = parent[pathPart];
return CollectionTypes[parent[relation.meta.one_collection_field]].getType().name;
}
}),
resolve: (obj, _, __, info) => {
return obj[info?.path?.key] ?? obj[info?.fieldName ?? relation.field];
}
} });
return {
CollectionTypes,
VersionTypes
};
}
//#endregion
export { getTypes };