ExternalJson::readData()   F
last analyzed

Complexity

Conditions 21
Paths 1344

Size

Total Lines 102
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 69
nc 1344
nop 1
dl 0
loc 102
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Datasource;
10
11
use OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N 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...
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
14
class ExternalJson implements IDatasource
15
{
16
    private LoggerInterface $logger;
17
    private IL10N $l10n;
18
19
    public function __construct(
20
        IL10N           $l10n,
21
        LoggerInterface $logger
22
    )
23
    {
24
        $this->l10n = $l10n;
25
        $this->logger = $logger;
26
    }
27
28
    /**
29
     * @return string Display Name of the datasource
30
     */
31
    public function getName(): string
32
    {
33
        return $this->l10n->t('External') . ': JSON';
34
    }
35
36
    /**
37
     * @return int digit unique datasource id
38
     */
39
    public function getId(): int
40
    {
41
        return 6;
42
    }
43
44
    /**
45
     * @return array available options of the datasoure
46
     */
47
    public function getTemplate(): array
48
    {
49
        $template = array();
50
        $template[] = ['id' => 'url', 'name' => 'URL', 'placeholder' => 'url'];
51
        $template[] = ['id' => 'method', 'name' => $this->l10n->t('HTTP method'), 'placeholder' => 'GET/POST', 'type' => 'tf'];
52
        $template[] = ['id' => 'path', 'name' => $this->l10n->t('Object path'), 'placeholder' => 'x/y/z'];
53
        $template[] = ['id' => 'section', 'name' => $this->l10n->t('More options'), 'type' => 'section'];
54
        $template[] = ['id' => 'content-type', 'name' => 'Header Content-Type', 'placeholder' => 'application/json'];
55
        $template[] = ['id' => 'customHeaders', 'name' => 'Custom headers', 'placeholder' => 'key: value,key: value'];
56
        $template[] = ['id' => 'auth', 'name' => $this->l10n->t('Authentication'), 'placeholder' => 'User:Password'];
57
        $template[] = ['id' => 'insecure', 'name' => $this->l10n->t('Allow insecure connections'), 'placeholder' => '2-' . $this->l10n->t('No') . '/0-' . $this->l10n->t('Yes'), 'type' => 'tf'];
58
        $template[] = ['id' => 'body', 'name' => 'Request body', 'placeholder' => ''];
59
        $template[] = ['id' => 'timestamp', 'name' => $this->l10n->t('Timestamp of data load'), 'placeholder' => 'true-' . $this->l10n->t('Yes') . '/false-' . $this->l10n->t('No'), 'type' => 'tf'];
60
        return $template;
61
    }
62
63
    /**
64
     * Read the Data
65
     * @param $option
66
     * @return array available options of the datasoure
67
     */
68
    public function readData($option): array
69
    {
70
        $url = htmlspecialchars_decode($option['url'], ENT_NOQUOTES);
71
        $path = $option['path'];
72
        $auth = $option['auth'];
73
        $post = $option['method'] === 'POST';
74
        $contentType = ($option['content-type'] && $option['content-type'] !== '') ? $option['content-type'] : 'application/json';
75
        $data = array();
76
        $http_code = '';
77
        $headers = ($option['customHeaders'] && $option['customHeaders'] !== '') ? explode(",", $option['customHeaders']) : [];
78
        $headers = array_map('trim', $headers);
79
        $headers[] = 'OCS-APIRequest: true';
80
        $headers[] = 'Content-Type: ' . $contentType;
81
        # VERITYHOST=0 to disable verification, 2 to enable. 1 is no longer a valid option.
82
        $verifyHost = intval($option['insecure']);
83
84
        $ch = curl_init();
85
        if ($ch !== false) {
86
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
87
            curl_setopt($ch, CURLOPT_URL, $url);
88
            curl_setopt($ch, CURLOPT_POST, $post);
89
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
90
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
91
            curl_setopt($ch, CURLOPT_USERPWD, $auth);
92
            curl_setopt($ch, CURLOPT_VERBOSE, true);
93
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifyHost);
94
            if ($option['body'] && $option['body'] !== '') {
95
                curl_setopt($ch, CURLOPT_POSTFIELDS, $option['body']);
96
            }
97
            $rawResult = curl_exec($ch);
98
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
99
            curl_close($ch);
100
        } else {
101
            $rawResult = '';
102
        }
103
104
        $json = json_decode($rawResult, true);
0 ignored issues
show
Bug introduced by
It seems like $rawResult can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

104
        $json = json_decode(/** @scrutinizer ignore-type */ $rawResult, true);
Loading history...
105
106
        // check if a specific array of values should be extracted
107
        // e.g. {BTC,tmsp,price}
108
        preg_match_all("/(?<={).*(?=})/", $path, $matches);
109
        if (count($matches[0]) > 0) {
110
            // array extraction
111
112
            // check if absolute path is in front of the array
113
            // e.g. data/data{from,to,intensity/forecast}
114
            $firstArray = strpos($path, '{');
115
            if ($firstArray && $firstArray !== 0) {
116
                $singlePath = substr($path, 0, $firstArray);
117
                $json = $this->get_nested_array_value($json, $singlePath);
118
            }
119
120
            // separate the fields of the array {BTC,tmsp,price}
121
            $paths = explode(',', $matches[0][0]);
122
            // fill up with dummies in case of missing columns
123
            while (count($paths) < 3) {
124
                array_unshift($paths, 'empty');
125
            }
126
            foreach ($json as $rowArray) {
127
                // get the array fields from the json
128
                // if no match is not found, the field name will be used as a constant string
129
                $dim1 = $this->get_nested_array_value($rowArray, $paths[0]) ?: $paths[0];
130
                $dim2 = $this->get_nested_array_value($rowArray, $paths[1]) ?: $paths[1];
131
                $val = $this->get_nested_array_value($rowArray, $paths[2]) ?: $paths[2];
132
                $data[] = [$dim1, $dim2, $val];
133
            }
134
        } else {
135
            // single value extraction
136
            // e.g. data/currentHashrate,data/averageHashrate
137
            $paths = explode(',', $path);
138
            foreach ($paths as $singlePath) {
139
                // e.g. data/currentHashrate
140
                $array = $this->get_nested_array_value($json, $singlePath);
141
142
                if (is_array($array)) {
143
                    // if the tartet is an array itself
144
                    foreach ($array as $key => $value) {
145
                        $pathArray = explode('/', $singlePath);
146
                        $group = end($pathArray);
147
                        $data[] = [$group, $key, $value];
148
                    }
149
                } else {
150
                    $pathArray = explode('/', $singlePath);
151
                    $key = end($pathArray);
152
                    $data[] = ['', $key, $array];
153
                }
154
            }
155
        }
156
157
        $header = array();
158
        $header[0] = '';
159
        $header[1] = 'Key';
160
        $header[2] = 'Value';
161
162
        return [
163
            'header' => $header,
164
            'dimensions' => array_slice($header, 0, count($header) - 1),
165
            'data' => $data,
166
            'rawdata' => $rawResult,
167
            'customHeaders' => $headers,
168
            'URL' => $url,
169
            'error' => ($http_code >= 200 && $http_code < 300) ? 0 : 'HTTP response code: ' . $http_code,
170
        ];
171
    }
172
173
    /**
174
     * get array object from string
175
     *
176
     * @NoAdminRequired
177
     * @param $array
178
     * @param $path
179
     * @return array|string|null
180
     */
181
    private function get_nested_array_value(&$array, $path)
182
    {
183
        $pathParts = explode('/', $path);
184
        $current = &$array;
185
        foreach ($pathParts as $key) {
186
            $current = &$current[$key];
187
        }
188
        return $current;
189
    }
190
}