mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-27 00:52:36 +03:00
✨ Added unsubscribe_url to member api response (#21207)
ref https://linear.app/tryghost/issue/ONC-387/ With some recent changes, we added validation to unsubscribe URLs to verify the source, allowing us to cut down on spam and improving security, as the underlying key could be re-generated should the need arise. This had the side effect of making unsubscribe URLs difficult to reconstruct when using third-party/downstream integrations, such as ActiveCampaign, which fills a gap in the current Ghost feature set. Now any authenticated query to `/api/members` will return an `unsubscribe_url` field that can be used directly.
This commit is contained in:
parent
a0600e3595
commit
63f25ece6d
@ -167,7 +167,8 @@ function serializeMember(member, options) {
|
|||||||
email_recipients: json.email_recipients,
|
email_recipients: json.email_recipients,
|
||||||
status: json.status,
|
status: json.status,
|
||||||
last_seen_at: json.last_seen_at,
|
last_seen_at: json.last_seen_at,
|
||||||
attribution: serializeAttribution(json.attribution)
|
attribution: serializeAttribution(json.attribution),
|
||||||
|
unsubscribe_url: json.unsubscribe_url
|
||||||
};
|
};
|
||||||
|
|
||||||
if (json.products) {
|
if (json.products) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const stripeService = require('../stripe');
|
const stripeService = require('../stripe');
|
||||||
const settingsCache = require('../../../shared/settings-cache');
|
const settingsCache = require('../../../shared/settings-cache');
|
||||||
|
const settingsHelpers = require('../../services/settings-helpers');
|
||||||
const MembersApi = require('@tryghost/members-api');
|
const MembersApi = require('@tryghost/members-api');
|
||||||
const logging = require('@tryghost/logging');
|
const logging = require('@tryghost/logging');
|
||||||
const mail = require('../mail');
|
const mail = require('../mail');
|
||||||
@ -236,7 +237,8 @@ function createApiInstance(config) {
|
|||||||
memberAttributionService: memberAttributionService.service,
|
memberAttributionService: memberAttributionService.service,
|
||||||
emailSuppressionList,
|
emailSuppressionList,
|
||||||
settingsCache,
|
settingsCache,
|
||||||
sentry
|
sentry,
|
||||||
|
settingsHelpers
|
||||||
});
|
});
|
||||||
|
|
||||||
return membersApiInstance;
|
return membersApiInstance;
|
||||||
|
@ -22,6 +22,7 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
|
|||||||
firstname: member.name && member.name.split(' ')[0],
|
firstname: member.name && member.name.split(' ')[0],
|
||||||
expertise: member.expertise,
|
expertise: member.expertise,
|
||||||
avatar_image: member.avatar_image,
|
avatar_image: member.avatar_image,
|
||||||
|
unsubscribe_url: member.unsubscribe_url,
|
||||||
subscribed: !!member.subscribed,
|
subscribed: !!member.subscribed,
|
||||||
subscriptions: member.subscriptions || [],
|
subscriptions: member.subscriptions || [],
|
||||||
paid: member.status !== 'free',
|
paid: member.status !== 'free',
|
||||||
|
@ -2,6 +2,7 @@ const tpl = require('@tryghost/tpl');
|
|||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
const {EmailAddressParser} = require('@tryghost/email-addresses');
|
const {EmailAddressParser} = require('@tryghost/email-addresses');
|
||||||
const logging = require('@tryghost/logging');
|
const logging = require('@tryghost/logging');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
incorrectKeyType: 'type must be one of "direct" or "connect".'
|
||||||
@ -179,6 +180,30 @@ class SettingsHelpers {
|
|||||||
return this.#managedEmailEnabled() || this.labs.isSet('newEmailAddresses');
|
return this.#managedEmailEnabled() || this.labs.isSet('newEmailAddresses');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUnsubscribeUrl(uuid, options = {}) {
|
||||||
|
const siteUrl = this.urlUtils.urlFor('home', true);
|
||||||
|
const unsubscribeUrl = new URL(siteUrl);
|
||||||
|
const key = this.getMembersValidationKey();
|
||||||
|
unsubscribeUrl.pathname = `${unsubscribeUrl.pathname}/unsubscribe/`.replace('//', '/');
|
||||||
|
if (uuid) {
|
||||||
|
// hash key with member uuid for verification (and to not leak uuid) - it's possible to update member email prefs without logging in
|
||||||
|
// @ts-ignore
|
||||||
|
const hmac = crypto.createHmac('sha256', key).update(`${uuid}`).digest('hex');
|
||||||
|
unsubscribeUrl.searchParams.set('uuid', uuid);
|
||||||
|
unsubscribeUrl.searchParams.set('key', hmac);
|
||||||
|
} else {
|
||||||
|
unsubscribeUrl.searchParams.set('preview', '1');
|
||||||
|
}
|
||||||
|
if (options.newsletterUuid) {
|
||||||
|
unsubscribeUrl.searchParams.set('newsletter', options.newsletterUuid);
|
||||||
|
}
|
||||||
|
if (options.comments) {
|
||||||
|
unsubscribeUrl.searchParams.set('comments', '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
return unsubscribeUrl.href;
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE
|
// PRIVATE
|
||||||
|
|
||||||
#managedEmailEnabled() {
|
#managedEmailEnabled() {
|
||||||
|
@ -122,6 +122,7 @@ Object {
|
|||||||
"yearly_price_id": Any<String>,
|
"yearly_price_id": Any<String>,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -133,7 +134,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription 2: [headers]
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2484",
|
"content-length": "2573",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -225,6 +226,7 @@ Object {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"tiers": Array [],
|
"tiers": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -236,7 +238,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription 4: [headers]
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "1584",
|
"content-length": "1673",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -272,6 +274,7 @@ Object {
|
|||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -283,7 +286,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "3635",
|
"content-length": "3724",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -319,6 +322,7 @@ Object {
|
|||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -330,7 +334,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2735",
|
"content-length": "2824",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -374,6 +378,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -385,7 +390,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "3425",
|
"content-length": "3514",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -429,6 +434,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -440,7 +446,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2525",
|
"content-length": "2614",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -484,6 +490,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -495,7 +502,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "3434",
|
"content-length": "3523",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -539,6 +546,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -550,7 +558,7 @@ exports[`Members API: edit subscriptions Can cancel a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2534",
|
"content-length": "2623",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -839,6 +847,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -850,7 +859,7 @@ exports[`Members API: edit subscriptions Can recover member products when we can
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "3467",
|
"content-length": "3556",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -894,6 +903,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -905,7 +915,7 @@ exports[`Members API: edit subscriptions Can recover member products when we can
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2567",
|
"content-length": "2656",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -949,6 +959,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -960,7 +971,7 @@ exports[`Members API: edit subscriptions Can recover member products when we upd
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "4222",
|
"content-length": "4311",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -1004,6 +1015,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -1015,7 +1027,7 @@ exports[`Members API: edit subscriptions Can update a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "4201",
|
"content-length": "4290",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -1059,6 +1071,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -1070,7 +1083,7 @@ exports[`Members API: edit subscriptions Can update a subscription for a member
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "4200",
|
"content-length": "4289",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
@ -25,6 +25,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -50,6 +51,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -71,7 +73,7 @@ exports[`Members API - With Newsletters - compat mode Can fetch members who are
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2229",
|
"content-length": "2407",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -105,6 +107,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -130,6 +133,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -155,6 +159,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -180,6 +185,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -205,6 +211,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -230,6 +237,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -251,7 +259,7 @@ exports[`Members API - With Newsletters - compat mode Can fetch members who are
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "7972",
|
"content-length": "8506",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -285,6 +293,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -310,6 +319,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -331,7 +341,7 @@ exports[`Members API - With Newsletters Can fetch members who are NOT subscribed
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2229",
|
"content-length": "2407",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -365,6 +375,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -390,6 +401,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -415,6 +427,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -440,6 +453,7 @@ Object {
|
|||||||
"status": "paid",
|
"status": "paid",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -465,6 +479,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -490,6 +505,7 @@ Object {
|
|||||||
"status": "free",
|
"status": "free",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -511,7 +527,7 @@ exports[`Members API - With Newsletters Can fetch members who are subscribed 2:
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "7972",
|
"content-length": "8506",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,8 @@ const assert = require('assert/strict');
|
|||||||
const models = require('../../../core/server/models');
|
const models = require('../../../core/server/models');
|
||||||
const {stripeMocker} = require('../../utils/e2e-framework-mock-manager');
|
const {stripeMocker} = require('../../utils/e2e-framework-mock-manager');
|
||||||
const DomainEvents = require('@tryghost/domain-events/lib/DomainEvents');
|
const DomainEvents = require('@tryghost/domain-events/lib/DomainEvents');
|
||||||
|
const settingsHelpers = require('../../../core/server/services/settings-helpers');
|
||||||
|
const sinon = require('sinon');
|
||||||
|
|
||||||
const subscriptionSnapshot = {
|
const subscriptionSnapshot = {
|
||||||
id: anyString,
|
id: anyString,
|
||||||
@ -49,6 +51,7 @@ describe('Members API: edit subscriptions', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme'); // member uuid changes with every test run
|
||||||
mockManager.mockStripe();
|
mockManager.mockStripe();
|
||||||
mockManager.mockMail();
|
mockManager.mockMail();
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework');
|
const {agentProvider, mockManager, fixtureManager, matchers} = require('../../utils/e2e-framework');
|
||||||
const {anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyArray} = matchers;
|
const {anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyArray} = matchers;
|
||||||
|
const settingsHelpers = require('../../../core/server/services/settings-helpers');
|
||||||
|
const sinon = require('sinon');
|
||||||
|
|
||||||
const memberMatcherShallowIncludesForNewsletters = {
|
const memberMatcherShallowIncludesForNewsletters = {
|
||||||
id: anyObjectId,
|
id: anyObjectId,
|
||||||
@ -20,6 +22,10 @@ describe('Members API - With Newsletters', function () {
|
|||||||
await agent.loginAsOwner();
|
await agent.loginAsOwner();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme');
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
mockManager.restore();
|
mockManager.restore();
|
||||||
});
|
});
|
||||||
@ -60,6 +66,10 @@ describe('Members API - With Newsletters - compat mode', function () {
|
|||||||
await agent.loginAsOwner();
|
await agent.loginAsOwner();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme');
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
mockManager.restore();
|
mockManager.restore();
|
||||||
});
|
});
|
||||||
|
@ -22,6 +22,7 @@ const settingsCache = require('../../../core/shared/settings-cache');
|
|||||||
const DomainEvents = require('@tryghost/domain-events');
|
const DomainEvents = require('@tryghost/domain-events');
|
||||||
const logging = require('@tryghost/logging');
|
const logging = require('@tryghost/logging');
|
||||||
const {stripeMocker, mockLabsDisabled} = require('../../utils/e2e-framework-mock-manager');
|
const {stripeMocker, mockLabsDisabled} = require('../../utils/e2e-framework-mock-manager');
|
||||||
|
const settingsHelpers = require('../../../core/server/services/settings-helpers');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert that haystack and needles match, ignoring the order.
|
* Assert that haystack and needles match, ignoring the order.
|
||||||
@ -136,7 +137,8 @@ function buildMemberWithIncludesSnapshot(options) {
|
|||||||
attribution: attributionSnapshot,
|
attribution: attributionSnapshot,
|
||||||
newsletters: new Array(options.newsletters).fill(newsletterSnapshot),
|
newsletters: new Array(options.newsletters).fill(newsletterSnapshot),
|
||||||
subscriptions: anyArray,
|
subscriptions: anyArray,
|
||||||
labels: anyArray
|
labels: anyArray,
|
||||||
|
unsubscribe_url: anyString
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +156,8 @@ const memberMatcherShallowIncludes = {
|
|||||||
created_at: anyISODateTime,
|
created_at: anyISODateTime,
|
||||||
updated_at: anyISODateTime,
|
updated_at: anyISODateTime,
|
||||||
subscriptions: anyArray,
|
subscriptions: anyArray,
|
||||||
labels: anyArray
|
labels: anyArray,
|
||||||
|
unsubscribe_url: anyString
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -487,13 +490,14 @@ describe('Members API', function () {
|
|||||||
agent = await agentProvider.getAdminAPIAgent();
|
agent = await agentProvider.getAdminAPIAgent();
|
||||||
await fixtureManager.init('posts', 'newsletters', 'members:newsletters', 'comments', 'redirects', 'clicks');
|
await fixtureManager.init('posts', 'newsletters', 'members:newsletters', 'comments', 'redirects', 'clicks');
|
||||||
await agent.loginAsOwner();
|
await agent.loginAsOwner();
|
||||||
|
|
||||||
newsletters = await getNewsletters();
|
newsletters = await getNewsletters();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockManager.mockStripe();
|
mockManager.mockStripe();
|
||||||
emailMockReceiver = mockManager.mockMail();
|
emailMockReceiver = mockManager.mockMail();
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
@ -1668,6 +1668,94 @@ Object {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Comments API when commenting enabled for all when authenticated Browsing comments does not return the member unsubscribe_url 1: [body] 1`] = `
|
||||||
|
Object {
|
||||||
|
"comments": Array [
|
||||||
|
Object {
|
||||||
|
"count": Object {
|
||||||
|
"likes": Any<Number>,
|
||||||
|
"replies": 0,
|
||||||
|
},
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"edited_at": null,
|
||||||
|
"html": "<p>This is a comment</p>",
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"liked": Any<Boolean>,
|
||||||
|
"member": Object {
|
||||||
|
"avatar_image": null,
|
||||||
|
"expertise": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"name": "Egon Spengler",
|
||||||
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
|
},
|
||||||
|
"replies": Array [],
|
||||||
|
"status": "published",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"count": Object {
|
||||||
|
"likes": Any<Number>,
|
||||||
|
"replies": Any<Number>,
|
||||||
|
},
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"edited_at": null,
|
||||||
|
"html": "<p>This is a comment</p>",
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"liked": Any<Boolean>,
|
||||||
|
"member": Object {
|
||||||
|
"avatar_image": null,
|
||||||
|
"expertise": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"name": "Mr Egg",
|
||||||
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
|
},
|
||||||
|
"replies": Array [
|
||||||
|
Object {
|
||||||
|
"count": Object {
|
||||||
|
"likes": Any<Number>,
|
||||||
|
},
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"edited_at": null,
|
||||||
|
"html": "<p>This is a reply</p>",
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"liked": Any<Boolean>,
|
||||||
|
"member": Object {
|
||||||
|
"avatar_image": null,
|
||||||
|
"expertise": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"name": null,
|
||||||
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
|
},
|
||||||
|
"status": "published",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"status": "published",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"meta": Object {
|
||||||
|
"pagination": Object {
|
||||||
|
"limit": 15,
|
||||||
|
"next": null,
|
||||||
|
"page": 1,
|
||||||
|
"pages": 1,
|
||||||
|
"prev": null,
|
||||||
|
"total": 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Comments API when commenting enabled for all when authenticated Browsing comments does not return the member unsubscribe_url 2: [headers] 1`] = `
|
||||||
|
Object {
|
||||||
|
"access-control-allow-origin": "*",
|
||||||
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
|
"content-length": "1118",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
"vary": "Accept-Encoding",
|
||||||
|
"x-powered-by": "Express",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Comments API when commenting enabled for all when authenticated Can browse all comments of a post (legacy) 1: [body] 1`] = `
|
exports[`Comments API when commenting enabled for all when authenticated Can browse all comments of a post (legacy) 1: [body] 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"comments": Array [
|
"comments": Array [
|
||||||
|
@ -501,6 +501,15 @@ describe('Comments API', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Browsing comments does not return the member unsubscribe_url', async function () {
|
||||||
|
await setupBrowseCommentsData();
|
||||||
|
const response = await testGetComments(`/api/comments/post/${postId}/`, [
|
||||||
|
commentMatcher,
|
||||||
|
commentMatcherWithReplies({replies: 1})
|
||||||
|
]);
|
||||||
|
should.not.exist(response.body.comments[0].unsubscribe_url);
|
||||||
|
});
|
||||||
|
|
||||||
it('Can reply to your own comment', async function () {
|
it('Can reply to your own comment', async function () {
|
||||||
// Should not update last_seen_at or last_commented_at when both are already set to a value on the same day
|
// Should not update last_seen_at or last_commented_at when both are already set to a value on the same day
|
||||||
const timezone = settingsCache.get('timezone');
|
const timezone = settingsCache.get('timezone');
|
||||||
|
@ -32,6 +32,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -40,7 +41,7 @@ exports[`Comments API when authenticated can get member data 2: [headers] 1`] =
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "621",
|
"content-length": "710",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
@ -159,6 +160,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -167,7 +169,7 @@ exports[`Comments API when authenticated can update comment notifications 2: [he
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "633",
|
"content-length": "722",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
@ -245,6 +247,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -253,7 +256,7 @@ exports[`Comments API when authenticated can update member expertise 2: [headers
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "634",
|
"content-length": "723",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
@ -293,6 +296,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -301,7 +305,7 @@ exports[`Comments API when authenticated can update name 2: [headers] 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "632",
|
"content-length": "721",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
@ -341,6 +345,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -349,7 +354,7 @@ exports[`Comments API when authenticated trims whitespace from expertise 2: [hea
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "623",
|
"content-length": "712",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"vary": "Accept-Encoding",
|
"vary": "Accept-Encoding",
|
||||||
@ -441,6 +446,7 @@ Object {
|
|||||||
"paid": false,
|
"paid": false,
|
||||||
"subscribed": false,
|
"subscribed": false,
|
||||||
"subscriptions": Array [],
|
"subscriptions": Array [],
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -449,7 +455,7 @@ exports[`Comments API when caching members content is enabled sets ghost-access
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "*",
|
"access-control-allow-origin": "*",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "621",
|
"content-length": "710",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
"set-cookie": Array [
|
"set-cookie": Array [
|
||||||
|
@ -27,6 +27,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -38,7 +39,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2550",
|
"content-length": "2639",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -74,6 +75,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -85,7 +87,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2564",
|
"content-length": "2653",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -121,6 +123,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -132,7 +135,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2452",
|
"content-length": "2541",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -168,6 +171,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -179,7 +183,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2612",
|
"content-length": "2701",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -215,6 +219,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -226,7 +231,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2578",
|
"content-length": "2667",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -262,6 +267,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -273,7 +279,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2592",
|
"content-length": "2681",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -309,6 +315,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -320,7 +327,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent with
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2506",
|
"content-length": "2595",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -356,6 +363,7 @@ Object {
|
|||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"subscriptions": Any<Array>,
|
"subscriptions": Any<Array>,
|
||||||
"tiers": Any<Array>,
|
"tiers": Any<Array>,
|
||||||
|
"unsubscribe_url": "http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme",
|
||||||
"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\\}\\\\\\.000Z/,
|
||||||
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
},
|
},
|
||||||
@ -367,7 +375,7 @@ exports[`Members API Member attribution Creates a SubscriptionCreatedEvent witho
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"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",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2452",
|
"content-length": "2541",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
@ -42,6 +42,7 @@ describe('Comments API', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme');
|
||||||
mockManager.mockMail();
|
mockManager.mockMail();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ const urlService = require('../../../core/server/services/url');
|
|||||||
const urlUtils = require('../../../core/shared/url-utils');
|
const urlUtils = require('../../../core/shared/url-utils');
|
||||||
const DomainEvents = require('@tryghost/domain-events');
|
const DomainEvents = require('@tryghost/domain-events');
|
||||||
const {anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyString, anyArray, anyObject} = matchers;
|
const {anyContentVersion, anyEtag, anyObjectId, anyUuid, anyISODateTime, anyString, anyArray, anyObject} = matchers;
|
||||||
|
const settingsHelpers = require('../../../core/server/services/settings-helpers');
|
||||||
|
const sinon = require('sinon');
|
||||||
|
|
||||||
let membersAgent;
|
let membersAgent;
|
||||||
let adminAgent;
|
let adminAgent;
|
||||||
@ -120,6 +122,8 @@ describe('Members API', function () {
|
|||||||
|
|
||||||
return [500];
|
return [500];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sinon.stub(settingsHelpers, 'createUnsubscribeUrl').returns('http://domain.com/unsubscribe/?uuid=memberuuid&key=abc123dontstealme');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
@ -829,9 +829,10 @@ describe('Front-end members behavior', function () {
|
|||||||
'created_at',
|
'created_at',
|
||||||
'enable_comment_notifications',
|
'enable_comment_notifications',
|
||||||
'newsletters',
|
'newsletters',
|
||||||
'email_suppression'
|
'email_suppression',
|
||||||
|
'unsubscribe_url'
|
||||||
]);
|
]);
|
||||||
Object.keys(memberData).should.have.length(13);
|
Object.keys(memberData).should.have.length(14);
|
||||||
memberData.should.not.have.property('id');
|
memberData.should.not.have.property('id');
|
||||||
memberData.newsletters.should.have.length(1);
|
memberData.newsletters.should.have.length(1);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ describe('Members Service - utils', function () {
|
|||||||
suppressed: false,
|
suppressed: false,
|
||||||
info: null
|
info: null
|
||||||
},
|
},
|
||||||
|
unsubscribe_url: undefined,
|
||||||
created_at: '2020-01-01T00:00:00.000Z'
|
created_at: '2020-01-01T00:00:00.000Z'
|
||||||
});
|
});
|
||||||
should(member1).deepEqual({
|
should(member1).deepEqual({
|
||||||
@ -37,6 +38,7 @@ describe('Members Service - utils', function () {
|
|||||||
expertise: null,
|
expertise: null,
|
||||||
firstname: 'Jamie',
|
firstname: 'Jamie',
|
||||||
avatar_image: 'https://gravatar.com/avatar/7d8efd2c2a781111599a8cae293cf704?s=250&d=blank',
|
avatar_image: 'https://gravatar.com/avatar/7d8efd2c2a781111599a8cae293cf704?s=250&d=blank',
|
||||||
|
unsubscribe_url: undefined,
|
||||||
subscribed: true,
|
subscribed: true,
|
||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
paid: false,
|
paid: false,
|
||||||
@ -69,6 +71,7 @@ describe('Members Service - utils', function () {
|
|||||||
sort_order: 0
|
sort_order: 0
|
||||||
}],
|
}],
|
||||||
enable_comment_notifications: false,
|
enable_comment_notifications: false,
|
||||||
|
unsubscribe_url: undefined,
|
||||||
created_at: '2020-01-01T00:00:00.000Z'
|
created_at: '2020-01-01T00:00:00.000Z'
|
||||||
});
|
});
|
||||||
should(member1).deepEqual({
|
should(member1).deepEqual({
|
||||||
@ -89,6 +92,7 @@ describe('Members Service - utils', function () {
|
|||||||
sort_order: 0
|
sort_order: 0
|
||||||
}],
|
}],
|
||||||
enable_comment_notifications: false,
|
enable_comment_notifications: false,
|
||||||
|
unsubscribe_url: undefined,
|
||||||
created_at: '2020-01-01T00:00:00.000Z'
|
created_at: '2020-01-01T00:00:00.000Z'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,9 @@ const should = require('should');
|
|||||||
const sinon = require('sinon');
|
const sinon = require('sinon');
|
||||||
const configUtils = require('../../../../utils/configUtils');
|
const configUtils = require('../../../../utils/configUtils');
|
||||||
const SettingsHelpers = require('../../../../../core/server/services/settings-helpers/SettingsHelpers');
|
const SettingsHelpers = require('../../../../../core/server/services/settings-helpers/SettingsHelpers');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const mockValidationKey = 'validation_key';
|
||||||
|
|
||||||
function createSettingsMock({setDirect, setConnect}) {
|
function createSettingsMock({setDirect, setConnect}) {
|
||||||
const getStub = sinon.stub();
|
const getStub = sinon.stub();
|
||||||
@ -27,67 +30,122 @@ function createSettingsMock({setDirect, setConnect}) {
|
|||||||
getStub.withArgs('stripe_connect_display_name').returns('Test');
|
getStub.withArgs('stripe_connect_display_name').returns('Test');
|
||||||
getStub.withArgs('stripe_connect_account_id').returns('ac_XXXXXXXXXXXXX');
|
getStub.withArgs('stripe_connect_account_id').returns('ac_XXXXXXXXXXXXX');
|
||||||
|
|
||||||
|
getStub.withArgs('members_email_auth_secret').returns(mockValidationKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get: getStub
|
get: getStub
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Settings Helpers - getActiveStripeKeys', function () {
|
describe('Settings Helpers', function () {
|
||||||
beforeEach(function () {
|
describe('getActiveStripeKeys', function () {
|
||||||
configUtils.set({
|
beforeEach(function () {
|
||||||
url: 'http://domain.tld/subdir',
|
configUtils.set({
|
||||||
admin: {url: 'http://sub.domain.tld'}
|
url: 'http://domain.tld/subdir',
|
||||||
|
admin: {url: 'http://sub.domain.tld'}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async function () {
|
||||||
|
await configUtils.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Uses direct keys when stripeDirect is true, regardles of which keys exist', function () {
|
||||||
|
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||||
|
configUtils.set({
|
||||||
|
stripeDirect: true
|
||||||
|
});
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||||
|
const keys = settingsHelpers.getActiveStripeKeys();
|
||||||
|
|
||||||
|
should.equal(keys.publicKey, 'direct_publishable');
|
||||||
|
should.equal(keys.secretKey, 'direct_secret');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Does not use connect keys if stripeDirect is true, and the direct keys do not exist', function () {
|
||||||
|
const fakeSettings = createSettingsMock({setDirect: false, setConnect: true});
|
||||||
|
configUtils.set({
|
||||||
|
stripeDirect: true
|
||||||
|
});
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||||
|
const keys = settingsHelpers.getActiveStripeKeys();
|
||||||
|
|
||||||
|
should.equal(keys, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Uses connect keys when stripeDirect is false, and the connect keys exist', function () {
|
||||||
|
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||||
|
configUtils.set({
|
||||||
|
stripeDirect: false
|
||||||
|
});
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||||
|
const keys = settingsHelpers.getActiveStripeKeys();
|
||||||
|
|
||||||
|
should.equal(keys.publicKey, 'connect_publishable');
|
||||||
|
should.equal(keys.secretKey, 'connect_secret');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Uses direct keys when stripeDirect is false, but the connect keys do not exist', function () {
|
||||||
|
const fakeSettings = createSettingsMock({setDirect: true, setConnect: false});
|
||||||
|
configUtils.set({
|
||||||
|
stripeDirect: false
|
||||||
|
});
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||||
|
const keys = settingsHelpers.getActiveStripeKeys();
|
||||||
|
|
||||||
|
should.equal(keys.publicKey, 'direct_publishable');
|
||||||
|
should.equal(keys.secretKey, 'direct_secret');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function () {
|
describe('getMembersValidationKey', function () {
|
||||||
await configUtils.restore();
|
it('returns a key that can be used to validate members', function () {
|
||||||
|
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
||||||
|
const key = settingsHelpers.getMembersValidationKey();
|
||||||
|
should.equal(key, 'validation_key');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Uses direct keys when stripeDirect is true, regardles of which keys exist', function () {
|
describe('createUnsubscribeUrl', function () {
|
||||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
const memberUuid = 'memberuuid';
|
||||||
configUtils.set({
|
const newsletterUuid = 'newsletteruuid';
|
||||||
stripeDirect: true
|
const urlUtils = {
|
||||||
|
urlFor: sinon.stub().returns('http://domain.com/')
|
||||||
|
};
|
||||||
|
const memberUuidHash = crypto.createHmac('sha256', mockValidationKey).update(`${memberUuid}`).digest('hex');
|
||||||
|
let fakeSettings;
|
||||||
|
|
||||||
|
before(function () {
|
||||||
|
fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
||||||
});
|
});
|
||||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
|
||||||
const keys = settingsHelpers.getActiveStripeKeys();
|
|
||||||
|
|
||||||
should.equal(keys.publicKey, 'direct_publishable');
|
afterEach(async function () {
|
||||||
should.equal(keys.secretKey, 'direct_secret');
|
await configUtils.restore();
|
||||||
});
|
|
||||||
|
|
||||||
it('Does not use connect keys if stripeDirect is true, and the direct keys do not exist', function () {
|
|
||||||
const fakeSettings = createSettingsMock({setDirect: false, setConnect: true});
|
|
||||||
configUtils.set({
|
|
||||||
stripeDirect: true
|
|
||||||
});
|
});
|
||||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
|
||||||
const keys = settingsHelpers.getActiveStripeKeys();
|
|
||||||
|
|
||||||
should.equal(keys, null);
|
it('returns a generic unsubscribe url when no uuid is provided', function () {
|
||||||
});
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils});
|
||||||
|
const url = settingsHelpers.createUnsubscribeUrl(null);
|
||||||
it('Uses connect keys when stripeDirect is false, and the connect keys exist', function () {
|
should.equal(url, 'http://domain.com/unsubscribe/?preview=1');
|
||||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: true});
|
|
||||||
configUtils.set({
|
|
||||||
stripeDirect: false
|
|
||||||
});
|
});
|
||||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
|
||||||
const keys = settingsHelpers.getActiveStripeKeys();
|
|
||||||
|
|
||||||
should.equal(keys.publicKey, 'connect_publishable');
|
it('returns a url that can be used to unsubscribe a member', function () {
|
||||||
should.equal(keys.secretKey, 'connect_secret');
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils});
|
||||||
});
|
const url = settingsHelpers.createUnsubscribeUrl(memberUuid);
|
||||||
|
should.equal(url, `http://domain.com/unsubscribe/?uuid=memberuuid&key=${memberUuidHash}`);
|
||||||
it('Uses direct keys when stripeDirect is false, but the connect keys do not exist', function () {
|
|
||||||
const fakeSettings = createSettingsMock({setDirect: true, setConnect: false});
|
|
||||||
configUtils.set({
|
|
||||||
stripeDirect: false
|
|
||||||
});
|
});
|
||||||
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils: {}});
|
|
||||||
const keys = settingsHelpers.getActiveStripeKeys();
|
|
||||||
|
|
||||||
should.equal(keys.publicKey, 'direct_publishable');
|
it('returns a url that can be used to unsubscribe a member for a given newsletter', function () {
|
||||||
should.equal(keys.secretKey, 'direct_secret');
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils});
|
||||||
|
const url = settingsHelpers.createUnsubscribeUrl(memberUuid, {newsletterUuid});
|
||||||
|
should.equal(url, `http://domain.com/unsubscribe/?uuid=memberuuid&key=${memberUuidHash}&newsletter=newsletteruuid`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a url that can be used to unsubscribe a member from comments', function () {
|
||||||
|
const settingsHelpers = new SettingsHelpers({settingsCache: fakeSettings, config: configUtils.config, urlUtils});
|
||||||
|
const url = settingsHelpers.createUnsubscribeUrl(memberUuid, {comments: true});
|
||||||
|
should.equal(url, `http://domain.com/unsubscribe/?uuid=memberuuid&key=${memberUuidHash}&comments=1`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -123,7 +123,7 @@ class EmailRenderer {
|
|||||||
/**
|
/**
|
||||||
* @param {object} dependencies
|
* @param {object} dependencies
|
||||||
* @param {object} dependencies.settingsCache
|
* @param {object} dependencies.settingsCache
|
||||||
* @param {{getNoReplyAddress(): string, getMembersSupportAddress(): string, getMembersValidationKey(): string}} dependencies.settingsHelpers
|
* @param {{getNoReplyAddress(): string, getMembersSupportAddress(): string, getMembersValidationKey(): string, createUnsubscribeUrl(uuid: string, options: object): string}} dependencies.settingsHelpers
|
||||||
* @param {object} dependencies.renderers
|
* @param {object} dependencies.renderers
|
||||||
* @param {{render(object, options): string}} dependencies.renderers.lexical
|
* @param {{render(object, options): string}} dependencies.renderers.lexical
|
||||||
* @param {{render(object, options): string}} dependencies.renderers.mobiledoc
|
* @param {{render(object, options): string}} dependencies.renderers.mobiledoc
|
||||||
@ -505,27 +505,7 @@ class EmailRenderer {
|
|||||||
* @param {boolean} [options.comments] Unsubscribe from comment emails
|
* @param {boolean} [options.comments] Unsubscribe from comment emails
|
||||||
*/
|
*/
|
||||||
createUnsubscribeUrl(uuid, options = {}) {
|
createUnsubscribeUrl(uuid, options = {}) {
|
||||||
const siteUrl = this.#urlUtils.urlFor('home', true);
|
return this.#settingsHelpers.createUnsubscribeUrl(uuid, options);
|
||||||
const unsubscribeUrl = new URL(siteUrl);
|
|
||||||
const key = this.#settingsHelpers.getMembersValidationKey();
|
|
||||||
unsubscribeUrl.pathname = `${unsubscribeUrl.pathname}/unsubscribe/`.replace('//', '/');
|
|
||||||
if (uuid) {
|
|
||||||
// hash key with member uuid for verification (and to not leak uuid) - it's possible to update member email prefs without logging in
|
|
||||||
// @ts-ignore
|
|
||||||
const hmac = crypto.createHmac('sha256', key).update(`${uuid}`).digest('hex');
|
|
||||||
unsubscribeUrl.searchParams.set('uuid', uuid);
|
|
||||||
unsubscribeUrl.searchParams.set('key', hmac);
|
|
||||||
} else {
|
|
||||||
unsubscribeUrl.searchParams.set('preview', '1');
|
|
||||||
}
|
|
||||||
if (options.newsletterUuid) {
|
|
||||||
unsubscribeUrl.searchParams.set('newsletter', options.newsletterUuid);
|
|
||||||
}
|
|
||||||
if (options.comments) {
|
|
||||||
unsubscribeUrl.searchParams.set('comments', '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
return unsubscribeUrl.href;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,6 +61,10 @@ async function validateHtml(html) {
|
|||||||
assert.equal(report.valid, true, 'Expected valid HTML without warnings, got errors:\n' + parsedErrors.join('\n\n'));
|
assert.equal(report.valid, true, 'Expected valid HTML without warnings, got errors:\n' + parsedErrors.join('\n\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createUnsubscribeUrl = (uuid) => {
|
||||||
|
return `https://example.com/unsubscribe/?uuid=${uuid}&key=456`;
|
||||||
|
};
|
||||||
|
|
||||||
const getMembersValidationKey = () => {
|
const getMembersValidationKey = () => {
|
||||||
return 'members-key';
|
return 'members-key';
|
||||||
};
|
};
|
||||||
@ -98,7 +102,7 @@ describe('Email renderer', function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
settingsHelpers: {getMembersValidationKey}
|
settingsHelpers: {getMembersValidationKey,createUnsubscribeUrl}
|
||||||
});
|
});
|
||||||
newsletter = createModel({
|
newsletter = createModel({
|
||||||
uuid: 'newsletteruuid'
|
uuid: 'newsletteruuid'
|
||||||
@ -119,8 +123,8 @@ describe('Email renderer', function () {
|
|||||||
assert.equal(replacements.length, 1);
|
assert.equal(replacements.length, 1);
|
||||||
assert.equal(replacements[0].token.toString(), '/%%\\{list_unsubscribe\\}%%/g');
|
assert.equal(replacements[0].token.toString(), '/%%\\{list_unsubscribe\\}%%/g');
|
||||||
assert.equal(replacements[0].id, 'list_unsubscribe');
|
assert.equal(replacements[0].id, 'list_unsubscribe');
|
||||||
const memberHmac = crypto.createHmac('sha256', getMembersValidationKey()).update(member.uuid).digest('hex');
|
const unsubscribeUrl = createUnsubscribeUrl(member.uuid);
|
||||||
assert.equal(replacements[0].getValue(member), `http://example.com/subdirectory/unsubscribe/?uuid=${member.uuid}&key=${memberHmac}&newsletter=newsletteruuid`);
|
assert.equal(replacements[0].getValue(member), unsubscribeUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a replacement if it is used', function () {
|
it('returns a replacement if it is used', function () {
|
||||||
@ -156,8 +160,8 @@ describe('Email renderer', function () {
|
|||||||
assert.equal(replacements.length, 2);
|
assert.equal(replacements.length, 2);
|
||||||
assert.equal(replacements[0].token.toString(), '/%%\\{unsubscribe_url\\}%%/g');
|
assert.equal(replacements[0].token.toString(), '/%%\\{unsubscribe_url\\}%%/g');
|
||||||
assert.equal(replacements[0].id, 'unsubscribe_url');
|
assert.equal(replacements[0].id, 'unsubscribe_url');
|
||||||
const memberHmac = crypto.createHmac('sha256', getMembersValidationKey()).update(member.uuid).digest('hex');
|
const unsubscribeUrl = createUnsubscribeUrl(member.uuid);
|
||||||
assert.equal(replacements[0].getValue(member), `http://example.com/subdirectory/unsubscribe/?uuid=${member.uuid}&key=${memberHmac}&newsletter=newsletteruuid`);
|
assert.equal(replacements[0].getValue(member), unsubscribeUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns correct name', function () {
|
it('returns correct name', function () {
|
||||||
@ -2303,7 +2307,8 @@ describe('Email renderer', function () {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
settingsHelpers: {
|
settingsHelpers: {
|
||||||
getMembersValidationKey
|
getMembersValidationKey,
|
||||||
|
createUnsubscribeUrl
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -2312,21 +2317,26 @@ describe('Email renderer', function () {
|
|||||||
const response = await emailRenderer.createUnsubscribeUrl('memberuuid', {
|
const response = await emailRenderer.createUnsubscribeUrl('memberuuid', {
|
||||||
newsletterUuid: 'newsletteruuid'
|
newsletterUuid: 'newsletteruuid'
|
||||||
});
|
});
|
||||||
const memberHmac = crypto.createHmac('sha256', getMembersValidationKey()).update('memberuuid').digest('hex');
|
const unsubscribeUrl = createUnsubscribeUrl('memberuuid', {
|
||||||
assert.equal(response, `http://example.com/subdirectory/unsubscribe/?uuid=memberuuid&key=${memberHmac}&newsletter=newsletteruuid`);
|
newsletterUuid: 'newsletteruuid'
|
||||||
|
});
|
||||||
|
assert.equal(response, unsubscribeUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes comments', async function () {
|
it('includes comments', async function () {
|
||||||
const response = await emailRenderer.createUnsubscribeUrl('memberuuid', {
|
const response = await emailRenderer.createUnsubscribeUrl('memberuuid', {
|
||||||
comments: true
|
comments: true
|
||||||
});
|
});
|
||||||
const memberHmac = crypto.createHmac('sha256', getMembersValidationKey()).update('memberuuid').digest('hex');
|
const unsubscribeUrl = createUnsubscribeUrl('memberuuid', {
|
||||||
assert.equal(response, `http://example.com/subdirectory/unsubscribe/?uuid=memberuuid&key=${memberHmac}&comments=1`);
|
comments: true
|
||||||
|
});
|
||||||
|
assert.equal(response, unsubscribeUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works for previews', async function () {
|
it('works for previews', async function () {
|
||||||
const response = await emailRenderer.createUnsubscribeUrl();
|
const response = await emailRenderer.createUnsubscribeUrl();
|
||||||
assert.equal(response, `http://example.com/subdirectory/unsubscribe/?preview=1`);
|
const unsubscribeUrl = createUnsubscribeUrl();
|
||||||
|
assert.equal(response, unsubscribeUrl);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -72,7 +72,8 @@ module.exports = function MembersAPI({
|
|||||||
memberAttributionService,
|
memberAttributionService,
|
||||||
emailSuppressionList,
|
emailSuppressionList,
|
||||||
settingsCache,
|
settingsCache,
|
||||||
sentry
|
sentry,
|
||||||
|
settingsHelpers
|
||||||
}) {
|
}) {
|
||||||
const tokenService = new TokenService({
|
const tokenService = new TokenService({
|
||||||
privateKey,
|
privateKey,
|
||||||
@ -144,7 +145,8 @@ module.exports = function MembersAPI({
|
|||||||
labsService,
|
labsService,
|
||||||
stripeService: stripeAPIService,
|
stripeService: stripeAPIService,
|
||||||
memberAttributionService,
|
memberAttributionService,
|
||||||
emailSuppressionList
|
emailSuppressionList,
|
||||||
|
settingsHelpers
|
||||||
});
|
});
|
||||||
|
|
||||||
const geolocationService = new GeolocationService();
|
const geolocationService = new GeolocationService();
|
||||||
|
@ -37,8 +37,9 @@ module.exports = class MemberBREADService {
|
|||||||
* @param {IStripeService} deps.stripeService
|
* @param {IStripeService} deps.stripeService
|
||||||
* @param {import('@tryghost/member-attribution/lib/service')} deps.memberAttributionService
|
* @param {import('@tryghost/member-attribution/lib/service')} deps.memberAttributionService
|
||||||
* @param {import('@tryghost/email-suppression-list/lib/email-suppression-list').IEmailSuppressionList} deps.emailSuppressionList
|
* @param {import('@tryghost/email-suppression-list/lib/email-suppression-list').IEmailSuppressionList} deps.emailSuppressionList
|
||||||
|
* @param {import('@tryghost/settings-helpers')} deps.settingsHelpers
|
||||||
*/
|
*/
|
||||||
constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList}) {
|
constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList, settingsHelpers}) {
|
||||||
this.offersAPI = offersAPI;
|
this.offersAPI = offersAPI;
|
||||||
/** @private */
|
/** @private */
|
||||||
this.memberRepository = memberRepository;
|
this.memberRepository = memberRepository;
|
||||||
@ -52,6 +53,8 @@ module.exports = class MemberBREADService {
|
|||||||
this.memberAttributionService = memberAttributionService;
|
this.memberAttributionService = memberAttributionService;
|
||||||
/** @private */
|
/** @private */
|
||||||
this.emailSuppressionList = emailSuppressionList;
|
this.emailSuppressionList = emailSuppressionList;
|
||||||
|
/** @private */
|
||||||
|
this.settingsHelpers = settingsHelpers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,6 +249,9 @@ module.exports = class MemberBREADService {
|
|||||||
info: suppressionData.info
|
info: suppressionData.info
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unsubscribeUrl = this.settingsHelpers.createUnsubscribeUrl(member.id);
|
||||||
|
member.unsubscribe_url = unsubscribeUrl;
|
||||||
|
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,6 +432,7 @@ module.exports = class MemberBREADService {
|
|||||||
suppressed: bulkSuppressionData[index].suppressed || !!model.get('email_disabled'),
|
suppressed: bulkSuppressionData[index].suppressed || !!model.get('email_disabled'),
|
||||||
info: bulkSuppressionData[index].info
|
info: bulkSuppressionData[index].info
|
||||||
};
|
};
|
||||||
|
member.unsubscribe_url = this.settingsHelpers.createUnsubscribeUrl(member.id);
|
||||||
return member;
|
return member;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ describe('MemberBreadService', function () {
|
|||||||
|
|
||||||
const getService = () => {
|
const getService = () => {
|
||||||
return new MemberBreadService({
|
return new MemberBreadService({
|
||||||
|
settingsHelpers: {
|
||||||
|
createUnsubscribeUrl: sinon.stub().returns('https://example.com/unsubscribe/?uuid=123&key=456')
|
||||||
|
},
|
||||||
memberRepository: memberRepositoryStub,
|
memberRepository: memberRepositoryStub,
|
||||||
memberAttributionService: memberAttributionServiceStub,
|
memberAttributionService: memberAttributionServiceStub,
|
||||||
emailSuppressionList: emailSuppressionListStub
|
emailSuppressionList: emailSuppressionListStub
|
||||||
@ -286,5 +289,12 @@ describe('MemberBreadService', function () {
|
|||||||
info: 'bounce'
|
info: 'bounce'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns a member with an unsubscribe url', async function () {
|
||||||
|
const memberBreadService = getService();
|
||||||
|
const member = await memberBreadService.read({id: MEMBER_ID});
|
||||||
|
|
||||||
|
assert.equal(member.unsubscribe_url, 'https://example.com/unsubscribe/?uuid=123&key=456');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user