Merge pull request #2707 from nextcloud/clear_appstore_cache_on_upgrade

Clear appstore cache on version upgrade
This commit is contained in:
Lukas Reschke 2016-12-16 17:14:53 +01:00 committed by GitHub
commit b0c1460a1d
8 changed files with 159 additions and 25 deletions

View File

@ -28,9 +28,6 @@ use OCP\Http\Client\IClientService;
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
@ -44,11 +41,11 @@ class AppFetcher extends Fetcher {
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(
@ -65,12 +62,8 @@ class AppFetcher extends Fetcher {
* @return array * @return array
*/ */
protected function fetch() { protected function fetch() {
$client = $this->clientService->newClient(); /** @var mixed[] $response */
$response = $client->get($this->endpointUrl); $response = parent::fetch();
$responseJson = [];
$responseJson['data'] = json_decode($response->getBody(), true);
$responseJson['timestamp'] = $this->timeFactory->getTime();
$response = $responseJson;
$ncVersion = $this->config->getSystemValue('version'); $ncVersion = $this->config->getSystemValue('version');
$ncMajorVersion = explode('.', $ncVersion)[0]; $ncMajorVersion = explode('.', $ncVersion)[0];

View File

@ -24,20 +24,24 @@ namespace OC\App\AppStore\Fetcher;
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';

View File

@ -25,6 +25,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
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;
@ -35,6 +36,8 @@ abstract class Fetcher {
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 */
@ -44,13 +47,16 @@ abstract class 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) {
$this->appData = $appData; $this->appData = $appData;
$this->clientService = $clientService; $this->clientService = $clientService;
$this->timeFactory = $timeFactory; $this->timeFactory = $timeFactory;
$this->config = $config;
} }
/** /**
@ -64,6 +70,7 @@ abstract class Fetcher {
$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;
} }
@ -80,8 +87,12 @@ abstract class Fetcher {
$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'];
} }
} }

View File

@ -360,7 +360,8 @@ class Server extends ServerContainer implements IServerContainer {
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) {

View File

@ -106,7 +106,8 @@ class Application extends App {
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()
); );
}); });
} }

View File

@ -1883,6 +1883,7 @@ EJL3BaQAQaASSsvFrcozYxrQG4VzEg==
), ),
), ),
'timestamp' => 1234, 'timestamp' => 1234,
'ncversion' => '11.0.0.2',
); );
$dataToPut = $expected; $dataToPut = $expected;

View File

@ -32,7 +32,8 @@ class CategoryFetcherTest extends FetcherBase {
$this->fetcher = new CategoryFetcher( $this->fetcher = new CategoryFetcher(
$this->appData, $this->appData,
$this->clientService, $this->clientService,
$this->timeFactory $this->timeFactory,
$this->config
); );
} }
} }

View File

@ -55,9 +55,16 @@ abstract class FetcherBase extends TestCase {
$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
@ -73,7 +80,7 @@ abstract class FetcherBase extends TestCase {
$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')
@ -87,7 +94,7 @@ abstract class FetcherBase extends TestCase {
$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
@ -120,7 +127,7 @@ abstract class FetcherBase extends TestCase {
->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')
@ -162,7 +169,7 @@ abstract class FetcherBase extends TestCase {
$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')
@ -182,7 +189,7 @@ abstract class FetcherBase extends TestCase {
->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')
@ -208,6 +215,121 @@ abstract class FetcherBase extends TestCase {
$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);