namespace OC\App\AppStore\Fetcher;
+use OCP\AppFramework\Http;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
/**
* Fetches the response from the server
*
+ * @param string $ETag
+ * @param string $content
+ *
* @return array
*/
- protected function fetch() {
+ protected function fetch($ETag, $content) {
+ $options = [];
+
+ if ($ETag !== '') {
+ $options['headers'] = [
+ 'If-None-Match' => $ETag,
+ ];
+ }
+
$client = $this->clientService->newClient();
- $response = $client->get($this->endpointUrl);
+ $response = $client->get($this->endpointUrl, $options);
+
$responseJson = [];
- $responseJson['data'] = json_decode($response->getBody(), true);
+ if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
+ $responseJson['data'] = json_decode($content, true);
+ } else {
+ $responseJson['data'] = json_decode($response->getBody(), true);
+ $ETag = $response->getHeader('ETag');
+ }
+
$responseJson['timestamp'] = $this->timeFactory->getTime();
$responseJson['ncversion'] = $this->config->getSystemValue('version');
+ if ($ETag !== '') {
+ $responseJson['ETag'] = $ETag;
+ }
+
return $responseJson;
}
public function get() {
$rootFolder = $this->appData->getFolder('/');
+ $ETag = '';
+ $content = '';
+
try {
// File does already exists
$file = $rootFolder->getFile($this->fileName);
isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->config->getSystemValue('version', '0.0.0')) {
return $jsonBlob['data'];
}
+
+ if (isset($jsonBlob['ETag'])) {
+ $ETag = $jsonBlob['ETag'];
+ $content = json_encode($jsonBlob['data']);
+ }
}
} catch (NotFoundException $e) {
// File does not already exists
// Refresh the file content
try {
- $responseJson = $this->fetch();
+ $responseJson = $this->fetch($ETag, $content);
$file->putContent(json_encode($responseJson));
return json_decode($file->getContent(), true)['data'];
} catch (\Exception $e) {
->expects($this->once())
->method('getBody')
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}';
+ $response->method('getHeader')
+ ->with($this->equalTo('ETag'))
+ ->willReturn('"myETag"');
+ $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
$file
->expects($this->at(0))
->method('putContent')
->expects($this->once())
->method('getBody')
->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2"}';
+ $response->method('getHeader')
+ ->with($this->equalTo('ETag'))
+ ->willReturn('"myETag"');
+ $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
$file
->expects($this->at(1))
->method('putContent')
->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"}';
+ $response->method('getHeader')
+ ->with($this->equalTo('ETag'))
+ ->willReturn('"myETag"');
+ $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
$file
->expects($this->at(1))
->method('putContent')
$file
->expects($this->at(0))
->method('getContent')
- ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"}');
+ ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"');
$this->timeFactory
->method('getTime')
->willReturn(1201);
->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"}';
+ $response->method('getHeader')
+ ->with($this->equalTo('ETag'))
+ ->willReturn('"myETag"');
+ $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
$file
->expects($this->at(1))
->method('putContent')
$this->assertSame([], $this->fetcher->get());
}
+
+ public function testGetMatchingETag() {
+ $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);
+ $origData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
+ $file
+ ->expects($this->at(0))
+ ->method('getContent')
+ ->willReturn($origData);
+ $this->timeFactory
+ ->expects($this->at(0))
+ ->method('getTime')
+ ->willReturn(1501);
+ $this->timeFactory
+ ->expects($this->at(1))
+ ->method('getTime')
+ ->willReturn(1502);
+ $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->equalTo($this->endpoint),
+ $this->equalTo([
+ 'headers' => [
+ 'If-None-Match' => '"myETag"'
+ ]
+ ])
+ )->willReturn($response);
+ $response->method('getStatusCode')
+ ->willReturn(304);
+
+ $newData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
+ $file
+ ->expects($this->at(1))
+ ->method('putContent')
+ ->with($newData);
+ $file
+ ->expects($this->at(2))
+ ->method('getContent')
+ ->willReturn($newData);
+
+ $expected = [
+ [
+ 'id' => 'MyNewApp',
+ 'foo' => 'foo',
+ ],
+ [
+ 'id' => 'bar',
+ ],
+ ];
+
+ $this->assertSame($expected, $this->fetcher->get());
+ }
+
+ public function testGetNoMatchingETag() {
+ $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)
+ ->willReturn($file);
+ $file
+ ->expects($this->at(0))
+ ->method('getContent')
+ ->willReturn('{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}');
+ $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->equalTo($this->endpoint),
+ $this->equalTo([
+ 'headers' => [
+ 'If-None-Match' => '"myETag"',
+ ]
+ ])
+ )
+ ->willReturn($response);
+ $response->method('getStatusCode')
+ ->willReturn(200);
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
+ $response->method('getHeader')
+ ->with($this->equalTo('ETag'))
+ ->willReturn('"newETag"');
+ $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"newETag\""}';
+ $file
+ ->expects($this->at(1))
+ ->method('putContent')
+ ->with($fileData);
+ $file
+ ->expects($this->at(2))
+ ->method('getContent')
+ ->willReturn($fileData);
+ $this->timeFactory
+ ->expects($this->at(0))
+ ->method('getTime')
+ ->willReturn(1501);
+ $this->timeFactory
+ ->expects($this->at(1))
+ ->method('getTime')
+ ->willReturn(1502);
+
+ $expected = [
+ [
+ 'id' => 'MyNewApp',
+ 'foo' => 'foo',
+ ],
+ [
+ 'id' => 'bar',
+ ],
+ ];
+ $this->assertSame($expected, $this->fetcher->get());
+ }
}