Use new appstore API
This change introduces the new appstore API in Nextcloud. Signed-off-by: Lukas Reschke <lukas@statuscode.ch>pull/1940/head
parent
357a823457
commit
32cf661215
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\App\AppStore\Fetcher;
|
||||
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
|
||||
class AppFetcher extends Fetcher {
|
||||
/**
|
||||
* @param IAppData $appData
|
||||
* @param IClientService $clientService
|
||||
* @param ITimeFactory $timeFactory
|
||||
* @param IConfig $config;
|
||||
*/
|
||||
public function __construct(IAppData $appData,
|
||||
IClientService $clientService,
|
||||
ITimeFactory $timeFactory,
|
||||
IConfig $config) {
|
||||
parent::__construct(
|
||||
$appData,
|
||||
$clientService,
|
||||
$timeFactory
|
||||
);
|
||||
|
||||
$this->fileName = 'apps.json';
|
||||
$this->endpointUrl = sprintf(
|
||||
'https://apps.nextcloud.com/api/v1/platform/%s/apps.json',
|
||||
substr(implode(\OC_Util::getVersion(), '.'), 0, 5)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\App\AppStore\Fetcher;
|
||||
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Http\Client\IClientService;
|
||||
|
||||
class CategoryFetcher extends Fetcher {
|
||||
/**
|
||||
* @param IAppData $appData
|
||||
* @param IClientService $clientService
|
||||
* @param ITimeFactory $timeFactory
|
||||
*/
|
||||
public function __construct(IAppData $appData,
|
||||
IClientService $clientService,
|
||||
ITimeFactory $timeFactory) {
|
||||
parent::__construct(
|
||||
$appData,
|
||||
$clientService,
|
||||
$timeFactory
|
||||
);
|
||||
$this->fileName = 'categories.json';
|
||||
$this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\App\AppStore\Fetcher;
|
||||
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Http\Client\IClientService;
|
||||
|
||||
abstract class Fetcher {
|
||||
const INVALIDATE_AFTER_SECONDS = 300;
|
||||
|
||||
/** @var IAppData */
|
||||
private $appData;
|
||||
/** @var IClientService */
|
||||
private $clientService;
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
/** @var string */
|
||||
protected $fileName;
|
||||
/** @var string */
|
||||
protected $endpointUrl;
|
||||
|
||||
/**
|
||||
* @param IAppData $appData
|
||||
* @param IClientService $clientService
|
||||
* @param ITimeFactory $timeFactory
|
||||
*/
|
||||
public function __construct(IAppData $appData,
|
||||
IClientService $clientService,
|
||||
ITimeFactory $timeFactory) {
|
||||
$this->appData = $appData;
|
||||
$this->clientService = $clientService;
|
||||
$this->timeFactory = $timeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array with the categories on the appstore server
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get() {
|
||||
$rootFolder = $this->appData->getFolder('/');
|
||||
|
||||
try {
|
||||
// File does already exists
|
||||
$file = $rootFolder->getFile($this->fileName);
|
||||
$jsonBlob = json_decode($file->getContent(), true);
|
||||
if(is_array($jsonBlob)) {
|
||||
// If the timestamp is older than 300 seconds request the files new
|
||||
if((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) {
|
||||
return $jsonBlob['data'];
|
||||
}
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
// File does not already exists
|
||||
$file = $rootFolder->newFile($this->fileName);
|
||||
}
|
||||
|
||||
// Refresh the file content
|
||||
$client = $this->clientService->newClient();
|
||||
try {
|
||||
$response = $client->get($this->endpointUrl);
|
||||
$responseJson = [];
|
||||
$responseJson['data'] = json_decode($response->getBody(), true);
|
||||
$responseJson['timestamp'] = $this->timeFactory->getTime();
|
||||
$file->putContent(json_encode($responseJson));
|
||||
return json_decode($file->getContent(), true)['data'];
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\App\AppStore\Version;
|
||||
|
||||
class Version {
|
||||
/** @var string */
|
||||
private $minVersion;
|
||||
/** @var string */
|
||||
private $maxVersion;
|
||||
|
||||
/**
|
||||
* @param string $minVersion
|
||||
* @param string $maxVersion
|
||||
*/
|
||||
public function __construct($minVersion, $maxVersion) {
|
||||
$this->minVersion = $minVersion;
|
||||
$this->maxVersion = $maxVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMinimumVersion() {
|
||||
return $this->minVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMaximumVersion() {
|
||||
return $this->maxVersion;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\App\AppStore\Version;
|
||||
|
||||
/**
|
||||
* Class VersionParser parses the versions as sent by the Nextcloud app store
|
||||
*
|
||||
* @package OC\App\AppStore
|
||||
*/
|
||||
class VersionParser {
|
||||
/**
|
||||
* Returns the version for a version string
|
||||
*
|
||||
* @param string $versionSpec
|
||||
* @return Version
|
||||
* @throws \Exception If the version cannot be parsed
|
||||
*/
|
||||
public function getVersion($versionSpec) {
|
||||
// * indicates that the version is compatible with all versions
|
||||
if($versionSpec === '*') {
|
||||
return new Version('', '');
|
||||
}
|
||||
|
||||
// Count the amount of =, if it is one then it's either maximum or minimum
|
||||
// version. If it is two then it is maximum and minimum.
|
||||
if (preg_match_all('/(?:>|<)(?:=|)[0-9.]+/', $versionSpec, $matches)) {
|
||||
switch(count($matches[0])) {
|
||||
case 1:
|
||||
if(substr($matches[0][0], 0, 1) === '>') {
|
||||
return new Version(substr($matches[0][0], 2), '');
|
||||
} else {
|
||||
return new Version('', substr($matches[0][0], 2));
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return new Version(substr($matches[0][0], 2), substr($matches[0][1], 2));
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Version cannot be parsed');
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('Version cannot be parsed');
|
||||
}
|
||||
}
|
||||
@ -1,351 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Bart Visscher <bartv@thisnet.nl>
|
||||
* @author Brice Maron <brice@bmaron.net>
|
||||
* @author Felix Moeller <mail@felixmoeller.de>
|
||||
* @author Frank Karlitschek <frank@karlitschek.de>
|
||||
* @author Jarrett <JetUni@users.noreply.github.com>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
|
||||
* @author Kamil Domanski <kdomanski@kdemail.net>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Robin McCorkell <robin@mccorkell.me.uk>
|
||||
* @author Sam Tuke <mail@samtuke.com>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC;
|
||||
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* Class OCSClient is a class for communication with the ownCloud appstore
|
||||
*
|
||||
* @package OC
|
||||
*/
|
||||
class OCSClient {
|
||||
/** @var IClientService */
|
||||
private $httpClientService;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* @param IClientService $httpClientService
|
||||
* @param IConfig $config
|
||||
* @param ILogger $logger
|
||||
*/
|
||||
public function __construct(IClientService $httpClientService,
|
||||
IConfig $config,
|
||||
ILogger $logger) {
|
||||
$this->httpClientService = $httpClientService;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the AppStore is enabled (i.e. because the AppStore is disabled for EE)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAppStoreEnabled() {
|
||||
return $this->config->getSystemValue('appstoreenabled', true) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url of the OCS AppStore server.
|
||||
*
|
||||
* @return string of the AppStore server
|
||||
*/
|
||||
private function getAppStoreUrl() {
|
||||
return $this->config->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @param string $action
|
||||
* @return null|\SimpleXMLElement
|
||||
*/
|
||||
private function loadData($body, $action) {
|
||||
$loadEntities = libxml_disable_entity_loader(true);
|
||||
$data = @simplexml_load_string($body);
|
||||
libxml_disable_entity_loader($loadEntities);
|
||||
|
||||
if($data === false) {
|
||||
libxml_clear_errors();
|
||||
$this->logger->error(
|
||||
sprintf('Could not get %s, content was no valid XML', $action),
|
||||
[
|
||||
'app' => 'core',
|
||||
]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the categories from the OCS server
|
||||
*
|
||||
* @param array $targetVersion The target ownCloud version
|
||||
* @return array|null an array of category ids or null
|
||||
* @note returns NULL if config value appstoreenabled is set to false
|
||||
* This function returns a list of all the application categories on the OCS server
|
||||
*/
|
||||
public function getCategories(array $targetVersion) {
|
||||
if (!$this->isAppStoreEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$client = $this->httpClientService->newClient();
|
||||
try {
|
||||
$response = $client->get(
|
||||
$this->getAppStoreUrl() . '/content/categories',
|
||||
[
|
||||
'timeout' => 20,
|
||||
'query' => [
|
||||
'version' => implode('x', $targetVersion),
|
||||
],
|
||||
]
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
$this->logger->error(
|
||||
sprintf('Could not get categories: %s', $e->getMessage()),
|
||||
[
|
||||
'app' => 'core',
|
||||
]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->loadData($response->getBody(), 'categories');
|
||||
if($data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tmp = $data->data;
|
||||
$cats = [];
|
||||
|
||||
foreach ($tmp->category as $value) {
|
||||
$id = (int)$value->id;
|
||||
$name = (string)$value->name;
|
||||
$cats[$id] = $name;
|
||||
}
|
||||
|
||||
return $cats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the applications from the OCS server
|
||||
* @param array $categories
|
||||
* @param int $page
|
||||
* @param string $filter
|
||||
* @param array $targetVersion The target ownCloud version
|
||||
* @return array An array of application data
|
||||
*/
|
||||
public function getApplications(array $categories, $page, $filter, array $targetVersion) {
|
||||
if (!$this->isAppStoreEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$client = $this->httpClientService->newClient();
|
||||
try {
|
||||
$response = $client->get(
|
||||
$this->getAppStoreUrl() . '/content/data',
|
||||
[
|
||||
'timeout' => 20,
|
||||
'query' => [
|
||||
'version' => implode('x', $targetVersion),
|
||||
'filter' => $filter,
|
||||
'categories' => implode('x', $categories),
|
||||
'sortmode' => 'new',
|
||||
'page' => $page,
|
||||
'pagesize' => 100,
|
||||
'approved' => $filter
|
||||
],
|
||||
]
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
$this->logger->error(
|
||||
sprintf('Could not get applications: %s', $e->getMessage()),
|
||||
[
|
||||
'app' => 'core',
|
||||
]
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = $this->loadData($response->getBody(), 'applications');
|
||||
if($data === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tmp = $data->data->content;
|
||||
$tmpCount = count($tmp);
|
||||
|
||||
$apps = [];
|
||||
for ($i = 0; $i < $tmpCount; $i++) {
|
||||
$app = [];
|
||||
$app['id'] = (string)$tmp[$i]->id;
|
||||
$app['name'] = (string)$tmp[$i]->name;
|
||||
$app['label'] = (string)$tmp[$i]->label;
|
||||
$app['version'] = (string)$tmp[$i]->version;
|
||||
$app['type'] = (string)$tmp[$i]->typeid;
|
||||
$app['typename'] = (string)$tmp[$i]->typename;
|
||||
$app['personid'] = (string)$tmp[$i]->personid;
|
||||
$app['profilepage'] = (string)$tmp[$i]->profilepage;
|
||||
$app['license'] = (string)$tmp[$i]->license;
|
||||
$app['detailpage'] = (string)$tmp[$i]->detailpage;
|
||||
$app['preview'] = (string)$tmp[$i]->smallpreviewpic1;
|
||||
$app['preview-full'] = (string)$tmp[$i]->previewpic1;
|
||||
$app['changed'] = strtotime($tmp[$i]->changed);
|
||||
$app['description'] = (string)$tmp[$i]->description;
|
||||
$app['score'] = (string)$tmp[$i]->score;
|
||||
$app['downloads'] = (int)$tmp[$i]->downloads;
|
||||
$app['level'] = (int)$tmp[$i]->approved;
|
||||
|
||||
$apps[] = $app;
|
||||
}
|
||||
|
||||
return $apps;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an the applications from the OCS server
|
||||
*
|
||||
* @param string $id
|
||||
* @param array $targetVersion The target ownCloud version
|
||||
* @return array|null an array of application data or null
|
||||
*
|
||||
* This function returns an applications from the OCS server
|
||||
*/
|
||||
public function getApplication($id, array $targetVersion) {
|
||||
if (!$this->isAppStoreEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$client = $this->httpClientService->newClient();
|
||||
try {
|
||||
$response = $client->get(
|
||||
$this->getAppStoreUrl() . '/content/data/' . urlencode($id),
|
||||
[
|
||||
'timeout' => 20,
|
||||
'query' => [
|
||||
'version' => implode('x', $targetVersion),
|
||||
],
|
||||
]
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
$this->logger->error(
|
||||
sprintf('Could not get application: %s', $e->getMessage()),
|
||||
[
|
||||
'app' => 'core',
|
||||
]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->loadData($response->getBody(), 'application');
|
||||
if($data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tmp = $data->data->content;
|
||||
if (is_null($tmp)) {
|
||||
\OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::DEBUG);
|
||||
return null;
|
||||
}
|
||||
|
||||
$app = [];
|
||||
$app['id'] = (int)$id;
|
||||
$app['name'] = (string)$tmp->name;
|
||||
$app['version'] = (string)$tmp->version;
|
||||
$app['type'] = (string)$tmp->typeid;
|
||||
$app['label'] = (string)$tmp->label;
|
||||
$app['typename'] = (string)$tmp->typename;
|
||||
$app['personid'] = (string)$tmp->personid;
|
||||
$app['profilepage'] = (string)$tmp->profilepage;
|
||||
$app['detailpage'] = (string)$tmp->detailpage;
|
||||
$app['preview1'] = (string)$tmp->smallpreviewpic1;
|
||||
$app['preview2'] = (string)$tmp->smallpreviewpic2;
|
||||
$app['preview3'] = (string)$tmp->smallpreviewpic3;
|
||||
$app['changed'] = strtotime($tmp->changed);
|
||||
$app['description'] = (string)$tmp->description;
|
||||
$app['detailpage'] = (string)$tmp->detailpage;
|
||||
$app['score'] = (int)$tmp->score;
|
||||
$app['level'] = (int)$tmp->approved;
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download url for an application from the OCS server
|
||||
* @param string $id
|
||||
* @param array $targetVersion The target ownCloud version
|
||||
* @return array|null an array of application data or null
|
||||
*/
|
||||
public function getApplicationDownload($id, array $targetVersion) {
|
||||
if (!$this->isAppStoreEnabled()) {
|
||||
return null;
|
||||
}
|
||||
$url = $this->getAppStoreUrl() . '/content/download/' . urlencode($id) . '/1';
|
||||
$client = $this->httpClientService->newClient();
|
||||
try {
|
||||
$response = $client->get(
|
||||
$url,
|
||||
[
|
||||
'timeout' => 20,
|
||||
'query' => [
|
||||
'version' => implode('x', $targetVersion),
|
||||
],
|
||||
]
|
||||
);
|
||||
} catch(\Exception $e) {
|
||||
$this->logger->error(
|
||||
sprintf('Could not get application download URL: %s', $e->getMessage()),
|
||||
[
|
||||
'app' => 'core',
|
||||
]
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->loadData($response->getBody(), 'application download URL');
|
||||
if($data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tmp = $data->data->content;
|
||||
$app = [];
|
||||
if (isset($tmp->downloadlink)) {
|
||||
$app['downloadlink'] = (string)$tmp->downloadlink;
|
||||
} else {
|
||||
$app['downloadlink'] = '';
|
||||
}
|
||||
return $app;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\App\AppStore\Fetcher;
|
||||
|
||||
use OC\App\AppStore\Fetcher\AppFetcher;
|
||||
|
||||
class AppFetcherTest extends FetcherBase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->fileName = 'apps.json';
|
||||
$this->endpoint = 'https://apps.nextcloud.com/api/v1/platform/9.2.0/apps.json';
|
||||
|
||||
$this->fetcher = new AppFetcher(
|
||||
$this->appData,
|
||||
$this->clientService,
|
||||
$this->timeFactory,
|
||||
$this->config
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\App\AppStore\Fetcher;
|
||||
|
||||
use OC\App\AppStore\Fetcher\CategoryFetcher;
|
||||
|
||||
class CategoryFetcherTest extends FetcherBase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->fileName = 'categories.json';
|
||||
$this->endpoint = 'https://apps.nextcloud.com/api/v1/categories.json';
|
||||
|
||||
$this->fetcher = new CategoryFetcher(
|
||||
$this->appData,
|
||||
$this->clientService,
|
||||
$this->timeFactory
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\App\AppStore\Fetcher;
|
||||
|
||||
use OC\App\AppStore\Fetcher\AppFetcher;
|
||||
use OC\App\AppStore\Fetcher\Fetcher;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\Files\SimpleFS\ISimpleFolder;
|
||||
use OCP\Http\Client\IClient;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\Http\Client\IResponse;
|
||||
use OCP\IConfig;
|
||||
use Test\TestCase;
|
||||
|
||||
abstract class FetcherBase extends TestCase {
|
||||
/** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $appData;
|
||||
/** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $clientService;
|
||||
/** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $timeFactory;
|
||||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $config;
|
||||
/** @var Fetcher */
|
||||
protected $fetcher;
|
||||
/** @var string */
|
||||
protected $fileName;
|
||||
/** @var string */
|
||||
protected $endpoint;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->appData = $this->createMock(IAppData::class);
|
||||
$this->clientService = $this->createMock(IClientService::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
}
|
||||
|
||||
public function testGetWithAlreadyExistingFileAndUpToDateTimestamp() {
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$file = $this->createMock(ISimpleFile::class);
|
||||
$this->appData
|
||||
->expects($this->once())
|
||||
->method('getFolder')
|
||||
->with('/')
|
||||
->willReturn($folder);
|
||||
$folder
|
||||
->expects($this->once())
|
||||
->method('getFile')
|
||||
->with($this->fileName)
|
||||
->willReturn($file);
|
||||
$file
|
||||
->expects($this->once())
|
||||
->method('getContent')
|
||||
->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}]}');
|
||||
$this->timeFactory
|
||||
->expects($this->once())
|
||||
->method('getTime')
|
||||
->willReturn(1499);
|
||||
|
||||
$expected = [
|
||||
[
|
||||
'id' => 'MyApp',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->fetcher->get());
|
||||
}
|
||||
|
||||
public function testGetWithNotExistingFileAndUpToDateTimestamp() {
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$file = $this->createMock(ISimpleFile::class);
|
||||
$this->appData
|
||||
->expects($this->once())
|
||||
->method('getFolder')
|
||||
->with('/')
|
||||
->willReturn($folder);
|
||||
$folder
|
||||
->expects($this->at(0))
|
||||
->method('getFile')
|
||||
->with($this->fileName)
|
||||
->willThrowException(new NotFoundException());
|
||||
$folder
|
||||
->expects($this->at(1))
|
||||
->method('newFile')
|
||||
->with($this->fileName)
|
||||
->willReturn($file);
|
||||
$client = $this->createMock(IClient::class);
|
||||
$this->clientService
|
||||
->expects($this->once())
|
||||
->method('newClient')
|
||||
->willReturn($client);
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$client
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->endpoint)
|
||||
->willReturn($response);
|
||||
$response
|
||||
->expects($this->once())
|
||||
->method('getBody')
|
||||
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
|
||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502}';
|
||||
$file
|
||||
->expects($this->at(0))
|
||||
->method('putContent')
|
||||
->with($fileData);
|
||||
$file
|
||||
->expects($this->at(1))
|
||||
->method('getContent')
|
||||
->willReturn($fileData);
|
||||
$this->timeFactory
|
||||
->expects($this->at(0))
|
||||
->method('getTime')
|
||||
->willReturn(1502);
|
||||
|
||||
$expected = [
|
||||
[
|
||||
'id' => 'MyNewApp',
|
||||
'foo' => 'foo',
|
||||
],
|
||||
[
|
||||
'id' => 'bar',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->fetcher->get());
|
||||
}
|
||||
|
||||
public function testGetWithAlreadyExistingFileAndOutdatedTimestamp() {
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$file = $this->createMock(ISimpleFile::class);
|
||||
$this->appData
|
||||
->expects($this->once())
|
||||
->method('getFolder')
|
||||
->with('/')
|
||||
->willReturn($folder);
|
||||
$folder
|
||||
->expects($this->once())
|
||||
->method('getFile')
|
||||
->with($this->fileName)
|
||||
->willReturn($file);
|
||||
$file
|
||||
->expects($this->at(0))
|
||||
->method('getContent')
|
||||
->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}');
|
||||
$this->timeFactory
|
||||
->expects($this->at(0))
|
||||
->method('getTime')
|
||||
->willReturn(1501);
|
||||
$client = $this->createMock(IClient::class);
|
||||
$this->clientService
|
||||
->expects($this->once())
|
||||
->method('newClient')
|
||||
->willReturn($client);
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$client
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->endpoint)
|
||||
->willReturn($response);
|
||||
$response
|
||||
->expects($this->once())
|
||||
->method('getBody')
|
||||
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
|
||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502}';
|
||||
$file
|
||||
->expects($this->at(1))
|
||||
->method('putContent')
|
||||
->with($fileData);
|
||||
$file
|
||||
->expects($this->at(2))
|
||||
->method('getContent')
|
||||
->willReturn($fileData);
|
||||
$this->timeFactory
|
||||
->expects($this->at(1))
|
||||
->method('getTime')
|
||||
->willReturn(1502);
|
||||
|
||||
$expected = [
|
||||
[
|
||||
'id' => 'MyNewApp',
|
||||
'foo' => 'foo',
|
||||
],
|
||||
[
|
||||
'id' => 'bar',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->fetcher->get());
|
||||
}
|
||||
|
||||
public function testGetWithExceptionInClient() {
|
||||
$folder = $this->createMock(ISimpleFolder::class);
|
||||
$file = $this->createMock(ISimpleFile::class);
|
||||
$this->appData
|
||||
->expects($this->once())
|
||||
->method('getFolder')
|
||||
->with('/')
|
||||
->willReturn($folder);
|
||||
$folder
|
||||
->expects($this->once())
|
||||
->method('getFile')
|
||||
->with($this->fileName)
|
||||
->willReturn($file);
|
||||
$file
|
||||
->expects($this->at(0))
|
||||
->method('getContent')
|
||||
->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}');
|
||||
$this->timeFactory
|
||||
->expects($this->at(0))
|
||||
->method('getTime')
|
||||
->willReturn(1501);
|
||||
$client = $this->createMock(IClient::class);
|
||||
$this->clientService
|
||||
->expects($this->once())
|
||||
->method('newClient')
|
||||
->willReturn($client);
|
||||
$client
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->endpoint)
|
||||
->willThrowException(new \Exception());
|
||||
|
||||
$this->assertSame([], $this->fetcher->get());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\App\AppStore\Version;
|
||||
|
||||
use OC\App\AppStore\Version\Version;
|
||||
use OC\App\AppStore\Version\VersionParser;
|
||||
use Test\TestCase;
|
||||
|
||||
class VersionParserTest extends TestCase {
|
||||
/** @var VersionParser */
|
||||
private $versionParser;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->versionParser = new VersionParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function versionProvider() {
|
||||
return [
|
||||
[
|
||||
'*',
|
||||
new Version('', ''),
|
||||
],
|
||||
[
|
||||
'<=8.1.2',
|
||||
new Version('', '8.1.2'),
|
||||
],
|
||||
[
|
||||
'<=9',
|
||||
new Version('', '9'),
|
||||
],
|
||||
[
|
||||
'>=9.3.2',
|
||||
new Version('9.3.2', ''),
|
||||
],
|
||||
[
|
||||
'>=8.1.2 <=9.3.2',
|
||||
new Version('8.1.2', '9.3.2'),
|
||||
],
|
||||
[
|
||||
'>=8.2 <=9.1',
|
||||
new Version('8.2', '9.1'),
|
||||
],
|
||||
[
|
||||
'>=9 <=11',
|
||||
new Version('9', '11'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider versionProvider
|
||||
*
|
||||
* @param string $input
|
||||
* @param Version $expected
|
||||
*/
|
||||
public function testGetVersion($input,
|
||||
Version $expected) {
|
||||
$this->assertEquals($expected, $this->versionParser->getVersion($input));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\App\AppStore\Version;
|
||||
|
||||
use OC\App\AppStore\Version\Version;
|
||||
use Test\TestCase;
|
||||
|
||||
class VersionTest extends TestCase {
|
||||
public function testGetMinimumVersion() {
|
||||
$version = new Version('9', '10');
|
||||
$this->assertSame('9', $version->getMinimumVersion());
|
||||
}
|
||||
|
||||
public function testGetMaximumVersion() {
|
||||
$version = new Version('9', '10');
|
||||
$this->assertSame('10', $version->getMaximumVersion());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue