Add real path to `ResolveInfo`
I'd like to be able to get the real (trusted) path in ResolveInfo next to the untrusted aliased path.
Similar to what was written in https://github.com/webonyx/graphql-php/issues/412.
query {
myAlias: actor {
myOtherAlias: movies
}
}
ResolveInfo->path will contain myAlias.myOtherAlias instead of actor.movies.
Why is this useful? It can be used to quickly log the path of a requested field. For example, when somebody does not have access. By logging the real path, you never have to guess what it was. Also, it allows trusting the path because it cannot be spoofed.
What would be the best way to implement this? I tried adding an additional safePath to ResolveInfo, but that requires many changes in ReferenceExecutor.
Another way could be to introduce a PathAlias value object that holds the alias + field name. For example:
protected function executeFields(ObjectType $parentType, $rootValue, array $path, \ArrayObject $fields)
{
// ...
foreach ($fields as $responseName => $fieldNodes) {
$fieldPath = $path;
if ($responseName !== $fieldNodes[0]->name->value) {
$fieldPath[] = new PathAlias($fieldNodes[0]->name->value, $responseName);
} else {
$fieldPath[] = $responseName;
}
So ResolveInfo->path could become [PathAlias('myAlias', 'actor'), PathAlias('myOtherAlias', 'movie'), 'some', 'other', 'field'].
We could put this behind a feature flag to not break existing projects that rely on the ResolveInfo->path.
Another simpler way could be to do this:
protected function executeFields(ObjectType $parentType, $rootValue, array $path, \ArrayObject $fields)
{
// ...
foreach ($fields as $responseName => $fieldNodes) {
$fieldPath = $path;
if ($responseName !== $fieldNodes[0]->name->value) {
$fieldPath[] = sprintf('%s: %s', $responseName, $fieldNodes[0]->name->value);
} else {
$fieldPath[] = $responseName;
}
This would produce ['myAlias: actor', 'myOtherAlias: movies'].
What do you think? I'd be happy to work on a PR to get this improved.
Consider that without aliases, the "real path" can be ambiguous or have conflicts.
{
foo: bar {
qux
}
baz: bar(arg: 3) {
qwak
}
}
I would not change existing functionality, but could see the addition of another API that allows retrieving the unaliased path.
I would not change existing functionality, but could see the addition of another API that allows retrieving the unaliased path.
So next to a path we keep a safePath or realPath? Would that work for you? If so, then I'll provide a PR.
If possible, I would like to see it implemented as a method in order to calculate it lazily. And I would call it unaliasedPath, that explains what it is.
To calculate it lazily, what's the best approach?
I was thinking to add a method to ResolveInfo that uses the path and then walks through the operation selection sets again to find the names by alias.
It feels nasty to me. Are there plans for v16 where we can break this behavior? And make it so that path is not just a list of strings, but a list of Path objects that contain the name and the nullable alias?
If lazy calculation is tricky and eager addition is easy and cheap, we can also add it as a property, I am not totally against that. My guess is that having two separate, simple lists is better. I don't see a scenario where one would care about aliases for some parts of the path - either you want aliases or you do not. A list of strings is easier to work with.
Implemented in https://github.com/webonyx/graphql-php/pull/1548
Released with https://github.com/webonyx/graphql-php/releases/tag/v15.12.0, thank you @ruudk.
Thanks @spawnia 🎉