spacecatninja / craft-imager-x

Image transforms, optimizations and manipulations for your Craft CMS site.
Other
26 stars 16 forks source link

GraphQL `transformImage()` directive not handling `null` results #170

Closed yoannisj closed 2 years ago

yoannisj commented 2 years ago
### I'm submitting a...

Steps to reproduce

  1. Set the supressExceptions config setting to true
  2. Clear all craft caches php craft clear-caches/all (just to be sure)
  3. Go to /admin/graphiql and run the following query (assuming there is no named transform called 'foo'):
    {
    assets {
    url @imagerTransform(handle:foo)
    title
    uri
    }
    }
  4. The results should contain the Exception and stack trace pasted below.

Description

The plugin's `transformImage()` function can return `null` in multiple cases: - the transformed source image was not found - an Exception was thrown and the plugin's `suppressExceptions` setting is `true` - a plugin cancelled the transform via the `EVENT_BEFORE_TRANSFORM_IMAGE` event - the transformer's `transform()` method return `null` - ... However the `transformImage()` graphQL directive is currently [not handling](https://github.com/spacecatninja/craft-imager-x/blob/master/src/gql/directives/ImagerTransform.php#L92-L116) cases where `ImagerX::$plugin->imagerx->transformImage()` returns `null`, and throws an Error (see below). The directive should probably just return `null` in the GraphQL results if that's what it gets from the `transformUrl()` method. ### Displayed errors, stack trace, relevant logs
{
  "errors": [
    {
      "debugMessage": "Call to a member function getUrl() on null",
      "message": "Internal server error",
      "extensions": {
        "category": "internal"
      },
      "file": "/var/www/html/vendor/spacecatninja/imager-x/src/gql/directives/ImagerTransform.php",
      "line": 116,
      "trace": [
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/helpers/Gql.php",
          "line": 379,
          "call": "spacecatninja\\imagerx\\gql\\directives\\ImagerTransform::apply(instance of craft\\elements\\Asset, (empty string), array(1), instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/gql/base/ObjectType.php",
          "line": 48,
          "call": "craft\\helpers\\Gql::applyDirectives(instance of craft\\elements\\Asset, instance of GraphQL\\Type\\Definition\\ResolveInfo, (empty string))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 623,
          "call": "craft\\gql\\base\\ObjectType::resolveWithDirectives(instance of craft\\elements\\Asset, array(0), array(2), instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 550,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, array(2), instance of craft\\elements\\Asset, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 1195,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: pictures_Asset, instance of craft\\elements\\Asset, instance of ArrayObject(1), array(3))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 1145,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeFields(GraphQLType: pictures_Asset, instance of craft\\elements\\Asset, array(2), instance of ArrayObject(3))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 1106,
          "call": "GraphQL\\Executor\\ReferenceExecutor::collectAndExecuteSubfields(GraphQLType: pictures_Asset, instance of ArrayObject(1), array(2), instance of craft\\elements\\Asset)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 974,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeObjectValue(GraphQLType: pictures_Asset, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), instance of craft\\elements\\Asset)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 789,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeAbstractValue(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), instance of craft\\elements\\Asset)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 654,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), instance of craft\\elements\\Asset)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 887,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(2), instance of craft\\elements\\Asset)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 761,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeListValue(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(3))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 654,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeValue(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(3))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 557,
          "call": "GraphQL\\Executor\\ReferenceExecutor::completeValueCatchingError(GraphQLType: AssetInterface, instance of ArrayObject(1), instance of GraphQL\\Type\\Definition\\ResolveInfo, array(1), array(3))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 1195,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Query, null, instance of ArrayObject(1), array(1))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 264,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeFields(GraphQLType: Query, null, array(0), instance of ArrayObject(1))"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 215,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/Executor/Executor.php",
          "line": 156,
          "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/GraphQL.php",
          "line": 162,
          "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, array(2), null, null, null)"
        },
        {
          "file": "/var/www/html/vendor/webonyx/graphql-php/src/GraphQL.php",
          "line": 94,
          "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, '{\n  assets {\n    url @imagerTransform(width:200)\n    title\n    uri\n  }\n}', null, array(2), null, null, null, array(26))"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/services/Gql.php",
          "line": 513,
          "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, '{\n  assets {\n    url @imagerTransform(width:200)\n    title\n    uri\n  }\n}', null, array(2), null, null, null, array(26))"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/controllers/GraphqlController.php",
          "line": 177,
          "call": "craft\\services\\Gql::executeQuery(instance of craft\\models\\GqlSchema, '{\n  assets {\n    url @imagerTransform(width:200)\n    title\n    uri\n  }\n}', null, null, true)"
        },
        {
          "call": "craft\\controllers\\GraphqlController::actionApi()"
        },
        {
          "file": "/var/www/html/vendor/yiisoft/yii2/base/InlineAction.php",
          "line": 57,
          "function": "call_user_func_array(array(2), array(0))"
        },
        {
          "file": "/var/www/html/vendor/yiisoft/yii2/base/Controller.php",
          "line": 178,
          "call": "yii\\base\\InlineAction::runWithParams(array(2))"
        },
        {
          "file": "/var/www/html/vendor/yiisoft/yii2/base/Module.php",
          "line": 552,
          "call": "yii\\base\\Controller::runAction('api', array(2))"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/web/Application.php",
          "line": 301,
          "call": "yii\\base\\Module::runAction('graphql/api', array(2))"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/web/Application.php",
          "line": 625,
          "call": "craft\\web\\Application::runAction('graphql/api', array(2))"
        },
        {
          "file": "/var/www/html/vendor/craftcms/cms/src/web/Application.php",
          "line": 280,
          "call": "craft\\web\\Application::_processActionRequest(instance of craft\\web\\Request)"
        },
        {
          "file": "/var/www/html/vendor/yiisoft/yii2/base/Application.php",
          "line": 384,
          "call": "craft\\web\\Application::handleRequest(instance of craft\\web\\Request)"
        },
        {
          "file": "/var/www/html/web/index.php",
          "line": 12,
          "call": "yii\\base\\Application::run()"
        }
      ]
    }
  ],
  "data": {
    "assets": [
      {
        "url": null,
        "title": "YNM Placeholder Portrait",
        "uri": null
      },
      {
        "url": "/imager/pictures/76/YNM-Placeholder-Square_5f28a37dfffeac17d8504dfc1bc3d34b.jpg",
        "title": "YNM Placeholder Square",
        "uri": null
      },
      {
        "url": "/imager/pictures/74/YNM-Placeholder-Landscape_5f28a37dfffeac17d8504dfc1bc3d34b.jpg",
        "title": "YNM Placeholder Landscape",
        "uri": null
      }
    ]
  }
}

Additional info

aelvan commented 2 years ago

Thanks for reporting. The issue actually was that suppressException was working, I just wasn't accounting for transformImage to return null. 🤦‍♂️ If suppressException was set to false, it would actually return null if an exception occured. 🤪

Fixed for the next release.