Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Push — master ( a14b66...df0125 )
by Jérémiah
18:19
created

AccessResolver::isThenable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Resolver;
6
7
use GraphQL\Executor\Promise\Adapter\SyncPromise;
8
use GraphQL\Executor\Promise\Promise;
9
use GraphQL\Executor\Promise\PromiseAdapter;
10
use GraphQL\Type\Definition\ListOfType;
11
use GraphQL\Type\Definition\ResolveInfo;
12
use Overblog\GraphQLBundle\Error\UserError;
13
use Overblog\GraphQLBundle\Error\UserWarning;
14
use Overblog\GraphQLBundle\Relay\Connection\Output\Connection;
15
use Overblog\GraphQLBundle\Relay\Connection\Output\Edge;
16
17
class AccessResolver
18
{
19
    /** @var PromiseAdapter */
20
    private $promiseAdapter;
21
22 101
    public function __construct(PromiseAdapter $promiseAdapter)
23
    {
24 101
        $this->promiseAdapter = $promiseAdapter;
25 101
    }
26
27 14
    public function resolve(callable $accessChecker, callable $resolveCallback, array $resolveArgs = [], $useStrictAccess = false)
28
    {
29 14
        if ($useStrictAccess || self::isMutationRootField($resolveArgs[3])) {
30 13
            return $this->checkAccessForStrictMode($accessChecker, $resolveCallback, $resolveArgs);
31
        }
32
33 4
        $resultOrPromise = \call_user_func_array($resolveCallback, $resolveArgs);
34
35 4
        if ($this->isThenable($resultOrPromise)) {
36 1
            return $this->createPromise(
37 1
                $resultOrPromise,
38
                function ($result) use ($accessChecker, $resolveArgs) {
39 1
                    return $this->processFilter($result, $accessChecker, $resolveArgs);
40 1
                }
41
            );
42
        }
43
44 3
        return $this->processFilter($resultOrPromise, $accessChecker, $resolveArgs);
45
    }
46
47 10
    private static function isMutationRootField(ResolveInfo $info): bool
48
    {
49 10
        return 'mutation' === $info->operation->operation && $info->parentType === $info->schema->getMutationType();
50
    }
51
52 13
    private function checkAccessForStrictMode(callable $accessChecker, callable $resolveCallback, array $resolveArgs = [])
53
    {
54 13
        $promiseOrHasAccess = $this->hasAccess($accessChecker, $resolveArgs);
55
        $callback = function ($hasAccess) use ($resolveArgs, $resolveCallback) {
56 13
            if (!$hasAccess) {
57 6
                $exceptionClassName = self::isMutationRootField($resolveArgs[3]) ? UserError::class : UserWarning::class;
58 6
                throw new $exceptionClassName('Access denied to this field.');
59
            }
60
61 7
            return \call_user_func_array($resolveCallback, $resolveArgs);
62 13
        };
63
64 13
        if ($this->isThenable($promiseOrHasAccess)) {
65 2
            return $this->createPromise($promiseOrHasAccess, $callback);
66
        } else {
67 11
            $promiseOrHasAccess = $this->extractAdoptedPromise($promiseOrHasAccess);
68 11
            if ($promiseOrHasAccess instanceof SyncPromise) {
69
                $promiseOrHasAccess = $promiseOrHasAccess->result;
70
            }
71
72 11
            return $callback($promiseOrHasAccess);
73
        }
74
    }
75
76 4
    private function processFilter($result, $accessChecker, $resolveArgs)
77
    {
78
        /** @var ResolveInfo $resolveInfo */
79 4
        $resolveInfo = $resolveArgs[3];
80
81 4
        if (\is_iterable($result) && $resolveInfo->returnType instanceof ListOfType) {
82 1
            foreach ($result as $i => $object) {
83 1
                $result[$i] = $this->hasAccess($accessChecker, $resolveArgs, $object) ? $object : null;
84
            }
85 3
        } elseif ($result instanceof Connection) {
86 1
            $result->setEdges(\array_map(
87
                function (Edge $edge) use ($accessChecker, $resolveArgs) {
88 1
                    $edge->setNode($this->hasAccess($accessChecker, $resolveArgs, $edge->getNode()) ? $edge->getNode() : null);
89
90 1
                    return $edge;
91 1
                },
92 1
                $result->getEdges()
93
            ));
94 2
        } elseif (!$this->hasAccess($accessChecker, $resolveArgs, $result)) {
95 1
            throw new UserWarning('Access denied to this field.');
96
        }
97
98 3
        return $result;
99
    }
100
101 14
    private function hasAccess(callable $accessChecker, array $resolveArgs = [], $object = null)
102
    {
103 14
        $resolveArgs[] = $object;
104 14
        $accessOrPromise = \call_user_func_array($accessChecker, $resolveArgs);
105
106 14
        return $accessOrPromise;
107
    }
108
109 14
    private function isThenable($object): bool
110
    {
111 14
        $object = $this->extractAdoptedPromise($object);
112
113 14
        return $this->promiseAdapter->isThenable($object) || $object instanceof SyncPromise;
114
    }
115
116 14
    private function extractAdoptedPromise($object)
117
    {
118 14
        if ($object instanceof Promise) {
119 3
            $object = $object->adoptedPromise;
120
        }
121
122 14
        return $object;
123
    }
124
125 3
    private function createPromise($promise, callable $onFulfilled = null): Promise
126
    {
127 3
        return $this->promiseAdapter->then(
128 3
            new Promise($this->extractAdoptedPromise($promise), $this->promiseAdapter),
129 3
            $onFulfilled
130
        );
131
    }
132
}
133