feat: adds restore revisions to collections
This commit is contained in:
@@ -106,6 +106,7 @@ export default function registerCollections(ctx: Payload): void {
|
||||
findByID,
|
||||
findRevisions,
|
||||
findRevisionByID,
|
||||
restoreRevision,
|
||||
delete: deleteHandler,
|
||||
} = ctx.requestHandlers.collections;
|
||||
|
||||
@@ -180,7 +181,8 @@ export default function registerCollections(ctx: Payload): void {
|
||||
.get(findRevisions);
|
||||
|
||||
router.route(`/${slug}/revisions/:id`)
|
||||
.get(findRevisionByID);
|
||||
.get(findRevisionByID)
|
||||
.post(restoreRevision);
|
||||
}
|
||||
|
||||
router.route(`/${slug}`)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import httpStatus from 'http-status';
|
||||
import { Payload } from '../../index';
|
||||
import { PayloadRequest } from '../../express/types';
|
||||
import { Collection } from '../config/types';
|
||||
import { Collection, CollectionModel } from '../config/types';
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields';
|
||||
import { Forbidden, NotFound } from '../../errors';
|
||||
import { APIError, Forbidden, NotFound } from '../../errors';
|
||||
import executeAccess from '../../auth/executeAccess';
|
||||
import { Where } from '../../types';
|
||||
import { hasWhereAccessResult } from '../../auth/types';
|
||||
@@ -19,7 +21,7 @@ export type Arguments = {
|
||||
depth?: number
|
||||
}
|
||||
|
||||
async function findRevisionByID<T extends TypeWithRevision<T> = any>(args: Arguments): Promise<T> {
|
||||
async function findRevisionByID<T extends TypeWithRevision<T> = any>(this: Payload, args: Arguments): Promise<T> {
|
||||
const {
|
||||
depth,
|
||||
collection: {
|
||||
@@ -36,7 +38,11 @@ async function findRevisionByID<T extends TypeWithRevision<T> = any>(args: Argum
|
||||
showHiddenFields,
|
||||
} = args;
|
||||
|
||||
const RevisionsModel = this.revisions[collectionConfig.slug];
|
||||
if (!id) {
|
||||
throw new APIError('Missing ID of revision.', httpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const RevisionsModel = (this.revisions[collectionConfig.slug]) as CollectionModel;
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
|
||||
@@ -8,6 +8,7 @@ import flattenWhereConstraints from '../../utilities/flattenWhereConstraints';
|
||||
import { buildSortParam } from '../../mongoose/buildSortParam';
|
||||
import { PaginatedDocs } from '../../mongoose/types';
|
||||
import { TypeWithRevision } from '../../revisions/types';
|
||||
import { Payload } from '../../index';
|
||||
|
||||
export type Arguments = {
|
||||
collection: Collection
|
||||
@@ -21,7 +22,7 @@ export type Arguments = {
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
async function findRevisions<T extends TypeWithRevision<T> = any>(args: Arguments): Promise<PaginatedDocs<T>> {
|
||||
async function findRevisions<T extends TypeWithRevision<T> = any>(this: Payload, args: Arguments): Promise<PaginatedDocs<T>> {
|
||||
const {
|
||||
where,
|
||||
page,
|
||||
|
||||
48
src/collections/operations/local/findRevisionByID.ts
Normal file
48
src/collections/operations/local/findRevisionByID.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Document } from '../../../types';
|
||||
import { PayloadRequest } from '../../../express/types';
|
||||
import { TypeWithRevision } from '../../../revisions/types';
|
||||
|
||||
export type Options = {
|
||||
collection: string
|
||||
id: string
|
||||
depth?: number
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
user?: Document
|
||||
overrideAccess?: boolean
|
||||
showHiddenFields?: boolean
|
||||
disableErrors?: boolean
|
||||
req?: PayloadRequest
|
||||
}
|
||||
|
||||
export default async function findRevisionByID<T extends TypeWithRevision<T> = any>(options: Options): Promise<T> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
depth,
|
||||
id,
|
||||
locale = this?.config?.localization?.defaultLocale,
|
||||
fallbackLocale = null,
|
||||
overrideAccess = true,
|
||||
disableErrors = false,
|
||||
showHiddenFields,
|
||||
req,
|
||||
} = options;
|
||||
|
||||
const collection = this.collections[collectionSlug];
|
||||
|
||||
return this.operations.collections.findRevisionByID({
|
||||
depth,
|
||||
id,
|
||||
collection,
|
||||
overrideAccess,
|
||||
disableErrors,
|
||||
showHiddenFields,
|
||||
req: {
|
||||
...req,
|
||||
payloadAPI: 'local',
|
||||
locale,
|
||||
fallbackLocale,
|
||||
payload: this,
|
||||
},
|
||||
});
|
||||
}
|
||||
53
src/collections/operations/local/findRevisions.ts
Normal file
53
src/collections/operations/local/findRevisions.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Document, Where } from '../../../types';
|
||||
import { PaginatedDocs } from '../../../mongoose/types';
|
||||
import { TypeWithRevision } from '../../../revisions/types';
|
||||
|
||||
export type Options = {
|
||||
collection: string
|
||||
depth?: number
|
||||
page?: number
|
||||
limit?: number
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
user?: Document
|
||||
overrideAccess?: boolean
|
||||
showHiddenFields?: boolean
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export default async function findRevisions<T extends TypeWithRevision<T> = any>(options: Options): Promise<PaginatedDocs<T>> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
depth,
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
locale = this?.config?.localization?.defaultLocale,
|
||||
fallbackLocale = null,
|
||||
user,
|
||||
overrideAccess = true,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
} = options;
|
||||
|
||||
const collection = this.collections[collectionSlug];
|
||||
|
||||
return this.operations.collections.findRevisions({
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
depth,
|
||||
collection,
|
||||
sort,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
req: {
|
||||
user,
|
||||
payloadAPI: 'local',
|
||||
locale,
|
||||
fallbackLocale,
|
||||
payload: this,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import create from './create';
|
||||
import update from './update';
|
||||
import localDelete from './delete';
|
||||
import auth from '../../../auth/operations/local';
|
||||
import findRevisionByID from './findRevisionByID';
|
||||
import findRevisions from './findRevisions';
|
||||
import restoreRevision from './restoreRevision';
|
||||
|
||||
export default {
|
||||
find,
|
||||
@@ -12,4 +15,7 @@ export default {
|
||||
update,
|
||||
localDelete,
|
||||
auth,
|
||||
findRevisionByID,
|
||||
findRevisions,
|
||||
restoreRevision,
|
||||
};
|
||||
|
||||
48
src/collections/operations/local/restoreRevision.ts
Normal file
48
src/collections/operations/local/restoreRevision.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Document } from '../../../types';
|
||||
import { TypeWithRevision } from '../../../revisions/types';
|
||||
|
||||
export type Options = {
|
||||
collection: string
|
||||
id: string
|
||||
data: Record<string, unknown>
|
||||
depth?: number
|
||||
locale?: string
|
||||
fallbackLocale?: string
|
||||
user?: Document
|
||||
overrideAccess?: boolean
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export default async function restoreRevision<T extends TypeWithRevision<T> = any>(options: Options): Promise<T> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
depth,
|
||||
locale = this?.config?.localization?.defaultLocale,
|
||||
fallbackLocale = null,
|
||||
data,
|
||||
id,
|
||||
user,
|
||||
overrideAccess = true,
|
||||
showHiddenFields,
|
||||
} = options;
|
||||
|
||||
const collection = this.collections[collectionSlug];
|
||||
|
||||
const args = {
|
||||
depth,
|
||||
data,
|
||||
collection,
|
||||
overrideAccess,
|
||||
id,
|
||||
showHiddenFields,
|
||||
req: {
|
||||
user,
|
||||
payloadAPI: 'local',
|
||||
locale,
|
||||
fallbackLocale,
|
||||
payload: this,
|
||||
},
|
||||
};
|
||||
|
||||
return this.operations.collections.restoreRevision(args);
|
||||
}
|
||||
51
src/collections/operations/restoreRevision.ts
Normal file
51
src/collections/operations/restoreRevision.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import httpStatus from 'http-status';
|
||||
import { PayloadRequest } from '../../express/types';
|
||||
import { Collection } from '../config/types';
|
||||
import { APIError } from '../../errors';
|
||||
import { TypeWithRevision } from '../../revisions/types';
|
||||
import { Payload } from '../../index';
|
||||
|
||||
export type Arguments = {
|
||||
collection: Collection
|
||||
id: string
|
||||
req: PayloadRequest
|
||||
disableErrors?: boolean
|
||||
currentDepth?: number
|
||||
overrideAccess?: boolean
|
||||
showHiddenFields?: boolean
|
||||
depth?: number
|
||||
}
|
||||
|
||||
async function restoreRevision<T extends TypeWithRevision<T> = any>(this: Payload, args: Arguments): Promise<T> {
|
||||
const {
|
||||
collection,
|
||||
id,
|
||||
// overrideAccess = false,
|
||||
} = args;
|
||||
|
||||
if (!id) {
|
||||
throw new APIError('Missing ID of revision to restore.', httpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Retrieve revision
|
||||
// /////////////////////////////////////
|
||||
|
||||
const revision = await this.findRevisionByID({
|
||||
...args,
|
||||
collection: collection.config.slug,
|
||||
});
|
||||
|
||||
const result = await this.update({
|
||||
...args,
|
||||
id: revision.parent,
|
||||
collection: collection.config.slug,
|
||||
data: revision.revision,
|
||||
locale: args.req.locale,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default restoreRevision;
|
||||
29
src/collections/requestHandlers/restoreRevision.ts
Normal file
29
src/collections/requestHandlers/restoreRevision.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Response, NextFunction } from 'express';
|
||||
import httpStatus from 'http-status';
|
||||
import { PayloadRequest } from '../../express/types';
|
||||
import { Document } from '../../types';
|
||||
import formatSuccessResponse from '../../express/responses/formatSuccess';
|
||||
|
||||
export type RestoreResult = {
|
||||
message: string
|
||||
doc: Document
|
||||
};
|
||||
|
||||
export default async function restoreRevision(req: PayloadRequest, res: Response, next: NextFunction): Promise<Response<RestoreResult> | void> {
|
||||
const options = {
|
||||
req,
|
||||
collection: req.collection,
|
||||
id: req.params.id,
|
||||
depth: req.query.depth,
|
||||
};
|
||||
|
||||
try {
|
||||
const doc = await this.operations.collections.restoreRevision(options);
|
||||
return res.status(httpStatus.OK).json({
|
||||
...formatSuccessResponse('Restored successfully.', 'message'),
|
||||
doc,
|
||||
});
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ export default function initGlobals(ctx: Payload): void {
|
||||
.get(ctx.requestHandlers.globals.findRevisions(global));
|
||||
|
||||
router.route(`/globals/${global.slug}/revisions/:id`)
|
||||
.get(ctx.requestHandlers.globals.findRevisionByID(global));
|
||||
.get(ctx.requestHandlers.globals.findRevisionByID(global))
|
||||
.post(ctx.requestHandlers.globals.restoreRevision(global));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
162
src/globals/operations/restoreRevision.ts
Normal file
162
src/globals/operations/restoreRevision.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Where } from '../../types';
|
||||
import { PayloadRequest } from '../../express/types';
|
||||
import executeAccess from '../../auth/executeAccess';
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields';
|
||||
import { PaginatedDocs } from '../../mongoose/types';
|
||||
import { hasWhereAccessResult } from '../../auth/types';
|
||||
import flattenWhereConstraints from '../../utilities/flattenWhereConstraints';
|
||||
import { buildSortParam } from '../../mongoose/buildSortParam';
|
||||
import { TypeWithRevision } from '../../revisions/types';
|
||||
import { SanitizedGlobalConfig } from '../config/types';
|
||||
|
||||
export type Arguments = {
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
where?: Where
|
||||
page?: number
|
||||
limit?: number
|
||||
sort?: string
|
||||
depth?: number
|
||||
req?: PayloadRequest
|
||||
overrideAccess?: boolean
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
// TODO: finish
|
||||
|
||||
async function restoreRevision<T extends TypeWithRevision<T> = any>(args: Arguments): Promise<PaginatedDocs<T>> {
|
||||
const {
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
depth,
|
||||
globalConfig,
|
||||
req,
|
||||
req: {
|
||||
locale,
|
||||
},
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
} = args;
|
||||
|
||||
const RevisionsModel = this.revisions[globalConfig.slug];
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
// /////////////////////////////////////
|
||||
|
||||
const queryToBuild: { where?: Where} = {};
|
||||
let useEstimatedCount = false;
|
||||
|
||||
if (where) {
|
||||
let and = [];
|
||||
|
||||
if (Array.isArray(where.and)) and = where.and;
|
||||
if (Array.isArray(where.AND)) and = where.AND;
|
||||
|
||||
queryToBuild.where = {
|
||||
...where,
|
||||
and: [
|
||||
...and,
|
||||
],
|
||||
};
|
||||
|
||||
const constraints = flattenWhereConstraints(queryToBuild);
|
||||
|
||||
useEstimatedCount = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'));
|
||||
}
|
||||
|
||||
if (!overrideAccess) {
|
||||
const accessResults = await executeAccess({ req }, globalConfig.access.readRevisions);
|
||||
|
||||
if (hasWhereAccessResult(accessResults)) {
|
||||
if (!where) {
|
||||
queryToBuild.where = {
|
||||
and: [
|
||||
accessResults,
|
||||
],
|
||||
};
|
||||
} else {
|
||||
(queryToBuild.where.and as Where[]).push(accessResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const query = await RevisionsModel.buildQuery(queryToBuild, locale);
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Find
|
||||
// /////////////////////////////////////
|
||||
|
||||
const [sortProperty, sortOrder] = buildSortParam(args.sort, true);
|
||||
|
||||
const optionsToExecute = {
|
||||
page: page || 1,
|
||||
limit: limit || 10,
|
||||
sort: {
|
||||
[sortProperty]: sortOrder,
|
||||
},
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
useEstimatedCount,
|
||||
};
|
||||
|
||||
const paginatedDocs = await RevisionsModel.paginate(query, optionsToExecute);
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
// /////////////////////////////////////
|
||||
|
||||
let result = {
|
||||
...paginatedDocs,
|
||||
docs: await Promise.all(paginatedDocs.docs.map(async (data) => ({
|
||||
...data,
|
||||
revision: await this.performFieldOperations(
|
||||
globalConfig,
|
||||
{
|
||||
depth,
|
||||
data: data.revision,
|
||||
req,
|
||||
id: data.revision.id,
|
||||
hook: 'afterRead',
|
||||
operation: 'read',
|
||||
overrideAccess,
|
||||
flattenLocales: true,
|
||||
showHiddenFields,
|
||||
isRevision: true,
|
||||
},
|
||||
),
|
||||
}))),
|
||||
};
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = {
|
||||
...result,
|
||||
docs: await Promise.all(result.docs.map(async (doc) => {
|
||||
const docRef = doc;
|
||||
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
docRef.revision = await hook({ req, query, doc: doc.revision }) || doc.revision;
|
||||
}, Promise.resolve());
|
||||
|
||||
return docRef;
|
||||
})),
|
||||
};
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = {
|
||||
...result,
|
||||
docs: result.docs.map((doc) => sanitizeInternalFields<T>(doc)),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default restoreRevision;
|
||||
25
src/globals/requestHandlers/restoreRevision.ts
Normal file
25
src/globals/requestHandlers/restoreRevision.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Response, NextFunction } from 'express';
|
||||
import { PayloadRequest } from '../../express/types';
|
||||
import { Document } from '../../types';
|
||||
import { SanitizedGlobalConfig } from '../config/types';
|
||||
|
||||
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.restoreRevision(options);
|
||||
return res.json(doc);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
const restoreRevisionHandler = handler.bind(this);
|
||||
return restoreRevisionHandler;
|
||||
}
|
||||
49
src/index.ts
49
src/index.ts
@@ -9,10 +9,11 @@ import {
|
||||
EmailOptions,
|
||||
InitOptions,
|
||||
} from './config/types';
|
||||
import { TypeWithRevision } from './revisions/types';
|
||||
import { PaginatedDocs } from './mongoose/types';
|
||||
|
||||
import Logger from './utilities/logger';
|
||||
import bindOperations from './init/bindOperations';
|
||||
import bindOperations, { Operations } from './init/bindOperations';
|
||||
import bindRequestHandlers, { RequestHandlers } from './init/bindRequestHandlers';
|
||||
import loadConfig from './config/load';
|
||||
import authenticate, { PayloadAuthenticate } from './express/middleware/authenticate';
|
||||
@@ -45,6 +46,9 @@ import { Options as FindOptions } from './collections/operations/local/find';
|
||||
import { Options as FindByIDOptions } from './collections/operations/local/findByID';
|
||||
import { Options as UpdateOptions } from './collections/operations/local/update';
|
||||
import { Options as DeleteOptions } from './collections/operations/local/delete';
|
||||
import { Options as FindRevisionsOptions } from './collections/operations/local/findRevisions';
|
||||
import { Options as FindRevisionByIDOptions } from './collections/operations/local/findRevisionByID';
|
||||
import { Options as RestoreRevisionOptions } from './collections/operations/local/restoreRevision';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -94,7 +98,7 @@ export class Payload {
|
||||
|
||||
decrypt = decrypt;
|
||||
|
||||
operations: { [key: string]: any };
|
||||
operations: Operations;
|
||||
|
||||
errorHandler: ErrorHandler;
|
||||
|
||||
@@ -269,6 +273,47 @@ export class Payload {
|
||||
return deleteOperation<T>(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Find revisions with criteria
|
||||
* @param options
|
||||
* @returns revisions satisfying query
|
||||
*/
|
||||
findRevisions = async <T extends TypeWithRevision<T> = any>(options: FindRevisionsOptions): Promise<PaginatedDocs<T>> => {
|
||||
let { findRevisions } = localOperations;
|
||||
findRevisions = findRevisions.bind(this);
|
||||
return findRevisions<T>(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Find revision by ID
|
||||
* @param options
|
||||
* @returns revision with specified ID
|
||||
*/
|
||||
findRevisionByID = async <T extends TypeWithRevision<T> = any>(options: FindRevisionByIDOptions): Promise<T> => {
|
||||
let { findRevisionByID } = localOperations;
|
||||
findRevisionByID = findRevisionByID.bind(this);
|
||||
return findRevisionByID(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Restore revision by ID
|
||||
* @param options
|
||||
* @returns revision with specified ID
|
||||
*/
|
||||
restoreRevision = async <T extends TypeWithRevision<T> = any>(options: RestoreRevisionOptions): Promise<T> => {
|
||||
let { restoreRevision } = localOperations;
|
||||
restoreRevision = restoreRevision.bind(this);
|
||||
return restoreRevision(options);
|
||||
}
|
||||
|
||||
// TODO: globals
|
||||
// findRevisionGlobal
|
||||
// findRevisionByIDGlobal
|
||||
// restoreRevisionGlobal
|
||||
// TODO:
|
||||
// graphql operations & request handlers, where
|
||||
// tests
|
||||
|
||||
login = async (options): Promise<any> => {
|
||||
let { login } = localOperations.auth;
|
||||
login = login.bind(this);
|
||||
|
||||
@@ -16,18 +16,58 @@ import find from '../collections/operations/find';
|
||||
import findByID from '../collections/operations/findByID';
|
||||
import findRevisions from '../collections/operations/findRevisions';
|
||||
import findRevisionByID from '../collections/operations/findRevisionByID';
|
||||
import restoreRevision from '../collections/operations/restoreRevision';
|
||||
import update from '../collections/operations/update';
|
||||
import deleteHandler from '../collections/operations/delete';
|
||||
|
||||
import findOne from '../globals/operations/findOne';
|
||||
import findGlobalRevisions from '../globals/operations/findRevisions';
|
||||
import findGlobalRevisionByID from '../globals/operations/findRevisionByID';
|
||||
import restoreGlobalRevision from '../globals/operations/restoreRevision';
|
||||
import globalUpdate from '../globals/operations/update';
|
||||
|
||||
import preferenceUpdate from '../preferences/operations/update';
|
||||
import preferenceFindOne from '../preferences/operations/findOne';
|
||||
import preferenceDelete from '../preferences/operations/delete';
|
||||
|
||||
export type Operations = {
|
||||
collections: {
|
||||
create: typeof create
|
||||
find: typeof find
|
||||
findByID: typeof findByID
|
||||
findRevisions: typeof findRevisions
|
||||
findRevisionByID: typeof findRevisionByID
|
||||
restoreRevision: typeof restoreRevision
|
||||
update: typeof update
|
||||
delete: typeof deleteHandler
|
||||
auth: {
|
||||
access: typeof access
|
||||
forgotPassword: typeof forgotPassword
|
||||
init: typeof init
|
||||
login: typeof login
|
||||
logout: typeof logout
|
||||
me: typeof me
|
||||
refresh: typeof refresh
|
||||
registerFirstUser: typeof registerFirstUser
|
||||
resetPassword: typeof resetPassword
|
||||
verifyEmail: typeof verifyEmail
|
||||
unlock: typeof unlock
|
||||
}
|
||||
}
|
||||
globals: {
|
||||
findOne: typeof findOne
|
||||
findRevisions: typeof findGlobalRevisions
|
||||
findRevisionByID: typeof findGlobalRevisionByID
|
||||
restoreRevision: typeof restoreGlobalRevision
|
||||
update: typeof globalUpdate
|
||||
}
|
||||
preferences: {
|
||||
update: typeof preferenceUpdate
|
||||
findOne: typeof preferenceFindOne
|
||||
delete: typeof preferenceDelete
|
||||
}
|
||||
}
|
||||
|
||||
function bindOperations(ctx: Payload): void {
|
||||
ctx.operations = {
|
||||
collections: {
|
||||
@@ -36,6 +76,7 @@ function bindOperations(ctx: Payload): void {
|
||||
findByID: findByID.bind(ctx),
|
||||
findRevisions: findRevisions.bind(ctx),
|
||||
findRevisionByID: findRevisionByID.bind(ctx),
|
||||
restoreRevision: restoreRevision.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
@@ -56,6 +97,7 @@ function bindOperations(ctx: Payload): void {
|
||||
findOne: findOne.bind(ctx),
|
||||
findRevisions: findGlobalRevisions.bind(ctx),
|
||||
findRevisionByID: findGlobalRevisionByID.bind(ctx),
|
||||
restoreRevision: restoreGlobalRevision.bind(ctx),
|
||||
update: globalUpdate.bind(ctx),
|
||||
},
|
||||
preferences: {
|
||||
|
||||
@@ -15,12 +15,14 @@ import find from '../collections/requestHandlers/find';
|
||||
import findByID from '../collections/requestHandlers/findByID';
|
||||
import findRevisions from '../collections/requestHandlers/findRevisions';
|
||||
import findRevisionByID from '../collections/requestHandlers/findRevisionByID';
|
||||
import restoreRevision from '../collections/requestHandlers/restoreRevision';
|
||||
import update from '../collections/requestHandlers/update';
|
||||
import deleteHandler from '../collections/requestHandlers/delete';
|
||||
|
||||
import findOne from '../globals/requestHandlers/findOne';
|
||||
import findGlobalRevisions from '../globals/requestHandlers/findRevisions';
|
||||
import findGlobalRevisionByID from '../globals/requestHandlers/findRevisionByID';
|
||||
import restoreGlobalRevision from '../globals/requestHandlers/restoreRevision';
|
||||
import globalUpdate from '../globals/requestHandlers/update';
|
||||
import { Payload } from '../index';
|
||||
import preferenceUpdate from '../preferences/requestHandlers/update';
|
||||
@@ -34,6 +36,7 @@ export type RequestHandlers = {
|
||||
findByID: typeof findByID,
|
||||
findRevisions: typeof findRevisions,
|
||||
findRevisionByID: typeof findRevisionByID,
|
||||
restoreRevision: typeof restoreRevision,
|
||||
update: typeof update,
|
||||
delete: typeof deleteHandler,
|
||||
auth: {
|
||||
@@ -55,6 +58,7 @@ export type RequestHandlers = {
|
||||
update: typeof globalUpdate,
|
||||
findRevisions: typeof findGlobalRevisions
|
||||
findRevisionByID: typeof findGlobalRevisionByID
|
||||
restoreRevision: typeof restoreGlobalRevision
|
||||
},
|
||||
preferences: {
|
||||
update: typeof preferenceUpdate,
|
||||
@@ -71,6 +75,7 @@ function bindRequestHandlers(ctx: Payload): void {
|
||||
findByID: findByID.bind(ctx),
|
||||
findRevisions: findRevisions.bind(ctx),
|
||||
findRevisionByID: findRevisionByID.bind(ctx),
|
||||
restoreRevision: restoreRevision.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
@@ -92,6 +97,7 @@ function bindRequestHandlers(ctx: Payload): void {
|
||||
update: globalUpdate.bind(ctx),
|
||||
findRevisions: findGlobalRevisions.bind(ctx),
|
||||
findRevisionByID: findGlobalRevisionByID.bind(ctx),
|
||||
restoreRevision: restoreGlobalRevision.bind(ctx),
|
||||
},
|
||||
preferences: {
|
||||
update: preferenceUpdate.bind(ctx),
|
||||
|
||||
@@ -116,4 +116,40 @@ describe('Revisions - REST', () => {
|
||||
expect(revisions.docs[0].revision.title.es).toStrictEqual(spanishTitle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Restore', () => {
|
||||
it('should allow a revision to be restored', async () => {
|
||||
const title2 = 'Here is an updated post title in EN';
|
||||
|
||||
const updatedPost = await fetch(`${url}/api/localized-posts/${postID}`, {
|
||||
body: JSON.stringify({
|
||||
title: title2,
|
||||
}),
|
||||
headers,
|
||||
method: 'put',
|
||||
}).then((res) => res.json());
|
||||
|
||||
expect(updatedPost.doc.title).toBe(title2);
|
||||
|
||||
const revisions = await fetch(`${url}/api/localized-posts/revisions`, {
|
||||
headers,
|
||||
}).then((res) => res.json());
|
||||
|
||||
revisionID = revisions.docs[0].id;
|
||||
|
||||
const restore = await fetch(`${url}/api/localized-posts/revisions/${revisionID}`, {
|
||||
headers,
|
||||
method: 'post',
|
||||
}).then((res) => res.json());
|
||||
|
||||
expect(restore.message).toBeDefined();
|
||||
expect(restore.doc.title).toBeDefined();
|
||||
|
||||
const restoredPost = await fetch(`${url}/api/localized-posts/${postID}`, {
|
||||
headers,
|
||||
}).then((res) => res.json());
|
||||
|
||||
expect(restoredPost.title).toBe(restore.doc.title);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user