Completed
Pull Request — 8.x-3.x (#519)
by Sebastian
02:34
created

Processor::reduceRequest()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 3
nop 2
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\graphql\GraphQL\Execution;
4
5
use Drupal\graphql\GraphQL\Execution\Visitor\VisitorInterface;
6
use Youshido\GraphQL\Exception\ResolveException;
7
use Youshido\GraphQL\Execution\DeferredResolverInterface;
8
use Youshido\GraphQL\Execution\DeferredResult;
9
use Youshido\GraphQL\Execution\Processor as BaseProcessor;
10
use Youshido\GraphQL\Execution\Request;
11
use Youshido\GraphQL\Field\FieldInterface;
12
use Youshido\GraphQL\Parser\Parser;
13
use Youshido\GraphQL\Schema\AbstractSchema;
14
use Youshido\GraphQL\Type\Enum\AbstractEnumType;
15
use Youshido\GraphQL\Type\Scalar\AbstractScalarType;
16
use Youshido\GraphQL\Validator\RequestValidator\RequestValidator;
17
18
class Processor extends BaseProcessor {
19
20
  /**
21
   * @var \Drupal\graphql\GraphQL\Execution\Reducer
22
   */
23
  protected $requestReducer;
24
25
  /**
26
   * Processor constructor.
27
   *
28
   * @param \Youshido\GraphQL\Schema\AbstractSchema $schema
29
   * @param $query
30
   * @param array $variables
31
   */
32
  public function __construct(AbstractSchema $schema, $query, array $variables = []) {
33
    parent::__construct($schema);
34
35
    $parser = new Parser();
36
    $request = new Request($parser->parse($query), $variables);
37
38
    // Validation of the query payload. This does not take into consideration
39
    // any schema specifics and simply validations the structure of the query.
40
    $validator = new RequestValidator();
41
    $validator->validate($request);
42
43
    // Set the request in the execution context.
44
    $this->executionContext->setRequest($request);
45
46
    // Create a request reducer for our query visitors.
47
    $this->requestReducer = new Reducer($this->executionContext);
48
  }
49
50
  /**
51
   * @param \Drupal\graphql\GraphQL\Execution\Visitor\VisitorInterface $visitor
52
   * @param callable $callback
53
   *
54
   * @return null
55
   */
56
  public function reduceRequest(VisitorInterface $visitor, callable $callback) {
57
    try {
58
      $result = $this->requestReducer->reduceRequest($visitor);
59
60
      // Call the 'finish' callback. This is useful because that way the
61
      // exceptions are caught here and can be written into the execution
62
      // context.
63
      return $callback($result);
64
    }
65
    catch (ResolveException $exception) {
66
      $this->executionContext->addError($exception);
67
    }
68
69
    return NULL;
70
  }
71
72
  /**
73
   * @return array|mixed
74
   */
75
  public function resolveRequest() {
76
    $output = [];
77
78
    if (!$this->executionContext->hasErrors()) {
79
      try {
80
        $operations = $this->executionContext->getRequest()->getAllOperations();
81
82
        foreach ($operations as $query) {
83
          if ($result = $this->resolveQuery($query)) {
84
            $output = array_replace_recursive($output, $result);
85
          };
86
        }
87
88
        // If the processor found any deferred results, resolve them now.
89
        if (!empty($output) && (!empty($this->deferredResultsLeaf) || !empty($this->deferredResultsComplex))) {
90
          try {
91
            while ($resolver = array_shift($this->deferredResultsComplex)) {
92
              $resolver->resolve();
93
            }
94
95
            // Deferred scalar and enum fields should be resolved last to pick up
96
            // as many as possible for a single batch.
97
            while ($resolver = array_shift($this->deferredResultsLeaf)) {
98
              $resolver->resolve();
99
            }
100
          }
101
          catch (ResolveException $exception) {
102
            $this->executionContext->addError($exception);
103
          }
104
          finally {
105
            $output = static::unpackDeferredResults($output);
106
          }
107
        }
108
      }
109
      catch (ResolveException $exception) {
110
        $this->executionContext->addError($exception);
111
      }
112
    }
113
114
    return $output;
115
  }
116
117
  /**
118
   * {@inheritdoc}
119
   */
120
  protected function deferredResolve($resolvedValue, FieldInterface $field, callable $callback) {
121
    if ($resolvedValue instanceof DeferredResolverInterface) {
122
      $deferredResult = new DeferredResult($resolvedValue, function ($resolvedValue) use ($field, $callback) {
123
        // Allow nested deferred resolvers.
124
        return $this->deferredResolve($resolvedValue, $field, $callback);
125
      });
126
127
      // Whenever we stumble upon a deferred resolver, add it to the queue to be
128
      // resolved later.
129
      $type = $field->getType()->getNamedType();
130
      if ($type instanceof AbstractScalarType || $type instanceof AbstractEnumType) {
131
        array_push($this->deferredResultsLeaf, $deferredResult);
132
      }
133
      else {
134
        array_push($this->deferredResultsComplex, $deferredResult);
135
      }
136
137
      return $deferredResult;
138
    }
139
140
    // For simple values, invoke the callback immediately.
141
    return $callback($resolvedValue);
142
  }
143
144
}