Passed
Push — master ( b84320...92b80c )
by Marcel
02:37
created

DatasourceController::read()   B

Complexity

Conditions 8
Paths 52

Size

Total Lines 38
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 22
c 1
b 0
f 0
nc 52
nop 2
dl 0
loc 38
rs 8.4444
1
<?php
2
/**
3
 * Analytics
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the LICENSE.md file.
7
 *
8
 * @author Marcel Scherello <[email protected]>
9
 * @copyright 2021 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Controller;
13
14
use OCA\Analytics\Datasource\DatasourceEvent;
15
use OCA\Analytics\Datasource\Excel;
16
use OCA\Analytics\Datasource\ExternalFile;
17
use OCA\Analytics\Datasource\File;
18
use OCA\Analytics\Datasource\Github;
19
use OCA\Analytics\Datasource\Json;
20
use OCA\Analytics\Datasource\Regex;
21
use OCP\AppFramework\Controller;
22
use OCP\EventDispatcher\IEventDispatcher;
23
use OCP\Files\NotFoundException;
24
use OCP\IL10N;
25
use OCP\ILogger;
26
use OCP\IRequest;
27
28
class DatasourceController extends Controller
29
{
30
    private $logger;
31
    private $GithubService;
32
    private $FileService;
33
    private $ExternalFileService;
34
    private $RegexService;
35
    private $JsonService;
36
    private $ExcelService;
37
    /** @var IEventDispatcher */
38
    private $dispatcher;
39
    private $l10n;
40
41
    const DATASET_TYPE_GROUP = 0;
42
    const DATASET_TYPE_FILE = 1;
43
    const DATASET_TYPE_INTERNAL_DB = 2;
44
    const DATASET_TYPE_GIT = 3;
45
    const DATASET_TYPE_EXTERNAL_FILE = 4;
46
    const DATASET_TYPE_REGEX = 5;
47
    const DATASET_TYPE_JSON = 6;
48
    const DATASET_TYPE_EXCEL = 7;
49
50
    public function __construct(
51
        string $AppName,
52
        IRequest $request,
53
        ILogger $logger,
54
        Github $GithubService,
55
        File $FileService,
56
        Regex $RegexService,
57
        Json $JsonService,
58
        ExternalFile $ExternalFileService,
59
        Excel $ExcelService,
60
        IL10N $l10n,
61
        IEventDispatcher $dispatcher
62
    )
63
    {
64
        parent::__construct($AppName, $request);
65
        $this->logger = $logger;
66
        $this->ExternalFileService = $ExternalFileService;
67
        $this->GithubService = $GithubService;
68
        $this->RegexService = $RegexService;
69
        $this->FileService = $FileService;
70
        $this->JsonService = $JsonService;
71
        $this->ExcelService = $ExcelService;
72
        $this->dispatcher = $dispatcher;
73
        $this->l10n = $l10n;
74
    }
75
76
    /**
77
     * get all datasource ids + names
78
     *
79
     * @NoAdminRequired
80
     */
81
    public function index()
82
    {
83
        $datasources = array();
84
        $result = array();
85
        foreach ($this->getDatasources() as $key => $class) {
86
            $datasources[$key] = $class->getName();
87
        }
88
        $result['datasources'] = $datasources;
89
90
        $options = array();
91
        foreach ($this->getDatasources() as $key => $class) {
92
            $options[$key] = $class->getTemplate();
93
        }
94
        $result['options'] = $options;
95
96
        return $result;
97
    }
98
99
    /**
100
     * get all datasource templates
101
     *
102
     * @NoAdminRequired
103
     * @return array
104
     */
105
    public function getTemplates()
106
    {
107
        $result = array();
108
        foreach ($this->getDatasources() as $key => $class) {
109
            $result[$key] = $class->getTemplate();
110
        }
111
        return $result;
112
    }
113
114
    /**
115
     * Get the data from a datasource;
116
     *
117
     * @NoAdminRequired
118
     * @param int $datasourceId
119
     * @param $datasetMetadata
120
     * @return array|NotFoundException
121
     */
122
    public function read(int $datasourceId, $datasetMetadata)
123
    {
124
        $option = array();
125
        // before 3.1.0, the options were in another format. as of 3.1.0 the standard option array is used
126
        if ($datasetMetadata['link'][0] !== '{') {
127
            $option['link'] = $datasetMetadata['link'];
128
        } else {
129
            $option = json_decode($datasetMetadata['link'], true);
130
        }
131
        $option['user_id'] = $datasetMetadata['user_id'];
132
133
        try {
134
            $result = $this->getDatasources()[$datasourceId]->readData($option);
135
136
            if (isset($option['timestamp']) and $option['timestamp'] === 'true') {
137
                // if datasource should be timestamped/snapshoted
138
                // shift values by one dimension and stores date in second column
139
                $result['data'] = array_map(function ($tag) {
140
                    $columns = count($tag);
141
                    return array($tag[$columns - 2], $tag[$columns - 2], $tag[$columns - 1]);
142
                }, $result['data']);
143
                date_default_timezone_set('UTC');
144
                $result['data'] = $this->replaceDimension($result['data'], 1, date("Y-m-d H:i:s") . 'Z');
145
            }
146
147
            // filter resultset if required
148
            if (isset($datasetMetadata['filteroptions']) && strlen($datasetMetadata['filteroptions']) >> 2) {
149
                $result['data'] = $this->filterData($result['data'], $datasetMetadata['filteroptions']);
150
            }
151
152
        } catch (\Error $e) {
153
            $result['error'] = $e->getMessage();
154
        }
155
156
        if (empty($result['data'])) {
157
            $result['status'] = 'nodata';
158
        }
159
        return $result;
160
    }
