Files
payload/src/collections/graphql/init.ts

444 lines
14 KiB
TypeScript

/* eslint-disable no-param-reassign */
import { DateTimeResolver } from 'graphql-scalars';
import {
GraphQLString,
GraphQLObjectType,
GraphQLBoolean,
GraphQLNonNull,
GraphQLInt,
} from 'graphql';
import formatName from '../../graphql/utilities/formatName';
import buildPaginatedListType from '../../graphql/schema/buildPaginatedListType';
import buildMutationInputType, { getCollectionIDType } from '../../graphql/schema/buildMutationInputType';
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields';
import createResolver from './resolvers/create';
import updateResolver from './resolvers/update';
import findResolver from './resolvers/find';
import findByIDResolver from './resolvers/findByID';
import findVersionByIDResolver from './resolvers/findVersionByID';
import findVersionsResolver from './resolvers/findVersions';
import restoreVersionResolver from './resolvers/restoreVersion';
import me from '../../auth/graphql/resolvers/me';
import init from '../../auth/graphql/resolvers/init';
import login from '../../auth/graphql/resolvers/login';
import logout from '../../auth/graphql/resolvers/logout';
import forgotPassword from '../../auth/graphql/resolvers/forgotPassword';
import resetPassword from '../../auth/graphql/resolvers/resetPassword';
import verifyEmail from '../../auth/graphql/resolvers/verifyEmail';
import unlock from '../../auth/graphql/resolvers/unlock';
import refresh from '../../auth/graphql/resolvers/refresh';
import { Payload } from '../../payload';
import { Field, fieldAffectsData } from '../../fields/config/types';
import buildObjectType, { ObjectTypeConfig } from '../../graphql/schema/buildObjectType';
import buildWhereInputType from '../../graphql/schema/buildWhereInputType';
import getDeleteResolver from './resolvers/delete';
import { toWords, formatNames } from '../../utilities/formatLabels';
import { Collection, SanitizedCollectionConfig } from '../config/types';
import { buildPolicyType } from '../../graphql/schema/buildPoliciesType';
import { docAccessResolver } from './resolvers/docAccess';
function initCollectionsGraphQL(payload: Payload): void {
Object.keys(payload.collections).forEach((slug) => {
const collection: Collection = payload.collections[slug];
const {
config: {
graphQL = {} as SanitizedCollectionConfig['graphQL'],
fields,
timestamps,
versions,
},
} = collection;
let singularName;
let pluralName;
const fromSlug = formatNames(collection.config.slug);
if (graphQL.singularName) {
singularName = toWords(graphQL.singularName, true);
} else {
singularName = fromSlug.singular;
}
if (graphQL.pluralName) {
pluralName = toWords(graphQL.pluralName, true);
} else {
pluralName = fromSlug.plural;
}
// For collections named 'Media' or similar,
// there is a possibility that the singular name
// will equal the plural name. Append `all` to the beginning
// of potential conflicts
if (singularName === pluralName) {
pluralName = `all${singularName}`;
}
collection.graphQL = {} as Collection['graphQL'];
const idField = fields.find((field) => fieldAffectsData(field) && field.name === 'id');
const idType = getCollectionIDType(collection.config);
const baseFields: ObjectTypeConfig = {};
const whereInputFields = [
...fields,
];
if (!idField) {
baseFields.id = { type: idType };
whereInputFields.push({
name: 'id',
type: 'text',
});
}
if (timestamps) {
baseFields.createdAt = {
type: new GraphQLNonNull(DateTimeResolver),
};
baseFields.updatedAt = {
type: new GraphQLNonNull(DateTimeResolver),
};
whereInputFields.push({
name: 'createdAt',
label: 'Created At',
type: 'date',
});
whereInputFields.push({
name: 'updatedAt',
label: 'Updated At',
type: 'date',
});
}
const forceNullableObjectType = Boolean(versions?.drafts);
collection.graphQL.type = buildObjectType({
payload,
name: singularName,
parentName: singularName,
fields,
baseFields,
forceNullable: forceNullableObjectType,
});
collection.graphQL.whereInputType = buildWhereInputType(
singularName,
whereInputFields,
singularName,
);
if (collection.config.auth && !collection.config.auth.disableLocalStrategy) {
fields.push({
name: 'password',
label: 'Password',
type: 'text',
required: true,
});
}
collection.graphQL.mutationInputType = new GraphQLNonNull(buildMutationInputType(
payload,
singularName,
fields,
singularName,
));
collection.graphQL.updateMutationInputType = new GraphQLNonNull(buildMutationInputType(
payload,
`${singularName}Update`,
fields.filter((field) => !(fieldAffectsData(field) && field.name === 'id')),
`${singularName}Update`,
true,
));
payload.Query.fields[singularName] = {
type: collection.graphQL.type,
args: {
id: { type: new GraphQLNonNull(idType) },
draft: { type: GraphQLBoolean },
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
} : {}),
},
resolve: findByIDResolver(collection),
};
payload.Query.fields[pluralName] = {
type: buildPaginatedListType(pluralName, collection.graphQL.type),
args: {
where: { type: collection.graphQL.whereInputType },
draft: { type: GraphQLBoolean },
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
} : {}),
page: { type: GraphQLInt },
limit: { type: GraphQLInt },
sort: { type: GraphQLString },
},
resolve: findResolver(collection),
};
payload.Query.fields[`docAccess${singularName}`] = {
type: buildPolicyType({
typeSuffix: 'DocAccess',
entity: collection.config,
type: 'collection',
scope: 'docAccess',
}),
args: {
id: { type: new GraphQLNonNull(idType) },
},
resolve: docAccessResolver(),
};
payload.Mutation.fields[`create${singularName}`] = {
type: collection.graphQL.type,
args: {
data: { type: collection.graphQL.mutationInputType },
draft: { type: GraphQLBoolean },
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
} : {}),
},
resolve: createResolver(collection),
};
payload.Mutation.fields[`update${singularName}`] = {
type: collection.graphQL.type,
args: {
id: { type: new GraphQLNonNull(idType) },
data: { type: collection.graphQL.updateMutationInputType },
draft: { type: GraphQLBoolean },
autosave: { type: GraphQLBoolean },
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
} : {}),
},
resolve: updateResolver(collection),
};
payload.Mutation.fields[`delete${singularName}`] = {
type: collection.graphQL.type,
args: {
id: { type: new GraphQLNonNull(idType) },
},
resolve: getDeleteResolver(collection),
};
if (collection.config.versions) {
const versionCollectionFields: Field[] = [
...buildVersionCollectionFields(collection.config),
{
name: 'id',
type: 'text',
},
{
name: 'createdAt',
label: 'Created At',
type: 'date',
},
{
name: 'updatedAt',
label: 'Updated At',
type: 'date',
},
];
collection.graphQL.versionType = buildObjectType({
payload,
name: `${singularName}Version`,
fields: versionCollectionFields,
parentName: `${singularName}Version`,
forceNullable: forceNullableObjectType,
});
payload.Query.fields[`version${formatName(singularName)}`] = {
type: collection.graphQL.versionType,
args: {
id: { type: GraphQLString },
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
} : {}),
},
resolve: findVersionByIDResolver(collection),
};
payload.Query.fields[`versions${pluralName}`] = {
type: buildPaginatedListType(`versions${formatName(pluralName)}`, collection.graphQL.versionType),
args: {
where: {
type: buildWhereInputType(
`versions${singularName}`,
versionCollectionFields,
`versions${singularName}`,
),
},
...(payload.config.localization ? {
locale: { type: payload.types.localeInputType },
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
} : {}),
page: { type: GraphQLInt },
limit: { type: GraphQLInt },
sort: { type: GraphQLString },
},
resolve: findVersionsResolver(collection),
};
payload.Mutation.fields[`restoreVersion${formatName(singularName)}`] = {
type: collection.graphQL.type,
args: {
id: { type: GraphQLString },
},
resolve: restoreVersionResolver(collection),
};
}
if (collection.config.auth) {
const authFields: Field[] = collection.config.auth.disableLocalStrategy ? [] : [{
name: 'email',
type: 'email',
required: true,
}];
collection.graphQL.JWT = buildObjectType({
payload,
name: formatName(`${slug}JWT`),
fields: [
...collection.config.fields.filter((field) => fieldAffectsData(field) && field.saveToJWT),
...authFields,
{
name: 'collection',
type: 'text',
required: true,
},
],
parentName: formatName(`${slug}JWT`),
});
payload.Query.fields[`me${singularName}`] = {
type: new GraphQLObjectType({
name: formatName(`${slug}Me`),
fields: {
token: {
type: GraphQLString,
},
user: {
type: collection.graphQL.type,
},
exp: {
type: GraphQLInt,
},
collection: {
type: GraphQLString,
},
},
}),
resolve: me(collection),
};
payload.Query.fields[`initialized${singularName}`] = {
type: GraphQLBoolean,
resolve: init(collection),
};
payload.Mutation.fields[`refreshToken${singularName}`] = {
type: new GraphQLObjectType({
name: formatName(`${slug}Refreshed${singularName}`),
fields: {
user: {
type: collection.graphQL.JWT,
},
refreshedToken: {
type: GraphQLString,
},
exp: {
type: GraphQLInt,
},
},
}),
args: {
token: { type: GraphQLString },
},
resolve: refresh(collection),
};
payload.Mutation.fields[`logout${singularName}`] = {
type: GraphQLString,
resolve: logout(collection),
};
if (!collection.config.auth.disableLocalStrategy) {
if (collection.config.auth.maxLoginAttempts > 0) {
payload.Mutation.fields[`unlock${singularName}`] = {
type: new GraphQLNonNull(GraphQLBoolean),
args: {
email: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: unlock(collection),
};
}
payload.Mutation.fields[`login${singularName}`] = {
type: new GraphQLObjectType({
name: formatName(`${slug}LoginResult`),
fields: {
token: {
type: GraphQLString,
},
user: {
type: collection.graphQL.type,
},
exp: {
type: GraphQLInt,
},
},
}),
args: {
email: { type: GraphQLString },
password: { type: GraphQLString },
},
resolve: login(collection),
};
payload.Mutation.fields[`forgotPassword${singularName}`] = {
type: new GraphQLNonNull(GraphQLBoolean),
args: {
email: { type: new GraphQLNonNull(GraphQLString) },
disableEmail: { type: GraphQLBoolean },
expiration: { type: GraphQLInt },
},
resolve: forgotPassword(collection),
};
payload.Mutation.fields[`resetPassword${singularName}`] = {
type: new GraphQLObjectType({
name: formatName(`${slug}ResetPassword`),
fields: {
token: { type: GraphQLString },
user: { type: collection.graphQL.type },
},
}),
args: {
token: { type: GraphQLString },
password: { type: GraphQLString },
},
resolve: resetPassword(collection),
};
payload.Mutation.fields[`verifyEmail${singularName}`] = {
type: GraphQLBoolean,
args: {
token: { type: GraphQLString },
},
resolve: verifyEmail(collection),
};
}
}
});
}
export default initCollectionsGraphQL;