Passed
Push — master ( d12dd3...408ed7 )
by Morris
19:22 queued 11s
created

Fetcher::get()   C

Complexity

Conditions 13
Paths 132

Size

Total Lines 56
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 31
nc 132
nop 1
dl 0
loc 56
rs 6.35
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
 * @copyright Copyright (c) 2016 Lukas Reschke <[email protected]>
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Daniel Kesselberg <[email protected]>
7
 * @author Georg Ehrke <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Morris Jobke <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Steffen Lindner <[email protected]>
13
 *
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
namespace OC\App\AppStore\Fetcher;
32
33
use GuzzleHttp\Exception\ConnectException;
34
use OC\Files\AppData\Factory;
35
use OCP\AppFramework\Http;
36
use OCP\AppFramework\Utility\ITimeFactory;
37
use OCP\Files\IAppData;
38
use OCP\Files\NotFoundException;
39
use OCP\Http\Client\IClientService;
40
use OCP\IConfig;
41
use OCP\ILogger;
42
43
abstract class Fetcher {
44
	public const INVALIDATE_AFTER_SECONDS = 3600;
45
46
	/** @var IAppData */
47
	protected $appData;
48
	/** @var IClientService */
49
	protected $clientService;
50
	/** @var ITimeFactory */
51
	protected $timeFactory;
52
	/** @var IConfig */
53
	protected $config;
54
	/** @var Ilogger */
55
	protected $logger;
56
	/** @var string */
57
	protected $fileName;
58
	/** @var string */
59
	protected $endpointName;
60
	/** @var string */
61
	protected $version;
62
	/** @var string */
63
	protected $channel;
64
65
	/**
66
	 * @param Factory $appDataFactory
67
	 * @param IClientService $clientService
68
	 * @param ITimeFactory $timeFactory
69
	 * @param IConfig $config
70
	 * @param ILogger $logger
71
	 */
72
	public function __construct(Factory $appDataFactory,
73
								IClientService $clientService,
74
								ITimeFactory $timeFactory,
75
								IConfig $config,
76
								ILogger $logger) {
77
		$this->appData = $appDataFactory->get('appstore');
78
		$this->clientService = $clientService;
79
		$this->timeFactory = $timeFactory;
80
		$this->config = $config;
81
		$this->logger = $logger;
82
	}
83
84
	/**
85
	 * Fetches the response from the server
86
	 *
87
	 * @param string $ETag
88
	 * @param string $content
89
	 *
90
	 * @return array
91
	 */
92
	protected function fetch($ETag, $content) {
93
		$appstoreenabled = $this->config->getSystemValue('appstoreenabled', true);
94
95
		if (!$appstoreenabled) {
96
			return [];
97
		}
98
99
		$options = [
100
			'timeout' => 60,
101
		];
102
103
		if ($ETag !== '') {
104
			$options['headers'] = [
105
				'If-None-Match' => $ETag,
106
			];
107
		}
108
109
		$client = $this->clientService->newClient();
110
		$response = $client->get($this->getEndpoint(), $options);
111
112
		$responseJson = [];
113
		if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
114
			$responseJson['data'] = json_decode($content, true);
115
		} else {
116
			$responseJson['data'] = json_decode($response->getBody(), true);
117
			$ETag = $response->getHeader('ETag');
118
		}
119
120
		$responseJson['timestamp'] = $this->timeFactory->getTime();
121
		$responseJson['ncversion'] = $this->getVersion();
122
		if ($ETag !== '') {
123
			$responseJson['ETag'] = $ETag;
124
		}
125
126
		return $responseJson;
127
	}
128
129
	/**
130
	 * Returns the array with the categories on the appstore server
131
	 *
132
	 * @param bool [$allowUnstable] Allow unstable releases
0 ignored issues
show
Documentation Bug introduced by
The doc comment [$allowUnstable] at position 0 could not be parsed: Unknown type name '[' at position 0 in [$allowUnstable].
Loading history...
133
	 * @return array
134
	 */
