From eb063f7a405d4ec65101932ee464dc54e37f2663 Mon Sep 17 00:00:00 2001 From: Simon Backx Date: Tue, 23 Jan 2024 16:10:11 +0100 Subject: [PATCH] Fixed clearing invalid sender_email when changing newsletter sender_reply_to (#19555) fixes PROD-102 When a newsletter has a sender_email stored in the database that Ghost is not allowed to send from, we no longer return it as sender_email in the API. Instead we return it as the sender_reply_to. That way the expected behaviour is shown correctly in the frontend and the API result also makes more sense. In addition to that, when a change is made to a newsletters reply_to address we'll clear any invalid sender_email values in that newsletter. That makes sure we can clear the sender_reply_to value instead of keeping the current fallback to sender_email if that one is stored. On top of that, this change correclty updates the browse endpoint to use the newsletter service instead of directly using the model. --- .../core/server/api/endpoints/newsletters.js | 3 +- .../serializers/output/mappers/newsletters.js | 12 + .../newsletters/NewslettersService.js | 25 +- .../__snapshots__/newsletters.test.js.snap | 730 ++++++++++++++++-- .../test/e2e-api/admin/newsletters.test.js | 287 +++++++ .../utils/serializers/output/mapper.test.js | 4 +- .../services/newsletters/service.test.js | 6 +- .../lib/controllers/RouterController.js | 4 +- .../lib/repositories/MemberRepository.js | 2 +- .../test/unit/lib/controllers/router.test.js | 12 +- 10 files changed, 1020 insertions(+), 65 deletions(-) diff --git a/ghost/core/core/server/api/endpoints/newsletters.js b/ghost/core/core/server/api/endpoints/newsletters.js index 8fa840e1da..f8eef9ed19 100644 --- a/ghost/core/core/server/api/endpoints/newsletters.js +++ b/ghost/core/core/server/api/endpoints/newsletters.js @@ -1,4 +1,3 @@ -const models = require('../../models'); const allowedIncludes = ['count.posts', 'count.members', 'count.active_members']; const newslettersService = require('../../services/newsletters'); @@ -27,7 +26,7 @@ module.exports = { }, permissions: true, query(frame) { - return models.Newsletter.findPage(frame.options); + return newslettersService.browse(frame.options); } }, diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js index 93093c5267..6b8dbd3720 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js @@ -1,4 +1,5 @@ const utils = require('../../../index'); +const emailAddressService = require('../../../../../../services/email-address'); module.exports = (model, frame) => { const jsonModel = model.toJSON(frame.options); @@ -19,6 +20,17 @@ module.exports = (model, frame) => { }; return serialized; + } else { + if (jsonModel.sender_email && jsonModel.sender_reply_to === 'newsletter') { + // If sender_email is not allowed, we'll return it as the sender_reply_to instead, so we display the current situation correctly in the frontend + // If one of the properties was changed, we need to reset sender_email in case it was not changed but is invalid in the database + // which can happen after a config change (= auto correcting behaviour) + const validated = emailAddressService.service.validate(jsonModel.sender_email, 'from'); + if (!validated.allowed) { + jsonModel.sender_reply_to = jsonModel.sender_email; + jsonModel.sender_email = null; + } + } } return jsonModel; diff --git a/ghost/core/core/server/services/newsletters/NewslettersService.js b/ghost/core/core/server/services/newsletters/NewslettersService.js index d946f184fb..cd2ba1ea09 100644 --- a/ghost/core/core/server/services/newsletters/NewslettersService.js +++ b/ghost/core/core/server/services/newsletters/NewslettersService.js @@ -92,7 +92,7 @@ class NewslettersService { * @public * @param {Object} options data (id, uuid, slug...) * @param {Object} [options] options - * @returns {Promise} JSONified Newsletter models + * @returns {Promise} */ async read(data, options = {}) { const newsletter = await this.NewsletterModel.findOne(data, options); @@ -108,11 +108,14 @@ class NewslettersService { /** * @public * @param {Object} [options] options - * @returns {Promise} JSONified Newsletter models + * @returns {Promise} */ async browse(options = {}) { - let newsletters = await this.NewsletterModel.findAll(options); + return await this.NewsletterModel.findPage(options); + } + async getAll(options = {}) { + const newsletters = await this.NewsletterModel.findAll(options); return newsletters.toJSON(); } @@ -298,6 +301,10 @@ class NewslettersService { } if (validated.verificationEmailRequired) { + if (type === 'replyTo' && email === newsletter.get('sender_email')) { + // This is some custom behaviour that allows swapping sender_email to sender_reply_to without requiring validation again + continue; + } delete cleanedAttrs[property]; emailsToVerify.push({email, property}); } @@ -311,6 +318,18 @@ class NewslettersService { } } + // If one of the properties was changed, we need to reset sender_email in case it was not changed but is invalid in the database + // which can happen after a config change (= auto correcting behaviour) + const didChangeReplyTo = newsletter && attrs.sender_reply_to !== undefined && newsletter.get('sender_reply_to') !== attrs.sender_reply_to; + const didChangeSenderEmail = newsletter && (attrs.sender_email !== undefined && newsletter.get('sender_email') !== attrs.sender_email); + if (didChangeReplyTo && !didChangeSenderEmail && newsletter.get('sender_email')) { + const validated = this.emailAddressService.service.validate(newsletter.get('sender_email'), 'from'); + if (!validated.allowed) { + logging.info(`Resetting sender_email for newsletter ${newsletter.id} because it became invalid`); + cleanedAttrs.sender_email = null; + } + } + return {cleanedAttrs, emailsToVerify}; } diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/newsletters.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/newsletters.test.js.snap index 75f8856f49..8f03ac84d8 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/newsletters.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/newsletters.test.js.snap @@ -2884,6 +2884,183 @@ Object { } `; +exports[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Browse returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 1: [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": "5375", + "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[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter (not the reply-to address) 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "My changed newsletter name", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "notvalid@acme.com", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter (not the reply-to address) 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": "924", + "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[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "My changed newsletter name", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "notvalid@acme.com", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 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": "924", + "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[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "My changed newsletter name", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "support", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email with custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address 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": "914", + "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[`Newsletters API Managed email with custom sending domain Can clear sender_email 1: [body] 1`] = ` Object { "newsletters": Array [ @@ -2897,10 +3074,10 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", + "name": "My changed newsletter name", "sender_email": null, "sender_name": "Jamie", - "sender_reply_to": "anything@sendingdomain.com", + "sender_reply_to": "newsletter", "show_badge": true, "show_comment_cta": true, "show_feature_image": true, @@ -2929,7 +3106,7 @@ exports[`Newsletters API Managed email with custom sending domain Can clear send 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": "923", + "content-length": "917", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -2952,10 +3129,10 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "default@email.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", - "sender_reply_to": "anything@sendingdomain.com", + "sender_reply_to": "existing@acme.com", "show_badge": true, "show_comment_cta": true, "show_feature_image": true, @@ -2984,7 +3161,7 @@ exports[`Newsletters API Managed email with custom sending domain Can keep sende 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": "938", + "content-length": "924", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3012,8 +3189,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "default@email.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "newsletter", "show_badge": true, @@ -3044,7 +3221,7 @@ exports[`Newsletters API Managed email with custom sending domain Can set newsle 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": "977", + "content-length": "972", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3326,8 +3503,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "default@email.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "anything@sendingdomain.com", "show_badge": true, @@ -3358,7 +3535,7 @@ exports[`Newsletters API Managed email with custom sending domain Can set newsle 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": "938", + "content-length": "933", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3381,8 +3558,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "default@email.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "support", "show_badge": true, @@ -3413,7 +3590,7 @@ exports[`Newsletters API Managed email with custom sending domain Can set newsle 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": "919", + "content-length": "914", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3436,8 +3613,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "default@email.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "newsletter", "show_badge": true, @@ -3468,7 +3645,7 @@ exports[`Newsletters API Managed email with custom sending domain Can set newsle 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": "922", + "content-length": "917", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3491,10 +3668,10 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", + "name": "My changed newsletter name", "sender_email": "anything@sendingdomain.com", "sender_name": "Jamie", - "sender_reply_to": "anything@sendingdomain.com", + "sender_reply_to": "newsletter", "show_badge": true, "show_comment_cta": true, "show_feature_image": true, @@ -3523,7 +3700,7 @@ exports[`Newsletters API Managed email with custom sending domain Can set sender 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": "947", + "content-length": "941", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3657,6 +3834,467 @@ Object { } `; +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Browse returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 1: [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": "5365", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Can switch sender_email to sender_reply_to without validation 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "notvalid@acme.com", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Can switch sender_email to sender_reply_to without validation 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": "914", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter (not the reply-to address) 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "My changed newsletter name", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "notvalid@acme.com", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter (not the reply-to address) 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": "924", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter address (not the reply-to address) 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "My changed newsletter name", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "newsletter", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Does not reset sender_email when editing the newsletter address (not the reply-to address) 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": "917", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "notvalid@acme.com", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 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": "914", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "support", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address 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": "904", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address in combination with keeping sender email 1: [body] 1`] = ` +Object { + "meta": Object { + "sent_email_verification": Array [ + "sender_reply_to", + ], + }, + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "newsletter", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address in combination with keeping sender email 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": "962", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address in combination with resetting sender email 1: [body] 1`] = ` +Object { + "meta": Object { + "sent_email_verification": Array [ + "sender_reply_to", + ], + }, + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": null, + "sender_name": "Jamie", + "sender_reply_to": "newsletter", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Resets sender_email when editing the newsletter reply_to address in combination with resetting sender email 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": "962", + "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[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 1: [body] 1`] = ` +Object { + "newsletters": Array [ + Object { + "background_color": "light", + "body_font_category": "serif", + "border_color": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": null, + "feedback_enabled": false, + "footer_content": null, + "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "sender_email": "notvalid@acme.com", + "sender_name": "Jamie", + "sender_reply_to": "newsletter", + "show_badge": true, + "show_comment_cta": true, + "show_feature_image": true, + "show_header_icon": true, + "show_header_name": true, + "show_header_title": true, + "show_latest_posts": false, + "show_post_title_section": true, + "show_subscription_details": false, + "slug": "daily-newsletter", + "sort_order": 1, + "status": "active", + "subscribe_on_signup": false, + "title_alignment": "center", + "title_color": null, + "title_font_category": "serif", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "members", + }, + ], +} +`; + +exports[`Newsletters API Managed email without custom sending domain Auto correcting invalid domains Returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter 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": "922", + "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[`Newsletters API Managed email without custom sending domain Can clear sender_email 1: [body] 1`] = ` Object { "newsletters": Array [ @@ -3726,9 +4364,9 @@ Object { "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "name": "Daily newsletter", - "sender_email": "jamie@example.com", + "sender_email": null, "sender_name": "Jamie", - "sender_reply_to": "newsletter", + "sender_reply_to": "existing@acme.com", "show_badge": true, "show_comment_cta": true, "show_feature_image": true, @@ -3757,7 +4395,7 @@ exports[`Newsletters API Managed email without custom sending domain Can keep se 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": "922", + "content-length": "914", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3896,7 +4534,7 @@ Object { "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "name": "Daily newsletter", - "sender_email": "jamie@example.com", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "newsletter", "show_badge": true, @@ -3927,7 +4565,7 @@ exports[`Newsletters API Managed email without custom sending domain Can set new 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": "977", + "content-length": "962", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4155,7 +4793,7 @@ Object { "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "name": "Daily newsletter", - "sender_email": "jamie@example.com", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "support", "show_badge": true, @@ -4186,7 +4824,7 @@ exports[`Newsletters API Managed email without custom sending domain Can set new 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": "919", + "content-length": "904", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4210,7 +4848,7 @@ Object { "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "name": "Daily newsletter", - "sender_email": "jamie@example.com", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "newsletter", "show_badge": true, @@ -4241,7 +4879,7 @@ exports[`Newsletters API Managed email without custom sending domain Can set new 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": "922", + "content-length": "907", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4265,7 +4903,7 @@ Object { "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, "name": "Daily newsletter", - "sender_email": "jamie@example.com", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "default@email.com", "show_badge": true, @@ -4296,7 +4934,7 @@ exports[`Newsletters API Managed email without custom sending domain Can set new 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": "929", + "content-length": "914", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4498,7 +5136,7 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", + "name": "My changed newsletter name", "sender_email": "hello@acme.com", "sender_name": "Jamie", "sender_reply_to": "hello@acme.com", @@ -4530,7 +5168,7 @@ exports[`Newsletters API Self hoster without managed email Can change sender_ema 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": "923", + "content-length": "933", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4553,7 +5191,7 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", + "name": "My changed newsletter name", "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "hello@acme.com", @@ -4585,7 +5223,7 @@ exports[`Newsletters API Self hoster without managed email Can clear sender_emai 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": "911", + "content-length": "921", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4663,8 +5301,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "anything@sendingdomain.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "hello@acme.com", "show_badge": true, @@ -4695,7 +5333,7 @@ exports[`Newsletters API Self hoster without managed email Can set newsletter re 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": "935", + "content-length": "921", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4718,8 +5356,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "anything@sendingdomain.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "support", "show_badge": true, @@ -4750,7 +5388,7 @@ exports[`Newsletters API Self hoster without managed email Can set newsletter re 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": "928", + "content-length": "914", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4773,8 +5411,8 @@ Object { "footer_content": null, "header_image": "http://127.0.0.1:2369/content/images/2022/05/test.jpg", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Daily newsletter", - "sender_email": "anything@sendingdomain.com", + "name": "My changed newsletter name", + "sender_email": null, "sender_name": "Jamie", "sender_reply_to": "newsletter", "show_badge": true, @@ -4805,7 +5443,7 @@ exports[`Newsletters API Self hoster without managed email Can set newsletter re 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": "931", + "content-length": "917", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/core/test/e2e-api/admin/newsletters.test.js b/ghost/core/test/e2e-api/admin/newsletters.test.js index c6394e8635..fbafff8829 100644 --- a/ghost/core/test/e2e-api/admin/newsletters.test.js +++ b/ghost/core/test/e2e-api/admin/newsletters.test.js @@ -14,6 +14,16 @@ const assertMemberRelationCount = async (newsletterId, expectedCount) => { assert.equal(relations.length, expectedCount); }; +// Change directly in database, to test edge cases +async function editNewsletter(id, changes) { + await dbUtils.knex('newsletters').where({id}).update(changes); +} + +// Get directly from the database +async function getNewsletter(id) { + return (await dbUtils.knex('newsletters').where({id}))[0]; +} + const newsletterSnapshot = { id: anyObjectId, uuid: anyUuid, @@ -868,6 +878,175 @@ describe('Newsletters API', function () { configUtils.set('mail:from', 'default@email.com'); }); + describe('Auto correcting invalid domains', function () { + const id = fixtureManager.get('newsletters', 0).id; + + beforeEach(async function () { + // Invalid situation in the database) + await editNewsletter(id, { + sender_email: 'notvalid@acme.com', + sender_reply_to: 'newsletter' + }); + }); + + after(async function () { + // Reset + await editNewsletter(id, { + sender_email: null, + sender_reply_to: 'newsletter' + }); + }); + + it('Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter', async function () { + const {body} = await agent.get(`newsletters/${id}`) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(body.newsletters[0].sender_email, null); + assert.equal(body.newsletters[0].sender_reply_to, 'notvalid@acme.com'); + }); + + it('Browse returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter', async function () { + const {body} = await agent.get(`newsletters`) + .expectStatus(200) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = body.newsletters.find(n => n.id === id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'notvalid@acme.com'); + }); + + it('Resets sender_email when editing the newsletter reply_to address', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + sender_reply_to: 'support' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'support'); + }); + + it('Resets sender_email when editing the newsletter reply_to address in combination with resetting sender email', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + sender_email: null, + sender_reply_to: 'something@allowed.com' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'newsletter'); // required validation + }); + + it('Resets sender_email when editing the newsletter reply_to address in combination with keeping sender email', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + sender_email: 'notvalid@acme.com', + sender_reply_to: 'something@allowed.com' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'newsletter'); // required validation + }); + + it('Can switch sender_email to sender_reply_to without validation', async function () { + // The frontend will try to do this because it gets the mapped values from the API + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + sender_email: null, + sender_reply_to: 'notvalid@acme.com' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'notvalid@acme.com'); // did not require validation + }); + + it('Does not reset sender_email when editing the newsletter (not the reply-to address)', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + name: 'My changed newsletter name' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + assert.equal(newsletter.name, 'My changed newsletter name'); + assert.equal(newsletter.sender_email, 'notvalid@acme.com'); + assert.equal(newsletter.sender_reply_to, 'newsletter'); + }); + }); + it('Can set newsletter reply-to to newsletter or support', async function () { const id = fixtureManager.get('newsletters', 0).id; @@ -1061,6 +1240,12 @@ describe('Newsletters API', function () { it('Can keep sender_email', async function () { const id = fixtureManager.get('newsletters', 0).id; + // Invalid situation in the database) + await editNewsletter(id, { + sender_email: 'existing@acme.com', + sender_reply_to: 'newsletter' + }); + const before = await models.Newsletter.findOne({id}); assert(before.get('sender_email'), 'This test requires a non empty sender_email'); @@ -1147,6 +1332,102 @@ describe('Newsletters API', function () { configUtils.set('hostSettings:managedEmail:sendingDomain', 'sendingdomain.com'); }); + describe('Auto correcting invalid domains', function () { + const id = fixtureManager.get('newsletters', 0).id; + + beforeEach(async function () { + // Invalid situation in the database) + await editNewsletter(id, { + sender_email: 'notvalid@acme.com', + sender_reply_to: 'newsletter' + }); + }); + + after(async function () { + // Reset + await editNewsletter(id, { + sender_email: null, + sender_reply_to: 'newsletter' + }); + }); + + it('Read returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter', async function () { + const {body} = await agent.get(`newsletters/${id}`) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(body.newsletters[0].sender_email, null); + assert.equal(body.newsletters[0].sender_reply_to, 'notvalid@acme.com'); + }); + + it('Browse returns sender_email as sender_reply_to in case we cannot send from sender_email and sender_reply_to is set to newsletter', async function () { + const {body} = await agent.get(`newsletters`) + .expectStatus(200) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = body.newsletters.find(n => n.id === id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'notvalid@acme.com'); + }); + + it('Resets sender_email when editing the newsletter reply_to address', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + sender_reply_to: 'support' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + + // Do a manual check to make sure we don't accidentally change snapshots + assert.equal(newsletter.sender_email, null); + assert.equal(newsletter.sender_reply_to, 'support'); + }); + + it('Does not reset sender_email when editing the newsletter (not the reply-to address)', async function () { + await agent.put(`newsletters/${id}`) + .body({ + newsletters: [{ + name: 'My changed newsletter name' + }] + }) + .expectStatus(200) + .matchBodySnapshot({ + newsletters: [newsletterSnapshot] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + + const newsletter = await getNewsletter(id); + assert.equal(newsletter.name, 'My changed newsletter name'); + assert.equal(newsletter.sender_email, 'notvalid@acme.com'); + assert.equal(newsletter.sender_reply_to, 'newsletter'); + }); + }); + it('Can set newsletter reply-to to newsletter or support', async function () { const id = fixtureManager.get('newsletters', 0).id; @@ -1333,6 +1614,12 @@ describe('Newsletters API', function () { it('Can keep sender_email', async function () { const id = fixtureManager.get('newsletters', 0).id; + // Invalid situation in the database) + await editNewsletter(id, { + sender_email: 'existing@acme.com', + sender_reply_to: 'newsletter' + }); + const before = await models.Newsletter.findOne({id}); assert(before.get('sender_email'), 'This test requires a non empty sender_email'); diff --git a/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js b/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js index eac6f40718..547de67148 100644 --- a/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js +++ b/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js @@ -219,7 +219,9 @@ describe('Unit: utils/serializers/output/mappers', function () { const newsletter = createJsonModel(testUtils.DataGenerator.forKnex.createNewsletter({ name: 'Full newsletter', - slug: 'full-newsletter' + slug: 'full-newsletter', + sender_email: null, + sender_reply_to: 'newsletter' })); const mapped = mappers.newsletters(newsletter, frame); diff --git a/ghost/core/test/unit/server/services/newsletters/service.test.js b/ghost/core/test/unit/server/services/newsletters/service.test.js index 61e394f64d..8836ee45f8 100644 --- a/ghost/core/test/unit/server/services/newsletters/service.test.js +++ b/ghost/core/test/unit/server/services/newsletters/service.test.js @@ -102,14 +102,12 @@ describe('NewslettersService', function () { // @TODO replace this with a specific function for fetching all available newsletters describe('browse', function () { - it('lists all newsletters by calling findAll and toJSON', async function () { - const toJSONStub = sinon.stub(); - const findAllStub = sinon.stub(models.Newsletter, 'findAll').returns({toJSON: toJSONStub}); + it('lists all newsletters by calling findPage', async function () { + const findAllStub = sinon.stub(models.Newsletter, 'findPage').returns({data: []}); await newsletterService.browse({}); sinon.assert.calledOnce(findAllStub); - sinon.assert.calledOnce(toJSONStub); }); }); diff --git a/ghost/members-api/lib/controllers/RouterController.js b/ghost/members-api/lib/controllers/RouterController.js index 3be126c547..6230607e8a 100644 --- a/ghost/members-api/lib/controllers/RouterController.js +++ b/ghost/members-api/lib/controllers/RouterController.js @@ -487,10 +487,10 @@ module.exports = class RouterController { if (requestedNewsletters && requestedNewsletters.length > 0 && requestedNewsletters.every(newsletter => newsletter.name !== undefined)) { const newsletterNames = requestedNewsletters.map(newsletter => newsletter.name); const newsletterNamesFilter = newsletterNames.map(newsletter => `'${newsletter.replace(/("|')/g, '\\$1')}'`); - const newsletters = await this._newslettersService.browse({ + const newsletters = (await this._newslettersService.getAll({ filter: `name:[${newsletterNamesFilter}]`, columns: ['id','name','status'] - }); + })); if (newsletters.length !== newsletterNames.length) { //check for invalid newsletters const validNewsletters = newsletters.map(newsletter => newsletter.name); diff --git a/ghost/members-api/lib/repositories/MemberRepository.js b/ghost/members-api/lib/repositories/MemberRepository.js index cf7ff1852d..4f03192d21 100644 --- a/ghost/members-api/lib/repositories/MemberRepository.js +++ b/ghost/members-api/lib/repositories/MemberRepository.js @@ -401,7 +401,7 @@ module.exports = class MemberRepository { // By default subscribe to all active auto opt-in newsletters with members visibility //TODO: Will mostly need to be updated later for paid-only newsletters browseOptions.filter = 'status:active+subscribe_on_signup:true+visibility:members'; - const newsletters = await this._newslettersService.browse(browseOptions); + const newsletters = await this._newslettersService.getAll(browseOptions); return newsletters || []; } diff --git a/ghost/members-api/test/unit/lib/controllers/router.test.js b/ghost/members-api/test/unit/lib/controllers/router.test.js index f3b23aa0f8..3e77a4cd8d 100644 --- a/ghost/members-api/test/unit/lib/controllers/router.test.js +++ b/ghost/members-api/test/unit/lib/controllers/router.test.js @@ -146,10 +146,10 @@ describe('RouterController', function () { const newsletterNames = newsletters.map(newsletter => newsletter.name); const newsletterNamesFilter = newsletterNames.map(newsletter => `'${newsletter.replace(/("|')/g, '\\$1')}'`); const newslettersServiceStub = { - browse: sinon.stub() + getAll: sinon.stub() }; - newslettersServiceStub.browse + newslettersServiceStub.getAll .withArgs({ filter: `name:[${newsletterNamesFilter}]`, columns: ['id','name','status'] @@ -181,10 +181,10 @@ describe('RouterController', function () { ]; const newslettersServiceStub = { - browse: sinon.stub() + getAll: sinon.stub() }; - newslettersServiceStub.browse + newslettersServiceStub.getAll .withArgs({ filter: `name:['${INVALID_NEWSLETTER_NAME}']`, columns: ['id','name','status'] @@ -221,10 +221,10 @@ describe('RouterController', function () { const newsletterNames = newsletters.map(newsletter => `'${newsletter.name}'`); const newslettersServiceStub = { - browse: sinon.stub() + getAll: sinon.stub() }; - newslettersServiceStub.browse + newslettersServiceStub.getAll .withArgs({ filter: `name:[${newsletterNames}]`, columns: ['id', 'name','status']