Passed
Push — master ( 3249f9...a458b3 )
by Marcel
02:34
created

ApiDataController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 11
c 2
b 0
f 0
nc 1
nop 9
dl 0
loc 24
rs 9.9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Activity\ActivityManager;
15
use OCA\Analytics\Db\StorageMapper;
16
use OCA\Analytics\Service\DatasetService;
17
use OCA\Analytics\Service\ReportService;
18
use OCA\Analytics\Service\StorageService;
19
use OCP\AppFramework\ApiController;
20
use OCP\AppFramework\Http;
21
use OCP\AppFramework\Http\DataResponse;
22
use OCP\Constants;
23
use OCP\IRequest;
24
use OCP\IUserSession;
25
use Psr\Log\LoggerInterface;
26
27
class ApiDataController extends ApiController
28
{
29
    const UNKNOWN = 9001;
30
    const MISSING_PARAM = 9002;
31
    const NOT_FOUND = 9003;
32
    const NOT_ALLOWED = 9004;
33
34
    protected $errors = [];
35
    private $logger;
36
    private $userSession;
37
    private $ActivityManager;
38
    private $DatasetService;
39
    private $ReportService;
40
    private $StorageService;
41
	private $StorageMapper;
42
43
    public function __construct(
44
        $appName,
45
        IRequest $request,
46
        LoggerInterface $logger,
47
        IUserSession $userSession,
48
        ActivityManager $ActivityManager,
49
        DatasetService $DatasetService,
50
        ReportService $ReportService,
51
        StorageService $StorageService,
52
		StorageMapper $StorageMapper
53
    )
54
    {
55
        parent::__construct(
56
            $appName,
57
            $request,
58
            'POST'
59
            );
60
        $this->logger = $logger;
61
        $this->userSession = $userSession;
62
        $this->ActivityManager = $ActivityManager;
63
        $this->DatasetService = $DatasetService;
64
        $this->ReportService = $ReportService;
65
        $this->StorageService = $StorageService;
66
        $this->StorageMapper = $StorageMapper;
67
    }
68
69
    /**
70
     * add data via there database names
71
     * @CORS
72
     * @NoCSRFRequired
73
     * @NoAdminRequired
74
     * @param int $datasetId
75
     * @return DataResponse
76
     * @throws \Exception
77
     */
78
    public function addData(int $datasetId)
79
    {
80
        $params = $this->request->getParams();
81
        $datasetMetadata = $this->DatasetService->read($datasetId);
82
83
        $this->deriveMaintenancePossible($datasetMetadata);
84
85
        if (!isset($params['dimension1'])) {
86
            $this->errors[] = 'Dimension 1 required';
87
        } elseif (!isset($params['dimension2'])) {
88
            $this->errors[] = 'Dimension 2 required';
89
        } elseif (!isset($params['dimension3'])) {
90
            $this->errors[] = 'Dimension 3 required';
91
        }
92
        if (!empty($this->errors)) {
93
            return $this->requestResponse(false, self::MISSING_PARAM, implode(',', $this->errors));
94
        }
95
96
        $this->StorageService->update($datasetId, $params['dimension1'], $params['dimension2'], $params['dimension3']);
97
        $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_API);
98
99
        return $this->requestResponse(
100
            true,
101
            Http::STATUS_OK,
102
            'Data update successfull');
103
    }
104
105
    /**
106
     * add data via there real field names
107
     * @CORS
108
     * @NoCSRFRequired
109
     * @NoAdminRequired
110
     * @param int $datasetId
111
     * @return DataResponse
112
     * @throws \Exception
113
     */
114
    public function addDataV2(int $datasetId)
115
    {
116
        $message = 'No -data- parameter';
117
        $params = $this->request->getParams();
118
        $datasetMetadata = $this->DatasetService->read($datasetId);
119
120
        $this->deriveMaintenancePossible($datasetMetadata);
121
122
        foreach ($params['data'] as $dataArray) {
123
124
            $dimension1 = $this->deriveParameterNames($dataArray, $datasetMetadata, 'dimension1');
125
            $dimension2 = $this->deriveParameterNames($dataArray, $datasetMetadata, 'dimension2');
126
            $value = $this->deriveParameterNames($dataArray, $datasetMetadata, 'value');
127
128
            if (!empty($this->errors)) {
129
                return $this->requestResponse(false, self::MISSING_PARAM, implode(',', $this->errors));
130
            }
131
132
            $this->StorageService->update($datasetId, $dimension1, $dimension2, $value);
133
            $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_API);
134
            $message = 'Data update successfull';
135
        }
136
137
        return $this->requestResponse(
138
            true,
139
            Http::STATUS_OK,
140
            $message);
141
    }
142
143
    /**
144
     * delete data
145
     * @CORS
146
     * @NoCSRFRequired
147
     * @NoAdminRequired
148
     * @param int $datasetId
149
     * @return DataResponse
150
     * @throws \Exception
151
     */
