better queries parser, add endpoint for replies

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/730/head
Maxence Lange 2019-09-12 11:38:03 -01:00
rodzic 115a8f87db
commit b1c50f91f6
9 zmienionych plików z 271 dodań i 139 usunięć

Wyświetl plik

@ -60,6 +60,7 @@ return [
['name' => 'OStatus#followRemote', 'url' => '/api/v1/ostatus/followRemote/{local}', 'verb' => 'GET'],
['name' => 'OStatus#getLink', 'url' => '/api/v1/ostatus/link/{local}/{account}', 'verb' => 'GET'],
// should it be moved to NavigationController ?
['name' => 'SocialPub#displayPost', 'url' => '/@{username}/{token}', 'verb' => 'GET'],
['name' => 'Local#streamHome', 'url' => '/api/v1/stream/home', 'verb' => 'GET'],
@ -72,6 +73,8 @@ return [
['name' => 'Local#streamAccount', 'url' => '/api/v1/account/{username}/stream', 'verb' => 'GET'],
['name' => 'Local#postGet', 'url' => '/local/v1/post', 'verb' => 'GET'],
['name' => 'Local#postReplies', 'url' => '/local/v1/post/replies', 'verb' => 'GET'],
['name' => 'Local#postCreate', 'url' => '/api/v1/post', 'verb' => 'POST'],
['name' => 'Local#postDelete', 'url' => '/api/v1/post', 'verb' => 'DELETE'],

Wyświetl plik

@ -196,15 +196,15 @@ class LocalController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $postId
* @param string $id
*
* @return DataResponse
*/
public function postGet(string $postId): DataResponse {
public function postGet(string $id): DataResponse {
try {
$this->initViewer(true);
$stream = $this->streamService->getStreamById($postId, true);
$stream = $this->streamService->getStreamById($id, true);
return $this->directSuccess($stream);
} catch (Exception $e) {
@ -213,6 +213,27 @@ class LocalController extends Controller {
}
/**
* get replies about a post (limited to viewer rights).
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $id
*
* @return DataResponse
*/
public function postReplies(string $id): DataResponse {
try {
$this->initViewer(true);
return $this->success($this->streamService->getRepliesByParentId($id, true));
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
* Delete your own post.
*

Wyświetl plik

@ -39,6 +39,8 @@ use OC\DB\SchemaWrapper;
use OCA\Social\AP;
use OCA\Social\Exceptions\DateTimeException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\RowNotFoundException;
use OCA\Social\IQueryRow;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Document;
use OCA\Social\Model\ActivityPub\Object\Follow;
@ -198,6 +200,17 @@ class CoreRequestBuilder {
}
/**
* Limit the request to the Id (string)
*
* @param IQueryBuilder $qb
* @param string $id
*/
protected function limitToInReplyTo(IQueryBuilder &$qb, string $id) {
$this->limitToDBField($qb, 'in_reply_to', $id, false);
}
/**
* Limit the request to the StreamId
*
@ -1160,14 +1173,52 @@ class CoreRequestBuilder {
* @param string $fieldActorId
* @param string $pf
*/
protected function leftJoinDetails(
IQueryBuilder $qb, string $fieldActorId = 'id', string $pf = ''
) {
protected function leftJoinDetails(IQueryBuilder $qb, string $fieldActorId = 'id', string $pf = '') {
$this->leftJoinFollowAsViewer($qb, $fieldActorId, true, 'as_follower', $pf);
$this->leftJoinFollowAsViewer($qb, $fieldActorId, false, 'as_followed', $pf);
}
/**
* @param IQueryBuilder $qb
* @param callable $method
*
* @return IQueryRow
* @throws RowNotFoundException
*/
public function getRowFromRequest(IQueryBuilder $qb, callable $method): IQueryRow {
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new RowNotFoundException();
}
return $method($data);
}
/**
* @param IQueryBuilder $qb
* @param callable $method
*
* @return array
*/
public function getRowsFromRequest(IQueryBuilder $qb, callable $method): array {
$rows = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$rows[] = $method($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $rows;
}
/**
* @param Person $actor
* @param array $data

Wyświetl plik

@ -171,7 +171,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param Document $document
* @param array $attachments
*
* @return array
* @return Document[]
*/
private function updateAttachmentInList(Document $document, array $attachments): array {
$new = [];
@ -216,17 +216,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, $type);
}
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -236,6 +226,7 @@ class StreamRequest extends StreamRequestBuilder {
*
* @return Stream
* @throws StreamNotFoundException
* @throws SocialAppConfigException
*/
public function getStreamById(string $id, bool $asViewer = false): Stream {
if ($id === '') {
@ -251,23 +242,41 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinStreamAction($qb);
}
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
try {
return $this->getStreamFromRequest($qb);
} catch (ItemUnknownException $e) {
throw new StreamNotFoundException('Malformed Stream');
} catch (StreamNotFoundException $e) {
throw new StreamNotFoundException(
'Stream (ById) not found - ' . $id . ' (asViewer: ' . $asViewer . ')'
);
}
}
try {
$stream = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
throw new StreamNotFoundException('Malformed Stream');
/**
* @param string $id
* @param bool $asViewer
*
* @return Stream[]
* @throws StreamNotFoundException
*/
public function getRepliesByParentId(string $id, bool $asViewer = false): array {
if ($id === '') {
throw new StreamNotFoundException();
};
$qb = $this->getStreamSelectSql();
$this->limitToInReplyTo($qb, $id);
$this->leftJoinCacheActors($qb, 'attributed_to');
if ($asViewer) {
$this->limitToViewer($qb);
$this->leftJoinStreamAction($qb);
}
return $stream;
return $this->getStreamsFromRequest($qb);
}
@ -286,15 +295,7 @@ class StreamRequest extends StreamRequestBuilder {
$qb = $this->getStreamSelectSql();
$this->limitToActivityId($qb, $id);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new StreamNotFoundException('Stream (ByActivityId) not found - ' . $id);
}
return $this->parseStreamSelectSql($data);
return $this->getStreamFromRequest($qb);
}
@ -319,17 +320,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, $type);
$this->limitToSubType($qb, $subType);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new StreamNotFoundException(
'StreamByObjectId not found - ' . $type . ' - ' . $objectId
);
}
return $this->parseStreamSelectSql($data);
return $this->getStreamFromRequest($qb);
}
@ -379,17 +370,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinStreamAction($qb);
$this->filterDuplicate($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -405,7 +386,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineNotifications(Person $actor, int $since = 0, int $limit = 5): array {
@ -418,17 +399,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -441,7 +412,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineAccount(string $actorId, int $since = 0, int $limit = 5): array {
@ -454,17 +425,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -477,7 +438,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineDirect(Person $actor, int $since = 0, int $limit = 5): array {
@ -492,17 +453,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -514,7 +465,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $limit
* @param bool $localOnly
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineGlobal(int $since = 0, int $limit = 5, bool $localOnly = true
@ -531,17 +482,7 @@ class StreamRequest extends StreamRequestBuilder {
// TODO: to: = real public, cc: = unlisted !?
$this->limitToRecipient($qb, ACore::CONTEXT_PUBLIC, true, ['to']);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -553,7 +494,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $limit
* @param bool $localOnly
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineLiked(int $since = 0, int $limit = 5, bool $localOnly = true): array {
@ -567,17 +508,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinActions($qb, Like::TYPE);
$this->filterDBField($qb, 'id', '', false, 'a');
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -592,7 +523,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getTimelineTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5
@ -612,14 +543,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$streams[] = $this->parseStreamSelectSql($data);
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -628,8 +552,6 @@ class StreamRequest extends StreamRequestBuilder {
*
* @return Stream[]
* @throws DateTimeException
* @throws ItemUnknownException
* @throws SocialAppConfigException
*/
public function getNoteSince(int $since): array {
$qb = $this->getStreamSelectSql();
@ -637,14 +559,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, Note::TYPE);
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$streams[] = $this->parseStreamSelectSql($data, Note::TYPE);
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}

Wyświetl plik

@ -36,7 +36,9 @@ use Doctrine\DBAL\Query\QueryBuilder;
use OCA\Social\AP;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\RowNotFoundException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Announce;
@ -447,6 +449,37 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
*
* @return Stream
* @throws StreamNotFoundException
*/
protected function getStreamFromRequest(IQueryBuilder $qb): Stream {
/** @var Stream $result */
try {
$result = $this->getRowFromRequest($qb, [$this, 'parseStreamSelectSql']);
} catch (RowNotFoundException $e) {
throw new StreamNotFoundException($e->getMessage());
}
return $result;
}
/**
* @param IQueryBuilder $qb
*
* @return Stream[]
*/
public function getStreamsFromRequest(IQueryBuilder $qb): array {
/** @var Stream[] $result */
$result = $this->getRowsFromRequest($qb, [$this, 'parseStreamSelectSql']);
return $result;
}
/**
* @param array $data
* @param string $as

Wyświetl plik

@ -0,0 +1,39 @@
<?php
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Exceptions;
use Exception;
class RowNotFoundException extends Exception {
}

57
lib/IQueryRow.php 100644
Wyświetl plik

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social;
use OCA\Social\Exceptions\ItemAlreadyExistsException;
use OCA\Social\Exceptions\ItemNotFoundException;
use OCA\Social\Model\ActivityPub\ACore;
/**
* Interface IQueryRow
*
* TODO: MOVE THIS TO MyPhpTools
*
* @package OCA\Social\Service
*/
interface IQueryRow {
/**
* import data to feed the model.
*
* @param array $data
*/
public function import(array $data);
}

Wyświetl plik

@ -35,11 +35,12 @@ use daita\MySmallPhpTools\Model\CacheItem;
use DateTime;
use Exception;
use JsonSerializable;
use OCA\Social\IQueryRow;
use OCA\Social\Model\StreamAction;
use OCA\Social\Traits\TDetails;
class Stream extends ACore implements JsonSerializable {
class Stream extends ACore implements IQueryRow, JsonSerializable {
use TDetails;

Wyświetl plik

@ -36,7 +36,6 @@ use OCA\Social\Db\StreamRequest;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\RedundancyLimitException;
use OCA\Social\Exceptions\RequestContentException;
use OCA\Social\Exceptions\RequestNetworkException;
@ -44,6 +43,7 @@ use OCA\Social\Exceptions\RequestResultNotJsonException;
use OCA\Social\Exceptions\RequestResultSizeException;
use OCA\Social\Exceptions\RequestServerException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\UnauthorizedFediverseException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
@ -389,6 +389,18 @@ class StreamService {
}
/**
* @param string $id
* @param bool $asViewer
*
* @return Stream[]
* @throws StreamNotFoundException
*/
public function getRepliesByParentId(string $id, bool $asViewer = false): array {
return $this->streamRequest->getRepliesByParentId($id, $asViewer);
}
/**
* @param Person $actor
* @param int $since