161
162
    /**
163
     * combine internal and registered datasources
164
     * @return array
165
     */
166
    private function getDatasources()
167
    {
168
        return $this->getOwnDatasources() + $this->getRegisteredDatasources();
169
    }
170
171
    /**
172
     * map all internal datasources to their IDs
173
     * @return array
174
     */
175
    private function getOwnDatasources()
176
    {
177
        $datasources = [];
178
        $datasources[self::DATASET_TYPE_FILE] = $this->FileService;
179
        $datasources[self::DATASET_TYPE_EXCEL] = $this->ExcelService;
180
        $datasources[self::DATASET_TYPE_GIT] = $this->GithubService;
181
        $datasources[self::DATASET_TYPE_EXTERNAL_FILE] = $this->ExternalFileService;
182
        $datasources[self::DATASET_TYPE_REGEX] = $this->RegexService;
183
        $datasources[self::DATASET_TYPE_JSON] = $this->JsonService;
184
        return $datasources;
185
    }
186
187
    /**
188
     * map all registered datasources to their IDs
189
     * @return array
190
     */
191
    private function getRegisteredDatasources()
192
    {
193
        $datasources = [];
194
        $event = new DatasourceEvent();
195
        $this->dispatcher->dispatchTyped($event);
196
197
        foreach ($event->getDataSources() as $class) {
198
            $uniqueId = '99' . \OC::$server->get($class)->getId();
199
200
            if (isset($datasources[$uniqueId])) {
201
                $this->logger->logException(new \InvalidArgumentException('Datasource with the same ID already registered: ' . \OC::$server->get($class)->getName()), ['level' => ILogger::INFO]);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::INFO has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

201
                $this->logger->logException(new \InvalidArgumentException('Datasource with the same ID already registered: ' . \OC::$server->get($class)->getName()), ['level' => /** @scrutinizer ignore-deprecated */ ILogger::INFO]);

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
Deprecated Code introduced by
The function OCP\ILogger::logException() has been deprecated: 20.0.0 use the `exception` entry in the context of any method in \Psr\Log\LoggerInterface ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

201
                /** @scrutinizer ignore-deprecated */ $this->logger->logException(new \InvalidArgumentException('Datasource with the same ID already registered: ' . \OC::$server->get($class)->getName()), ['level' => ILogger::INFO]);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
202
                continue;
203
            }
204
            $datasources[$uniqueId] = \OC::$server->get($class);
205
        }
206
        return $datasources;
207
    }
208
209
    /**
210
     * replace all values of one dimension
211
     *
212
     * @NoAdminRequired
213
     * @param $Array
214
     * @param $Find
215
     * @param $Replace
216
     * @return array
217
     */
218
    private function replaceDimension($Array, $Find, $Replace)
219
    {
220
        if (is_array($Array)) {
221
            foreach ($Array as $Key => $Val) {
222
                if (is_array($Array[$Key])) {
223
                    $Array[$Key] = $this->replaceDimension($Array[$Key], $Find, $Replace);
224
                } else {
225
                    if ($Key === $Find) {
226
                        $Array[$Key] = $Replace;
227
                    }
228
                }
229
            }
230
        }
231
        return $Array;
232
    }
233
234
    /**
235
     * apply the fiven filters to the hole result set
236
     *
237
     * @NoAdminRequired
238
     * @param $data
239
     * @param $filter
240
     * @return array
241
     */
242
    private function filterData($data, $filter)
243
    {
244
        $options = json_decode($filter, true);
245
        if (isset($options['filter'])) {
246
247
            foreach ($options['filter'] as $key => $value) {
248
249
                $filterValue = $value['value'];
250
                $filterOption = $value['option'];
251
                $filtered = array();
252
253
                foreach ($data as $record) {
254
                    if (
255
                        ($filterOption === 'EQ' && $record[$key] === $filterValue)
256
                        || ($filterOption === 'GT' && $record[$key] > $filterValue)
257
                        || ($filterOption === 'LT' && $record[$key] < $filterValue)
258
                        || ($filterOption === 'LIKE' && strpos($record[$key], $filterValue) !== FALSE)
259
                    ) {
260
                        array_push($filtered, $record);
261
                    }
262
                }
263
                $data = $filtered;
264
            }
265
        }
266
        return $data;
267
    }
268
}