152
    public function deleteDataV2(int $datasetId)
153
    {
154
        $message = 'No -delete- parameter';
155
        $params = $this->request->getParams();
156
        //$this->logger->debug('array: ' . json_encode($params));
157
        $datasetMetadata = $this->DatasetService->read($datasetId);
158
159
        $this->deriveMaintenancePossible($datasetMetadata);
160
161
        foreach ($params['delete'] as $dataArray) {
162
            $dimension1 = $this->deriveParameterNames($dataArray, $datasetMetadata, 'dimension1');
163
            $dimension2 = $this->deriveParameterNames($dataArray, $datasetMetadata, 'dimension2');
164
165
            if (!empty($this->errors)) {
166
                return $this->requestResponse(false, self::MISSING_PARAM, implode(',', $this->errors));
167
            }
168
169
            $this->StorageService->delete($datasetId, $dimension1, $dimension2);
170
            $message = 'Data deleted';
171
        }
172
173
        return $this->requestResponse(
174
            true,
175
            Http::STATUS_OK,
176
            $message);
177
    }
178
179
180
    ///
181
    /// API V3
182
    ///
183
184
    /**
185
     * get all data of a report and respect filter options
186
     * @CORS
187
     * @NoCSRFRequired
188
     * @NoAdminRequired
189
     * @return DataResponse
190
     * @throws \Exception
191
     */
192
    public function dataGetV3(int $reportId)
193
    {
194
        $params = $this->request->getParams();
0 ignored issues
show
Unused Code introduced by
The assignment to $params is dead and can be removed.
Loading history...
195
        $reportMetadata = $this->ReportService->read($reportId);
196
197
        if (!empty($reportMetadata)) {
198
            $options = json_decode($reportMetadata['filteroptions'], true);
199
            $allData = $this->StorageMapper->read((int)$reportMetadata['dataset'], $options);
200
201
            return new DataResponse($allData, HTTP::STATUS_OK);
202
        } else {
203
            return new DataResponse([
204
                'message' => 'No data available for given report id',
205
            ], HTTP::STATUS_OK);
206
        }
207
    }
208
209
    /**
210
     * delete data
211
     * @CORS
212
     * @NoCSRFRequired
213
     * @NoAdminRequired
214
     * @param int $datasetId
215
     * @return DataResponse
216
     * @throws \Exception
217
     */
218
    public function dataDeleteV3(int $datasetId)
219
    {
220
        return $this->deleteDataV2($datasetId);
221
    }
222
223
    /**
224
     * add data via there real field names
225
     * @CORS
226
     * @NoCSRFRequired
227
     * @NoAdminRequired
228
     * @param int $datasetId
229
     * @return DataResponse
230
     * @throws \Exception
231
     */
232
    public function dataAddV3(int $datasetId)
233
    {
234
        return $this->addDataV2($datasetId);
235
    }
236
237
    /**
238
     * list datasets
239
     * @CORS
240
     * @NoCSRFRequired
241
     * @NoAdminRequired
242
     * @return DataResponse
243
     * @throws \Exception
244
     */
245
    public function datasetIndexV3()
246
    {
247
        return $this->DatasetService->index();
248
    }
249
250
    /**
251
     * list reports
252
     * @CORS
253
     * @NoCSRFRequired
254
     * @NoAdminRequired
255
     * @return DataResponse
256
     * @throws \Exception
257
     */
258
    public function reportIndexV3()
259
    {
260
        return $this->ReportService->index();
261
    }
262
263
    /**
264
     * read data of a dataset with additional information for table and series
265
     * @CORS
266
     * @NoCSRFRequired
267
     * @NoAdminRequired
268
     * @return DataResponse
269
     * @throws \Exception
270
     */
271
    public function datasetsDetailV3(int $datasetId)
272
    {
273
        $datasetMetadata = $this->DatasetService->read($datasetId);
274
        unset($datasetMetadata['user_id']
275
            , $datasetMetadata['link']
276
            , $datasetMetadata['dimension3']
277
        );
278
279
        if (!empty($datasetMetadata)) {
280
            /*** todo **/
281
            /**(int)$reportMetadata['dataset'] **/
282
283
            $allData = $this->StorageService->read($datasetMetadata);
0 ignored issues
show
Bug introduced by
The call to OCA\Analytics\Service\StorageService::read() has too few arguments starting with options. ( Ignorable by Annotation )

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

283
            /** @scrutinizer ignore-call */ 
284
            $allData = $this->StorageService->read($datasetMetadata);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
284
            $series = array_values(array_unique(array_map('array_shift', $allData['data'])));
285
286
            return new DataResponse([
287
                'options' => $datasetMetadata,
288
                'header' => $allData['header'],
289
                'dimensions' => $allData['dimensions'],
290
                'series' => $series,
291
            ], HTTP::STATUS_OK);
292
293
        } else {
294
            return new DataResponse([
295
                'message' => 'No metadata available for given $datasetId',
296
            ], HTTP::STATUS_OK);
297
        }
298
    }
