Merge branch 'feat/graphql-versions' of github.com:payloadcms/payload into feat/graphql-versions
This commit is contained in:
@@ -11,9 +11,11 @@ import formatName from '../../graphql/utilities/formatName';
|
||||
import buildPaginatedListType from '../../graphql/schema/buildPaginatedListType';
|
||||
import { BaseFields } from './types';
|
||||
import { getCollectionIDType } from '../../graphql/schema/buildMutationInputType';
|
||||
import buildVersionWhereInputType from '../../graphql/schema/buildVersionWhereInputType';
|
||||
|
||||
function registerCollections(): void {
|
||||
const {
|
||||
findVersions, findVersionByID, restoreVersion,
|
||||
create, find, findByID, deleteResolver, update,
|
||||
} = this.graphQL.resolvers.collections;
|
||||
|
||||
@@ -179,6 +181,42 @@ function registerCollections(): void {
|
||||
resolve: deleteResolver(collection),
|
||||
};
|
||||
|
||||
if (collection.config.versions) {
|
||||
collection.graphQL.versionType = this.buildVersionType(collection.graphQL.type, collection.config.versions);
|
||||
this.Query.fields[`version${formatName(singularLabel)}`] = {
|
||||
type: collection.graphQL.versionType,
|
||||
args: {
|
||||
id: { type: GraphQLString },
|
||||
...(this.config.localization ? {
|
||||
locale: { type: this.types.localeInputType },
|
||||
fallbackLocale: { type: this.types.fallbackLocaleInputType },
|
||||
} : {}),
|
||||
},
|
||||
resolve: findVersionByID(collection),
|
||||
};
|
||||
this.Query.fields[`versions${pluralLabel}`] = {
|
||||
type: buildPaginatedListType(`versions${formatName(pluralLabel)}`, collection.graphQL.versionType),
|
||||
args: {
|
||||
where: { type: buildVersionWhereInputType(singularLabel, collection.config) },
|
||||
...(this.config.localization ? {
|
||||
locale: { type: this.types.localeInputType },
|
||||
fallbackLocale: { type: this.types.fallbackLocaleInputType },
|
||||
} : {}),
|
||||
page: { type: GraphQLInt },
|
||||
limit: { type: GraphQLInt },
|
||||
sort: { type: GraphQLString },
|
||||
},
|
||||
resolve: findVersions(collection),
|
||||
};
|
||||
this.Mutation.fields[`restoreVersion${formatName(singularLabel)}`] = {
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
args: {
|
||||
id: { type: GraphQLString },
|
||||
},
|
||||
resolve: restoreVersion(collection),
|
||||
};
|
||||
}
|
||||
|
||||
if (collection.config.auth) {
|
||||
collection.graphQL.JWT = this.buildObjectType(
|
||||
formatName(`${slug}JWT`),
|
||||
|
||||
37
src/collections/graphql/resolvers/findVersionByID.ts
Normal file
37
src/collections/graphql/resolvers/findVersionByID.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { Response } from 'express';
|
||||
import { Collection } from '../../config/types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
},
|
||||
context: {
|
||||
req: PayloadRequest,
|
||||
res: Response
|
||||
}
|
||||
) => Promise<Document>
|
||||
|
||||
export default function findVersionByID(collection: Collection): Resolver {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale;
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale;
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
id: args.id,
|
||||
req: context.req,
|
||||
draft: args.draft,
|
||||
};
|
||||
|
||||
const result = await this.operations.collections.findVersionByID(options);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const findVersionByIDResolver = resolver.bind(this);
|
||||
return findVersionByIDResolver;
|
||||
}
|
||||
45
src/collections/graphql/resolvers/findVersions.ts
Normal file
45
src/collections/graphql/resolvers/findVersions.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import { Response } from 'express';
|
||||
import { Where } from '../../../types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { Collection } from '../../config/types';
|
||||
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
where: Where
|
||||
limit?: number
|
||||
page?: number
|
||||
sort?: string
|
||||
},
|
||||
context: {
|
||||
req: PayloadRequest,
|
||||
res: Response
|
||||
}
|
||||
) => Promise<Document>
|
||||
|
||||
export default function findVersions(collection: Collection): Resolver {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale;
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale;
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
where: args.where,
|
||||
limit: args.limit,
|
||||
page: args.page,
|
||||
sort: args.sort,
|
||||
req: context.req,
|
||||
};
|
||||
|
||||
const result = await this.operations.collections.findVersions(options);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const findVersionsResolver = resolver.bind(this);
|
||||
return findVersionsResolver;
|
||||
}
|
||||
35
src/collections/graphql/resolvers/restoreVersion.ts
Normal file
35
src/collections/graphql/resolvers/restoreVersion.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { Response } from 'express';
|
||||
import { Collection } from '../../config/types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
},
|
||||
context: {
|
||||
req: PayloadRequest,
|
||||
res: Response
|
||||
}
|
||||
) => Promise<Document>
|
||||
|
||||
export default function restoreVersion(collection: Collection): Resolver {
|
||||
async function resolver(_, args, context) {
|
||||
if (args.locale) context.req.locale = args.locale;
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale;
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
id: args.id,
|
||||
req: context.req,
|
||||
};
|
||||
|
||||
await this.operations.collections.restoreVersion(options);
|
||||
return true;
|
||||
}
|
||||
|
||||
const restoreVersionResolver = resolver.bind(this);
|
||||
return restoreVersionResolver;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export default function registerCollections(ctx: Payload): void {
|
||||
findByID,
|
||||
findVersions,
|
||||
findVersionByID,
|
||||
publishVersion,
|
||||
restoreVersion,
|
||||
delete: deleteHandler,
|
||||
} = ctx.requestHandlers.collections;
|
||||
|
||||
@@ -182,7 +182,7 @@ export default function registerCollections(ctx: Payload): void {
|
||||
|
||||
router.route(`/${slug}/versions/:id`)
|
||||
.get(findVersionByID)
|
||||
.post(publishVersion);
|
||||
.post(restoreVersion);
|
||||
}
|
||||
|
||||
router.route(`/${slug}`)
|
||||
|
||||
@@ -6,7 +6,7 @@ import localDelete from './delete';
|
||||
import auth from '../../../auth/operations/local';
|
||||
import findVersionByID from './findVersionByID';
|
||||
import findVersions from './findVersions';
|
||||
import publishVersion from './publishVersion';
|
||||
import restoreVersion from './restoreVersion';
|
||||
|
||||
export default {
|
||||
find,
|
||||
@@ -17,5 +17,5 @@ export default {
|
||||
auth,
|
||||
findVersionByID,
|
||||
findVersions,
|
||||
publishVersion,
|
||||
restoreVersion,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ export type Options = {
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export default async function publishVersion<T extends TypeWithVersion<T> = any>(options: Options): Promise<T> {
|
||||
export default async function restoreVersion<T extends TypeWithVersion<T> = any>(options: Options): Promise<T> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
depth,
|
||||
@@ -44,5 +44,5 @@ export default async function publishVersion<T extends TypeWithVersion<T> = any>
|
||||
},
|
||||
};
|
||||
|
||||
return this.operations.collections.publishVersion(args);
|
||||
return this.operations.collections.restoreVersion(args);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export type Arguments = {
|
||||
depth?: number
|
||||
}
|
||||
|
||||
async function publishVersion<T extends TypeWithID = any>(this: Payload, args: Arguments): Promise<T> {
|
||||
async function restoreVersion<T extends TypeWithID = any>(this: Payload, args: Arguments): Promise<T> {
|
||||
const {
|
||||
collection: {
|
||||
Model,
|
||||
@@ -171,4 +171,4 @@ async function publishVersion<T extends TypeWithID = any>(this: Payload, args: A
|
||||
return result;
|
||||
}
|
||||
|
||||
export default publishVersion;
|
||||
export default restoreVersion;
|
||||
@@ -9,7 +9,7 @@ export type RestoreResult = {
|
||||
doc: Document
|
||||
};
|
||||
|
||||
export default async function publishVersion(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<RestoreResult> | void> {
|
||||
export default async function restoreVersion(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<RestoreResult> | void> {
|
||||
const options = {
|
||||
req,
|
||||
collection: req.collection,
|
||||
@@ -18,7 +18,7 @@ export default async function publishVersion(req: PayloadRequest, res: Response,
|
||||
};
|
||||
|
||||
try {
|
||||
const doc = await this.operations.collections.publishVersion(options);
|
||||
const doc = await this.operations.collections.restoreVersion(options);
|
||||
return res.status(httpStatus.OK).json({
|
||||
...formatSuccessResponse('Restored successfully.', 'message'),
|
||||
doc,
|
||||
@@ -54,7 +54,7 @@ export default function initGlobals(ctx: Payload): void {
|
||||
|
||||
router.route(`/globals/${global.slug}/versions/:id`)
|
||||
.get(ctx.requestHandlers.globals.findVersionByID(global))
|
||||
.post(ctx.requestHandlers.globals.publishVersion(global));
|
||||
.post(ctx.requestHandlers.globals.restoreVersion(global));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export type Arguments = {
|
||||
|
||||
// TODO: finish
|
||||
|
||||
async function publishVersion<T extends TypeWithVersion<T> = any>(this: Payload, args: Arguments): Promise<PaginatedDocs<T>> {
|
||||
async function restoreVersion<T extends TypeWithVersion<T> = any>(this: Payload, args: Arguments): Promise<PaginatedDocs<T>> {
|
||||
const { globals: { Model } } = this;
|
||||
|
||||
const {
|
||||
@@ -143,4 +143,4 @@ async function publishVersion<T extends TypeWithVersion<T> = any>(this: Payload,
|
||||
return result;
|
||||
}
|
||||
|
||||
export default publishVersion;
|
||||
export default restoreVersion;
|
||||
@@ -15,7 +15,7 @@ export default function (globalConfig: SanitizedGlobalConfig) {
|
||||
};
|
||||
|
||||
try {
|
||||
const doc = await this.operations.globals.publishVersion(options);
|
||||
const doc = await this.operations.globals.restoreVersion(options);
|
||||
return res.status(httpStatus.OK).json({
|
||||
...formatSuccessResponse('Restored successfully.', 'message'),
|
||||
doc,
|
||||
@@ -25,6 +25,6 @@ export default function (globalConfig: SanitizedGlobalConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
const publishVersionHandler = handler.bind(this);
|
||||
return publishVersionHandler;
|
||||
const restoreVersionHandler = handler.bind(this);
|
||||
return restoreVersionHandler;
|
||||
}
|
||||
@@ -14,6 +14,9 @@ import find from '../collections/graphql/resolvers/find';
|
||||
import findByID from '../collections/graphql/resolvers/findByID';
|
||||
import update from '../collections/graphql/resolvers/update';
|
||||
import deleteResolver from '../collections/graphql/resolvers/delete';
|
||||
import findVersions from '../collections/graphql/resolvers/findVersions';
|
||||
import findVersionByID from '../collections/graphql/resolvers/findVersionByID';
|
||||
import restoreVersion from '../collections/graphql/resolvers/restoreVersion';
|
||||
|
||||
import findOne from '../globals/graphql/resolvers/findOne';
|
||||
import globalUpdate from '../globals/graphql/resolvers/update';
|
||||
@@ -24,7 +27,10 @@ export type GraphQLResolvers = {
|
||||
collections: {
|
||||
create: typeof create,
|
||||
find: typeof find,
|
||||
findVersions: typeof findVersions,
|
||||
findByID: typeof findByID,
|
||||
findVersionByID: typeof findVersionByID,
|
||||
restoreVersion: typeof restoreVersion,
|
||||
update: typeof update,
|
||||
deleteResolver: typeof deleteResolver,
|
||||
auth: {
|
||||
@@ -52,7 +58,10 @@ function bindResolvers(ctx: Payload): void {
|
||||
collections: {
|
||||
create: create.bind(ctx),
|
||||
find: find.bind(ctx),
|
||||
findVersions: findVersions.bind(ctx),
|
||||
findByID: findByID.bind(ctx),
|
||||
findVersionByID: findVersionByID.bind(ctx),
|
||||
restoreVersion: restoreVersion.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
deleteResolver: deleteResolver.bind(ctx),
|
||||
auth: {
|
||||
|
||||
@@ -13,6 +13,7 @@ import initCollections from '../collections/graphql/init';
|
||||
import initGlobals from '../globals/graphql/init';
|
||||
import initPreferences from '../preferences/graphql/init';
|
||||
import { GraphQLResolvers } from './bindResolvers';
|
||||
import buildVersionType from './schema/buildVersionType';
|
||||
import buildWhereInputType from './schema/buildWhereInputType';
|
||||
import { SanitizedConfig } from '../config/types';
|
||||
|
||||
@@ -46,6 +47,8 @@ class InitializeGraphQL {
|
||||
|
||||
buildPoliciesType: typeof buildPoliciesType;
|
||||
|
||||
buildVersionType: typeof buildVersionType;
|
||||
|
||||
initCollections: typeof initCollections;
|
||||
|
||||
initGlobals: typeof initGlobals;
|
||||
@@ -90,6 +93,7 @@ class InitializeGraphQL {
|
||||
this.buildWhereInputType = buildWhereInputType;
|
||||
this.buildObjectType = buildObjectType.bind(this);
|
||||
this.buildPoliciesType = buildPoliciesType.bind(this);
|
||||
this.buildVersionType = buildVersionType.bind(this);
|
||||
this.initCollections = initCollections.bind(this);
|
||||
this.initGlobals = initGlobals.bind(this);
|
||||
this.initPreferences = initPreferences.bind(this);
|
||||
|
||||
27
src/graphql/schema/buildVersionType.ts
Normal file
27
src/graphql/schema/buildVersionType.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLNonNull,
|
||||
GraphQLObjectType,
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
import { DateTimeResolver } from 'graphql-scalars';
|
||||
import formatName from '../utilities/formatName';
|
||||
import { SanitizedCollectionVersions } from '../../versions/types';
|
||||
|
||||
const buildVersionType = (type: GraphQLObjectType, versionsConfig: SanitizedCollectionVersions): GraphQLObjectType => {
|
||||
const autosave = (versionsConfig.drafts && versionsConfig.drafts?.autosave && { autosave: { type: GraphQLBoolean } });
|
||||
|
||||
return new GraphQLObjectType({
|
||||
name: formatName(`${type.name}Version`),
|
||||
fields: {
|
||||
id: { type: GraphQLString },
|
||||
parent: { type: GraphQLString },
|
||||
version: { type },
|
||||
updatedAt: { type: new GraphQLNonNull(DateTimeResolver) },
|
||||
createdAt: { type: new GraphQLNonNull(DateTimeResolver) },
|
||||
...autosave,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default buildVersionType;
|
||||
47
src/graphql/schema/buildVersionWhereInputType.ts
Normal file
47
src/graphql/schema/buildVersionWhereInputType.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
import { DateTimeResolver } from 'graphql-scalars';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
import formatName from '../utilities/formatName';
|
||||
import withOperators from './withOperators';
|
||||
import { FieldAffectingData } from '../../fields/config/types';
|
||||
import withWhereAndOr from './withWhereAndOr';
|
||||
import operators from './operators';
|
||||
import { SanitizedCollectionConfig } from '../../collections/config/types';
|
||||
import recursivelyBuildNestedPaths from './recursivelyBuildNestedPaths';
|
||||
|
||||
const buildVersionWhereInputType = (singularLabel: string, parentCollection: SanitizedCollectionConfig): GraphQLInputObjectType => {
|
||||
const name = `version${formatName(singularLabel)}`;
|
||||
const fieldTypes = {
|
||||
id: { type: GraphQLString },
|
||||
autosave: { type: GraphQLBoolean },
|
||||
// TODO: test with custom id field types, may need to support number
|
||||
updatedAt: { type: DateTimeResolver },
|
||||
createdAt: { type: DateTimeResolver },
|
||||
parent: {
|
||||
type: withOperators(
|
||||
{ name: 'parent' } as FieldAffectingData,
|
||||
GraphQLJSON,
|
||||
name,
|
||||
[...operators.equality, ...operators.contains],
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const versionFields = recursivelyBuildNestedPaths(name, {
|
||||
name: 'version',
|
||||
type: 'group',
|
||||
fields: parentCollection.fields,
|
||||
});
|
||||
|
||||
versionFields.forEach((versionField) => {
|
||||
fieldTypes[versionField.key] = versionField.type;
|
||||
});
|
||||
|
||||
return withWhereAndOr(name, fieldTypes);
|
||||
};
|
||||
|
||||
export default buildVersionWhereInputType;
|
||||
@@ -1,46 +1,22 @@
|
||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
/* eslint-disable no-use-before-define */
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLEnumType,
|
||||
GraphQLFloat,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInt,
|
||||
GraphQLList,
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
|
||||
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars';
|
||||
import {
|
||||
optionIsObject,
|
||||
ArrayField,
|
||||
CheckboxField,
|
||||
CodeField,
|
||||
DateField,
|
||||
EmailField,
|
||||
Field,
|
||||
FieldWithSubFields,
|
||||
GroupField,
|
||||
NumberField,
|
||||
RadioField,
|
||||
RelationshipField,
|
||||
RichTextField,
|
||||
RowField,
|
||||
SelectField,
|
||||
TextareaField,
|
||||
TextField,
|
||||
UploadField,
|
||||
PointField,
|
||||
FieldAffectingData,
|
||||
fieldAffectsData,
|
||||
fieldHasSubFields,
|
||||
fieldIsPresentationalOnly,
|
||||
} from '../../fields/config/types';
|
||||
import formatName from '../utilities/formatName';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
import withOperators from './withOperators';
|
||||
import operators from './operators';
|
||||
import withWhereAndOr from './withWhereAndOr';
|
||||
import fieldToSchemaMap from './fieldToSchemaMap';
|
||||
|
||||
// buildWhereInputType is similar to buildObjectType and operates
|
||||
// on a field basis with a few distinct differences.
|
||||
@@ -52,281 +28,10 @@ import withOperators from './withOperators';
|
||||
const buildWhereInputType = (name: string, fields: Field[], parentName: string): GraphQLInputObjectType => {
|
||||
// This is the function that builds nested paths for all
|
||||
// field types with nested paths.
|
||||
const recursivelyBuildNestedPaths = (field: FieldWithSubFields & FieldAffectingData) => {
|
||||
const nestedPaths = field.fields.reduce((nestedFields, nestedField) => {
|
||||
if (!fieldIsPresentationalOnly(nestedField)) {
|
||||
const getFieldSchema = fieldToSchemaMap[nestedField.type];
|
||||
const nestedFieldName = fieldAffectsData(nestedField) ? `${field.name}__${nestedField.name}` : undefined;
|
||||
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema({
|
||||
...nestedField,
|
||||
name: nestedFieldName,
|
||||
});
|
||||
|
||||
if (Array.isArray(fieldSchema)) {
|
||||
return [
|
||||
...nestedFields,
|
||||
...fieldSchema,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
...nestedFields,
|
||||
{
|
||||
key: nestedFieldName,
|
||||
type: fieldSchema,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return nestedFields;
|
||||
}, []);
|
||||
|
||||
return nestedPaths;
|
||||
};
|
||||
|
||||
const operators = {
|
||||
equality: ['equals', 'not_equals'],
|
||||
contains: ['in', 'not_in', 'all'],
|
||||
comparison: ['greater_than_equal', 'greater_than', 'less_than_equal', 'less_than'],
|
||||
geo: ['near'],
|
||||
};
|
||||
|
||||
const fieldToSchemaMap = {
|
||||
number: (field: NumberField) => {
|
||||
const type = GraphQLFloat;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison],
|
||||
),
|
||||
};
|
||||
},
|
||||
text: (field: TextField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
email: (field: EmailField) => {
|
||||
const type = EmailAddressResolver;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
textarea: (field: TextareaField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
richText: (field: RichTextField) => {
|
||||
const type = GraphQLJSON;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
code: (field: CodeField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
radio: (field: RadioField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Input`,
|
||||
values: field.options.reduce((values, option) => {
|
||||
if (optionIsObject(option)) {
|
||||
return {
|
||||
...values,
|
||||
[formatName(option.value)]: {
|
||||
value: option.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...values,
|
||||
[formatName(option)]: {
|
||||
value: option,
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
}),
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
}),
|
||||
date: (field: DateField) => {
|
||||
const type = DateTimeResolver;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
point: (field: PointField) => {
|
||||
const type = GraphQLList(GraphQLFloat);
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison, ...operators.geo],
|
||||
),
|
||||
};
|
||||
},
|
||||
relationship: (field: RelationshipField) => {
|
||||
let type = withOperators(
|
||||
field,
|
||||
GraphQLString,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.contains],
|
||||
);
|
||||
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
type = new GraphQLInputObjectType({
|
||||
name: `${combineParentName(parentName, field.name)}_Relation`,
|
||||
fields: {
|
||||
relationTo: {
|
||||
type: new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Relation_RelationTo`,
|
||||
values: field.relationTo.reduce((values, relation) => ({
|
||||
...values,
|
||||
[formatName(relation)]: {
|
||||
value: relation,
|
||||
},
|
||||
}), {}),
|
||||
}),
|
||||
},
|
||||
value: { type: GraphQLString },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
return {
|
||||
type: new GraphQLList(type),
|
||||
};
|
||||
}
|
||||
|
||||
return { type };
|
||||
},
|
||||
upload: (field: UploadField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
GraphQLString,
|
||||
parentName,
|
||||
[...operators.equality],
|
||||
),
|
||||
}),
|
||||
checkbox: (field: CheckboxField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
GraphQLBoolean,
|
||||
parentName,
|
||||
[...operators.equality],
|
||||
),
|
||||
}),
|
||||
select: (field: SelectField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Input`,
|
||||
values: field.options.reduce((values, option) => {
|
||||
if (typeof option === 'object' && option.value) {
|
||||
return {
|
||||
...values,
|
||||
[formatName(option.value)]: {
|
||||
value: option.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof option === 'string') {
|
||||
return {
|
||||
...values,
|
||||
[option]: {
|
||||
value: option,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return values;
|
||||
}, {}),
|
||||
}),
|
||||
parentName,
|
||||
[...operators.equality, ...operators.contains],
|
||||
),
|
||||
}),
|
||||
array: (field: ArrayField) => recursivelyBuildNestedPaths(field),
|
||||
group: (field: GroupField) => recursivelyBuildNestedPaths(field),
|
||||
row: (field: RowField) => field.fields.reduce((rowSchema, rowField) => {
|
||||
const getFieldSchema = fieldToSchemaMap[rowField.type];
|
||||
|
||||
if (getFieldSchema) {
|
||||
const rowFieldSchema = getFieldSchema(rowField);
|
||||
|
||||
if (fieldHasSubFields(rowField)) {
|
||||
return [
|
||||
...rowSchema,
|
||||
...rowFieldSchema,
|
||||
];
|
||||
}
|
||||
|
||||
if (fieldAffectsData(rowField)) {
|
||||
return [
|
||||
...rowSchema,
|
||||
{
|
||||
key: rowField.name,
|
||||
type: rowFieldSchema,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rowSchema;
|
||||
}, []),
|
||||
};
|
||||
|
||||
const fieldTypes = fields.reduce((schema, field) => {
|
||||
if (!fieldIsPresentationalOnly(field) && !field.hidden) {
|
||||
const getFieldSchema = fieldToSchemaMap[field.type];
|
||||
const getFieldSchema = fieldToSchemaMap(parentName)[field.type];
|
||||
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema(field);
|
||||
@@ -362,31 +67,7 @@ const buildWhereInputType = (name: string, fields: Field[], parentName: string):
|
||||
|
||||
const fieldName = formatName(name);
|
||||
|
||||
return new GraphQLInputObjectType({
|
||||
name: `${fieldName}_where`,
|
||||
fields: {
|
||||
...fieldTypes,
|
||||
OR: {
|
||||
type: new GraphQLList(new GraphQLInputObjectType({
|
||||
name: `${fieldName}_where_or`,
|
||||
fields: {
|
||||
...fieldTypes,
|
||||
},
|
||||
})),
|
||||
},
|
||||
AND: {
|
||||
type: new GraphQLList(new GraphQLInputObjectType({
|
||||
name: `${fieldName}_where_and`,
|
||||
fields: {
|
||||
...fieldTypes,
|
||||
},
|
||||
})),
|
||||
},
|
||||
page: { type: GraphQLInt },
|
||||
limit: { type: GraphQLInt },
|
||||
sort: { type: GraphQLString },
|
||||
},
|
||||
});
|
||||
return withWhereAndOr(fieldName, fieldTypes);
|
||||
};
|
||||
|
||||
export default buildWhereInputType;
|
||||
|
||||
258
src/graphql/schema/fieldToSchemaMap.ts
Normal file
258
src/graphql/schema/fieldToSchemaMap.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLEnumType,
|
||||
GraphQLFloat,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLList,
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
import {
|
||||
ArrayField,
|
||||
CheckboxField,
|
||||
CodeField, DateField,
|
||||
EmailField, fieldAffectsData, fieldHasSubFields, GroupField,
|
||||
NumberField, optionIsObject, PointField,
|
||||
RadioField, RelationshipField,
|
||||
RichTextField, RowField, SelectField,
|
||||
TextareaField,
|
||||
TextField, UploadField,
|
||||
} from '../../fields/config/types';
|
||||
import withOperators from './withOperators';
|
||||
import operators from './operators';
|
||||
import combineParentName from '../utilities/combineParentName';
|
||||
import formatName from '../utilities/formatName';
|
||||
import recursivelyBuildNestedPaths from './recursivelyBuildNestedPaths';
|
||||
|
||||
const fieldToSchemaMap: (parentName: string) => any = (parentName: string) => ({
|
||||
number: (field: NumberField) => {
|
||||
const type = GraphQLFloat;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison],
|
||||
),
|
||||
};
|
||||
},
|
||||
text: (field: TextField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
email: (field: EmailField) => {
|
||||
const type = EmailAddressResolver;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
textarea: (field: TextareaField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
richText: (field: RichTextField) => {
|
||||
const type = GraphQLJSON;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
code: (field: CodeField) => {
|
||||
const type = GraphQLString;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
radio: (field: RadioField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Input`,
|
||||
values: field.options.reduce((values, option) => {
|
||||
if (optionIsObject(option)) {
|
||||
return {
|
||||
...values,
|
||||
[formatName(option.value)]: {
|
||||
value: option.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...values,
|
||||
[formatName(option)]: {
|
||||
value: option,
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
}),
|
||||
parentName,
|
||||
[...operators.equality, 'like'],
|
||||
),
|
||||
}),
|
||||
date: (field: DateField) => {
|
||||
const type = DateTimeResolver;
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison, 'like'],
|
||||
),
|
||||
};
|
||||
},
|
||||
point: (field: PointField) => {
|
||||
const type = GraphQLList(GraphQLFloat);
|
||||
return {
|
||||
type: withOperators(
|
||||
field,
|
||||
type,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.comparison, ...operators.geo],
|
||||
),
|
||||
};
|
||||
},
|
||||
relationship: (field: RelationshipField) => {
|
||||
let type = withOperators(
|
||||
field,
|
||||
GraphQLString,
|
||||
parentName,
|
||||
[...operators.equality, ...operators.contains],
|
||||
);
|
||||
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
type = new GraphQLInputObjectType({
|
||||
name: `${combineParentName(parentName, field.name)}_Relation`,
|
||||
fields: {
|
||||
relationTo: {
|
||||
type: new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Relation_RelationTo`,
|
||||
values: field.relationTo.reduce((values, relation) => ({
|
||||
...values,
|
||||
[formatName(relation)]: {
|
||||
value: relation,
|
||||
},
|
||||
}), {}),
|
||||
}),
|
||||
},
|
||||
value: { type: GraphQLString },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
return {
|
||||
type: new GraphQLList(type),
|
||||
};
|
||||
}
|
||||
|
||||
return { type };
|
||||
},
|
||||
upload: (field: UploadField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
GraphQLString,
|
||||
parentName,
|
||||
[...operators.equality],
|
||||
),
|
||||
}),
|
||||
checkbox: (field: CheckboxField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
GraphQLBoolean,
|
||||
parentName,
|
||||
[...operators.equality],
|
||||
),
|
||||
}),
|
||||
select: (field: SelectField) => ({
|
||||
type: withOperators(
|
||||
field,
|
||||
new GraphQLEnumType({
|
||||
name: `${combineParentName(parentName, field.name)}_Input`,
|
||||
values: field.options.reduce((values, option) => {
|
||||
if (typeof option === 'object' && option.value) {
|
||||
return {
|
||||
...values,
|
||||
[formatName(option.value)]: {
|
||||
value: option.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof option === 'string') {
|
||||
return {
|
||||
...values,
|
||||
[option]: {
|
||||
value: option,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return values;
|
||||
}, {}),
|
||||
}),
|
||||
parentName,
|
||||
[...operators.equality, ...operators.contains],
|
||||
),
|
||||
}),
|
||||
array: (field: ArrayField) => recursivelyBuildNestedPaths(parentName, field),
|
||||
group: (field: GroupField) => recursivelyBuildNestedPaths(parentName, field),
|
||||
row: (field: RowField) => field.fields.reduce((rowSchema, rowField) => {
|
||||
const getFieldSchema = fieldToSchemaMap(parentName)[rowField.type];
|
||||
|
||||
if (getFieldSchema) {
|
||||
const rowFieldSchema = getFieldSchema(rowField);
|
||||
|
||||
if (fieldHasSubFields(rowField)) {
|
||||
return [
|
||||
...rowSchema,
|
||||
...rowFieldSchema,
|
||||
];
|
||||
}
|
||||
|
||||
if (fieldAffectsData(rowField)) {
|
||||
return [
|
||||
...rowSchema,
|
||||
{
|
||||
key: rowField.name,
|
||||
type: rowFieldSchema,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rowSchema;
|
||||
}, []),
|
||||
});
|
||||
|
||||
export default fieldToSchemaMap;
|
||||
8
src/graphql/schema/operators.ts
Normal file
8
src/graphql/schema/operators.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
const operators = {
|
||||
equality: ['equals', 'not_equals'],
|
||||
contains: ['in', 'not_in', 'all'],
|
||||
comparison: ['greater_than_equal', 'greater_than', 'less_than_equal', 'less_than'],
|
||||
geo: ['near'],
|
||||
};
|
||||
|
||||
export default operators;
|
||||
44
src/graphql/schema/recursivelyBuildNestedPaths.ts
Normal file
44
src/graphql/schema/recursivelyBuildNestedPaths.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
FieldAffectingData,
|
||||
fieldAffectsData,
|
||||
fieldIsPresentationalOnly,
|
||||
FieldWithSubFields,
|
||||
} from '../../fields/config/types';
|
||||
import fieldToSchemaMap from './fieldToSchemaMap';
|
||||
|
||||
const recursivelyBuildNestedPaths = (parentName: string, field: FieldWithSubFields & FieldAffectingData) => {
|
||||
const nestedPaths = field.fields.reduce((nestedFields, nestedField) => {
|
||||
if (!fieldIsPresentationalOnly(nestedField)) {
|
||||
const getFieldSchema = fieldToSchemaMap(parentName)[nestedField.type];
|
||||
const nestedFieldName = fieldAffectsData(nestedField) ? `${field.name}__${nestedField.name}` : undefined;
|
||||
|
||||
if (getFieldSchema) {
|
||||
const fieldSchema = getFieldSchema({
|
||||
...nestedField,
|
||||
name: nestedFieldName,
|
||||
});
|
||||
|
||||
if (Array.isArray(fieldSchema)) {
|
||||
return [
|
||||
...nestedFields,
|
||||
...fieldSchema,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
...nestedFields,
|
||||
{
|
||||
key: nestedFieldName,
|
||||
type: fieldSchema,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return nestedFields;
|
||||
}, []);
|
||||
|
||||
return nestedPaths;
|
||||
};
|
||||
|
||||
export default recursivelyBuildNestedPaths;
|
||||
29
src/graphql/schema/withWhereAndOr.ts
Normal file
29
src/graphql/schema/withWhereAndOr.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
GraphQLInputFieldConfigMap,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLList,
|
||||
Thunk,
|
||||
} from 'graphql';
|
||||
|
||||
const withWhereAndOr = (name: string, fieldTypes: Thunk<GraphQLInputFieldConfigMap>): GraphQLInputObjectType => {
|
||||
return new GraphQLInputObjectType({
|
||||
name: `${name}_where`,
|
||||
fields: {
|
||||
...fieldTypes,
|
||||
OR: {
|
||||
type: new GraphQLList(new GraphQLInputObjectType({
|
||||
name: `${name}_where_or`,
|
||||
fields: fieldTypes,
|
||||
})),
|
||||
},
|
||||
AND: {
|
||||
type: new GraphQLList(new GraphQLInputObjectType({
|
||||
name: `${name}_where_and`,
|
||||
fields: fieldTypes,
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default withWhereAndOr;
|
||||
17
src/index.ts
17
src/index.ts
@@ -48,7 +48,7 @@ import { Options as UpdateOptions } from './collections/operations/local/update'
|
||||
import { Options as DeleteOptions } from './collections/operations/local/delete';
|
||||
import { Options as FindVersionsOptions } from './collections/operations/local/findVersions';
|
||||
import { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID';
|
||||
import { Options as RestoreVersionOptions } from './collections/operations/local/publishVersion';
|
||||
import { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion';
|
||||
import { Result } from './auth/operations/login';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
@@ -301,19 +301,14 @@ export class Payload {
|
||||
* @param options
|
||||
* @returns version with specified ID
|
||||
*/
|
||||
publishVersion = async <T extends TypeWithVersion<T> = any>(options: RestoreVersionOptions): Promise<T> => {
|
||||
let { publishVersion } = localOperations;
|
||||
publishVersion = publishVersion.bind(this);
|
||||
return publishVersion(options);
|
||||
restoreVersion = async <T extends TypeWithVersion<T> = any>(options: RestoreVersionOptions): Promise<T> => {
|
||||
let { restoreVersion } = localOperations;
|
||||
restoreVersion = restoreVersion.bind(this);
|
||||
return restoreVersion(options);
|
||||
}
|
||||
|
||||
// TODO: globals
|
||||
// findVersionGlobal
|
||||
// findVersionByIDGlobal
|
||||
// publishVersionGlobal
|
||||
// TODO:
|
||||
// graphql operations & request handlers, where
|
||||
// tests
|
||||
// graphql Global Versions
|
||||
|
||||
login = async <T extends TypeWithID = any>(options): Promise<Result & { user: T}> => {
|
||||
let { login } = localOperations.auth;
|
||||
|
||||
@@ -16,14 +16,14 @@ import find from '../collections/operations/find';
|
||||
import findByID from '../collections/operations/findByID';
|
||||
import findVersions from '../collections/operations/findVersions';
|
||||
import findVersionByID from '../collections/operations/findVersionByID';
|
||||
import publishVersion from '../collections/operations/publishVersion';
|
||||
import restoreVersion from '../collections/operations/restoreVersion';
|
||||
import update from '../collections/operations/update';
|
||||
import deleteHandler from '../collections/operations/delete';
|
||||
|
||||
import findOne from '../globals/operations/findOne';
|
||||
import findGlobalVersions from '../globals/operations/findVersions';
|
||||
import findGlobalVersionByID from '../globals/operations/findVersionByID';
|
||||
import publishGlobalVersion from '../globals/operations/publishVersion';
|
||||
import restoreGlobalVersion from '../globals/operations/restoreVersion';
|
||||
import globalUpdate from '../globals/operations/update';
|
||||
|
||||
import preferenceUpdate from '../preferences/operations/update';
|
||||
@@ -37,7 +37,7 @@ export type Operations = {
|
||||
findByID: typeof findByID
|
||||
findVersions: typeof findVersions
|
||||
findVersionByID: typeof findVersionByID
|
||||
publishVersion: typeof publishVersion
|
||||
restoreVersion: typeof restoreVersion
|
||||
update: typeof update
|
||||
delete: typeof deleteHandler
|
||||
auth: {
|
||||
@@ -58,7 +58,7 @@ export type Operations = {
|
||||
findOne: typeof findOne
|
||||
findVersions: typeof findGlobalVersions
|
||||
findVersionByID: typeof findGlobalVersionByID
|
||||
publishVersion: typeof publishGlobalVersion
|
||||
restoreVersion: typeof restoreGlobalVersion
|
||||
update: typeof globalUpdate
|
||||
}
|
||||
preferences: {
|
||||
@@ -76,7 +76,7 @@ function bindOperations(ctx: Payload): void {
|
||||
findByID: findByID.bind(ctx),
|
||||
findVersions: findVersions.bind(ctx),
|
||||
findVersionByID: findVersionByID.bind(ctx),
|
||||
publishVersion: publishVersion.bind(ctx),
|
||||
restoreVersion: restoreVersion.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
@@ -97,7 +97,7 @@ function bindOperations(ctx: Payload): void {
|
||||
findOne: findOne.bind(ctx),
|
||||
findVersions: findGlobalVersions.bind(ctx),
|
||||
findVersionByID: findGlobalVersionByID.bind(ctx),
|
||||
publishVersion: publishGlobalVersion.bind(ctx),
|
||||
restoreVersion: restoreGlobalVersion.bind(ctx),
|
||||
update: globalUpdate.bind(ctx),
|
||||
},
|
||||
preferences: {
|
||||
|
||||
@@ -15,14 +15,14 @@ import find from '../collections/requestHandlers/find';
|
||||
import findByID from '../collections/requestHandlers/findByID';
|
||||
import findVersions from '../collections/requestHandlers/findVersions';
|
||||
import findVersionByID from '../collections/requestHandlers/findVersionByID';
|
||||
import publishVersion from '../collections/requestHandlers/publishVersion';
|
||||
import restoreVersion from '../collections/requestHandlers/restoreVersion';
|
||||
import update from '../collections/requestHandlers/update';
|
||||
import deleteHandler from '../collections/requestHandlers/delete';
|
||||
|
||||
import findOne from '../globals/requestHandlers/findOne';
|
||||
import findGlobalVersions from '../globals/requestHandlers/findVersions';
|
||||
import findGlobalVersionByID from '../globals/requestHandlers/findVersionByID';
|
||||
import publishGlobalVersion from '../globals/requestHandlers/publishVersion';
|
||||
import restoreGlobalVersion from '../globals/requestHandlers/restoreVersion';
|
||||
import globalUpdate from '../globals/requestHandlers/update';
|
||||
import { Payload } from '../index';
|
||||
import preferenceUpdate from '../preferences/requestHandlers/update';
|
||||
@@ -36,7 +36,7 @@ export type RequestHandlers = {
|
||||
findByID: typeof findByID,
|
||||
findVersions: typeof findVersions
|
||||
findVersionByID: typeof findVersionByID,
|
||||
publishVersion: typeof publishVersion,
|
||||
restoreVersion: typeof restoreVersion,
|
||||
update: typeof update,
|
||||
delete: typeof deleteHandler,
|
||||
auth: {
|
||||
@@ -58,7 +58,7 @@ export type RequestHandlers = {
|
||||
update: typeof globalUpdate,
|
||||
findVersions: typeof findGlobalVersions
|
||||
findVersionByID: typeof findGlobalVersionByID
|
||||
publishVersion: typeof publishGlobalVersion
|
||||
restoreVersion: typeof restoreGlobalVersion
|
||||
},
|
||||
preferences: {
|
||||
update: typeof preferenceUpdate,
|
||||
@@ -75,7 +75,7 @@ function bindRequestHandlers(ctx: Payload): void {
|
||||
findByID: findByID.bind(ctx),
|
||||
findVersions: findVersions.bind(ctx),
|
||||
findVersionByID: findVersionByID.bind(ctx),
|
||||
publishVersion: publishVersion.bind(ctx),
|
||||
restoreVersion: restoreVersion.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
@@ -97,7 +97,7 @@ function bindRequestHandlers(ctx: Payload): void {
|
||||
update: globalUpdate.bind(ctx),
|
||||
findVersions: findGlobalVersions.bind(ctx),
|
||||
findVersionByID: findGlobalVersionByID.bind(ctx),
|
||||
publishVersion: publishGlobalVersion.bind(ctx),
|
||||
restoreVersion: restoreGlobalVersion.bind(ctx),
|
||||
},
|
||||
preferences: {
|
||||
update: preferenceUpdate.bind(ctx),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Document as MongooseDocument } from 'mongoose';
|
||||
import { TypeWithID, TypeWithTimestamps } from '../collections/config/types';
|
||||
import { TypeWithTimestamps } from '../collections/config/types';
|
||||
import { FileData } from '../uploads/types';
|
||||
|
||||
export type Operator = 'equals'
|
||||
|
||||
@@ -58,14 +58,16 @@ describe('Collection Versions - REST', () => {
|
||||
method: 'put',
|
||||
}).then((res) => res.json());
|
||||
|
||||
expect(updatedPost.doc.title).toBe(title2);
|
||||
expect(updatedPost.doc._status).toStrictEqual('draft');
|
||||
|
||||
const versions = await fetch(`${url}/api/autosave-posts/versions`, {
|
||||
headers,
|
||||
}).then((res) => res.json());
|
||||
|
||||
versionID = versions.docs[0].id;
|
||||
|
||||
expect(updatedPost.doc.title).toBe(title2);
|
||||
expect(updatedPost.doc._status).toStrictEqual('draft');
|
||||
|
||||
expect(versionID).toBeDefined();
|
||||
});
|
||||
|
||||
it('should allow a version to be retrieved by ID', async () => {
|
||||
|
||||
161
src/versions/tests/graphql.spec.ts
Normal file
161
src/versions/tests/graphql.spec.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
import { request, GraphQLClient } from 'graphql-request';
|
||||
import getConfig from '../../config/load';
|
||||
import { email, password } from '../../mongoose/testCredentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
const url = `${config.serverURL}${config.routes.api}${config.routes.graphQL}`;
|
||||
|
||||
let client;
|
||||
let token;
|
||||
let postID;
|
||||
let versionID;
|
||||
|
||||
describe('GrahpQL Version Resolvers', () => {
|
||||
const title = 'autosave title';
|
||||
|
||||
beforeAll(async (done) => {
|
||||
const login = `
|
||||
mutation {
|
||||
loginAdmin(
|
||||
email: "${email}",
|
||||
password: "${password}"
|
||||
) {
|
||||
token
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await request(url, login);
|
||||
|
||||
token = response.loginAdmin.token;
|
||||
|
||||
client = new GraphQLClient(url, { headers: { Authorization: `JWT ${token}` } });
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
describe('Create', () => {
|
||||
it('should allow a new autosavePost to be created with draft status', async () => {
|
||||
const description = 'autosave description';
|
||||
|
||||
const query = `mutation {
|
||||
createAutosavePost(data: {title: "${title}", description: "${description}"}) {
|
||||
id
|
||||
title
|
||||
description
|
||||
createdAt
|
||||
updatedAt
|
||||
_status
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await client.request(query);
|
||||
|
||||
const data = response.createAutosavePost;
|
||||
postID = data.id;
|
||||
|
||||
expect(data._status).toStrictEqual('draft');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', () => {
|
||||
const updatedTitle = 'updated title';
|
||||
|
||||
beforeAll(async (done) => {
|
||||
// modify the post to create a new version
|
||||
// language=graphQL
|
||||
const update = `mutation {
|
||||
updateAutosavePost(id: "${postID}", data: {title: "${updatedTitle}"}) {
|
||||
title
|
||||
}
|
||||
}`;
|
||||
await client.request(update);
|
||||
|
||||
// language=graphQL
|
||||
const query = `query {
|
||||
versionsAutosavePosts(where: { parent: { equals: "${postID}" } }) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await client.request(query);
|
||||
|
||||
versionID = response.versionsAutosavePosts.docs[0].id;
|
||||
done();
|
||||
});
|
||||
|
||||
it('should allow read of versions by version id', async () => {
|
||||
const query = `query {
|
||||
versionAutosavePost(id: "${versionID}") {
|
||||
id
|
||||
parent
|
||||
version {
|
||||
title
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await client.request(query);
|
||||
|
||||
const data = response.versionAutosavePost;
|
||||
versionID = data.id;
|
||||
|
||||
expect(data.id).toBeDefined();
|
||||
expect(data.parent).toStrictEqual(postID);
|
||||
expect(data.version.title).toStrictEqual(title);
|
||||
});
|
||||
|
||||
it('should allow read of versions by querying version content', async () => {
|
||||
// language=graphQL
|
||||
const query = `query {
|
||||
versionsAutosavePosts(where: { version__title: {equals: "${title}" } }) {
|
||||
docs {
|
||||
id
|
||||
parent
|
||||
version {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await client.request(query);
|
||||
|
||||
const data = response.versionsAutosavePosts;
|
||||
const doc = data.docs[0];
|
||||
versionID = doc.id;
|
||||
|
||||
expect(doc.id).toBeDefined();
|
||||
expect(doc.parent).toStrictEqual(postID);
|
||||
expect(doc.version.title).toStrictEqual(title);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Restore', () => {
|
||||
it('should allow a version to be restored', async () => {
|
||||
// update a versionsPost
|
||||
const restore = `mutation {
|
||||
restoreVersionAutosavePost(id: "${versionID}")
|
||||
}`;
|
||||
|
||||
await client.request(restore);
|
||||
|
||||
const query = `query {
|
||||
AutosavePost(id: "${postID}") {
|
||||
title
|
||||
}
|
||||
}`;
|
||||
|
||||
const response = await client.request(query);
|
||||
const data = response.AutosavePost;
|
||||
expect(data.title).toStrictEqual(title);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user