MetadataRequestService.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\FilesMetadata\Service;
  8. use OC\FilesMetadata\Model\FilesMetadata;
  9. use OCP\DB\Exception;
  10. use OCP\DB\QueryBuilder\IQueryBuilder;
  11. use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
  12. use OCP\FilesMetadata\Model\IFilesMetadata;
  13. use OCP\IDBConnection;
  14. use Psr\Log\LoggerInterface;
  15. /**
  16. * manage sql request to the metadata table
  17. */
  18. class MetadataRequestService {
  19. public const TABLE_METADATA = 'files_metadata';
  20. public function __construct(
  21. private IDBConnection $dbConnection,
  22. private LoggerInterface $logger
  23. ) {
  24. }
  25. /**
  26. * store metadata into database
  27. *
  28. * @param IFilesMetadata $filesMetadata
  29. *
  30. * @throws Exception
  31. */
  32. public function store(IFilesMetadata $filesMetadata): void {
  33. $qb = $this->dbConnection->getQueryBuilder();
  34. $qb->insert(self::TABLE_METADATA)
  35. ->setValue('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT))
  36. ->setValue('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize())))
  37. ->setValue('sync_token', $qb->createNamedParameter($this->generateSyncToken()))
  38. ->setValue('last_update', (string) $qb->createFunction('NOW()'));
  39. $qb->executeStatement();
  40. }
  41. /**
  42. * returns metadata for a file id
  43. *
  44. * @param int $fileId file id
  45. *
  46. * @return IFilesMetadata
  47. * @throws FilesMetadataNotFoundException if no metadata are found in database
  48. */
  49. public function getMetadataFromFileId(int $fileId): IFilesMetadata {
  50. try {
  51. $qb = $this->dbConnection->getQueryBuilder();
  52. $qb->select('json', 'sync_token')->from(self::TABLE_METADATA);
  53. $qb->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
  54. $result = $qb->executeQuery();
  55. $data = $result->fetch();
  56. $result->closeCursor();
  57. } catch (Exception $e) {
  58. $this->logger->warning('exception while getMetadataFromDatabase()', ['exception' => $e, 'fileId' => $fileId]);
  59. throw new FilesMetadataNotFoundException();
  60. }
  61. if ($data === false) {
  62. throw new FilesMetadataNotFoundException();
  63. }
  64. $metadata = new FilesMetadata($fileId);
  65. $metadata->importFromDatabase($data);
  66. return $metadata;
  67. }
  68. /**
  69. * returns metadata for multiple file ids
  70. *
  71. * @param array $fileIds file ids
  72. *
  73. * @return array File ID is the array key, files without metadata are not returned in the array
  74. * @psalm-return array<int, IFilesMetadata>
  75. */
  76. public function getMetadataFromFileIds(array $fileIds): array {
  77. $qb = $this->dbConnection->getQueryBuilder();
  78. $qb->select('file_id', 'json', 'sync_token')->from(self::TABLE_METADATA);
  79. $qb->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
  80. $list = [];
  81. $result = $qb->executeQuery();
  82. while ($data = $result->fetch()) {
  83. $fileId = (int) $data['file_id'];
  84. $metadata = new FilesMetadata($fileId);
  85. try {
  86. $metadata->importFromDatabase($data);
  87. } catch (FilesMetadataNotFoundException) {
  88. continue;
  89. }
  90. $list[$fileId] = $metadata;
  91. }
  92. $result->closeCursor();
  93. return $list;
  94. }
  95. /**
  96. * drop metadata related to a file id
  97. *
  98. * @param int $fileId file id
  99. *
  100. * @return void
  101. * @throws Exception
  102. */
  103. public function dropMetadata(int $fileId): void {
  104. $qb = $this->dbConnection->getQueryBuilder();
  105. $qb->delete(self::TABLE_METADATA)
  106. ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
  107. $qb->executeStatement();
  108. }
  109. /**
  110. * update metadata in the database
  111. *
  112. * @param IFilesMetadata $filesMetadata metadata
  113. *
  114. * @return int number of affected rows
  115. * @throws Exception
  116. */
  117. public function updateMetadata(IFilesMetadata $filesMetadata): int {
  118. $qb = $this->dbConnection->getQueryBuilder();
  119. $expr = $qb->expr();
  120. $qb->update(self::TABLE_METADATA)
  121. ->set('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize())))
  122. ->set('sync_token', $qb->createNamedParameter($this->generateSyncToken()))
  123. ->set('last_update', $qb->createFunction('NOW()'))
  124. ->where(
  125. $expr->andX(
  126. $expr->eq('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)),
  127. $expr->eq('sync_token', $qb->createNamedParameter($filesMetadata->getSyncToken()))
  128. )
  129. );
  130. return $qb->executeStatement();
  131. }
  132. /**
  133. * generate a random token
  134. * @return string
  135. */
  136. private function generateSyncToken(): string {
  137. $chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
  138. $str = '';
  139. $max = strlen($chars);
  140. for ($i = 0; $i < 7; $i++) {
  141. try {
  142. $str .= $chars[random_int(0, $max - 2)];
  143. } catch (\Exception $e) {
  144. $this->logger->warning('exception during generateSyncToken', ['exception' => $e]);
  145. }
  146. }
  147. return $str;
  148. }
  149. }