mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-04 02:01:58 -05:00
Added DEL /collections/:id/posts/:post_id to Admin API
refs https://github.com/TryGhost/Team/issues/3260 - We need a way to remove posts form collections without fetching the whole collection's content. This API method allows to remove posts from manual collections by collection id and post id. - As a response it returns up to date collection state without the removed post.
This commit is contained in:
parent
2edaf2c42c
commit
e8220b1387
9 changed files with 238 additions and 1 deletions
|
@ -45,6 +45,12 @@ export class Collection {
|
|||
this.posts.splice(index, 0, post.id);
|
||||
}
|
||||
|
||||
removePost(id: string) {
|
||||
if (this.posts.includes(id)) {
|
||||
this._posts = this.posts.filter(postId => postId !== id);
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(data: any) {
|
||||
this.id = data.id;
|
||||
this.title = data.title;
|
||||
|
|
|
@ -175,4 +175,19 @@ export class CollectionsService {
|
|||
|
||||
return collection;
|
||||
}
|
||||
|
||||
async removePostFromCollection(id: string, postId: string): Promise<CollectionDTO | null> {
|
||||
const collection = await this.getById(id);
|
||||
|
||||
if (!collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (collection) {
|
||||
collection.removePost(postId);
|
||||
await this.collectionsRepository.save(collection);
|
||||
}
|
||||
|
||||
return this.toDTO(collection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,4 +138,22 @@ describe('Collection', function () {
|
|||
assert(collection.posts.length as number === 4);
|
||||
assert(collection.posts[collection.posts.length - 2] === '3');
|
||||
});
|
||||
|
||||
it('Removes a post by id', async function () {
|
||||
const collection = await Collection.create({
|
||||
title: 'Testing adding posts'
|
||||
});
|
||||
|
||||
assert.equal(collection.posts.length, 0);
|
||||
|
||||
collection.addPost({
|
||||
id: '0'
|
||||
});
|
||||
|
||||
assert.equal(collection.posts.length, 1);
|
||||
|
||||
collection.removePost('0');
|
||||
|
||||
assert.equal(collection.posts.length, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,5 +117,34 @@ describe('CollectionsService', function () {
|
|||
assert.equal(editedCollection?.posts[0].id, posts[0].id, 'Collection should have the correct post');
|
||||
assert.equal(editedCollection?.posts[0].sort_order, 0, 'Collection should have the correct post sort order');
|
||||
});
|
||||
|
||||
it('Removes a Post from a Collection', async function () {
|
||||
const collection = await collectionsService.createCollection({
|
||||
title: 'testing collections',
|
||||
description: 'testing collections description',
|
||||
type: 'manual'
|
||||
});
|
||||
|
||||
let editedCollection = await collectionsService.edit({
|
||||
id: collection.id,
|
||||
posts: [{
|
||||
id: posts[0].id
|
||||
}, {
|
||||
id: posts[1].id
|
||||
}]
|
||||
});
|
||||
|
||||
assert.equal(editedCollection?.posts.length, 2, 'Collection should have two posts');
|
||||
|
||||
editedCollection = await collectionsService.removePostFromCollection(collection.id, posts[0].id);
|
||||
|
||||
assert.equal(editedCollection?.posts.length, 1, 'Collection should have one posts');
|
||||
});
|
||||
|
||||
it('Returns null when removing post from non existing collection', async function () {
|
||||
const collection = await collectionsService.removePostFromCollection('i-do-not-exist', posts[0].id);
|
||||
|
||||
assert.equal(collection, null, 'Collection should be null');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -150,5 +150,40 @@ module.exports = {
|
|||
async query(frame) {
|
||||
return await collectionsService.api.destroy(frame.options.id);
|
||||
}
|
||||
},
|
||||
|
||||
destroyPost: {
|
||||
docName: 'collection_posts',
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
cacheInvalidate: true
|
||||
},
|
||||
options: [
|
||||
'id',
|
||||
'post_id'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
id: {
|
||||
required: true
|
||||
},
|
||||
post_id: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
},
|
||||
// @NOTE: should have permissions when moving out of Alpha
|
||||
permissions: false,
|
||||
async query(frame) {
|
||||
const collection = await collectionsService.api.destroyCollectionPost(frame.options.id, frame.options.post_id);
|
||||
|
||||
if (!collection) {
|
||||
throw new errors.NotFoundError({
|
||||
message: tpl(messages.collectionNotFound)
|
||||
});
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,8 @@ class CollectionsServiceWrapper {
|
|||
add: collectionsService.createCollection.bind(collectionsService),
|
||||
edit: collectionsService.edit.bind(collectionsService),
|
||||
addPost: collectionsService.addPostToCollection.bind(collectionsService),
|
||||
destroy: collectionsService.destroy.bind(collectionsService)
|
||||
destroy: collectionsService.destroy.bind(collectionsService),
|
||||
destroyCollectionPost: collectionsService.removePostFromCollection.bind(collectionsService)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ module.exports = function apiRoutes() {
|
|||
router.put('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.edit));
|
||||
router.del('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.destroy));
|
||||
router.post('/collections/:id/posts', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.addPost));
|
||||
router.del('/collections/:id/posts/:post_id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.destroyPost));
|
||||
|
||||
// ## Configuration
|
||||
router.get('/config', mw.authAdminApi, http(api.config.read));
|
||||
|
|
|
@ -610,6 +610,111 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Can remove a Post from a Collection 1: [body] 1`] = `
|
||||
Object {
|
||||
"collections": Array [
|
||||
Object {
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"description": null,
|
||||
"feature_image": null,
|
||||
"filter": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"posts": Array [
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
],
|
||||
"title": "Test Collection Edited",
|
||||
"type": "manual",
|
||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Can remove a Post from a Collection 2: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "440",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Can remove a Post from a Collection 3: [body] 1`] = `
|
||||
Object {
|
||||
"collections": Array [
|
||||
Object {
|
||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
"description": null,
|
||||
"feature_image": null,
|
||||
"filter": null,
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"posts": Array [
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
Object {
|
||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||
"sort_order": Any<Number>,
|
||||
},
|
||||
],
|
||||
"title": "Test Collection Edited",
|
||||
"type": "manual",
|
||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Can remove a Post from a Collection 3: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Version, Origin",
|
||||
"x-cache-invalidate": "/*",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Can remove a Post from a Collection 4: [headers] 1`] = `
|
||||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "391",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||
"x-cache-invalidate": "/*",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Collections API edit Fails to edit unexistent Collection 1: [body] 1`] = `
|
||||
Object {
|
||||
"errors": Array [
|
||||
|
|
|
@ -256,6 +256,33 @@ describe('Collections API', function () {
|
|||
|
||||
assert.equal(readResponse.body.collections[0].posts.length, 4, 'Post should have been added to a Collection');
|
||||
});
|
||||
|
||||
it('Can remove a Post from a Collection', async function () {
|
||||
const collectionId = collectionToEdit.id;
|
||||
const readResponse = await agent
|
||||
.get(`/collections/${collectionId}/`)
|
||||
.expectStatus(200)
|
||||
.matchHeaderSnapshot({
|
||||
'content-version': anyContentVersion,
|
||||
etag: anyEtag
|
||||
})
|
||||
.matchBodySnapshot({
|
||||
collections: [buildMatcher(4, {withSortOrder: true})]
|
||||
});
|
||||
|
||||
const postIdToRemove = readResponse.body.collections[0].posts[0]?.id;
|
||||
|
||||
await agent
|
||||
.delete(`/collections/${collectionId}/posts/${postIdToRemove}`)
|
||||
.expectStatus(200)
|
||||
.matchHeaderSnapshot({
|
||||
'content-version': anyContentVersion,
|
||||
etag: anyEtag
|
||||
})
|
||||
.matchBodySnapshot({
|
||||
collections: [buildMatcher(3, {withSortOrder: true})]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Can delete a Collection', async function () {
|
||||
|
|
Loading…
Add table
Reference in a new issue