MetadataRequestService.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2023 Maxence Lange <maxence@artificial-owl.com>
  5. *
  6. * @author Maxence Lange <maxence@artificial-owl.com>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace OC\FilesMetadata\Service;
  25. use OC\FilesMetadata\Model\FilesMetadata;
  26. use OCP\DB\Exception;
  27. use OCP\DB\QueryBuilder\IQueryBuilder;
  28. use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
  29. use OCP\FilesMetadata\Model\IFilesMetadata;
  30. use OCP\IDBConnection;
  31. use Psr\Log\LoggerInterface;
  32. /**
  33. * manage sql request to the metadata table
  34. */
  35. class MetadataRequestService {
  36. public const TABLE_METADATA = 'files_metadata';
  37. public function __construct(
  38. private IDBConnection $dbConnection,
  39. private LoggerInterface $logger
  40. ) {
  41. }
  42. /**
  43. * store metadata into database
  44. *
  45. * @param IFilesMetadata $filesMetadata
  46. *
  47. * @throws Exception
  48. */
  49. public function store(IFilesMetadata $filesMetadata): void {
  50. $qb = $this->dbConnection->getQueryBuilder();
  51. $qb->insert(self::TABLE_METADATA)
  52. ->setValue('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT))
  53. ->setValue('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize())))
  54. ->setValue('sync_token', $qb->createNamedParameter($this->generateSyncToken()))
  55. ->setValue('last_update', (string) $qb->createFunction('NOW()'));
  56. $qb->executeStatement();
  57. }
  58. /**
  59. * returns metadata for a file id
  60. *
  61. * @param int $fileId file id
  62. *
  63. * @return IFilesMetadata
  64. * @throws FilesMetadataNotFoundException if no metadata are found in database
  65. */
  66. public function getMetadataFromFileId(int $fileId): IFilesMetadata {
  67. try {
  68. $qb = $this->dbConnection->getQueryBuilder();
  69. $qb->select('json', 'sync_token')->from(self::TABLE_METADATA);
  70. $qb->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
  71. $result = $qb->executeQuery();
  72. $data = $result->fetch();
  73. $result->closeCursor();
  74. } catch (Exception $e) {
  75. $this->logger->warning('exception while getMetadataFromDatabase()', ['exception' => $e, 'fileId' => $fileId]);
  76. throw new FilesMetadataNotFoundException();
  77. }
  78. if ($data === false) {
  79. throw new FilesMetadataNotFoundException();
  80. }
  81. $metadata = new FilesMetadata($fileId);
  82. $metadata->importFromDatabase($data);
  83. return $metadata;
  84. }
  85. /**
  86. * returns metadata for multiple file ids
  87. *
  88. * @param array $fileIds file ids
  89. *
  90. * @return array File ID is the array key, files without metadata are not returned in the array
  91. * @psalm-return array<int, IFilesMetadata>
  92. */
  93. public function getMetadataFromFileIds(array $fileIds): array {
  94. $qb = $this->dbConnection->getQueryBuilder();
  95. $qb->select('file_id', 'json', 'sync_token')->from(self::TABLE_METADATA);
  96. $qb->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
  97. $list = [];
  98. $result = $qb->executeQuery();
  99. while ($data = $result->fetch()) {
  100. $fileId = (int) $data['file_id'];
  101. $metadata = new FilesMetadata($fileId);
  102. try {
  103. $metadata->importFromDatabase($data);
  104. } catch (FilesMetadataNotFoundException) {
  105. continue;
  106. }
  107. $list[$fileId] = $metadata;
  108. }
  109. $result->closeCursor();
  110. return $list;
  111. }
  112. /**
  113. * drop metadata related to a file id
  114. *
  115. * @param int $fileId file id
  116. *
  117. * @return void
  118. * @throws Exception
  119. */
  120. public function dropMetadata(int $fileId): void {
  121. $qb = $this->dbConnection->getQueryBuilder();
  122. $qb->delete(self::TABLE_METADATA)
  123. ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
  124. $qb->executeStatement();
  125. }
  126. /**
  127. * update metadata in the database
  128. *
  129. * @param IFilesMetadata $filesMetadata metadata
  130. *
  131. * @return int number of affected rows
  132. * @throws Exception
  133. */
  134. public function updateMetadata(IFilesMetadata $filesMetadata): int {
  135. $qb = $this->dbConnection->getQueryBuilder();
  136. $expr = $qb->expr();
  137. $qb->update(self::TABLE_METADATA)
  138. ->set('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize())))
  139. ->set('sync_token', $qb->createNamedParameter($this->generateSyncToken()))
  140. ->set('last_update', $qb->createFunction('NOW()'))
  141. ->where(
  142. $expr->andX(
  143. $expr->eq('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)),
  144. $expr->eq('sync_token', $qb->createNamedParameter($filesMetadata->getSyncToken()))
  145. )
  146. );
  147. return $qb->executeStatement();
  148. }
  149. /**
  150. * generate a random token
  151. * @return string
  152. */
  153. private function generateSyncToken(): string {
  154. $chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
  155. $str = '';
  156. $max = strlen($chars);
  157. for ($i = 0; $i < 7; $i++) {
  158. try {
  159. $str .= $chars[random_int(0, $max - 2)];
  160. } catch (\Exception $e) {
  161. $this->logger->warning('exception during generateSyncToken', ['exception' => $e]);
  162. }
  163. }
  164. return $str;
  165. }
  166. }