feat: abstracts revisions components for reuse in globals

This commit is contained in:
James
2021-12-01 12:21:56 -05:00
parent 1920a937b2
commit da5684df27
21 changed files with 364 additions and 211 deletions

View File

@@ -9,7 +9,7 @@ import { requests } from '../api';
import Loading from './elements/Loading';
import StayLoggedIn from './modals/StayLoggedIn';
import Unlicensed from './views/Unlicensed';
import Revisions from './views/collections/Revisions';
import Revisions from './views/Revisions';
const Dashboard = lazy(() => import('./views/Dashboard'));
const ForgotPassword = lazy(() => import('./views/ForgotPassword'));
@@ -117,70 +117,65 @@ const Routes = () => {
<Account />
</Route>
{collections.map((collection) => (
<Route
key={`${collection.slug}-list`}
path={`${match.url}/collections/${collection.slug}`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.read?.permission) {
return (
<List
{...routeProps}
collection={collection}
/>
);
}
{collections.reduce((collectionRoutes, collection) => {
const routesToReturn = [
...collectionRoutes,
<Route
key={`${collection.slug}-list`}
path={`${match.url}/collections/${collection.slug}`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.read?.permission) {
return (
<List
{...routeProps}
collection={collection}
/>
);
}
return <Unauthorized />;
}}
/>
))}
return <Unauthorized />;
}}
/>,
<Route
key={`${collection.slug}-create`}
path={`${match.url}/collections/${collection.slug}/create`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.create?.permission) {
return (
<Edit
{...routeProps}
collection={collection}
/>
);
}
{collections.map((collection) => (
<Route
key={`${collection.slug}-create`}
path={`${match.url}/collections/${collection.slug}/create`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.create?.permission) {
return (
<Edit
{...routeProps}
collection={collection}
/>
);
}
return <Unauthorized />;
}}
/>,
<Route
key={`${collection.slug}-edit`}
path={`${match.url}/collections/${collection.slug}/:id`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.read?.permission) {
return (
<Edit
isEditing
{...routeProps}
collection={collection}
/>
);
}
return <Unauthorized />;
}}
/>
))}
return <Unauthorized />;
}}
/>,
];
{collections.map((collection) => (
<Route
key={`${collection.slug}-edit`}
path={`${match.url}/collections/${collection.slug}/:id`}
exact
render={(routeProps) => {
if (permissions?.collections?.[collection.slug]?.read?.permission) {
return (
<Edit
isEditing
{...routeProps}
collection={collection}
/>
);
}
return <Unauthorized />;
}}
/>
))}
{collections.map((collection) => {
if (collection.revisions) {
return (
routesToReturn.push(
<Route
key={`${collection.slug}-revisions`}
path={`${match.url}/collections/${collection.slug}/:id/revisions`}
@@ -197,16 +192,10 @@ const Routes = () => {
return <Unauthorized />;
}}
/>
/>,
);
}
return null;
})}
{collections.map((collection) => {
if (collection.revisions) {
return (
routesToReturn.push(
<Route
key={`${collection.slug}-view-revision`}
path={`${match.url}/collections/${collection.slug}/:id/revisions/:revisionID`}
@@ -218,32 +207,72 @@ const Routes = () => {
return <Unauthorized />;
}}
/>
/>,
);
}
return null;
})}
return routesToReturn;
}, [])}
{globals && globals.map((global) => (
<Route
key={`${global.slug}`}
path={`${match.url}/globals/${global.slug}`}
exact
render={(routeProps) => {
if (permissions?.globals?.[global.slug]?.read?.permission) {
return (
<EditGlobal
{...routeProps}
global={global}
/>
);
}
{globals && globals.reduce((globalRoutes, global) => {
const routesToReturn = [
...globalRoutes,
<Route
key={`${global.slug}`}
path={`${match.url}/globals/${global.slug}`}
exact
render={(routeProps) => {
if (permissions?.globals?.[global.slug]?.read?.permission) {
return (
<EditGlobal
{...routeProps}
global={global}
/>
);
}
return <Unauthorized />;
}}
/>
))}
return <Unauthorized />;
}}
/>,
];
if (global.revisions) {
routesToReturn.push(
<Route
key={`${global.slug}-revisions`}
path={`${match.url}/globals/${global.slug}/revisions`}
exact
render={(routeProps) => {
if (permissions?.globals?.[global.slug]?.readRevisions?.permission) {
return (
<Revisions
{...routeProps}
global={global}
/>
);
}
return <Unauthorized />;
}}
/>,
);
routesToReturn.push(
<Route
key={`${global.slug}-view-revision`}
path={`${match.url}/globals/${global.slug}/revisions/:revisionID`}
exact
render={(routeProps) => {
if (permissions?.globals?.[global.slug]?.readRevisions?.permission) {
return null;
}
return <Unauthorized />;
}}
/>,
);
}
return routesToReturn;
}, [])}
<Route path={`${match.url}*`}>
<NotFound />

