lighthouse icon indicating copy to clipboard operation
lighthouse copied to clipboard

Directive to upload a file and return storage path

Open eNzyOfficial opened this issue 3 years ago • 10 comments

What problem does this feature proposal attempt to solve?

While it is possible to pass a file via input using Upload scalar, it would be nice if there was a directive that would upload the file to a storage disk, drop the file from the input and replace it with the path.

For example, if I have a posts table with a thumbnail url, I would pass the thumbnail image file in an input payload along with other details about my blog post (title, body, tags, etc), uploading the file to storage and storing the url as a column in the database.

Which possible solutions should be considered?

Possibly something similar to image: Upload @upload(attribute:"image_url", disk:"public", path:"uploads/").

eNzyOfficial avatar Mar 23 '22 03:03 eNzyOfficial

Quick example I whipped up as proof-of-concept:

@upload

Click to expand
class UploadDirective extends BaseDirective
{
    public static function definition(): string
    {
        return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Uploads given file to storage, removes the argument and sets 
the returned path to the attribute key provided.

This does not change the schema from a client perspective.
"""
directive @upload(
  """
  The internal name of an attribute/property/key.
  """
  attribute: String!
  """
  The storage disk to be used, defaults to config value `filesystems.default`.
  """
  disk: String
  """
  The path where the file should be stored, defaults to `/`.
  """
  path: String
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
GRAPHQL;
    }

    /**
     * Retrieves the attribute argument for the directive.
     *
     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
     */
    public function attributeArgValue(): string
    {
        $attribute = $this->directiveArgValue('attribute');

        if (! $attribute) {
            throw new DefinitionException(
                "The @{$this->name()} directive requires an `attribute` argument."
            );
        }

        return $attribute;
    }

    /**
     * @throws Exception
     */
    public function diskArgValue(): string
    {
        $disk = $this->directiveArgValue('disk', config('filesystems.default'));
        $disks = config('filesystems.disks', []);

        if (empty($disks[$disk])) {
            throw new Exception("Could not find storage disk: $disk");
        }

        return $disk;
    }

    public function pathArgValue(): string
    {
        return $this->directiveArgValue('path', '/');
    }
}

@uploadArgs

Click to expand
class UploadArgsDirective extends BaseDirective implements FieldMiddleware
{
    public static function definition(): string
    {
        return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Apply the @upload directives on the incoming arguments.
"""
directive @uploadArgs on FIELD_DEFINITION
GRAPHQL;
    }

    public function handleField(FieldValue $fieldValue, Closure $next)
    {
        $resolver = $fieldValue->getResolver();

        return $next(
            $fieldValue->setResolver(
                function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver) {
                    $argumentSet = $resolveInfo->argumentSet;
                    $this->upload($argumentSet);

                    return $resolver(
                        $root,
                        $argumentSet->toArray(),
                        $context,
                        $resolveInfo
                    );
                }
            )
        );
    }

    /**
     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException|\Exception
     */
    protected function upload(ArgumentSet &$argumentSet): void
    {
        foreach ($argumentSet->arguments as $name => $argument) {
            $maybeUploadDirective = $argument->directives->first(function (Directive $directive): bool {
                return $directive instanceof UploadDirective;
            });

            if ($maybeUploadDirective instanceof UploadDirective) {
                if (!($argument->value instanceof UploadedFile)) {
                    throw new \Exception("Expected UploadedFile from `$name`");
                }

                /** @var Argument $argument */
                $uploadedFile = $argument->value;

                // TODO: $pathToStoreFile, if it contains filename already
                // TODO: splice the filename and trim trailing slashes
                // TODO: if not, use hashName()
                $filename = $uploadedFile->hashName();
                $disk = $maybeUploadDirective->diskArgValue();
                $pathToStoreFile = rtrim($maybeUploadDirective->pathArgValue(), '\\/');

                if (!$filepathInStorage = $uploadedFile->storeAs($pathToStoreFile, $filename, $disk)) {
                    throw new \Exception("Unable to upload `$name` file to `$pathToStoreFile` via disk `$disk`");
                }

                $argument->value = $filepathInStorage;
                $argument->type->name = 'String';
                $argument->type->nonNull = true;

                $argumentSet->arguments[$maybeUploadDirective->attributeArgValue()] = $argument;
                unset($argumentSet->arguments[$name]);
            } else {
                // Recursively apply the uploading to nested inputs.
                // We look for further ArgumentSet instances, they
                // might be contained within an array.
                Utils::applyEach(
                    function ($value) {
                        if ($value instanceof ArgumentSet) {
                            $this->upload($value);
                        }
                    },
                    $argument->value
                );
            }
        }
    }
}

