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

ApiDataController::datasetIndexV3()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
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