View File

@@ -1,4 +1,4 @@
@import '../../../../../scss/styles.scss';
@import '../../../scss/styles.scss';
.revisions-count__button {
font-weight: 600;

View File

@@ -1,24 +1,40 @@
import { useConfig } from '@payloadcms/config-provider';
import React, { useEffect } from 'react';
import Button from '../../../../elements/Button';
import usePayloadAPI from '../../../../../hooks/usePayloadAPI';
import Button from '../Button';
import usePayloadAPI from '../../../hooks/usePayloadAPI';
import { Props } from './types';
import './index.scss';
const baseClass = 'revisions-count';
const Revisions: React.FC<Props> = ({ collection, id, submissionCount }) => {
const Revisions: React.FC<Props> = ({ collection, global, id, submissionCount }) => {
const { serverURL, routes: { admin } } = useConfig();
const [{ data, isLoading }, { setParams }] = usePayloadAPI(`${serverURL}/api/${collection.slug}/revisions`, {
initialParams: {
let initialParams;
let fetchURL: string;
let revisionsURL: string;
if (collection) {
initialParams = {
where: {
parent: {
equals: id,
},
},
},
};
fetchURL = `${serverURL}/api/${collection.slug}/revisions`;
revisionsURL = `${admin}/collections/${collection.slug}/${id}/revisions`;
}
if (global) {
fetchURL = `${serverURL}/api/globals/${global.slug}/revisions`;
revisionsURL = `${admin}/globals/${global.slug}/revisions`;
}
const [{ data, isLoading }, { setParams }] = usePayloadAPI(fetchURL, {
initialParams,
});
useEffect(() => {
@@ -51,7 +67,7 @@ const Revisions: React.FC<Props> = ({ collection, id, submissionCount }) => {
className={`${baseClass}__button`}
buttonStyle="none"
el="link"
to={`${admin}/collections/${collection.slug}/${id}/revisions`}
to={revisionsURL}
>
{data.docs.length}
{' '}

View File

@@ -0,0 +1,9 @@
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
import { SanitizedGlobalConfig } from '../../../../globals/config/types';
export type Props = {
collection?: SanitizedCollectionConfig,
global?: SanitizedGlobalConfig
id?: string | number
submissionCount: number
}

View File

@@ -10,6 +10,7 @@ import CopyToClipboard from '../../elements/CopyToClipboard';
import Meta from '../../utilities/Meta';
import fieldTypes from '../../forms/field-types';
import LeaveWithoutSaving from '../../modals/LeaveWithoutSaving';
import RevisionsCount from '../../elements/RevisionsCount';
import { Props } from './types';
import './index.scss';
@@ -20,12 +21,13 @@ const baseClass = 'global-edit';
const DefaultGlobalView: React.FC<Props> = (props) => {
const { admin: { dateFormat } } = useConfig();
const {
global, data, onSave, permissions, action, apiURL, initialState,
global, data, onSave, permissions, action, apiURL, initialState, submissionCount,
} = props;
const {
fields,
preview,
revisions,
label,
admin: {
description,
@@ -99,6 +101,15 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
</div>
{data && (
<ul className={`${baseClass}__meta`}>
{revisions && (
<li>
<div className={`${baseClass}__label`}>Revisions</div>
<RevisionsCount
submissionCount={submissionCount}
global={global}
/>
</li>
)}
{data && (
<li className={`${baseClass}__api-url`}>
<span className={`${baseClass}__label`}>

View File

@@ -15,16 +15,15 @@ import { IndexProps } from './types';
const GlobalView: React.FC<IndexProps> = (props) => {
const { state: locationState } = useLocation<{data?: Record<string, unknown>}>();
const history = useHistory();
const locale = useLocale();
const { setStepNav } = useStepNav();
const { permissions } = useAuth();
const [initialState, setInitialState] = useState({});
const [submissionCount, setSubmissionCount] = useState(0);
const {
serverURL,
routes: {
admin,
api,
},
} = useConfig();
@@ -44,14 +43,10 @@ const GlobalView: React.FC<IndexProps> = (props) => {
} = {},
} = global;
const onSave = (json) => {
history.push(`${admin}/globals/${global.slug}`, {
status: {
message: json.message,
type: 'success',
},
data: json.doc,
});
const onSave = async (json) => {
const state = await buildStateFromSchema(fields, json.doc);
setInitialState(state);
setSubmissionCount((count) => count + 1);
};
const [{ data }] = usePayloadAPI(
@@ -97,6 +92,7 @@ const GlobalView: React.FC<IndexProps> = (props) => {
onSave,
apiURL: `${serverURL}${api}/globals/${slug}?depth=0`,
action: `${serverURL}${api}/globals/${slug}?locale=${locale}&depth=0&fallback-locale=null`,
submissionCount,
}}
/>
</NegativeFieldGutterProvider>

View File

@@ -14,4 +14,5 @@ export type Props = {
action: string
apiURL: string
initialState: Fields
submissionCount: number
}

View File

@@ -2,22 +2,29 @@ import React from 'react';
import { Link, useRouteMatch } from 'react-router-dom';
import { useConfig } from '@payloadcms/config-provider';
import format from 'date-fns/format';
import { Column } from '../../../elements/Table/types';
import SortColumn from '../../../elements/SortColumn';
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
import { Column } from '../../elements/Table/types';
import SortColumn from '../../elements/SortColumn';
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
import { SanitizedGlobalConfig } from '../../../../globals/config/types';
type CreatedAtCellProps = {
id: string
date: string
collection: SanitizedCollectionConfig
collection?: SanitizedCollectionConfig
global?: SanitizedGlobalConfig
}
const CreatedAtCell: React.FC<CreatedAtCellProps> = ({ collection, id, date }) => {
const CreatedAtCell: React.FC<CreatedAtCellProps> = ({ collection, global, id, date }) => {
const { routes: { admin }, admin: { dateFormat } } = useConfig();
const { params: { id: docID } } = useRouteMatch<{ id: string }>();
let to: string;
if (collection) to = `${admin}/collections/${collection.slug}/${docID}/revisions/${id}`;
if (global) to = `${admin}/globals/${global.slug}/revisions/${id}`;
return (
<Link to={`${admin}/collections/${collection.slug}/${docID}/revisions/${id}`}>
<Link to={to}>
{date && format(new Date(date), dateFormat)}
</Link>
);
@@ -29,7 +36,7 @@ const IDCell: React.FC = ({ children }) => (
</span>
);
export const getColumns = (collection: SanitizedCollectionConfig): Column[] => [
export const getColumns = (collection: SanitizedCollectionConfig, global: SanitizedGlobalConfig): Column[] => [
{
accessor: 'createdAt',
components: {
@@ -42,6 +49,7 @@ export const getColumns = (collection: SanitizedCollectionConfig): Column[] => [
renderCell: (row, data) => (
<CreatedAtCell
collection={collection}
global={global}
id={row?.id}
date={data}
/>
@@ -53,7 +61,7 @@ export const getColumns = (collection: SanitizedCollectionConfig): Column[] => [
components: {
Heading: (
<SortColumn
label="ID"
label="Revision ID"
disable
name="id"
/>

View File

@@ -1,6 +1,6 @@
@import '../../../../scss/styles';
@import '../../../scss/styles.scss';
.collection-revisions {
.revisions {
width: 100%;
margin-bottom: base(2);
@@ -14,6 +14,10 @@
margin-bottom: $baseline;
}
&__intro {
margin-bottom: base(.5);
}
.table {
table {
width: 100%;

View File

@@ -1,55 +1,85 @@
import { useConfig } from '@payloadcms/config-provider';
import React, { useEffect, useState } from 'react';
import { useRouteMatch } from 'react-router-dom';
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
import Eyebrow from '../../../elements/Eyebrow';
import Loading from '../../../elements/Loading';
import { useStepNav } from '../../../elements/StepNav';
import { StepNavItem } from '../../../elements/StepNav/types';
import Meta from '../../../utilities/Meta';
import usePayloadAPI from '../../../hooks/usePayloadAPI';
import Eyebrow from '../../elements/Eyebrow';
import Loading from '../../elements/Loading';
import { useStepNav } from '../../elements/StepNav';
import { StepNavItem } from '../../elements/StepNav/types';
import Meta from '../../utilities/Meta';
import { Props } from './types';
import IDLabel from '../../../elements/IDLabel';
import IDLabel from '../../elements/IDLabel';
import { getColumns } from './columns';
import Table from '../../../elements/Table';
import Paginator from '../../../elements/Paginator';
import PerPage from '../../../elements/PerPage';
import Table from '../../elements/Table';
import Paginator from '../../elements/Paginator';
import PerPage from '../../elements/PerPage';
import { useSearchParams } from '../../utilities/SearchParams';
import './index.scss';
import { useSearchParams } from '../../../utilities/SearchParams';
const baseClass = 'collection-revisions';
const baseClass = 'revisions';
const Revisions: React.FC<Props> = ({ collection }) => {
const Revisions: React.FC<Props> = ({ collection, global }) => {
const { serverURL, routes: { admin, api } } = useConfig();
const { setStepNav } = useStepNav();
const { params: { id } } = useRouteMatch<{ id: string }>();
const [tableColumns] = useState(() => getColumns(collection));
const [tableColumns] = useState(() => getColumns(collection, global));
const [fetchURL, setFetchURL] = useState('');
const { page, sort, limit } = useSearchParams();
const [{ data: doc }] = usePayloadAPI(`${serverURL}/api/${collection.slug}/${id}`);
let docURL: string;
let entityLabel: string;
let slug: string;
if (collection) {
({ slug } = collection);
docURL = `${serverURL}/api/${slug}/${id}`;
entityLabel = collection.labels.singular;
}
if (global) {
({ slug } = global);
docURL = `${serverURL}/api/globals/${slug}`;
entityLabel = global.label;
}
const useAsTitle = collection?.admin?.useAsTitle || 'id';
const [{ data: doc }] = usePayloadAPI(docURL);
const [{ data: revisionsData, isLoading: isLoadingRevisions }, { setParams }] = usePayloadAPI(fetchURL);
const useAsTitle = collection.admin.useAsTitle || 'id';
useEffect(() => {
const nav: StepNavItem[] = [
{
url: `${admin}/collections/${collection.slug}`,
label: collection.labels.plural,
},
{
label: doc ? doc[useAsTitle] : '',
url: `${admin}/collections/${collection.slug}/${id}`,
},
{
label: 'Revisions',
},
];
let nav: StepNavItem[] = [];
if (collection) {
nav = [
{
url: `${admin}/collections/${collection.slug}`,
label: collection.labels.plural,
},
{
label: doc ? doc[useAsTitle] : '',
url: `${admin}/collections/${collection.slug}/${id}`,
},
{
label: 'Revisions',
},
];
}
if (global) {
nav = [
{
url: `${admin}/globals/${global.slug}`,
label: global.label,
},
{
label: 'Revisions',
},
];
}
setStepNav(nav);
}, [setStepNav, collection, useAsTitle, doc, admin, id]);
}, [setStepNav, collection, global, useAsTitle, doc, admin, id]);
useEffect(() => {
const params = {
@@ -70,29 +100,43 @@ const Revisions: React.FC<Props> = ({ collection }) => {
// Performance enhancement
// Setting the Fetch URL this way
// prevents a double-fetch
setFetchURL(`${serverURL}${api}/${collection.slug}/revisions`);
setFetchURL(`${serverURL}${api}/${slug}/revisions`);
setParams(params);
}, [setParams, page, sort, collection, limit, serverURL, api, id]);
}, [setParams, page, sort, slug, limit, serverURL, api, id]);
const useIDLabel = doc[useAsTitle] === doc?.id;
let heading: string;
let metaDesc: string;
if (collection) {
metaDesc = `Viewing revisions for the ${entityLabel} ${doc[useAsTitle]}`;
heading = doc?.[useAsTitle];
}
if (global) {
metaDesc = `Viewing revisions for the global ${entityLabel}`;
heading = entityLabel;
}
return (
<div className={baseClass}>
<Meta
title={`Revisions - ${doc[useAsTitle]} - ${collection.labels.singular}`}
description={`Viewing revisions for the ${collection.labels.singular} ${doc[useAsTitle]}`}
keywords={`Revisions, ${collection.labels.singular}, Payload, CMS`}
title={`Revisions - ${doc[useAsTitle]} - ${entityLabel}`}
description={metaDesc}
keywords={`Revisions, ${entityLabel}, Payload, CMS`}
/>
<Eyebrow />
<div className={`${baseClass}__wrap`}>
<header className={`${baseClass}__header`}>
Showing revisions for:
<div className={`${baseClass}__intro`}>Showing revisions for:</div>
{useIDLabel && (
<IDLabel id={doc?.id} />
)}
{!useIDLabel && (
<h1>
{doc[useAsTitle]}
{heading}
</h1>
)}
</header>

View File

@@ -0,0 +1,7 @@
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
import { SanitizedGlobalConfig } from '../../../../globals/config/types';
export type Props = {
collection?: SanitizedCollectionConfig
global?: SanitizedGlobalConfig
}

View File

@@ -16,7 +16,7 @@ import fieldTypes from '../../../forms/field-types';
import RenderTitle from '../../../elements/RenderTitle';
import LeaveWithoutSaving from '../../../modals/LeaveWithoutSaving';
import Auth from './Auth';
import Revisions from './Revisions';
import RevisionsCount from '../../../elements/RevisionsCount';
import Upload from './Upload';
import { Props } from './types';
@@ -192,7 +192,7 @@ const DefaultEditView: React.FC<Props> = (props) => {
{revisions && (
<li>
<div className={`${baseClass}__label`}>Revisions</div>
<Revisions
<RevisionsCount
submissionCount={submissionCount}
collection={collection}
id={id}

View File

@@ -1,7 +0,0 @@
import { SanitizedCollectionConfig } from '../../../../../../collections/config/types';
export type Props = {
collection: SanitizedCollectionConfig,
id: string | number
submissionCount: number
}

View File

@@ -1,5 +0,0 @@
import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
export type Props = {
collection: SanitizedCollectionConfig
}

View File

@@ -117,7 +117,12 @@ async function accessOperation(this: Payload, args: Arguments): Promise<Permissi
});
config.globals.forEach((global) => {
executeEntityPolicies(global, ['read', 'update'], 'globals');
const globalOperations = ['read', 'update'];
if (global.revisions) {
globalOperations.push('readRevisions');
}
executeEntityPolicies(global, globalOperations, 'globals');
});
await Promise.all(promises);

View File

@@ -1,5 +1,7 @@
import express from 'express';
import mongoose from 'mongoose';
import paginate from 'mongoose-paginate-v2';
import buildQueryPlugin from '../mongoose/buildQuery';
import buildModel from './buildModel';
import { Payload } from '../index';
import { getRevisionsModelName } from '../revisions/getRevisionsModelName';
@@ -29,6 +31,9 @@ export default function initGlobals(ctx: Payload): void {
},
);
revisionSchema.plugin(paginate, { useEstimatedCount: true })
.plugin(buildQueryPlugin);
ctx.revisions[global.slug] = mongoose.model(revisionModelName, revisionSchema) as GlobalModel;
}
});
@@ -42,6 +47,14 @@ export default function initGlobals(ctx: Payload): void {
.route(`/globals/${global.slug}`)
.get(ctx.requestHandlers.globals.findOne(global))
.post(ctx.requestHandlers.globals.update(global));
if (global.revisions) {
router.route(`/globals/${global.slug}/revisions`)
.get(ctx.requestHandlers.globals.findRevisions(global));
router.route(`/globals/${global.slug}/revisions/:id`)
.get(ctx.requestHandlers.globals.findRevisionByID(global));
}
});
ctx.router.use(router);

View File

@@ -85,10 +85,14 @@ async function findRevisions<T extends TypeWithRevision<T> = any>(args: Argument
// Find
// /////////////////////////////////////
const [sortProperty, sortOrder] = buildSortParam(args.sort, true);
const optionsToExecute = {
page: page || 1,
limit: limit || 10,
sort: buildSortParam(args.sort, true),
sort: {
[sortProperty]: sortOrder,
},
lean: true,
leanWithId: true,
useEstimatedCount,
@@ -104,7 +108,7 @@ async function findRevisions<T extends TypeWithRevision<T> = any>(args: Argument
...paginatedDocs,
docs: await Promise.all(paginatedDocs.docs.map(async (data) => ({
...data,
revision: this.performFieldOperations(
revision: await this.performFieldOperations(
globalConfig,
{
depth,

View File

@@ -3,18 +3,23 @@ import { PayloadRequest } from '../../express/types';
import { Document } from '../../types';
import { SanitizedGlobalConfig } from '../config/types';
export default (globalConfig: SanitizedGlobalConfig) => async function findRevisionByID(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<Document> | void> {
const options = {
req,
globalConfig,
id: req.params.id,
depth: req.query.depth,
};
export default function (globalConfig: SanitizedGlobalConfig) {
async function handler(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<Document> | void> {
const options = {
req,
globalConfig,
id: req.params.id,
depth: req.query.depth,
};
try {
const doc = await this.operations.globals.findRevisionByID(options);
return res.json(doc);
} catch (error) {
return next(error);
try {
const doc = await this.operations.globals.findRevisionByID(options);
return res.json(doc);
} catch (error) {
return next(error);
}
}
};
const findRevisionByIDHandler = handler.bind(this);
return findRevisionByIDHandler;
}

View File

@@ -5,32 +5,38 @@ import { TypeWithID } from '../../collections/config/types';
import { PaginatedDocs } from '../../mongoose/types';
import { SanitizedGlobalConfig } from '../config/types';
export default (global: SanitizedGlobalConfig) => async function findRevisions<T extends TypeWithID = any>(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<PaginatedDocs<T>> | void> {
try {
let page;
export default function (global: SanitizedGlobalConfig) {
async function handler<T extends TypeWithID = any>(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<PaginatedDocs<T>> | void> {
try {
let page;
if (typeof req.query.page === 'string') {
const parsedPage = parseInt(req.query.page, 10);
if (typeof req.query.page === 'string') {
const parsedPage = parseInt(req.query.page, 10);
if (!Number.isNaN(parsedPage)) {
page = parsedPage;
if (!Number.isNaN(parsedPage)) {
page = parsedPage;
}
}
const options = {
req,
globalConfig: global,
where: req.query.where,
page,
limit: req.query.limit,
sort: req.query.sort,
depth: req.query.depth,
};
const result = await this.operations.globals.findRevisions(options);
return res.status(httpStatus.OK).json(result);
} catch (error) {
return next(error);
}
const options = {
req,
globalConfig: global,
where: req.query.where,
page,
limit: req.query.limit,
sort: req.query.sort,
depth: req.query.depth,
};
const result = await this.operations.globals.findRevisions(options);
return res.status(httpStatus.OK).json(result);
} catch (error) {
return next(error);
}
};
const findRevisionsHandler = handler.bind(this);
return findRevisionsHandler;
}

View File

@@ -123,10 +123,16 @@ export default function buildPoliciesType(): GraphQLObjectType {
});
Object.values(this.config.globals).forEach((global: SanitizedGlobalConfig) => {
const globalOperations: OperationType[] = ['read', 'update'];
if (global.revisions) {
globalOperations.push('readRevisions');
}
fields[formatName(global.slug)] = {
type: new GraphQLObjectType({
name: formatName(`${global.label}Access`),
fields: buildEntity(global.label, global.fields, ['read', 'update']),
fields: buildEntity(global.label, global.fields, globalOperations),
}),
};
});