Completed
Pull Request — 8.x-3.x (#459)
by
unknown
02:00
created

QueryRouteEnhancer   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 175
rs 10
c 0
b 0
f 0
wmc 25
lcom 1
cbo 1

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A applies() 0 3 1
A enhance() 0 21 4
A enhanceBatch() 0 22 3
B enhanceSingle() 0 18 5
A extractQueryFromParameterBag() 0 7 3
B extractParams() 0 17 8
1
<?php
2
3
namespace Drupal\graphql\Routing;
4
5
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
6
use Drupal\graphql\QueryProvider\QueryProviderInterface;
7
use Symfony\Component\HttpFoundation\Request;
8
use Symfony\Component\Routing\Route;
9
10
class QueryRouteEnhancer implements RouteEnhancerInterface {
11
12
  const SINGLE = 'single';
13
  const BATCH = 'batch';
14
15
  /**
16
   * The query provider service.
17
   *
18
   * @var \Drupal\graphql\QueryProvider\QueryProviderInterface
19
   */
20
  protected $queryProvider;
21
22
  /**
23
   * QueryRouteEnhancer constructor.
24
   *
25
   * @param \Drupal\graphql\QueryProvider\QueryProviderInterface $queryProvider
26
   *   The query provider service.
27
   */
28
  public function __construct(QueryProviderInterface $queryProvider) {
29
    $this->queryProvider = $queryProvider;
30
  }
31
32
33
  /**
34
   * {@inheritdoc}
35
   */
36
  public function applies(Route $route) {
37
    return $route->hasDefault('_graphql');
38
  }
39
40
  /**
41
   * {@inheritdoc}
42
   */
43
  public function enhance(array $defaults, Request $request) {
44
    if (!empty($defaults['_controller'])) {
45
      return $defaults;
46
    }
47
48
    $params = $this->extractParams($request);
49
    if ($enhanced = $this->enhanceSingle($defaults, $params, $request)) {
50
      return $enhanced;
51
    }
52
53
    if ($enhanced = $this->enhanceBatch($defaults, $params, $request)) {
54
      return $enhanced;
55
    }
56
57
    // By default we assume a 'single' request. This is going to fail in the
58
    // graphql processor due to a missing query string but at least provides
59
    // the right format for the client to act upon.
60
    return $defaults + [
61
      '_controller' => $defaults['_graphql']['single'],
62
    ];
63
  }
64
65
  /**
66
   * Attempts to enhance the request as a batch query.
67
   *
68
   * @param array $defaults
69
   *   The controller defaults.
70
   * @param array $params
71
   *   The query parameters.
72
   * @param \Symfony\Component\HttpFoundation\Request $request
73
   *   The request object.
74
   *
75
   * @return array|bool
76
   *   The enhanced controller defaults.
77
   */
78
  protected function enhanceBatch(array $defaults, array $params, Request $request) {
79
    // PHP 5.5.x does not yet support the ARRAY_FILTER_USE_KEYS constant.
80
    $keys = array_filter(array_keys($params), function($index) {
81
      return is_numeric($index);
82
    });
83
84
    $queries = array_intersect_key($params, array_flip($keys));
85
    if (!isset($queries[0])) {
86
      return FALSE;
87
    }
88
89
    if (array_keys($queries) !== range(0, count($queries) - 1)) {
90
      // If this is not a continuously numeric array, don't do anything.
91
      return FALSE;
92
    }
93
94
    return $defaults + [
95
      '_controller' => $defaults['_graphql']['multiple'],
96
      'queries' => $queries,
97
      'type' => static::BATCH,
98
    ];
99
  }
100
101
  /**
102
   * Attempts to enhance the request as a single query.
103
   *
104
   * @param array $defaults
105
   *   The controller defaults.
106
   * @param array $params
107
   *   The query parameters.
108
   * @param \Symfony\Component\HttpFoundation\Request $request
109
   *   The request object.
110
   *
111
   * @return array|boolean
112
   *   The enhanced controller defaults.
113
   */
114
  protected function enhanceSingle(array $defaults, array $params, Request $request) {
115
    $values = $params + [
116
      'query' => empty($params['query']) ? $this->queryProvider->getQuery($params) : $params['query'],
117
      'variables' => [],
118
    ];
119
120
    if (empty($values['query'])) {
121
      return FALSE;
122
    }
123
124
    return $defaults + [
125
      '_controller' => $defaults['_graphql']['single'],
126
      'query' => $values['query'] ?: '',
127
      'variables' => $values['variables'] ?: [],
128
      'persisted' => empty($params['query']),
129
      'type' => static::SINGLE,
130
    ];
131
  }
132
133
  /**
134
   * Extract the query from the parameter bag
135
   *
136
   * Search the parameter bag array for a query
137
   *
138
   * @param array $parameters
139
   *   The parameter bag array.
140
   *
141
   * @return string
142
   *   The query
143
   */
144
  protected function extractQueryFromParameterBag(array $parameters){
145
    foreach ($parameters as $key => $value){
146
      if (strpos($value, 'query')){
147
        return $value;
148
      }
149
    }
150
  }
151
152
  /**
153
   * Extract an associative array of query parameters from the request.
154
   *
155
   * If the given request does not have any POST body content check for a POST
156
   * query parameter otherwise use the GET query parameters instead. The additional
157
   * check for the ParametersBag query is necessary when sending FormData such as
158
   * needed when sending files along with the query from the client.
159
   *
160
   * @param \Symfony\Component\HttpFoundation\Request $request
161
   *   The request object.
162
   *
163
   * @return array
164
   *   An associative array of query parameters.
165
   */
166
  protected function extractParams(Request $request) {
167
168
    $values = ($content = $request->getContent()) ? json_decode($content, TRUE) : $request->query->all();
169
    $parameters = $request->request->all();
170
    if (count($parameters) > 0){
171
      $values = isset($request->request) ? json_decode($this->extractQueryFromParameterBag($request->request->all()), TRUE): $request->query->all();
172
    }
173
174
    return array_map(function($value) {
175
      if (!is_string($value)) {
176
        return $value;
177
      }
178
179
      $decoded = json_decode($value, TRUE);
180
      return ($decoded != $value) && $decoded ? $decoded : $value;
181
    }, $values ?: []);
182
  }
183
184
}
185