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 — master (#494)
by Jérémiah
18:56
created

AccessResolver   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Test Coverage

Coverage 42.59%

Importance

Changes 0
Metric Value
wmc 25
eloc 47
dl 0
loc 108
ccs 23
cts 54
cp 0.4259
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A isThenable() 0 5 2
A __construct() 0 3 1
A hasAccess() 0 6 1
A createPromise() 0 5 1
B processFilter() 0 23 8
A extractAdoptedPromise() 0 7 2
A isMutationRootField() 0 3 2
A resolve() 0 18 4
A checkAccessForStrictMode() 0 16 4
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 69
    public function __construct(PromiseAdapter $promiseAdapter)
23
    {
24 69
        $this->promiseAdapter = $promiseAdapter;
25 69
    }
26
27 1
    public function resolve(callable $accessChecker, callable $resolveCallback, array $resolveArgs = [], $useStrictAccess = false)
28
    {
29 1
        if ($useStrictAccess || self::isMutationRootField($resolveArgs[3])) {
30 1
            return $this->checkAccessForStrictMode($accessChecker, $resolveCallback, $resolveArgs);
31
        }
32
33
        $resultOrPromise = \call_user_func_array($resolveCallback, $resolveArgs);
34
35
        if ($this->isThenable($resultOrPromise)) {
36
            return $this->createPromise(
37
                $resultOrPromise,
38
                function ($result) use ($accessChecker, $resolveArgs) {
39
                    return $this->processFilter($result, $accessChecker, $resolveArgs);
40
                }
41
            );
42
        }
43
44
        return $this->processFilter($resultOrPromise, $accessChecker, $resolveArgs);
45
    }
46
47
    private static function isMutationRootField(ResolveInfo $info): bool
48
    {
49
        return 'mutation' === $info->operation->operation && $info->parentType === $info->schema->getMutationType();
50
    }
51
52 1
    private function checkAccessForStrictMode(callable $accessChecker, callable $resolveCallback, array $resolveArgs = [])
53
    {
54 1
        $promiseOrHasAccess = $this->hasAccess($accessChecker, $resolveArgs);
55
        $callback = function ($hasAccess) use ($resolveArgs, $resolveCallback) {
56 1
            if (!$hasAccess) {
57
                $exceptionClassName = self::isMutationRootField($resolveArgs[3]) ? UserError::class : UserWarning::class;
58
                throw new $exceptionClassName('Access denied to this field.');
59
            }
60
61 1
            return \call_user_func_array($resolveCallback, $resolveArgs);
62 1
        };
63
64 1
        if ($this->isThenable($promiseOrHasAccess)) {
65
            return $this->createPromise($promiseOrHasAccess, $callback);
66
        } else {
67 1
            return $callback($promiseOrHasAccess);
68
        }
69
    }
70
71
    private function processFilter($result, $accessChecker, $resolveArgs)
72
    {
73
        /** @var ResolveInfo $resolveInfo */
74
        $resolveInfo = $resolveArgs[3];
75
76
        if (\is_iterable($result) && $resolveInfo->returnType instanceof ListOfType) {
77
            foreach ($result as $i => $object) {
78
                $result[$i] = $this->hasAccess($accessChecker, $resolveArgs, $object) ? $object : null;
79
            }
80
        } elseif ($result instanceof Connection) {
81
            $result->setEdges(\array_map(
82
                function (Edge $edge) use ($accessChecker, $resolveArgs) {
83
                    $edge->setNode($this->hasAccess($accessChecker, $resolveArgs, $edge->getNode()) ? $edge->getNode() : null);
84
85
                    return $edge;
86
                },
87
                $result->getEdges()
88
            ));
89
        } elseif (!$this->hasAccess($accessChecker, $resolveArgs, $result)) {
90
            throw new UserWarning('Access denied to this field.');
91
        }
92
93
        return $result;
94
    }
95
96 1
    private function hasAccess(callable $accessChecker, array $resolveArgs = [], $object = null)
97
    {
98 1
        $resolveArgs[] = $object;
99 1
        $accessOrPromise = \call_user_func_array($accessChecker, $resolveArgs);
100
101 1
        return $accessOrPromise;
102
    }
103
104 1
    private function isThenable($object): bool
105
    {
106 1
        $object = $this->extractAdoptedPromise($object);
107
108 1
        return $this->promiseAdapter->isThenable($object) || $object instanceof SyncPromise;
109
    }
110
111 1
    private function extractAdoptedPromise($object)
112
    {
113 1
        if ($object instanceof Promise) {
114
            $object = $object->adoptedPromise;
115
        }
116
117 1
        return $object;
118
    }
119
120
    private function createPromise($promise, callable $onFulfilled = null): Promise
121
    {
122
        return $this->promiseAdapter->then(
123
            new Promise($this->extractAdoptedPromise($promise), $this->promiseAdapter),
124
            $onFulfilled
125
        );
126
    }
127
}
128