diff --git a/ghost/core/core/server/services/collections/PostsRepository.js b/ghost/core/core/server/services/collections/PostsRepository.js index 60e5cb12b2..6a5185afe8 100644 --- a/ghost/core/core/server/services/collections/PostsRepository.js +++ b/ghost/core/core/server/services/collections/PostsRepository.js @@ -1,16 +1,44 @@ class PostsRepository { - constructor({models, browsePostsAPI}) { + constructor({models, browsePostsAPI, moment}) { this.models = models; this.browsePostsAPI = browsePostsAPI; + this.moment = moment; + } + + /** + * @NOTE: This is a copy of the date serialization from Posts Content API. + * We need the dates serialized instead of keeping them as plain dates + * to be able to apply NQL filtering inside of Collections. + * @param {Object} attrs + * @returns + */ + serializeDates(attrs) { + const formatDate = (date) => { + return this.moment(date) + .toISOString(true); + }; + + ['created_at', 'updated_at', 'published_at'].forEach((field) => { + if (attrs[field]) { + attrs[field] = formatDate(attrs[field]); + } + }); + + return attrs; } async getAll({filter}) { - const posts = await this.models.Post.findAll({ - // @NOTE: enforce "post" type to avoid ever fetching pages - filter: `(${filter})+type:post` + const response = await this.browsePostsAPI({ + options: { + filter: `(${filter})+type:post`, + limit: 'all' + } }); - return posts.toJSON(); + response.posts = response.posts + .map(this.serializeDates.bind(this)); + + return response.posts; } async getBulk(ids) { @@ -20,6 +48,9 @@ class PostsRepository { } }); + response.posts = response.posts + .map(this.serializeDates.bind(this)); + return response.posts; } } @@ -27,6 +58,7 @@ class PostsRepository { module.exports = PostsRepository; module.exports.getInstance = () => { + const moment = require('moment-timezone'); const models = require('../../models'); const browsePostsAPI = async (options) => { const rawPosts = await require('../../api/').endpoints.posts.browse.query(options); @@ -35,5 +67,5 @@ module.exports.getInstance = () => { return options.response; }; - return new PostsRepository({models, browsePostsAPI}); + return new PostsRepository({models, browsePostsAPI, moment}); }; diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap index d1a039dba2..c87c765bd6 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap @@ -104,48 +104,6 @@ Object { } `; -exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a relational tag filter 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": "Test Collection Description with relational tag filter", - "feature_image": null, - "filter": "tag:kitchen-sink", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "posts": Array [ - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": 0, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": 1, - }, - ], - "title": "Test Collection with relational tag filter", - "type": "automatic", - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - }, - ], -} -`; - -exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a relational tag filter 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": "431", - "content-type": "application/json; charset=utf-8", - "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/collections\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Version, Origin, Accept-Encoding", - "x-cache-invalidate": "/*", - "x-powered-by": "Express", -} -`; - exports[`Collections API Browse Can browse Collections 1: [body] 1`] = ` Object { "collections": Array [ @@ -284,7 +242,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a77", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "We've crammed the most important information to help you get started with Ghost into this one post. It's your cheat-sheet to get started, and your shortcut to advanced features.", "custom_template": null, "email_only": false, @@ -303,7 +261,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 2, "slug": "welcome", "status": "published", @@ -311,7 +269,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/welcome/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -321,7 +279,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a76", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "How to tweak a few settings in Ghost to transform your site from a generic template to a custom brand with style and personality.", "custom_template": null, "email_only": false, @@ -352,7 +310,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 3, "slug": "design", "status": "published", @@ -360,7 +318,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/design/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -370,7 +328,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a75", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "A full overview of all the features built into the Ghost editor, including powerful workflow automations to speed up your creative process.", "custom_template": null, "email_only": false, @@ -389,7 +347,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 5, "slug": "write", "status": "published", @@ -397,7 +355,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/write/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -407,7 +365,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a74", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "How Ghost allows you to turn anonymous readers into an audience of active subscribers, so you know what's working and what isn't.", "custom_template": null, "email_only": false, @@ -426,7 +384,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 2, "slug": "portal", "status": "published", @@ -434,7 +392,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/portal/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -444,7 +402,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a73", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": null, "custom_template": null, "email_only": false, @@ -469,7 +427,7 @@ Using subscrip", "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 1, "slug": "sell", "status": "published", @@ -477,7 +435,7 @@ Using subscrip", "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/sell/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "paid", @@ -487,7 +445,7 @@ Using subscrip", "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a72", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "A guide to collaborating with other staff users to publish, and some resources to help you with the next steps of growing your business", "custom_template": null, "email_only": false, @@ -506,7 +464,7 @@ Using subscrip", "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 2, "slug": "grow", "status": "published", @@ -514,7 +472,7 @@ Using subscrip", "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/grow/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -524,7 +482,7 @@ Using subscrip", "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a71", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "Work with all your favorite apps and tools or create your own custom integrations using the Ghost API.", "custom_template": null, "email_only": false, @@ -544,7 +502,7 @@ Using subscrip", "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 1, "slug": "integrations", "status": "published", @@ -552,7 +510,7 @@ Using subscrip", "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/integrations/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -562,7 +520,7 @@ Using subscrip", "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "618ba1ffbe2896088840a6e7", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": null, "custom_template": null, "email_only": false, @@ -592,7 +550,7 @@ Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 1, "slug": "not-so-short-bit-complex", "status": "published", @@ -600,7 +558,7 @@ Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/not-so-short-bit-complex/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -610,7 +568,7 @@ Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "618ba1ffbe2896088840a6e3", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": null, "custom_template": null, "email_only": false, @@ -645,7 +603,7 @@ mctesters "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 0, "slug": "short-and-sweet", "status": "published", @@ -653,7 +611,7 @@ mctesters "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/short-and-sweet/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -663,7 +621,7 @@ mctesters "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "618ba1ffbe2896088840a6e1", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": null, "custom_template": null, "email_only": false, @@ -684,7 +642,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 1, "slug": "ghostly-kitchen-sink", "status": "published", @@ -692,7 +650,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/ghostly-kitchen-sink/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -702,7 +660,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "618ba1ffbe2896088840a6df", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "This is my custom excerpt!", "custom_template": null, "email_only": false, @@ -721,7 +679,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 1, "slug": "html-ipsum", "status": "published", @@ -729,7 +687,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/html-ipsum/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -752,49 +710,7 @@ exports[`Collections API Browse Posts Can browse Collections Posts 2: [headers] 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": "45967", - "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 Browse Posts Can browse Collections Posts 3: [body] 1`] = ` -Object { - "collection_posts": Array [ - Object { - "featured": false, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "slug": "write", - "title": "Writing and managing content in Ghost, an advanced guide", - }, - Object { - "featured": false, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "slug": "portal", - "title": "Building your audience with subscriber signups", - }, - ], - "meta": Object { - "pagination": Object { - "limit": "2", - "next": null, - "page": "2", - "pages": 1, - "prev": null, - "total": 2, - }, - }, -} -`; - -exports[`Collections API Browse Posts Can browse Collections Posts 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": "370", + "content-length": "46132", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -811,7 +727,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a75", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "A full overview of all the features built into the Ghost editor, including powerful workflow automations to speed up your creative process.", "custom_template": null, "email_only": false, @@ -830,7 +746,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 5, "slug": "write", "status": "published", @@ -838,7 +754,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/write/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -848,7 +764,7 @@ Object { "codeinjection_foot": null, "codeinjection_head": null, "comment_id": "6194d3ce51e2700162531a74", - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "custom_excerpt": "How Ghost allows you to turn anonymous readers into an audience of active subscribers, so you know what's working and what isn't.", "custom_template": null, "email_only": false, @@ -867,7 +783,7 @@ Object { "og_description": null, "og_image": null, "og_title": null, - "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "reading_time": 2, "slug": "portal", "status": "published", @@ -875,7 +791,7 @@ Object { "twitter_description": null, "twitter_image": null, "twitter_title": null, - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000\\\\\\+\\\\d\\{2\\}:\\\\d\\{2\\}/, "url": "http://127.0.0.1:2369/portal/", "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, "visibility": "public", @@ -898,7 +814,7 @@ exports[`Collections API Browse Posts Can browse Collections Posts using paging 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": "14035", + "content-length": "14065", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -940,125 +856,6 @@ Object { } `; -exports[`Collections API Can browse Collections 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": "Collection with all posts", - "feature_image": null, - "filter": "status:published", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "posts": Array [ - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - ], - "slug": "index", - "title": "Index", - "type": "automatic", - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - }, - Object { - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "description": "Collection of featured posts", - "feature_image": null, - "filter": "featured:true", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "posts": Array [ - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - Object { - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "sort_order": Any, - }, - ], - "slug": "featured", - "title": "Featured Posts", - "type": "automatic", - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - }, - Object { - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "description": "Test Collection Description", - "feature_image": null, - "filter": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "posts": Array [], - "title": "Test Collection", - "type": "manual", - "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - }, - ], - "meta": Object { - "pagination": Object { - "limit": 3, - "next": null, - "page": 1, - "pages": 1, - "prev": null, - "total": 3, - }, - }, -} -`; - -exports[`Collections API Can browse Collections 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": "1530", - "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 Can delete a Collection 1: [body] 1`] = ` Object { "collections": Array [ diff --git a/ghost/core/test/e2e-api/admin/collections.test.js b/ghost/core/test/e2e-api/admin/collections.test.js index 67d7592a94..2d6ac99b66 100644 --- a/ghost/core/test/e2e-api/admin/collections.test.js +++ b/ghost/core/test/e2e-api/admin/collections.test.js @@ -12,6 +12,7 @@ const { anyLocationFor, anyObjectId, anyISODateTime, + anyISODateTimeWithTZ, anyNumber, anyUuid, anyString @@ -25,9 +26,9 @@ const matchCollection = { const matchCollectionPost = { id: anyObjectId, - created_at: anyISODateTime, - updated_at: anyISODateTime, - published_at: anyISODateTime, + created_at: anyISODateTimeWithTZ, + updated_at: anyISODateTimeWithTZ, + published_at: anyISODateTimeWithTZ, uuid: anyUuid }; @@ -352,40 +353,5 @@ describe('Collections API', function () { collections: [buildMatcher(7)] }); }); - - it('Creates an automatic Collection with a relational tag filter', async function () { - const collection = { - title: 'Test Collection with relational tag filter', - description: 'Test Collection Description with relational tag filter', - type: 'automatic', - filter: 'tag:kitchen-sink' - }; - - const automaticTagCollection = await agent - .post('/collections/') - .body({ - collections: [collection] - }) - .expectStatus(201) - .matchHeaderSnapshot({ - 'content-version': anyContentVersion, - etag: anyEtag, - location: anyLocationFor('collections') - }) - .matchBodySnapshot({ - collections: [buildMatcher(2)] - }); - - const kitchenSinkTagPosts = await agent - .get('/posts/?filter=tag:kitchen-sink'); - - assert.equal(automaticTagCollection.body.collections[0].posts.length, 2); - assert.equal(kitchenSinkTagPosts.body.posts.length, 2); - - const collectionPostIds = automaticTagCollection.body.collections[0].posts.map(p => p.id); - const tagFilteredPosts = kitchenSinkTagPosts.body.posts.map(p => p.id); - - assert.deepEqual(collectionPostIds, tagFilteredPosts); - }); }); });