fix https://linear.app/tryghost/issue/SLO-82/query-error-unexpected-character-in-filter-at-char-1
- previously, we weren't handling a parsing error, and just bubbling it
back up the chain
- this would result in an InternalServerError somewhere, which caused
500s
- we can handle this, because it's just a bad filter
- this adds handling so we return a 422 upon receiving an invalid filter
fixes PROD-102
When a newsletter has a sender_email stored in the database that Ghost
is not allowed to send from, we no longer return it as sender_email in
the API. Instead we return it as the sender_reply_to. That way the
expected behaviour is shown correctly in the frontend and the API result
also makes more sense.
In addition to that, when a change is made to a newsletters reply_to
address we'll clear any invalid sender_email values in that newsletter.
That makes sure we can clear the sender_reply_to value instead of
keeping the current fallback to sender_email if that one is stored.
On top of that, this change correclty updates the browse endpoint to use
the newsletter service instead of directly using the model.
fixes https://github.com/TryGhost/Product/issues/4108
- Updates filters behind a new alpha feature flag so you can also filter
on members who have email disabled (because the email had a permanent
bounce, they reported spam or the email address is invalid)
- When returning members, we now also use the email_disabled flag to set
email_suppression.suppressed correctly (in case they are out of sync,
which should normally never happen).
refs https://github.com/TryGhost/Product/issues/3875
When a member had a comped subscription, the portal was showing an
incorrect expiry date. This was because the `expiry_date` was being set
to the `created_at` date of the subscription, rather than the
`expiry_date` of the comped subscription
fixes https://github.com/TryGhost/Product/issues/3752
- Added some extra tests for edge cases
- Updated handling of multiple subscriptions so they are handled better
- Canceling a subscription when the member still has other subscriptions will now get handled correctly where the status and products of the member stay intact
refs: https://github.com/TryGhost/Toolbox/issues/595
We're rolling out new rules around the node assert library, the first of which is enforcing the use of assert/strict. This means we don't need to use the strict version of methods, as the standard version will work that way by default.
This caught some gotchas in our existing usage of assert where the lack of strict mode had unexpected results:
- Url matching needs to be done on `url.href` see aa58b354a4
- Null and undefined are not the same thing, there were a few cases of this being confused
- Particularly questionable changes in [PostExporter tests](c1a468744b) tracked [here](https://github.com/TryGhost/Team/issues/3505).
- A typo see eaac9c293a
Moving forward, using assert strict should help us to catch unexpected behaviour, particularly around nulls and undefineds during implementation.
As discussed with the product team we want to enforce kebab-case file names for
all files, with the exception of files which export a single class, in which
case they should be PascalCase and reflect the class which they export.
This will help find classes faster, and should push better naming for them too.
Some files and packages have been excluded from this linting, specifically when
a library or framework depends on the naming of a file for the functionality
e.g. Ember, knex-migrator, adapter-manager
refs https://github.com/TryGhost/Team/issues/2204
This was found during Tiers flows testing, the logic for fetching
price information from Tiers had not been updated to use the new Tiers
package and Payments service. This only affects Tiers created since 5.22.x
closes https://github.com/TryGhost/Team/issues/2184
- when using the old legacy method of `comped:true` to add complimentary subs to a member along with a label, the API call failed with `Internal Server error` and the member was added as free on the site.
- patches the options sent for fetching default product to only pick the relevant keys, as it was picking up the `withRelated` for `labels` that caused the API failure
fixes https://github.com/TryGhost/Team/issues/2129
- This changes how the activity feed API parses the filter.
- We now parse the filter early to a MongoDB filter, and split it in two. One of the filters is applied to the pageActions, and the other one is used individually for every event type. We now allow to use grouping and OR's inside the filters because of this change. As long as we don't combine filters on 'type' with other filters inside grouped filters or OR, then it is allowed.
- We make use of mongoTransformer to manually inject a mongo filter without needing to parse it from a string value again (that would make it a lot harder because we would have to convert the splitted filter back to a string and we currently don't have methods for that).
- Added sorting by id for events with the same timestamp (required for reliable pagination)
- Added id to each event (required for pagination)
- Added more tests for filters
- Added test for pagination
- Removed unsued getSubscriptions and getVolume methods
Used new mongo utility methods introduced here: https://github.com/TryGhost/NQL/pull/49
refs 82ed10473b
refs https://github.com/TryGhost/Team/issues/1869
- getDefaultProduct has unified logic across different places (see refed commit). It is recommended to use instead of writing custom queries prone to mistakes.
- Also added more readable name to the possible error message thrown by setComplimentarySubscription
refs https://github.com/TryGhost/Team/issues/1869
- There are multiple places in the codebase fetching "default product". The code is slightly divergent in each one of them and has been a source of bugs (like the one referenced). Having the logic captured in one place will allow reducing the code duplication, making code less bug prone, and making testing the modules dependent on the "setDefaultProduct" method easier
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/345
- this commit bumps `eslint-plugin-ghost`, which bumps compatiblity to
2022
- this also removes a lot of the manually-added
`parserOptions.ecmaVersion` that we had in imported packages, in favor
of the value set in `eslint-plugin-ghost`
refs https://github.com/TryGhost/Team/issues/1141
- when a member had canceled subscriptions the check we have to match products to subscriptions to determine whether to insert the hardcoded complimentary subscription was incorrectly matching against the canceled subscriptions
- updated to match only active subscriptions
refs https://github.com/TryGhost/Team/issues/1299
- Convert `created_at` to the right column in each function query
- Renamed the misspelled `getEmailDeliveredEvents` function
- Updated existing unit tests to cover the order
refs https://github.com/TryGhost/Team/issues/1277
- In `getEventTimeline` we filter to only perform the relevant queries, passing to each query function the filters (subset of NQL)
- In each query function, we rewrite the filters to adapt them to the internal data shape.
- We need to do this rewrite to allow API consumers to create filters based on the output on the API instead of the internal data structure.
- Added partial unit tests as there is a lot of repetition between the query functions.
refs https://github.com/TryGhost/Team/issues/1277
- This will allow to filter events within `getEventTimeline`
- The subset of NQL has the following rules:
- Only one level of filters, now parenthesis allowed
- Only three filter keys allowed
- No `or` allowed outside of the bracket notation (this is allowed: `type:-[email_opened_event,email_failed_event]` but this isn't: `type:1,data.created_at:1`)
- The return is an object with a NQL filter by allowed filter key
closes https://github.com/TryGhost/Team/issues/1238
- previously returned 500 errors when a subscription had multiple prices due to external tampering on Stripe directly
- instead now returns 400 Bad Request error when subscriptions don't have right number of prices
no-issue
Since we do not necessarily have a single stripe product anymore, we
should be checking if an invoice webhook is for a stripe product which
we know about. We use the Products repository to search our database for
one.
refs https://github.com/TryGhost/Team/issues/637
All the APIs that currently work with price names needs to be updated to work with price ids instead to work with custom prices/products. This change updates APIs to work with Price IDs in `checkout` , `updateSubscription` and other APIs/methods.
refs https://github.com/TryGhost/Team/issues/530
The RouterController was a grab bag of all controller methods, making it
difficult to mock & test. This adds a MemberController with a smaller
API - making it easier to test.
no-issue
1. We do not want to store payment events for payments of 0 value
2. Stripe webhooks can arrive and be processed "out of order", which can
result in us attempting to add a payment event for a member which
does not yet exist. The change here will 404 in such (edge) cases, so
that Stripe will retry the webhook at a later point, when the Member
has been created, allowing us to store the payment event.
no-issue
This refactors the members-api module so that it is easier to test going forward,
as well as easier to understand & navigate. The Stripe API no longer contains
storage code, this is all handled via the member repository. And we have dedicated
services for webhooks, and stripe plans initialisation.
requires f38d490886
- adds `lib/geolocation.js` with `getGeolocationFromIP()` function which uses https://geojs.io to lookup geolocation data from an IPv4 or IPv6 address
- updates `create/updateMember()` functions to work with a `geolocation` property in the passed in object
- if `geolocation` is `undefined` when updating a member do not reset any existing property
- updates `sendMagicLink` middleware to extract the IP address from the request and stores it as part of the token payload
- updates `getMemberDataFromMagicLinkToken()` method to extract the IP address from the token payload and perform a geolocation lookup if we have an IP address and a matching member does not already have geolocation data