Clear appstore cache on version upgradetags/v12.0.0beta1
use OCP\IConfig; | use OCP\IConfig; | ||||
class AppFetcher extends Fetcher { | class AppFetcher extends Fetcher { | ||||
/** @var IConfig */ | |||||
private $config; | |||||
/** | /** | ||||
* @param IAppData $appData | * @param IAppData $appData | ||||
* @param IClientService $clientService | * @param IClientService $clientService | ||||
parent::__construct( | parent::__construct( | ||||
$appData, | $appData, | ||||
$clientService, | $clientService, | ||||
$timeFactory | |||||
$timeFactory, | |||||
$config | |||||
); | ); | ||||
$this->fileName = 'apps.json'; | $this->fileName = 'apps.json'; | ||||
$this->config = $config; | |||||
$versionArray = explode('.', $this->config->getSystemValue('version')); | $versionArray = explode('.', $this->config->getSystemValue('version')); | ||||
$this->endpointUrl = sprintf( | $this->endpointUrl = sprintf( | ||||
* @return array | * @return array | ||||
*/ | */ | ||||
protected function fetch() { | protected function fetch() { | ||||
$client = $this->clientService->newClient(); | |||||
$response = $client->get($this->endpointUrl); | |||||
$responseJson = []; | |||||
$responseJson['data'] = json_decode($response->getBody(), true); | |||||
$responseJson['timestamp'] = $this->timeFactory->getTime(); | |||||
$response = $responseJson; | |||||
/** @var mixed[] $response */ | |||||
$response = parent::fetch(); | |||||
$ncVersion = $this->config->getSystemValue('version'); | $ncVersion = $this->config->getSystemValue('version'); | ||||
$ncMajorVersion = explode('.', $ncVersion)[0]; | $ncMajorVersion = explode('.', $ncVersion)[0]; |
use OCP\AppFramework\Utility\ITimeFactory; | use OCP\AppFramework\Utility\ITimeFactory; | ||||
use OCP\Files\IAppData; | use OCP\Files\IAppData; | ||||
use OCP\Http\Client\IClientService; | use OCP\Http\Client\IClientService; | ||||
use OCP\IConfig; | |||||
class CategoryFetcher extends Fetcher { | class CategoryFetcher extends Fetcher { | ||||
/** | /** | ||||
* @param IAppData $appData | * @param IAppData $appData | ||||
* @param IClientService $clientService | * @param IClientService $clientService | ||||
* @param ITimeFactory $timeFactory | * @param ITimeFactory $timeFactory | ||||
* @param IConfig $config | |||||
*/ | */ | ||||
public function __construct(IAppData $appData, | public function __construct(IAppData $appData, | ||||
IClientService $clientService, | IClientService $clientService, | ||||
ITimeFactory $timeFactory) { | |||||
ITimeFactory $timeFactory, | |||||
IConfig $config) { | |||||
parent::__construct( | parent::__construct( | ||||
$appData, | $appData, | ||||
$clientService, | $clientService, | ||||
$timeFactory | |||||
$timeFactory, | |||||
$config | |||||
); | ); | ||||
$this->fileName = 'categories.json'; | $this->fileName = 'categories.json'; | ||||
$this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json'; | $this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json'; |
use OCP\Files\IAppData; | use OCP\Files\IAppData; | ||||
use OCP\Files\NotFoundException; | use OCP\Files\NotFoundException; | ||||
use OCP\Http\Client\IClientService; | use OCP\Http\Client\IClientService; | ||||
use OCP\IConfig; | |||||
abstract class Fetcher { | abstract class Fetcher { | ||||
const INVALIDATE_AFTER_SECONDS = 300; | const INVALIDATE_AFTER_SECONDS = 300; | ||||
protected $clientService; | protected $clientService; | ||||
/** @var ITimeFactory */ | /** @var ITimeFactory */ | ||||
protected $timeFactory; | protected $timeFactory; | ||||
/** @var IConfig */ | |||||
protected $config; | |||||
/** @var string */ | /** @var string */ | ||||
protected $fileName; | protected $fileName; | ||||
/** @var string */ | /** @var string */ | ||||
* @param IAppData $appData | * @param IAppData $appData | ||||
* @param IClientService $clientService | * @param IClientService $clientService | ||||
* @param ITimeFactory $timeFactory | * @param ITimeFactory $timeFactory | ||||
* @param IConfig $config | |||||
*/ | */ | ||||
public function __construct(IAppData $appData, | public function __construct(IAppData $appData, | ||||
IClientService $clientService, | IClientService $clientService, | ||||
ITimeFactory $timeFactory) { | |||||
ITimeFactory $timeFactory, | |||||
IConfig $config) { | |||||
$this->appData = $appData; | $this->appData = $appData; | ||||
$this->clientService = $clientService; | $this->clientService = $clientService; | ||||
$this->timeFactory = $timeFactory; | $this->timeFactory = $timeFactory; | ||||
$this->config = $config; | |||||
} | } | ||||
/** | /** | ||||
$responseJson = []; | $responseJson = []; | ||||
$responseJson['data'] = json_decode($response->getBody(), true); | $responseJson['data'] = json_decode($response->getBody(), true); | ||||
$responseJson['timestamp'] = $this->timeFactory->getTime(); | $responseJson['timestamp'] = $this->timeFactory->getTime(); | ||||
$responseJson['ncversion'] = $this->config->getSystemValue('version'); | |||||
return $responseJson; | return $responseJson; | ||||
} | } | ||||
$file = $rootFolder->getFile($this->fileName); | $file = $rootFolder->getFile($this->fileName); | ||||
$jsonBlob = json_decode($file->getContent(), true); | $jsonBlob = json_decode($file->getContent(), true); | ||||
if(is_array($jsonBlob)) { | 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)) { | |||||
/* | |||||
* If the timestamp is older than 300 seconds request the files new | |||||
* If the version changed (update!) also refresh | |||||
*/ | |||||
if((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS) && | |||||
isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->config->getSystemValue('version', '0.0.0')) { | |||||
return $jsonBlob['data']; | return $jsonBlob['data']; | ||||
} | } | ||||
} | } |
return new CategoryFetcher( | return new CategoryFetcher( | ||||
$this->getAppDataDir('appstore'), | $this->getAppDataDir('appstore'), | ||||
$this->getHTTPClientService(), | $this->getHTTPClientService(), | ||||
$this->query(TimeFactory::class) | |||||
$this->query(TimeFactory::class), | |||||
$this->getConfig() | |||||
); | ); | ||||
}); | }); | ||||
$this->registerService('UserCache', function ($c) { | $this->registerService('UserCache', function ($c) { |
return new CategoryFetcher( | return new CategoryFetcher( | ||||
$server->getAppDataDir('appstore'), | $server->getAppDataDir('appstore'), | ||||
$server->getHTTPClientService(), | $server->getHTTPClientService(), | ||||
$server->query(TimeFactory::class) | |||||
$server->query(TimeFactory::class), | |||||
$server->getConfig() | |||||
); | ); | ||||
}); | }); | ||||
} | } |
), | ), | ||||
), | ), | ||||
'timestamp' => 1234, | 'timestamp' => 1234, | ||||
'ncversion' => '11.0.0.2', | |||||
); | ); | ||||
$dataToPut = $expected; | $dataToPut = $expected; |
$this->fetcher = new CategoryFetcher( | $this->fetcher = new CategoryFetcher( | ||||
$this->appData, | $this->appData, | ||||
$this->clientService, | $this->clientService, | ||||
$this->timeFactory | |||||
$this->timeFactory, | |||||
$this->config | |||||
); | ); | ||||
} | } | ||||
} | } |
$this->clientService = $this->createMock(IClientService::class); | $this->clientService = $this->createMock(IClientService::class); | ||||
$this->timeFactory = $this->createMock(ITimeFactory::class); | $this->timeFactory = $this->createMock(ITimeFactory::class); | ||||
$this->config = $this->createMock(IConfig::class); | $this->config = $this->createMock(IConfig::class); | ||||
$this->config | |||||
->method('getSystemValue') | |||||
->with( | |||||
$this->equalTo('version'), | |||||
$this->anything() | |||||
)->willReturn('11.0.0.2'); | |||||
} | } | ||||
public function testGetWithAlreadyExistingFileAndUpToDateTimestamp() { | |||||
public function testGetWithAlreadyExistingFileAndUpToDateTimestampAndVersion() { | |||||
$folder = $this->createMock(ISimpleFolder::class); | $folder = $this->createMock(ISimpleFolder::class); | ||||
$file = $this->createMock(ISimpleFile::class); | $file = $this->createMock(ISimpleFile::class); | ||||
$this->appData | $this->appData | ||||
$file | $file | ||||
->expects($this->once()) | ->expects($this->once()) | ||||
->method('getContent') | ->method('getContent') | ||||
->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}]}'); | |||||
->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}],"ncversion":"11.0.0.2"}'); | |||||
$this->timeFactory | $this->timeFactory | ||||
->expects($this->once()) | ->expects($this->once()) | ||||
->method('getTime') | ->method('getTime') | ||||
$this->assertSame($expected, $this->fetcher->get()); | $this->assertSame($expected, $this->fetcher->get()); | ||||
} | } | ||||
public function testGetWithNotExistingFileAndUpToDateTimestamp() { | |||||
public function testGetWithNotExistingFileAndUpToDateTimestampAndVersion() { | |||||
$folder = $this->createMock(ISimpleFolder::class); | $folder = $this->createMock(ISimpleFolder::class); | ||||
$file = $this->createMock(ISimpleFile::class); | $file = $this->createMock(ISimpleFile::class); | ||||
$this->appData | $this->appData | ||||
->expects($this->once()) | ->expects($this->once()) | ||||
->method('getBody') | ->method('getBody') | ||||
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); | ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); | ||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502}'; | |||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}'; | |||||
$file | $file | ||||
->expects($this->at(0)) | ->expects($this->at(0)) | ||||
->method('putContent') | ->method('putContent') | ||||
$file | $file | ||||
->expects($this->at(0)) | ->expects($this->at(0)) | ||||
->method('getContent') | ->method('getContent') | ||||
->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}'); | |||||
->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.2"}'); | |||||
$this->timeFactory | $this->timeFactory | ||||
->expects($this->at(0)) | ->expects($this->at(0)) | ||||
->method('getTime') | ->method('getTime') | ||||
->expects($this->once()) | ->expects($this->once()) | ||||
->method('getBody') | ->method('getBody') | ||||
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); | ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]'); | ||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502}'; | |||||
$fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}'; | |||||
$file | $file | ||||
->expects($this->at(1)) | ->expects($this->at(1)) | ||||
->method('putContent') | ->method('putContent') | ||||
$this->assertSame($expected, $this->fetcher->get()); | $this->assertSame($expected, $this->fetcher->get()); | ||||
} | } | ||||
public function testGetWithAlreadyExistingFileAndNoVersion() { | |||||
$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(1201); | |||||
$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":1201,"ncversion":"11.0.0.2"}'; | |||||
$file | |||||
->expects($this->at(1)) | |||||
->method('putContent') | |||||
->with($fileData); | |||||
$file | |||||
->expects($this->at(2)) | |||||
->method('getContent') | |||||
->willReturn($fileData); | |||||
$expected = [ | |||||
[ | |||||
'id' => 'MyNewApp', | |||||
'foo' => 'foo', | |||||
], | |||||
[ | |||||
'id' => 'bar', | |||||
], | |||||
]; | |||||
$this->assertSame($expected, $this->fetcher->get()); | |||||
} | |||||
public function testGetWithAlreadyExistingFileAndOutdatedVersion() { | |||||
$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"}},"ncversion":"11.0.0.1"}'); | |||||
$this->timeFactory | |||||
->method('getTime') | |||||
->willReturn(1201); | |||||
$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":1201,"ncversion":"11.0.0.2"}'; | |||||
$file | |||||
->expects($this->at(1)) | |||||
->method('putContent') | |||||
->with($fileData); | |||||
$file | |||||
->expects($this->at(2)) | |||||
->method('getContent') | |||||
->willReturn($fileData); | |||||
$expected = [ | |||||
[ | |||||
'id' => 'MyNewApp', | |||||
'foo' => 'foo', | |||||
], | |||||
[ | |||||
'id' => 'bar', | |||||
], | |||||
]; | |||||
$this->assertSame($expected, $this->fetcher->get()); | |||||
} | |||||
public function testGetWithExceptionInClient() { | public function testGetWithExceptionInClient() { | ||||
$folder = $this->createMock(ISimpleFolder::class); | $folder = $this->createMock(ISimpleFolder::class); | ||||
$file = $this->createMock(ISimpleFile::class); | $file = $this->createMock(ISimpleFile::class); |