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
Pull Request — 0.11 (#429)
by Jérémiah
16:31
created

AccessResolver::isThenable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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