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

Passed
Push — 0.11 ( 2038aa...b637cc )
by Jérémiah
33:57 queued 30:47
created

AccessResolver   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Test Coverage

Coverage 98.21%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 29
eloc 51
dl 0
loc 122
ccs 55
cts 56
cp 0.9821
rs 10
c 2
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A createPromise() 0 5 1
A extractAdoptedPromise() 0 7 2
A isIterable() 0 6 4
A isThenable() 0 5 2
A __construct() 0 3 1
A hasAccess() 0 6 1
A isMutationRootField() 0 3 2
A checkAccessForStrictMode() 0 16 4
B processFilter() 0 23 8
A resolve() 0 18 4
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
                $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 (true === $hasAccess) {
55 7
                return \call_user_func_array($resolveCallback, $resolveArgs);
56
            }
57
58 6
            $exceptionClassName = self::isMutationRootField($resolveArgs[3]) ? UserError::class : UserWarning::class;
59 6
            throw new $exceptionClassName('Access denied to this field.');
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
                },
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