eNzyOfficial avatar Mar 23 '22 05:03 eNzyOfficial

@eNzyOfficial great to see you work on more directive magic. For this case, it should be much simpler to have @upload implement ArgResolver.

spawnia avatar Mar 23 '22 09:03 spawnia

https://lighthouse-php.com/5/concepts/arg-resolvers.html#solution

Using the docs as an example, I came up with this solution. It seems a little odd though having to make a db call for a single column to persist it, rather than just replacing the arg before it reaches @update directive. I feel like maybe my approach is incorrect, but without setting the attribute and calling save(), the string wasn't being persisted.

Click to expand
class UploadDirective extends BaseDirective implements ArgResolver
{
    public static function definition(): string
    {
        return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Uploads given file to storage, removes the argument and sets 
the returned path to the attribute key provided.

This does not change the schema from a client perspective.
"""
directive @upload(
  """
  The internal name of an attribute/property/key.
  """
  attribute: String!
  """
  The storage disk to be used, defaults to config value `filesystems.default`.
  """
  disk: String
  """
  The path where the file should be stored, defaults to `/`.
  """
  path: String
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
GRAPHQL;
    }

    /**
     * @param \Illuminate\Database\Eloquent\Model $parent
     * @param array<\Nuwave\Lighthouse\Execution\Arguments\ArgumentSet> $argsList
     * @return string
     * @throws Exception
     */
    public function __invoke($parent, $argsList): string
    {
        if (!($argsList instanceof UploadedFile)) {
            throw new \Exception("Expected UploadedFile from `{$this->name()}`");
        }

        // TODO: $pathToStoreFile, if it contains filename already
        // TODO: splice the filename and trim trailing slashes
        // TODO: if not, use hashName()
        $filename = $argsList->hashName();

        if (!$filepathInStorage = $argsList->storeAs($this->pathArgValue(), $filename, $this->diskArgValue())) {
            throw new \Exception("Unable to upload `{$this->name()}` file to `{$this->pathArgValue()}` via disk `{$this->diskArgValue()}`");
        }

        $parent->setAttribute($this->attributeArgValue(), $filepathInStorage);
        $parent->save();

        return $filepathInStorage;
    }

    /**
     * @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
     */
    public function attributeArgValue(): string
    {
        $attribute = $this->directiveArgValue('attribute');

        if (! $attribute) {
            throw new DefinitionException(
                "The @{$this->name()} directive requires an `attribute` argument."
            );
        }

        return $attribute;
    }

    /**
     * @throws Exception
     */
    public function diskArgValue(): string
    {
        $disk = $this->directiveArgValue('disk', config('filesystems.default'));
        $disks = config('filesystems.disks', []);

        if (empty($disks[$disk])) {
            throw new Exception("Could not find storage disk: $disk");
        }

        return $disk;
    }

    public function pathArgValue(): string
    {
        return rtrim(
            $this->directiveArgValue('path', ''),
            '\\/'
        );
    }
}

eNzyOfficial avatar Mar 24 '22 03:03 eNzyOfficial

Oh, I just noticed the upload is also supposed to store the path in the model. In this case, there is yet another directive interface that might work better than a nested resolver: https://lighthouse-php.com/master/custom-directives/argument-directives.html#argtransformerdirective

spawnia avatar Mar 25 '22 08:03 spawnia

Based on my understanding from the docs, the ArgTransformerDirective will only transform the value, there's not access to the field name.

An example in my case, the field name is image but the actual column name in the database is image_path. using ArgTransformerDirective, there's no way to do something similar to @rename after successfully storing the file, is that correct?

I'm not sure if it's possible to use multiple directives, something like the following though:

input SomeInput {
  image: Upload @upload(disk:"public" path:"/images") @rename(attribute:"image_url")
}

In which case, it would work fine.

eNzyOfficial avatar Mar 25 '22 09:03 eNzyOfficial

Composing @rename and ArgTransformerDirective should work fine.

spawnia avatar Mar 25 '22 09:03 spawnia

I made the following class based on the docs but for some reason it's not being called, do I need to register the directive somewhere?

class UploadDirective extends BaseDirective implements ArgTransformerDirective
{
    public static function definition(): string
    {
        return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Uploads given file to storage, removes the argument and sets 
the returned path to the attribute key provided.

This does not change the schema from a client perspective.
"""
directive @upload(
  """
  The storage disk to be used, defaults to config value `filesystems.default`.
  """
  disk: String
  """
  The path where the file should be stored, defaults to `/`.
  """
  path: String
) on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
GRAPHQL;
    }

