mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-20 01:03:23 +03:00
6140a98351
no-issue * Used email_recipient_filter in MEGA This officially decouples the newsletter recipients from the post visibility allowing us to send emails to free members only * Supported enum for send_email_when_published in model This allows us to migrate from the previously used boolean to an enum when we eventually rename the email_recipient_filter column to send_email_when_published * Updated the posts API to handle email_recipient_filter We now no longer rely on the send_email_when_published property to send newsletters, meaning we can remove the column and start cleaning up the new columns name * Handled draft status changes when emails not sent We want to reset any concept of sending an email when a post is transition to the draft status, if and only if, and email has not already been sent. If an email has been sent, we should leave the email related fields as they were. * Removed send_email_when_published from add method This is not supported at the model layer * Removed email_recipient_filter from v2&Content API This should not be exposed on previous api versions, or publicly * Removed reference to send_email_when_published This allows us to move completely to the email_recipient_filter property, keeping the code clean and allowing us to delete the send_email_when_published column in the database. We plan to then migrate _back_ to the send_email_when_published name at both the database and api level. |
||
---|---|---|
.. | ||
canary | ||
shared | ||
v2 | ||
index.js | ||
README.md |
API Versioning
Ghost supports multiple API versions. Each version lives in a separate folder e.g. api/v2, api/v3, api/canary etc. Next to the API folders there is a shared folder, which contains shared code, which all API versions use.
Stages
Each request goes through the following stages:
- input validation
- input serialisation
- permissions
- query
- output serialisation
The framework we are building pipes a request through these stages in respect of the API controller configuration.
Frame
Is a class, which holds all the information for request processing. We pass this instance by reference. Each function can modify the original instance. No need to return the class instance.
Structure
{
original: Object,
options: Object,
data: Object,
user: Object,
file: Object,
files: Array
}
Example
{
original: {
include: 'tags'
},
options: {
withRelated: ['tags']
},
data: {
posts: []
}
}
API Controller
A controller is no longer just a function, it's a set of configurations.
Structure
edit: function || object
edit: {
headers: object,
options: Array,
data: Array,
validation: object | function,
permissions: boolean | object | function,
query: function
}
Examples
edit: {
headers: {
cacheInvalidate: true
},
// Allowed url/query params
options: ['include']
// Url/query param validation configuration
validation: {
options: {
include: {
required: true,
values: ['tags']
}
}
},
permissions: true,
// Returns a model response!
query(frame) {
return models.Post.edit(frame.data, frame.options);
}
}
read: {
// Allowed url/query params, which will be remembered inside `frame.data`
// This is helpful for READ requests e.g. `model.findOne(frame.data, frame.options)`.
// Our model layer requires sending the where clauses as first parameter.
data: ['slug']
validation: {
data: {
slug: {
values: ['eins']
}
}
},
permissions: true,
query(frame) {
return models.Post.findOne(frame.data, frame.options);
}
}
edit: {
validation() {
// custom validation, skip framework
},
permissions: {
unsafeAttrs: ['author']
},
query(frame) {
return models.Post.edit(frame.data, frame.options);
}
}