299
300
    /**
301
     * derive if the parameter is technical or the free text description from the report
302
     * @param $data
303
     * @param $datasetMetadata
304
     * @param $dimension
305
     * @return array | bool
306
     */
307
    protected function deriveParameterNames($data, $datasetMetadata, $dimension)
308
    {
309
        if (isset($data[$dimension])) {
310
            return $data[$dimension];
311
        } elseif (isset($data[$datasetMetadata[$dimension]])) {
312
            return $data[$datasetMetadata[$dimension]];
313
        } else {
314
            $this->errors[] = $dimension . ' required';
315
            return false;
316
        }
317
    }
318
319
    /**
320
     * derive if maintenance is possible
321
     * @param $datasetMetadata
322
     * @return DataResponse | bool
323
     */
324
    protected function deriveMaintenancePossible($datasetMetadata)
325
    {
326
        if (empty($datasetMetadata)) {
327
            $this->errors[] = 'Unknown report or dataset';
328
            return $this->requestResponse(false, self::NOT_FOUND, implode(',', $this->errors));
329
        } elseif ((int)$datasetMetadata['type'] !== DatasourceController::DATASET_TYPE_INTERNAL_DB) {
330
            $this->errors[] = 'Report does not allow data maintenance';
331
            return $this->requestResponse(false, self::NOT_ALLOWED, implode(',', $this->errors));
332
        }
333
        return true;
334
    }
335
336
    /**
337
     * @param bool $success
338
     * @param int|null $code
339
     * @param string|null $message
340
     * @return DataResponse
341
     */
342
    protected function requestResponse($success, $code = null, $message = null)
343
    {
344
        if (!$success) {
345
            if ($code === null) {
346
                $code = self::UNKNOWN;
347
            }
348
            $array = [
349
                'success' => false,
350
                'error' => ['code' => $code,
351
                    'message' => $message
352
                ]
353
            ];
354
        } else {
355
            $array = [
356
                'success' => true,
357
                'message' => $message
358
            ];
359
        }
360
        $response = new DataResponse();
361
        $response->setData($array)->render();
362
        return $response;
363
    }
364
    // curl -u Admin:2sroW-SxRcK-AmdsF-RYMJ5-CKSyf -d '{"dimension1": "x", "dimension2": "x", "dimension3": "333,3"}' -X POST -H "Content-Type: application/json" http://ncxx/nextcloud/apps/analytics/api/1.0/adddata/158
365
    // curl -u Admin:2sroW-SxRcK-AmdsF-RYMJ5-CKSyf -d '[{"Spalte 1": "x", "Spalte 2": "x", "toller wert": "333,3"}]' -X POST -H "Content-Type: application/json" http://ncxx/nextcloud/apps/analytics/api/2.0/adddata/158
366
    // curl -u Admin:2sroW-SxRcK-AmdsF-RYMJ5-CKSyf -d '{"data":[{"Spalte 1": "a", "Spalte 2": "a", "toller wert": "1"}, {"dimension1": "b", "dimension2": "b", "value": "2"}]}' -X POST -H "Content-Type: application/json" http://ncxx/nextcloud/apps/analytics/api/2.0/adddata/158
367
368
    // curl -u Admin:2sroW-SxRcK-AmdsF-RYMJ5-CKSyf -d '{"delete":[{"dimension1": "a", "dimension2": "a"}]}' -X POST -H "Content-Type: application/json" http://ncxx/nextcloud/apps/analytics/api/2.0/deletedata/158
369
    // curl -u Admin:2sroW-SxRcK-AmdsF-RYMJ5-CKSyf -d '{"del":[{"dimension1": "a", "dimension2": "a"}]}' -X POST -H "Content-Type: application/json" http://ncxx/nextcloud/apps/analytics/api/2.0/deletedata/158
370
    // curl -u admin:cZMLJ-DTpYA-Ci5QM-M4ZRy-KBcTp -X GET -H "Content-Type: application/json" https://ncxx/nextcloud/apps/analytics/api/3.0/data/52 --insecure
371
    // curl -u admin:cZMLJ-DTpYA-Ci5QM-M4ZRy-KBcTp -X GET -H "Content-Type: application/json" https://ncxx/nextcloud/apps/analytics/api/3.0/datasets --insecure
372
    // curl -u admin:cZMLJ-DTpYA-Ci5QM-M4ZRy-KBcTp -X GET -H "Content-Type: application/json" https://ncxx/nextcloud/apps/analytics/api/3.0/reports --insecure
373
}
374