Ghost/core/server/api
kirrg001 449bae9a48 🐛 Fixed missing "value" property for settings Admin API v2
closes #10518

- we had a very generic logic to remove "unwanted" null values
  - copied from v0.1
  - originally added in 7d4107fec4
- this logic transformed: settings = [{key: 'key', value: null}] to [{key: 'key'}], which is wrong
- i've removed this generic logic completely, because i don't know which purpose it serves
- if there a specific case where we want to remove null values, we should either use the JSON schema or use a specific serializer for the target resource
- added tests to proof that settings API behaves as it should
- one test failed because we removed the isNull logic -> if you send published_at = null on a published post
  - the model layer has a piece of logic to force a date if you set published_at to null if the status is published
  - protected
2019-03-04 20:06:53 +01:00
..
shared 🐛 Fixed missing "value" property for settings Admin API v2 2019-03-04 20:06:53 +01:00
v0.1 Added back "theme.uploaded" analytics event (#10450) 2019-02-05 17:38:40 +01:00
v2 🐛 Fixed missing "value" property for settings Admin API v2 2019-03-04 20:06:53 +01:00
index.js Added more webhooks & changed payload 2019-02-07 23:14:27 +01:00
README.md Added tiny framework to support multiple API versions (#9933) 2018-10-05 00:50:45 +02:00

API Versioning

Ghost supports multiple API versions. Each version lives in a separate folder e.g. api/v0.1, api/v2. Next to the API folders there is a shared folder, which the API versions use.

NOTE: v0.1 is deprecated and we won't touch this folder at all. The v0.1 folder contains the API layer which we have used since Ghost was born.

Stages

Each request goes through the following stages:

  • validation
  • input serialisation
  • permissions
  • query
  • output serialisation

The framework we are building pipes a request through these stages depending on the API controller implementation.

Frame

Is a class, which holds all the information for API processing. We pass this instance per reference. The target 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
  },
  options: ['include']
  validation: {
    options: {
      include: {
        required: true,
        values: ['tags']
      }
    }
  },
  permissions: true,
  query(frame) {
    return models.Post.edit(frame.data, frame.options);
  }
}
read: {
  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);
  }
}