0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00
ghost/core/server/api
Naz Gargol c295435b41
Added new fields to members CSV import (#11539)
no issue

- New fields that are accepted through members CSV import endpoint are:
  - `subscribed_to_emails` - corresponds to `subscribed` flag in API
  - `stripe_customer_id` - links existing Stripe customer to created member
  - `complimentary_plan` - flag controlling "Complimentary" plan subscription creation for imported member

- Noteworthy exception in field naming - `subscribed_to_emails` that corresponds to `subscribed` API flag present on members resources. It's a special case of CSV format, where users can be less technical it's more explicit to what the flag does (also the same naming is applied in the Admin UI)

- Failing to either link Stripe customer or assign "Complimentary" subscription to imported member behaves in a transaction-like manner - imported record is not created in the database. This is needed to be able to retry imports when it fails for reasons like connectivity failure with Stripe or Stripe miss-configuration.

- To avoid conflicts with linking same Stripe customer to multiple members there is a special handling for duplicate `stripe_customer_id` fields. Records with duplicates are removed from imported set.
2020-02-04 13:51:24 +08:00
..
canary Added new fields to members CSV import (#11539) 2020-02-04 13:51:24 +08:00
shared Reduced API debug statements 2019-10-15 15:07:38 +01:00
v2 Allowed pages to accept HTML as a source (#11422) 2020-01-08 17:44:34 +01:00
index.js 🔥 Removed v0.1 controllers & routes (#11103) 2019-09-11 19:10:10 +02:00
README.md Updated API versioning README.md 2019-09-12 16:27:09 +02:00

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);
  }
}