TryGhost / Zapier

Ghost <-> Zapier Integration
https://developer.zapier.com/app/1566
MIT License
5 stars 3 forks source link

[Regression] Create post action not working with tags selected #56

Closed kymellis closed 2 years ago

kymellis commented 2 years ago

Use case

Sending content from an external location into Ghost, using the Create Post action in Zapier.

Problem

It’s not possible to apply tags in the “Create Post” action, Zapier receives a 500 error from Ghost.

When using the Tags field in the "Set up action" step, testing the automation fails with the following error:

CleanShot 2022-01-06 at 15 02 17@2x

Tests carried out

This has been tested using two different zap templates.

RSS Feed to Ghost

cleanshot_2022-01-06_at_13 44 08_2x

The RSS feed outputs the tag as "Raw Category" as shown above. This prevents the Zap test from succeeding. If the Tag field is left empty, the test succeeds.

Ghost to Ghost

I also tested sending content from one Ghost instance to another, to rule out issues with the RSS Feed Trigger.

Attempting to use all of the following attributes in the Tags field resulted in the same error message:

CleanShot 2022-01-06 at 14 20 28@2x CleanShot 2022-01-06 at 14 20 55@2x CleanShot 2022-01-06 at 14 21 48@2x

Leaving the Tags field empty resulted in the test succeeding.

sam-lord commented 2 years ago

Seems like the API in general has issues generating new tags with a post.

The error from logs:

BookshelfRelationsError: Unable to update nested relation

err.context: {"key":"tags","tableName":"posts","method":"setBelongsToMany"}

When I run this from @tryghost/admin-api, it's successful and creates the tag:

api.posts.add({
  title: 'API test',
  tags: ['My next tag']
});

It seems like the API isn't being called the same way / correctly from Zapier. Unsure why this doesn't work now or if this always didn't work.

kevinansfield commented 2 years ago

The issue appears to be Zapier always sending the tags array as tags: [{slug: 'my-slug'}]. That format only works when a tag with that slug already exists:

Working request formats:

Not working request formats:

I can't find any reference to whether the latter should/shouldn't work or if it has worked at any time in the past. In any case I'm assuming we at least shouldn't be throwing a 500 error.

In terms of fixing in Zapier there are a couple of options:

  1. leave the create post setup as-is (i.e. when creating a zap it still asks for tag slugs) but perform additional API queries to fetch tags by the supplied slugs so the tags: [] array can be sent as tags: ['Existing', 'new'] (where 'Existing' is the name of a tag that matched a supplied slug)
  2. change the create post setup to ask for tag names and always send as tags: ['Existing', 'New']

Option 2 has the downside that it will be a breaking change for anyone who already has a zap set up that creates posts. We'll either need to release a new version that people can manually upgrade to by disconnecting and reconnecting their Ghost app, or we have to do something similar to 1 with additional API queries to map the supplied tags array from slugs/names to names.

Alternatively Ghost's API can be changed so tags: [{slug: 'new'}] is accepted. In that case no changes are needed in Zapier but anyone hitting the problem will need to upgrade their Ghost version.

kevinansfield commented 2 years ago

be a breaking change for anyone who already has a zap set up that creates posts

Our integration's analytics show 291 live zaps that use the create post action but there are no details on how many of those attach tags to the posts.

matthanley commented 2 years ago

tags: [{slug: 'new'}] is expected to work, so this should be treated as a bug in Ghost.

kevinansfield commented 2 years ago

FYI when debugging this I found that the underlying error was hidden by layered error handlers. The cause was a missing name property on the tag throwing a validation error, however the only error exposed was in the 500 API response and gave no clue to the real problem:

{
    "errors": [
        {
            "message": "Internal server error, cannot save post.",
            "context": "Unable to update nested relation. [object Object]",
            "type": "InternalServerError",
            "details": null,
            "property": null,
            "help": null,
            "code": "UPDATE_RELATION",
            "id": "aca33061-7395-11ec-b1ab-f5848438db74"
        }
    ]
}

By adding a .catch and console.log to the API promise I was able to see the full problem:

BookshelfRelationsError: Unable to update nested relation.
    at new BookshelfRelationsError (/Users/kevin/code/Ghost/node_modules/bookshelf-relations/errors.js:10:26)
    at /Users/kevin/code/Ghost/node_modules/bookshelf-relations/lib/relations.js:71:27
    at tryCatcher (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:547:31)
    at Promise._settlePromise (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:604:18)
    at Promise._settlePromise0 (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:649:10)
    at Promise._settlePromises (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:725:18)
    at _drainQueueStep (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:93:12)
    at _drainQueue (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:86:9)
    at Async._drainQueues (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:102:5)
    at Immediate.Async.drainQueues [as _onImmediate] (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:15:14)
    at processImmediate (node:internal/timers:464:21)
 {
  '0': ValidationError: Value in [tags.name] cannot be blank.
      at each (/Users/kevin/code/Ghost/core/server/data/schema/validator.js:55:39)
      at arrayEach (/Users/kevin/code/Ghost/node_modules/lodash/lodash.js:530:11)
      at Function.forEach (/Users/kevin/code/Ghost/node_modules/lodash/lodash.js:9410:14)
      at Object.validateSchema [as validate] (/Users/kevin/code/Ghost/core/server/data/schema/validator.js:37:7)
      at Child.onValidate (/Users/kevin/code/Ghost/core/server/models/base/plugins/events.js:105:27)
      at Child.onCreating (/Users/kevin/code/Ghost/core/server/models/base/plugins/events.js:169:41)
      at /Users/kevin/code/Ghost/node_modules/bookshelf/lib/base/events.js:101:64
      at tryCatcher (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/util.js:16:23)
      at Object.gotValue (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/reduce.js:166:18)
      at Object.gotAccum (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/reduce.js:155:25)
      at Object.tryCatcher (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/util.js:16:23)
      at Promise._settlePromiseFromHandler (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:547:31)
      at Promise._settlePromise (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:604:18)
      at Promise._settlePromise0 (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:649:10)
      at Promise._settlePromises (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/promise.js:729:18)
      at _drainQueueStep (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:93:12)
      at _drainQueue (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:86:9)
      at Async._drainQueues (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:102:5)
      at Immediate.Async.drainQueues (/Users/kevin/code/Ghost/node_modules/bluebird/js/release/async.js:15:14)
      at processImmediate (node:internal/timers:464:21)
   {
    statusCode: 422,
    errorType: 'ValidationError',
    level: 'normal',
    id: 'caaa9870-7456-11ec-8e47-35dc620121f1',
    context: 'tags.name',
    help: undefined,
    errorDetails: undefined,
    code: null,
    property: null,
    redirect: null,
    hideStack: undefined
  },
  statusCode: 500,
  errorType: 'BookshelfRelationsError',
  level: 'critical',
  id: 'caaa9871-7456-11ec-8e47-35dc620121f1',
  context: { key: 'tags', tableName: 'posts', method: 'setBelongsToMany' },
  help: undefined,
  errorDetails: undefined,
  code: 'UPDATE_RELATION',
  property: null,
  redirect: null,
  hideStack: undefined,
  length: 1
}