    /**
     * @throws Exception
     */
    public function transform($argumentValue): String
    {
        if (!($argumentValue instanceof UploadedFile) && $argumentValue !== null) {
            throw new Exception("Expected UploadedFile from `{$this->name()}`");
        }

        // TODO: $this->pathArgValue(), if it contains filename already
        // TODO: splice the filename and trim trailing slashes
        // TODO: if not, use hashName()
        $filename = $argumentValue->hashName();

        if (!$filepathInStorage = $argumentValue->storeAs($this->pathArgValue(), $filename, $this->diskArgValue())) {
            throw new Exception("Unable to upload `{$this->name()}` file to `{$this->pathArgValue()}` via disk `{$this->diskArgValue()}`");
        }

        return $filepathInStorage;
    }

    /**
     * @throws Exception
     */
    public function diskArgValue(): string
    {
        $disk = $this->directiveArgValue('disk', config('filesystems.default'));
        $disks = config('filesystems.disks', []);

        if (empty($disks[$disk])) {
            throw new Exception("Could not find storage disk: $disk");
        }

        return $disk;
    }

    public function pathArgValue(): string
    {
        return rtrim(
            $this->directiveArgValue('path', ''),
            '\\/'
        );
    }
}

with the input field being:

input UserCreateInput @validator {
    image: NullableUpload @upload(path:"images/avatars/users") @rename(attribute:"image_path")
}

And because it's not called the file gets passed to the model to be created causing an error:

Click to expand
{
  "errors": [
    {
      "debugMessage": "Serialization of 'Illuminate\\Http\\UploadedFile' is not allowed",
      "message": "Internal server error",
      "extensions": {
        "category": "internal"
      },
      "locations": [
        {
          "line": 8,
          "column": 3
        }
      ],
      "path": [
        "createUser"
      ],
      ...
    }
  ]
}

eNzyOfficial avatar Mar 28 '22 03:03 eNzyOfficial

Assuming your schema passes lighthouse:validate-schema, the directive should be registered and getting called. Can you debug what is happening in TransformArgsDirective?

I also just added a test to make sure that @rename does not cause issues, works fine: https://github.com/nuwave/lighthouse/commit/e09943d8abc786cd266f4837ade0239fb508164c

spawnia avatar Mar 28 '22 09:03 spawnia

Inside TransformArgsDirective:

if (!($directive instanceof  ArgumentValidation)) {
    dd($directive);
}

But it never dies, so I can only assume it's not getting called? and lighthouse:validate-schema returns The defined schema is valid.

~~So after some poking around and looking through the stacktrace properly, I noticed that it could be related to the event being fired.~~

~~When model created event is a fired, the model gets serialized, and this is what's causing the error.~~

~~I'm not too sure how the internals work in regards to this, but does TransformArgsDirective remove and replace the original value with the transformed value before passing it into the model to be created/updated? If not this could be the issue.~~

Here is the full trace:

