TopController   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 65
c 3
b 1
f 0
dl 0
loc 225
rs 10
wmc 13

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getRowData() 0 19 2
A build() 0 10 2
A group() 0 26 3
A __construct() 0 9 1
A buildMainTableRows() 0 12 2
A buildMainTableHeader() 0 7 1
A create() 0 17 1
A buildMainTable() 0 7 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Drupal\mongodb_watchdog\Controller;
6
7
use Drupal\Core\Config\ImmutableConfig;
0 ignored issues
show
Bug introduced by
The type Drupal\Core\Config\ImmutableConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Drupal\Core\Pager\PagerManagerInterface;
0 ignored issues
show
Bug introduced by
The type Drupal\Core\Pager\PagerManagerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Drupal\mongodb_watchdog\Logger;
10
use MongoDB\Collection;
11
use MongoDB\Database;
12
use Psr\Log\LoggerInterface;
0 ignored issues
show
Bug introduced by
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Symfony\Component\DependencyInjection\ContainerInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Depend...tion\ContainerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Symfony\Component\HttpFoundation\Request;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\HttpFoundation\Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
/**
17
 * The Top403/Top404 controllers.
18
 */
19
class TopController extends ControllerBase {
20
  const TYPES = [
21
    'page not found',
22
    'access denied',
23
  ];
24
25
  const TYPE_MAP = [
26
    'root' => TopResult::class,
27
  ];
28
29
30
  /**
31
   * The database holding the logger collections.
32
   *
33
   * @var \MongoDB\Database
34
   */
35
  protected $database;
36
37
  /**
38
   * TopController constructor.
39
   *
40
   * @param \Psr\Log\LoggerInterface $logger
41
   *   The logger service, to log intervening events.
42
   * @param \Drupal\mongodb_watchdog\Logger $watchdog
43
   *   The MongoDB logger, to load stored events.
44
   * @param \Drupal\Core\Config\ImmutableConfig $config
45
   *   The module configuration.
46
   * @param \MongoDB\Database $database
47
   *   Needed because there is no group() command in phplib yet.
48
   * @param \Drupal\Core\Pager\PagerManagerInterface $pagerManager
49
   *   The core pager.manager service.
50
   *
51
   * @see https://jira.mongodb.org/browse/PHPLIB-177
52
   */
53
  public function __construct(
54
    LoggerInterface $logger,
55
    Logger $watchdog,
56
    ImmutableConfig $config,
57
    Database $database,
58
    PagerManagerInterface $pagerManager) {
59
    parent::__construct($logger, $watchdog, $pagerManager, $config);
60
61
    $this->database = $database;
62
  }
63
64
  /**
65
   * Controller.
66
   *
67
   * @param \Symfony\Component\HttpFoundation\Request $request
68
   *   The current request.
69
   * @param string $type
70
   *   The type of top report to produce.
71
   *
72
   * @return array<string,mixed>
73
   *   A render array.
74
   */
75
  public function build(Request $request, string $type): array {
76
    $top = $this->getTop();
77
78
    $rows = $this->getRowData($request, $type);
79
    $main = empty($rows)
80
      ? $this->buildEmpty($this->t('No "%type" message found', ['%type' => $type]))
81
      : $this->buildMainTable($rows);
82
83
    $ret = $this->buildDefaults($main, $top);
84
    return $ret;
85
  }
86
87
  /**
88
   * Build the main table.
89
   *
90
   * @param \stdClass[] $rows
91
   *   The event data.
92
   *
93
   * @return array<string,mixed>
94
   *   A render array for the main table.
95
   */
96
  protected function buildMainTable(array $rows): array {
97
    $ret = [
98
      '#header' => $this->buildMainTableHeader(),
99
      '#rows' => $this->buildMainTableRows($rows),
100
      '#type' => 'table',
101
    ];
102
    return $ret;
103
  }
104
105
  /**
106
   * Build the main table header.
107
   *
108
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
109
   *   A table header array.
110
   */
111
  protected function buildMainTableHeader(): array {
112
    $header = [
113
      $this->t('#'),
114
      $this->t('Paths'),
115
    ];
116
117
    return $header;
118
  }
119
120
  /**
121
   * Build the main table rows.
122
   *
123
   * @param \stdClass[] $counts
124
   *   The array of counts per 403/404 page.
125
   *
126
   * @return array<int,array{0:int,1:string}>
127
   *   A render array for a table.
128
   */
129
  protected function buildMainTableRows(array $counts): array {
130
    $rows = [];
131
    /** @var \Drupal\mongodb_watchdog\Controller\TopResult $result */
132
    foreach ($counts as $result) {
133
      $row = [
134
        $result->count,
135
        $result->uri,
136
      ];
137
      $rows[] = $row;
138
    }
139
140
    return $rows;
141
  }
142
143
  /**
144
   * {@inheritdoc}
145
   */
146
  public static function create(ContainerInterface $container): self {
147
    /** @var \Psr\Log\LoggerInterface $logger */
148
    $logger = $container->get('logger.channel.mongodb_watchdog');
149
150
    /** @var \Drupal\mongodb_watchdog\Logger $watchdog */
151
    $watchdog = $container->get(Logger::SERVICE_LOGGER);
152
153
    /** @var \Drupal\Core\Config\ImmutableConfig $config */
154
    $config = $container->get('config.factory')->get('mongodb_watchdog.settings');
155
156
    /** @var \MongoDB\Database $database */
157
    $database = $container->get('mongodb.watchdog_storage');
158
159
    /** @var \Drupal\Core\Pager\PagerManagerInterface $pagerManager */
160
    $pagerManager = $container->get('pager.manager');
161
162
    return new static($logger, $watchdog, $config, $database, $pagerManager);
163
  }
164
165
  /**
166
   * Obtain the data from the logger.
167
   *
168
   * @param \Symfony\Component\HttpFoundation\Request $request
169
   *   The current request. Needed for paging.
170
   * @param string $type
171
   *   The type of top list to retrieve.
172
   *
173
   * @return \stdClass[]
174
   *   The data array.
175
   */
176
  protected function getRowData(Request $request, string $type): array {
177
    // Find _id for the error type.
178
    $templateCollection = $this->watchdog->templateCollection();
179
    $template = $templateCollection->findOne(['type' => $type], ['_id']);
180
    if (empty($template)) {
181
      return [];
182
    }
183
184
    // Find occurrences of error type.
185
    $collectionName = $template['_id'];
186
    $eventCollection = $this->watchdog->eventCollection($collectionName);
187
188
    $counts = $this->group($eventCollection, 'variables.@uri', []);
189
190
    $page = $this->setupPager($request, count($counts));
191
    $skip = $page * $this->itemsPerPage;
192
    $counts = array_slice($counts, $skip, $this->itemsPerPage);
193
194
    return $counts;
195
  }
196
197
  /**
198
   * Command wrapper for removed MongoDB group() method/command.
199
   *
200
   * @param \MongoDB\Collection $collection
201
   *   The collection on which to perform the command.
202
   * @param string $key
203
   *   The grouping key.
204
   * @param array<mixed,mixed> $cond
205
   *   The condition.
206
   *
207
   * @return \stdClass[]
208
   *   An array of stdClass rows with the following properties:
209
   *   - _id: the URL
210
   *   - count: the number of occurrences.
211
   *   It may be empty.
212
   *
213
   * @throws \MongoDB\Driver\Exception\RuntimeException
214
   * @throws \MongoDB\Exception\InvalidArgumentException
215
   * @throws \MongoDB\Exception\UnexpectedValueException
216
   * @throws \MongoDB\Exception\UnsupportedException
217
   */
218
  public function group(Collection $collection, string $key, array $cond): array {
219
    $pipeline = [];
220
    if (!empty($cond)) {
221
      $pipeline[] = ['$match' => $cond];
222
    }
223
    if (!empty($key)) {
224
      $pipeline[] = [
225
        '$group' => [
226
          '_id' => "\${$key}",
227
          'count' => ['$sum' => 1],
228
        ],
229
      ];
230
    }
231
    $pipeline[] = [
232
      '$sort' => [
233
        'count' => -1,
234
        '_id' => 1,
235
      ],
236
    ];
237
238
    // Aggregate always returns a cursor since MongoDB 3.6.
239
    /** @var \MongoDB\Driver\CursorInterface<array> $res */
240
    $res = $collection->aggregate($pipeline);
241
    $res->setTypeMap(static::TYPE_MAP);
242
    $ret = $res->toArray();
243
    return $ret;
244
  }
245
246
}
247