135
	public function get($allowUnstable = false) {
136
		$appstoreenabled = $this->config->getSystemValue('appstoreenabled', true);
137
		$internetavailable = $this->config->getSystemValue('has_internet_connection', true);
138
139
		if (!$appstoreenabled || !$internetavailable) {
140
			return [];
141
		}
142
143
		$rootFolder = $this->appData->getFolder('/');
144
145
		$ETag = '';
146
		$content = '';
147
148
		try {
149
			// File does already exists
150
			$file = $rootFolder->getFile($this->fileName);
151
			$jsonBlob = json_decode($file->getContent(), true);
152
153
			// Always get latests apps info if $allowUnstable
154
			if (!$allowUnstable && is_array($jsonBlob)) {
155
156
				// No caching when the version has been updated
157
				if (isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->getVersion()) {
158
159
					// If the timestamp is older than 3600 seconds request the files new
160
					if ((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) {
161
						return $jsonBlob['data'];
162
					}
163
164
					if (isset($jsonBlob['ETag'])) {
165
						$ETag = $jsonBlob['ETag'];
166
						$content = json_encode($jsonBlob['data']);
167
					}
168
				}
169
			}
170
		} catch (NotFoundException $e) {
171
			// File does not already exists
172
			$file = $rootFolder->newFile($this->fileName);
173
		}
174
175
		// Refresh the file content
176
		try {
177
			$responseJson = $this->fetch($ETag, $content, $allowUnstable);
0 ignored issues
show
Unused Code introduced by
The call to OC\App\AppStore\Fetcher\Fetcher::fetch() has too many arguments starting with $allowUnstable. ( Ignorable by Annotation )

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

177
			/** @scrutinizer ignore-call */ 
178
   $responseJson = $this->fetch($ETag, $content, $allowUnstable);

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
178
			// Don't store the apps request file
179
			if ($allowUnstable) {
180
				return $responseJson['data'];
181
			}
182
183
			$file->putContent(json_encode($responseJson));
184
			return json_decode($file->getContent(), true)['data'];
185
		} catch (ConnectException $e) {
186
			$this->logger->warning('Could not connect to appstore: ' . $e->getMessage(), ['app' => 'appstoreFetcher']);
187
			return [];
188
		} catch (\Exception $e) {
189
			$this->logger->logException($e, ['app' => 'appstoreFetcher', 'level' => ILogger::WARN]);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::WARN 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

189
			$this->logger->logException($e, ['app' => 'appstoreFetcher', 'level' => /** @scrutinizer ignore-deprecated */ ILogger::WARN]);

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...
190
			return [];
191
		}
192
	}
193
194
	/**
195
	 * Get the currently Nextcloud version
196
	 * @return string
197
	 */
198
	protected function getVersion() {
199
		if ($this->version === null) {
200
			$this->version = $this->config->getSystemValue('version', '0.0.0');
201
		}
202
		return $this->version;
203
	}
204
205
	/**
206
	 * Set the current Nextcloud version
207
	 * @param string $version
208
	 */
209
	public function setVersion(string $version) {
210
		$this->version = $version;
211
	}
212
213
	/**
214
	 * Get the currently Nextcloud update channel
215
	 * @return string
216
	 */
217
	protected function getChannel() {
218
		if ($this->channel === null) {
219
			$this->channel = \OC_Util::getChannel();
220
		}
221
		return $this->channel;
222
	}
223
224
	/**
225
	 * Set the current Nextcloud update channel
226
	 * @param string $channel
227
	 */
228
	public function setChannel(string $channel) {
229
		$this->channel = $channel;
230
	}
231
232
	protected function getEndpoint(): string {
233
		return $this->config->getSystemValue('appstoreurl', 'https://apps.nextcloud.com/api/v1') . '/' . $this->endpointName;
234
	}
235
}
236