closes https://github.com/TryGhost/Toolbox/issues/386
- When the API request was made using staff token the source attribution was "user" instead of "api". Misattribution caused ripple effects in limit service.
- The fix also adds a new combination of data available on the `req` object - both `user` and `api_key` can be present when the request is done using a staff (user) token. Having both pieces of data on the request object gives more context for business logic, did not find a good reason to keep it "pure" with either `api_key` or `user` property.
refs https://github.com/TryGhost/Toolbox/issues/386
- Reusing tontext mapping logic to improve maintainability. It seems like the `update` method was not updated properly or intentionally was left out from 'import' source as that should not ever happen theoretically. Probably the latter is most likely.
- My reasoning on reusing same context to source mapping is: it is better to attribute an appropriate "import" source here. Who knows, maybe we'll have logic in the future where the importer updates instead of skipping existing members. It would not make sense to attribute the source to 'member' in that case, amirite?
- This refactor also makes maintainability of this code way easier
refs https://github.com/TryGhost/Team/issues/1728
- previously, we allowed a member to be mapped to multiple tiers simultaneously as an edge case, in case they managed to signup via another subscription
- since this was always an edge case and not supported, to simplify the flows going forward now that complimentary members can also upgrade, in case of an active subscription we'll always just attach the associated tier to member and remove all other tiers mapped to it
refs https://github.com/TryGhost/Team/issues/1808
refs https://github.com/TryGhost/Team/issues/1809
refs https://github.com/TryGhost/Team/issues/1820
refs https://github.com/TryGhost/Team/issues/1814
### Changes in `member-events` package
- Added MemberCreatedEvent (event, not model)
- Added SubscriptionCreatedEvent (event, not model)
### Added `member-attribution` package (new)
- Added the AttributionBuilder class which is able to convert a url history to an attribution object (exposed as getAttribution on the service itself, which handles the dependencies)
```
[{
"path": "/",
"time": 123
}]
```
to
```
{
"url": "/",
"id": null,
"type": "url"
}
```
- event handler listens for MemberCreatedEvent and SubscriptionCreatedEvent and creates the corresponding models in the database.
### Changes in `members-api` package
- Added urlHistory to `sendMagicLink` endpoint body + convert the urlHistory to an attribution object that is stored in the tokenData of the magic link (sent by Portal in this PR: https://github.com/TryGhost/Portal/pull/256).
- Added urlHistory to `createCheckoutSession` endpoint + convert the urlHistory to attribution keys that are saved in the Stripe Session metadata (sent by Portal in this PR: https://github.com/TryGhost/Portal/pull/256).
- Added attribution data property to member repository's create method (when a member is created)
- Dispatch MemberCreatedEvent with attribution
### Changes in `members-stripe-service` package (`ghost/stripe`)
- Dispatch SubscriptionCreatedEvent in WebhookController on subscription checkout (with attribution from session metadata)
refs https://github.com/TryGhost/Team/issues/1726
Free trial offers don't have a Stripe coupon created for them, as the trial is directly added to checkout session. So for mapping a subscription to offer, we pass the offer id directly from checkout metadata to link the subscription in backend with right offer data. This also handles the case where the offer id against a subscription can get overwritten for a subsequent subscription event, as the sub event from Stripe doesn't has the trial offer info.
- handles storing an offer id for a subscription
- updates member detail in Admin to show the offer info for a subscription
Without this check, an inactive price in our database will just be
reactivated each time it is required. This can cause issues when
prices have been deleted.
By adding this constraint to the query, we will create a new price in
Stripe and our database when attempting to use an inactive price, this
is particularly useful when trying to fix problems caused by Stripe
prices being deleted.
refs https://github.com/TryGhost/Team/issues/1724
With free trials, members can start subscriptions with a trial period. This change stores the information about trial start and end date for every subscription so it can be shown on Admin/Portal for member.
- adds new `trial_start_at` column for storing trial start date on Stripe subscription. Will in most cases match the start of subscription date.
- adds new `trial_end_at` column for storing trial end date on Stripe subscription.
- wires storing trial start and end values on stripe subscription
refs https://github.com/TryGhost/Team/issues/1724
- wires trial days stored on a tier to stripe checkout session creation
- removes deprecated `trial_from_plan` if trial days is set
refs https://github.com/TryGhost/Team/issues/1716
- Adds the bio field to the API output
- Allow setting bio when updating the member
- Includes new E2E tests for the members API that were missing
refs https://github.com/TryGhost/Team/issues/1709
- New event type `comment_event` (comments and replies of a member in the activity feed)
- Includes member, post and parent relation by default
- Added new output mapper for ActivityFeed events
**Changes to `Comment` model:**
* **Only limit comment fetched to root comments when not authenticated as a user:**
`enforcedFilters` is applied to all queries, which is a problem because for the activity feed we also need to fetch comments which have a parent_id that is not null (`Member x replied to a comment`). The current filter in the model is specifically for the members API, not the admin API (so checking the user should fix that, not sure if that is a good pattern but couldn’t find a better alternative).
* **Only set default relations for comments when withRelated is empty or not set:**
`defaultRelations`: Right now, for every fetch it would force all these relations. But we don’t need all those relations for the activity feed; So I updated the pattern to only set the default relations when it is empty (which we also do on a couple of other places and seems like a good pattern). I also updated the comments-ui frontend to not send ?include
refs https://github.com/TryGhost/Team/issues/1717
- Updates last_commented_at and last_seen_at (only once a day)
- Used the LastSeenAtUpdater, so we can combine updating last_commented_at and last_seen_at in one query + used same pattern
- Updated comments service to await emails in order to make E2E tests more stable (as we don't have any method to await emails and test emails otherwise). This removed the email sending logic from the `onCreated` hook of the model.
refs https://github.com/TryGhost/Team/issues/1674
- While preparing the changes had a look around and made small refactors to understand the codebase a little better. In general it's best to keep the method parameters as small and precise as possible instead of passing around a "bag-of-all-the-things" like "data" around
refs https://github.com/TryGhost/Team/issues/1674
- While preparing the changes had a look around and made small refactors to understand the codebase a little better. In general it's best to keep the method parameters as small and precise as possible instead of passing around a "bag-of-all-the-things" like "data" around
refs https://ghost.slack.com/archives/C02G9E68C/p1652980792270029
When bulk unsubscribing members, the number of deleted newsletter relations are returned instead of the number of members with newsletters that were cleared. This update deletes newsletter relations on member_id, so we can return the count of members instead of newsletter relations that were deleted.
Tests in https://github.com/TryGhost/Ghost/pull/14871
no issue
- Setting a dedicated source for imported members was already done with 455778662c
- This unifies the same source but keeps the usage of context setting on init alive
refs https://github.com/TryGhost/Team/issues/1577
The call to `edit` was not loading the newsletter relations which is needed
by the serializer used by the webhooks service.
Co-authored-by: Fabien "egg" O'Carroll <fabien@allou.is>
no issue
Using a designated source for members being added within the `importer` context will better describe the source in our members event table. It's more consistent with the usage of the other possible sources.
no issue
When importing members, the members-importer isn't aware of the context and therefore falls back to use `member` as a source in members event table. This makes it impossible to determine imported members from others.
- Added an `context` property to the options in the members-importer constructor
- Checked for `importer` context when creating member events and assigned the source `admin` to it
refs https://github.com/TryGhost/Team/issues/1478
- When creating or editing a member, we'll add the newsletter ids to the subscribe events
- When susbcribing to multiple newsletters, we'll create multiple subscribe events
- When removing newsletters from a member, we'll create one or more subscribe events
- Tests in https://github.com/TryGhost/Ghost/pull/14579
refs https://github.com/TryGhost/Team/issues/1519
- Removed offer metadata again from offer (added in one of the previous commits)
- Added `getByStripeCouponId` method in offer repository (required to find an offer based on the stripe_coupon_id)
- Match discounts from Stripe based on the stripe_coupon_id instead of metadata
refs https://github.com/TryGhost/Team/issues/1519
- Added offer repository dependency to member repository (offerAPI didn't work because it creates a new transaction that resulted in a deadlock during tests)
- Store the offer id from the Stripe subscription metadata in the subscription (only if the discount is still active)
- Also added the offer id to the metadata for a Stripe coupon, this will make adding and removing coupons a bit more foolproof
- Prefer the usage of the offer metadata from a coupon if it is present
- When no discount is applied to a subscription, it always sets the offer id to null, even when the metadata still contains the offer
- The offer_id remains stored when a subscription is canceled/expired
refs https://github.com/TryGhost/Team/issues/1469
Previously, members were subscribed to all available newsletters by default when added. This change updates the default newsletters subscription for member to take into account newsletter preferences for auto opt-in as well as visibility.
refs: https://github.com/TryGhost/Toolbox/issues/293
Things needed to create this:
* MemberSubscriptionEvent now has an import source
* Importer now creates events with this type
* Verification trigger logic changed to use 30 day window of imports
* Updated member mrr_delta calculation to use stored mrr
refs https://github.com/TryGhost/Team/issues/1456
Simplifies the calculation of MRR deltas, which will make it easier to update MRR to include offers and cancellations in the future.
* Adjusted MRR and MRR delta calculation to consider "forever" duration offers
refs https://github.com/TryGhost/Team/issues/1451
Uses the discount information from Stripe to calculate the MRR (this was the easiest way to include it + also supports manually created discounts from users)
refs https://github.com/TryGhost/Team/issues/1456
- Sets the `mrr` column correctly when updating a subscription
- We can use this value in the future to have an improved calculation of mrr_delta, but we first need to have the migration in place
- Updated missing cancelled -> canceled
refs https://github.com/TryGhost/Team/issues/1469
When a new member signs up on the site, by default they should get subscribed to all newsletters. A new member can get added via different ways (stripe webhook, admin add, free signup), so this change updates the base member repository which gets used irrespective of how member is added to ensure default newsletters are always included for new member.
If a member already has a custom newsletters list attached, we don't change anything.
refs https://github.com/TryGhost/Team/issues/1387
First, this default value didn't make sense as it was written when we
had a boolean visible column, second, we don't need to add defaults here
- this implementation is super tied to the models anyway, so we may as
well rely on the model defaults - if we do pull it out similar to
Offers, then it may make sense to add a default there.
We also update the JSDoc to match the updated data structure.
refs: 23b383bedf
- @tryghost/error constructors take an object, not a string - the expectation is that message, context & help should all be set
- This does the bare minimum and just ensures message is set correctly