Click to expand
{
  "errors": [
    {
      "debugMessage": "Serialization of 'Illuminate\\Http\\UploadedFile' is not allowed",
      "message": "Internal server error",
      "extensions": {
        "category": "internal"
      },
      "locations": [
        {
          "line": 8,
          "column": 3
        }
      ],
      "path": [
        "createCompany"
      ],
      "trace": [
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
          "line": 158,
          "function": "serialize(instance of Illuminate\\Events\\CallQueuedListener)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
          "line": 127,
          "call": "Illuminate\\Queue\\Queue::createObjectPayload(instance of Illuminate\\Events\\CallQueuedListener, null)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
          "line": 105,
          "call": "Illuminate\\Queue\\Queue::createPayloadArray(instance of Illuminate\\Events\\CallQueuedListener, null, (empty string))"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php",
          "line": 38,
          "call": "Illuminate\\Queue\\Queue::createPayload(instance of Illuminate\\Events\\CallQueuedListener, null, (empty string))"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
          "line": 57,
          "call": "Illuminate\\Queue\\SyncQueue::push(instance of Illuminate\\Events\\CallQueuedListener, (empty string), null)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
          "line": 574,
          "call": "Illuminate\\Queue\\Queue::pushOn(null, instance of Illuminate\\Events\\CallQueuedListener)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
          "line": 498,
          "call": "Illuminate\\Events\\Dispatcher::queueHandler('App\\Support\\Listeners\\GenerateModelCreatedActivity', 'handle', array(1))"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
          "line": 424,
          "call": "Illuminate\\Events\\Dispatcher::Illuminate\\Events\\{closure}(instance of App\\Support\\Events\\ModelCreated)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
          "line": 249,
          "call": "Illuminate\\Events\\Dispatcher::Illuminate\\Events\\{closure}('App\\Support\\Events\\ModelCreated', array(1))"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php",
          "line": 206,
          "call": "Illuminate\\Events\\Dispatcher::dispatch('App\\Support\\Events\\ModelCreated')"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php",
          "line": 181,
          "call": "Illuminate\\Database\\Eloquent\\Model::fireCustomModelEvent('created', 'dispatch')"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php",
          "line": 1174,
          "call": "Illuminate\\Database\\Eloquent\\Model::fireModelEvent('created', false)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php",
          "line": 994,
          "call": "Illuminate\\Database\\Eloquent\\Model::performInsert(instance of Illuminate\\Database\\Eloquent\\Builder)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Execution/Arguments/SaveModel.php",
          "line": 76,
          "call": "Illuminate\\Database\\Eloquent\\Model::save()"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Execution/Arguments/ResolveNested.php",
          "line": 36,
          "call": "Nuwave\\Lighthouse\\Execution\\Arguments\\SaveModel::__invoke(instance of App\\Company\\Models\\Company, instance of Nuwave\\Lighthouse\\Execution\\Arguments\\ArgumentSet)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/MutationExecutorDirective.php",
          "line": 82,
          "call": "Nuwave\\Lighthouse\\Execution\\Arguments\\ResolveNested::__invoke(instance of App\\Company\\Models\\Company, instance of Nuwave\\Lighthouse\\Execution\\Arguments\\ArgumentSet)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Support/Utils.php",
          "line": 99,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\MutationExecutorDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(instance of Nuwave\\Lighthouse\\Execution\\Arguments\\ArgumentSet)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/MutationExecutorDirective.php",
          "line": 84,
          "call": "Nuwave\\Lighthouse\\Support\\Utils::applyEach(instance of Closure, instance of Nuwave\\Lighthouse\\Execution\\Arguments\\ArgumentSet)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/MutationExecutorDirective.php",
          "line": 38,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\MutationExecutorDirective::executeMutation(instance of App\\Company\\Models\\Company, instance of Nuwave\\Lighthouse\\Execution\\Arguments\\ArgumentSet)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php",
          "line": 29,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\MutationExecutorDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(instance of Illuminate\\Database\\MySqlConnection)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Execution/TransactionalMutations.php",
          "line": 39,
          "call": "Illuminate\\Database\\Connection::transaction(instance of Closure)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/MutationExecutorDirective.php",
          "line": 43,
          "call": "Nuwave\\Lighthouse\\Execution\\TransactionalMutations::execute(instance of Closure, null)"
        },
        {
          "file": "/project/app/GraphQL/Directives/InjectWhenMissingDirective.php",
          "line": 92,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\MutationExecutorDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(6), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Auth/GuardDirective.php",
          "line": 70,
          "call": "App\\GraphQL\\Directives\\InjectWhenMissingDirective::App\\GraphQL\\Directives\\{closure}(null, array(5), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/RenameArgsDirective.php",
          "line": 37,
          "call": "Nuwave\\Lighthouse\\Auth\\GuardDirective::Nuwave\\Lighthouse\\Auth\\{closure}(null, array(5), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/SpreadDirective.php",
          "line": 36,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\RenameArgsDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(5), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/ArgTraversalDirective.php",
          "line": 29,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\SpreadDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Validation/ValidateDirective.php",
          "line": 51,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ArgTraversalDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/ArgTraversalDirective.php",
          "line": 29,
          "call": "Nuwave\\Lighthouse\\Validation\\ValidateDirective::Nuwave\\Lighthouse\\Validation\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Directives/TrimDirective.php",
          "line": 56,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\ArgTraversalDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Tracing/TracingDirective.php",
          "line": 45,
          "call": "Nuwave\\Lighthouse\\Schema\\Directives\\TrimDirective::Nuwave\\Lighthouse\\Schema\\Directives\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Schema/Factories/FieldFactory.php",
          "line": 99,
          "call": "Nuwave\\Lighthouse\\Tracing\\TracingDirective::Nuwave\\Lighthouse\\Tracing\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 623,
          "call": "Nuwave\\Lighthouse\\Schema\\Factories\\FieldFactory::Nuwave\\Lighthouse\\Schema\\Factories\\{closure}(null, array(1), instance of Nuwave\\Lighthouse\\Schema\\Context, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/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, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 474,
          "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 857,
          "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'createCompany')"
        },
        {
          "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'createCompany')"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 859,
          "function": "array_reduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 490,
          "call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 263,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php",
          "line": 215,
          "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
        },
        {
          "file": "/project/vendor/webonyx/graphql-php/src/Executor/Executor.php",
          "line": 156,
          "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
        },
        {
          "file": "/project/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, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'createCompany', null)"
        },
        {
          "file": "/project/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, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'createCompany', null, array(29))"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 268,
          "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), 'createCompany', null, array(29))"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 203,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeParsedQuery(instance of GraphQL\\Language\\AST\\DocumentNode, instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, 'createCompany')"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 162,
          "call": "Nuwave\\Lighthouse\\GraphQL::parseAndExecuteQuery('mutation Login {\n  login(input: { email: \"[email protected]\", password: \"password\" }) {\n    token\n  }\n}\n\nmutation createCompany($file: NullableUpload) {\n  createCompany(input:{\n    name:\"test-me\"\n    display_name:\"Test Me\"\n    source:\"some source\"\n    image: $file\n    stage:{\n      connect:\"8ng01z93\"\n    }\n  }){\n    image_url\n  }\n}\n\nmutation updateCompany {\n  updateCompany(input: {\n    id: \"8ng01z93\",\n    source: \"aspernatur\",\n    stage: { connect: \"8ng01z93\" },\n    perks: { sync: [] }\n  }) {\n    id\n    name\n    perks(first:100) {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n  }\n}\n\nmutation CreateCandidate($file: NullableUpload) {\n  createCandidate(\n    input: {\n      name: \"Test Name\"\n      source: \"facebook\"\n      status: { connect: \"8ng01z93\" }\n      image: $file\n    }\n  ) {\n    id\n    name\n    source\n    status {\n      id\n      name\n    }\n  }\n}\n\nmutation UpdateCandidate {\n  updateCandidate(\n    input: {\n      id: \"1vovn9g8\"\n      name: \"Test Name\"\n      source: \"facebook\"\n      status: { connect: \"8ng01z93\" }\n    }\n  ) {\n    id\n    name\n    source\n    status {\n      id\n      name\n    }\n  }\n}', instance of Nuwave\\Lighthouse\\Schema\\Context, array(1), null, 'createCompany')"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 121,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperation(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Support/Utils.php",
          "line": 99,
          "call": "Nuwave\\Lighthouse\\GraphQL::Nuwave\\Lighthouse\\{closure}(instance of GraphQL\\Server\\OperationParams)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 120,
          "call": "Nuwave\\Lighthouse\\Support\\Utils::applyEach(instance of Closure, instance of GraphQL\\Server\\OperationParams)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Support/Http/Controllers/GraphQLController.php",
          "line": 32,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperationOrOperations(instance of GraphQL\\Server\\OperationParams, instance of Nuwave\\Lighthouse\\Schema\\Context)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
          "line": 48,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController::__invoke(instance of Illuminate\\Http\\Request, instance of Nuwave\\Lighthouse\\GraphQL, instance of Illuminate\\Events\\Dispatcher, instance of Laragraph\\Utils\\RequestParser, instance of Nuwave\\Lighthouse\\Execution\\SingleResponse, instance of Nuwave\\Lighthouse\\Execution\\ContextFactory)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 262,
          "call": "Illuminate\\Routing\\ControllerDispatcher::dispatch(instance of Illuminate\\Routing\\Route, instance of Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController, '__invoke')"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 205,
          "call": "Illuminate\\Routing\\Route::runController()"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 721,
          "call": "Illuminate\\Routing\\Route::run()"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "call": "Illuminate\\Routing\\Router::Illuminate\\Routing\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Support/Http/Middleware/AttemptAuthentication.php",
          "line": 34,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/nuwave/lighthouse/src/Support/Http/Middleware/AcceptJson.php",
          "line": 27,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AcceptJson::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 723,
          "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 698,
          "call": "Illuminate\\Routing\\Router::runRouteWithinStack(instance of Illuminate\\Routing\\Route, instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 662,
          "call": "Illuminate\\Routing\\Router::runRoute(instance of Illuminate\\Http\\Request, instance of Illuminate\\Routing\\Route)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 651,
          "call": "Illuminate\\Routing\\Router::dispatchToRoute(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 167,
          "call": "Illuminate\\Routing\\Router::dispatch(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "call": "Illuminate\\Foundation\\Http\\Kernel::Illuminate\\Foundation\\Http\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/beyondcode/laravel-query-detector/src/QueryDetectorMiddleware.php",
          "line": 33,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "BeyondCode\\QueryDetector\\QueryDetectorMiddleware::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
          "line": 31,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
          "line": 40,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
          "line": 27,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
          "line": 86,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/fruitcake/laravel-cors/src/HandleCors.php",
          "line": 38,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Fruitcake\\Cors\\HandleCors::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/fideloper/proxy/src/TrustProxies.php",
          "line": 57,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Fideloper\\Proxy\\TrustProxies::handle(instance of Illuminate\\Http\\Request, instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 142,
          "call": "Illuminate\\Pipeline\\Pipeline::then(instance of Closure)"
        },
        {
          "file": "/project/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 111,
          "call": "Illuminate\\Foundation\\Http\\Kernel::sendRequestThroughRouter(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/project/public/index.php",
          "line": 52,
          "call": "Illuminate\\Foundation\\Http\\Kernel::handle(instance of Illuminate\\Http\\Request)"
        },
        {
          "file": "/Users/user/.composer/vendor/laravel/valet/server.php",
          "line": 234,
          "function": "require('/project/public/index.php')"
        }
      ]
    }
  ],
  "extensions": {
    "tracing": {
      "version": 1,
      "startTime": "2022-04-04T05:23:44.484+00:00",
      "endTime": "2022-04-04T05:23:44.554+00:00",
      "duration": 70029399,
      "execution": {
        "resolvers": []
      }
    }
  }
}

eNzyOfficial avatar Apr 04 '22 05:04 eNzyOfficial

Spotted the issue: you must implement ArgDirective - see https://lighthouse-php.com/master/custom-directives/argument-directives.html#argument-directives

spawnia avatar Apr 06 '22 08:04 spawnia