Merge pull request #58 from nextcloud-gmbh/network-request-queue

Priority, Queue and Async
alpha1
Maxence Lange 2018-11-27 17:31:22 -01:00 zatwierdzone przez GitHub
commit ce6edf240c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
33 zmienionych plików z 1486 dodań i 111 usunięć

Wyświetl plik

@ -268,6 +268,13 @@
<primary>true</primary> <primary>true</primary>
</field> </field>
<field>
<name>type</name>
<type>text</type>
<length>31</length>
<notnull>true</notnull>
</field>
<field> <field>
<name>account</name> <name>account</name>
<type>text</type> <type>text</type>
@ -461,5 +468,79 @@
</declaration> </declaration>
</table> </table>
<table>
<name>*dbprefix*social_request_queue</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<length>11</length>
<unsigned>true</unsigned>
<notnull>true</notnull>
<autoincrement>true</autoincrement>
<primary>true</primary>
</field>
<field>
<name>token</name>
<type>text</type>
<length>63</length>
<notnull>true</notnull>
</field>
<field>
<name>author</name>
<type>text</type>
<length>1270</length>
<notnull>true</notnull>
</field>
<field>
<name>activity</name>
<type>text</type>
<length>6000</length>
<notnull>true</notnull>
</field>
<field>
<name>instance</name>
<type>text</type>
<length>500</length>
<notnull>false</notnull>
</field>
<field>
<name>priority</name>
<type>integer</type>
<length>1</length>
<default>0</default>
<notnull>false</notnull>
</field>
<field>
<name>status</name>
<type>integer</type>
<length>1</length>
<default>0</default>
<notnull>false</notnull>
</field>
<field>
<name>tries</name>
<type>integer</type>
<length>2</length>
<default>0</default>
<notnull>false</notnull>
</field>
<field>
<name>last</name>
<type>timestamp</type>
</field>
</declaration>
</table>
</database> </database>

Wyświetl plik

@ -5,7 +5,7 @@
<name>Social</name> <name>Social</name>
<summary>🎉 Nextcloud becomes part of the federated social networks!</summary> <summary>🎉 Nextcloud becomes part of the federated social networks!</summary>
<description><![CDATA[test]]></description> <description><![CDATA[test]]></description>
<version>0.0.43</version> <version>0.0.50</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="maxence@artificial-owl.com">Maxence Lange</author> <author mail="maxence@artificial-owl.com">Maxence Lange</author>
<author mail="jus@bitgrid.net">Julius Härtl</author> <author mail="jus@bitgrid.net">Julius Härtl</author>
@ -32,6 +32,7 @@
<commands> <commands>
<command>OCA\Social\Command\CacheRefresh</command> <command>OCA\Social\Command\CacheRefresh</command>
<command>OCA\Social\Command\QueueStatus</command>
<command>OCA\Social\Command\NoteCreate</command> <command>OCA\Social\Command\NoteCreate</command>
</commands> </commands>

Wyświetl plik

@ -1,12 +1,39 @@
<?php <?php
declare(strict_types=1);
/** /**
* Create your routes in here. The name is the lowercase name of the controller * Nextcloud - Social Support
* without the controller part, the stuff after the hash is the method. *
* e.g. page#index -> OCA\MailTest\Controller\PageController->index() * 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/>.
* *
* The controller class has to be registered in the application.php file since
* it's instantiated in there
*/ */
namespace OCA\Social\AppInfo;
use OCA\Social\Service\CurlService;
return [ return [
'routes' => [ 'routes' => [
['name' => 'Navigation#navigate', 'url' => '/', 'verb' => 'GET'], ['name' => 'Navigation#navigate', 'url' => '/', 'verb' => 'GET'],
@ -52,6 +79,8 @@ return [
['name' => 'Local#actorInfo', 'url' => '/api/v1/actor/info', 'verb' => 'GET'], ['name' => 'Local#actorInfo', 'url' => '/api/v1/actor/info', 'verb' => 'GET'],
['name' => 'Local#documentsCache', 'url' => '/api/v1/documents/cache', 'verb' => 'POST'], ['name' => 'Local#documentsCache', 'url' => '/api/v1/documents/cache', 'verb' => 'POST'],
['name' => 'Queue#asyncWithToken', 'url' => CurlService::ASYNC_TOKEN, 'verb' => 'POST'],
[ [
'name' => 'Config#setCloudAddress', 'url' => '/api/v1/config/cloudAddress', 'name' => 'Config#setCloudAddress', 'url' => '/api/v1/config/cloudAddress',
'verb' => 'POST' 'verb' => 'POST'

8
composer.lock wygenerowano
Wyświetl plik

@ -12,12 +12,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/daita/my-small-php-tools.git", "url": "https://github.com/daita/my-small-php-tools.git",
"reference": "fc822e4c08072844ad9d58846d407e83a62b3d38" "reference": "ba839f3253672defc4bbb181493c78e2c96e272a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/fc822e4c08072844ad9d58846d407e83a62b3d38", "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/ba839f3253672defc4bbb181493c78e2c96e272a",
"reference": "fc822e4c08072844ad9d58846d407e83a62b3d38", "reference": "ba839f3253672defc4bbb181493c78e2c96e272a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -40,7 +40,7 @@
} }
], ],
"description": "My small PHP Tools", "description": "My small PHP Tools",
"time": "2018-11-20T22:42:57+00:00" "time": "2018-11-27T14:31:43+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

Wyświetl plik

@ -135,10 +135,10 @@ class NoteCreate extends Base {
$post->setReplyTo(($replyTo === null) ? '' : $replyTo); $post->setReplyTo(($replyTo === null) ? '' : $replyTo);
$post->addTo(($to === null) ? '' : $to); $post->addTo(($to === null) ? '' : $to);
$result = $this->postService->createPost($post, $activity); $token = $this->postService->createPost($post, $activity);
echo 'object: ' . json_encode($activity, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; echo 'object: ' . json_encode($activity, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
echo 'result: ' . json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; echo 'token: ' . $token . "\n";
} }
} }

Wyświetl plik

@ -0,0 +1,110 @@
<?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\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\QueueService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class QueueStatus extends Base {
/** @var ConfigService */
private $configService;
/** @var QueueService */
private $queueService;
/** @var MiscService */
private $miscService;
/**
* NoteCreate constructor.
*
* @param QueueService $queueService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
QueueService $queueService, ConfigService $configService, MiscService $miscService
) {
parent::__construct();
$this->queueService = $queueService;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('social:queue:status')
->addOption(
'token', 't', InputOption::VALUE_OPTIONAL, 'token of a request'
)
->setDescription('Return status on the request queue');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$token = $input->getOption('token');
if ($token === null) {
throw new Exception('As of today, --token is mandatory');
}
$requests = $this->queueService->getRequestFromToken($token);
foreach ($requests as $request) {
$output->writeLn(json_encode($request));
}
}
}

Wyświetl plik

@ -87,7 +87,7 @@ class LocalController extends Controller {
/** /**
* NavigationController constructor. * LocalController constructor.
* *
* @param IRequest $request * @param IRequest $request
* @param string $userId * @param string $userId

Wyświetl plik

@ -0,0 +1,115 @@
<?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\Controller;
use daita\MySmallPhpTools\Traits\TAsync;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\RequestQueue;
use OCA\Social\Service\ActivityService;
use OCA\Social\Service\CurlService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\QueueService;
use OCP\AppFramework\Controller;
use OCP\IRequest;
/**
* Class QueueController
*
* @package OCA\Social\Controller
*/
class QueueController extends Controller {
use TAsync;
/** @var QueueService */
private $queueService;
/** @var ActivityService */
private $activityService;
/** @var MiscService */
private $miscService;
/**
* QueueController constructor.
*
* @param IRequest $request
* @param QueueService $queueService
* @param ActivityService $activityService
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, QueueService $queueService, ActivityService $activityService,
MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
$this->queueService = $queueService;
$this->activityService = $activityService;
$this->miscService = $miscService;
}
/**
* // TODO: Delete the NoCSRF check
*
* @PublicPage
* @NoCSRFRequired
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $token
*/
public function asyncWithToken(string $token) {
$this->async();
$requests = $this->queueService->getRequestFromToken($token, RequestQueue::STATUS_STANDBY);
foreach ($requests as $request) {
try {
$this->activityService->manageRequest($request);
} catch (ActorDoesNotExistException $e) {
} catch (RequestException $e) {
} catch (SocialAppConfigException $e) {
}
}
// or it will feed the logs.
exit();
}
}

Wyświetl plik

@ -113,6 +113,7 @@ class ActorsRequestBuilder extends CoreRequestBuilder {
$actor = new Person(); $actor = new Person();
$actor->importFromDatabase($data); $actor->importFromDatabase($data);
$actor->setType('Person');
$actor->setInbox($actor->getId() . '/inbox') $actor->setInbox($actor->getId() . '/inbox')
->setOutbox($actor->getId() . '/outbox') ->setOutbox($actor->getId() . '/outbox')
->setFollowers($actor->getId() . '/followers') ->setFollowers($actor->getId() . '/followers')

Wyświetl plik

@ -69,6 +69,7 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
$qb = $this->getCacheActorsInsertSql(); $qb = $this->getCacheActorsInsertSql();
$qb->setValue('id', $qb->createNamedParameter($actor->getId())) $qb->setValue('id', $qb->createNamedParameter($actor->getId()))
->setValue('account', $qb->createNamedParameter($actor->getAccount())) ->setValue('account', $qb->createNamedParameter($actor->getAccount()))
->setValue('type', $qb->createNamedParameter($actor->getType()))
->setValue('local', $qb->createNamedParameter(($actor->isLocal()) ? '1' : '0')) ->setValue('local', $qb->createNamedParameter(($actor->isLocal()) ? '1' : '0'))
->setValue('following', $qb->createNamedParameter($actor->getFollowing())) ->setValue('following', $qb->createNamedParameter($actor->getFollowing()))
->setValue('followers', $qb->createNamedParameter($actor->getFollowers())) ->setValue('followers', $qb->createNamedParameter($actor->getFollowers()))

Wyświetl plik

@ -78,7 +78,7 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder {
/** @noinspection PhpMethodParametersCountMismatchInspection */ /** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select( $qb->select(
'ca.id', 'ca.account', 'ca.following', 'ca.followers', 'ca.inbox', 'ca.id', 'ca.account', 'ca.following', 'ca.followers', 'ca.inbox',
'ca.shared_inbox', 'ca.outbox', 'ca.featured', 'ca.url', 'ca.shared_inbox', 'ca.outbox', 'ca.featured', 'ca.url', 'ca.type',
'ca.preferred_username', 'ca.name', 'ca.summary', 'ca.preferred_username', 'ca.name', 'ca.summary',
'ca.public_key', 'ca.local', 'ca.source', 'ca.creation' 'ca.public_key', 'ca.local', 'ca.source', 'ca.creation'
) )

Wyświetl plik

@ -53,6 +53,8 @@ use OCP\IDBConnection;
class CoreRequestBuilder { class CoreRequestBuilder {
const TABLE_REQUEST_QUEUE = 'social_request_queue';
const TABLE_SERVER_ACTORS = 'social_server_actors'; const TABLE_SERVER_ACTORS = 'social_server_actors';
const TABLE_SERVER_NOTES = 'social_server_notes'; const TABLE_SERVER_NOTES = 'social_server_notes';
const TABLE_SERVER_FOLLOWS = 'social_server_follows'; const TABLE_SERVER_FOLLOWS = 'social_server_follows';
@ -158,6 +160,17 @@ class CoreRequestBuilder {
} }
/**
* Limit the request to the token
*
* @param IQueryBuilder $qb
* @param string $token
*/
protected function limitToToken(IQueryBuilder &$qb, string $token) {
$this->limitToDBField($qb, 'token', $token);
}
/** /**
* Limit the request to the ActorId * Limit the request to the ActorId
* *
@ -262,10 +275,10 @@ class CoreRequestBuilder {
* Limit the request to the status * Limit the request to the status
* *
* @param IQueryBuilder $qb * @param IQueryBuilder $qb
* @param string $status * @param int $status
*/ */
protected function limitToStatus(IQueryBuilder &$qb, $status) { protected function limitToStatus(IQueryBuilder &$qb, int $status) {
$this->limitToDBField($qb, 'status', $status); $this->limitToDBFieldInt($qb, 'status', $status);
} }
@ -348,6 +361,14 @@ class CoreRequestBuilder {
} }
/**
* @param IQueryBuilder $qb
*/
protected function orderByPriority(IQueryBuilder &$qb) {
$qb->orderBy('priority', 'desc');
}
/** /**
* @param IQueryBuilder $qb * @param IQueryBuilder $qb
* @param string $field * @param string $field
@ -474,6 +495,7 @@ class CoreRequestBuilder {
// /** @noinspection PhpMethodParametersCountMismatchInspection */ // /** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('ca.id', 'cacheactor_id') $qb->selectAlias('ca.id', 'cacheactor_id')
->selectAlias('ca.type', 'cacheactor_type')
->selectAlias('ca.account', 'cacheactor_account') ->selectAlias('ca.account', 'cacheactor_account')
->selectAlias('ca.following', 'cacheactor_following') ->selectAlias('ca.following', 'cacheactor_following')
->selectAlias('ca.followers', 'cacheactor_followers') ->selectAlias('ca.followers', 'cacheactor_followers')

Wyświetl plik

@ -177,7 +177,9 @@ class NotesRequestBuilder extends CoreRequestBuilder {
$instances = json_decode($data['instances'], true); $instances = json_decode($data['instances'], true);
if (is_array($instances)) { if (is_array($instances)) {
foreach ($instances as $instance) { foreach ($instances as $instance) {
$note->addInstancePath(new InstancePath($instance['uri'], $instance['type'])); $instancePath = new InstancePath();
$instancePath->import($instance);
$note->addInstancePath($instancePath);
} }
} }

Wyświetl plik

@ -0,0 +1,199 @@
<?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\Db;
use DateTime;
use Exception;
use OCA\Social\Exceptions\QueueStatusException;
use OCA\Social\Model\RequestQueue;
use OCA\Social\Service\QueueService;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class RequestQueueRequest
*
* @package OCA\Social\Db
*/
class RequestQueueRequest extends RequestQueueRequestBuilder {
/**
* create a new Queue in the database.
*
* @param RequestQueue[] $queues
*
* @throws Exception
*/
public function multiple(array $queues) {
foreach ($queues as $queue) {
$this->create($queue);
// $qb->values(
// [
// 'source' => $qb->createNamedParameter($queue->getSource()),
// 'activity' => $qb->createNamedParameter($queue->getActivity()),
// 'instance' => $qb->createNamedParameter(
// json_encode($queue->getInstance(), JSON_UNESCAPED_SLASHES)
// ),
// 'status' => $qb->createNamedParameter($queue->getStatus()),
// 'tries' => $qb->createNamedParameter($queue->getTries()),
// 'last' => $qb->createNamedParameter($queue->getLast())
// ]
// );
}
}
/**
* create a new Queue in the database.
*
* @param RequestQueue $queue
*
* @throws Exception
*/
public function create(RequestQueue $queue) {
$qb = $this->getQueueInsertSql();
$qb->setValue('token', $qb->createNamedParameter($queue->getToken()))
->setValue('author', $qb->createNamedParameter($queue->getAuthor()))
->setValue('activity', $qb->createNamedParameter($queue->getActivity()))
->setValue(
'instance', $qb->createNamedParameter(
json_encode($queue->getInstance(), JSON_UNESCAPED_SLASHES)
)
)
->setValue('priority', $qb->createNamedParameter($queue->getPriority()))
->setValue('status', $qb->createNamedParameter($queue->getStatus()))
->setValue('tries', $qb->createNamedParameter($queue->getTries()))
->setValue('last', $qb->createNamedParameter($queue->getLast()));
$qb->execute();
}
/**
* return Actor from database based on the username
*
* @param string $token
* @param int $status
*
* @return RequestQueue[]
*/
public function getFromToken(string $token, int $status = -1): array {
$qb = $this->getQueueSelectSql();
$this->limitToToken($qb, $token);
if ($status > -1) {
$this->limitToStatus($qb, $status);
}
$this->orderByPriority($qb);
$requests = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$requests[] = $this->parseQueueSelectSql($data);
}
$cursor->closeCursor();
return $requests;
}
/**
* @param RequestQueue $queue
*
* @throws QueueStatusException
*/
public function setAsRunning(RequestQueue &$queue) {
$qb = $this->getQueueUpdateSql();
$qb->set('status', $qb->createNamedParameter(RequestQueue::STATUS_RUNNING))
->set(
'last',
$qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
);
$this->limitToId($qb, $queue->getId());
$this->limitToStatus($qb, RequestQueue::STATUS_STANDBY);
$count = $qb->execute();
if ($count === 0) {
throw new QueueStatusException();
}
$queue->setStatus(RequestQueue::STATUS_RUNNING);
}
/**
* @param RequestQueue $queue
*
* @throws QueueStatusException
*/
public function setAsSuccess(RequestQueue &$queue) {
$qb = $this->getQueueUpdateSql();
$qb->set('status', $qb->createNamedParameter(RequestQueue::STATUS_SUCCESS));
$this->limitToId($qb, $queue->getId());
$this->limitToStatus($qb, RequestQueue::STATUS_RUNNING);
$count = $qb->execute();
if ($count === 0) {
throw new QueueStatusException();
}
$queue->setStatus(RequestQueue::STATUS_SUCCESS);
}
/**
* @param RequestQueue $queue >ll
*
* @throws QueueStatusException
*/
public function setAsFailure(RequestQueue &$queue) {
$qb = $this->getQueueUpdateSql();
$qb->set('status', $qb->createNamedParameter(RequestQueue::STATUS_STANDBY));
// TODO - increment tries++
// ->set('tries', 'tries+1');
$this->limitToId($qb, $queue->getId());
$this->limitToStatus($qb, RequestQueue::STATUS_RUNNING);
$count = $qb->execute();
if ($count === 0) {
throw new QueueStatusException();
}
$queue->setStatus(RequestQueue::STATUS_SUCCESS);
}
}

Wyświetl plik

@ -0,0 +1,116 @@
<?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\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Model\RequestQueue;
use OCP\DB\QueryBuilder\IQueryBuilder;
class RequestQueueRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getQueueInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_REQUEST_QUEUE);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getQueueUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_REQUEST_QUEUE);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function getQueueSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'rq.id', 'rq.token', 'rq.author', 'rq.activity', 'rq.instance', 'rq.priority',
'rq.status', 'rq.tries', 'rq.last'
)
->from(self::TABLE_REQUEST_QUEUE, 'rq');
$this->defaultSelectAlias = 'rq';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getQueueDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_REQUEST_QUEUE);
return $qb;
}
/**
* @param array $data
*
* @return RequestQueue
*/
protected function parseQueueSelectSql($data): RequestQueue {
$queue = new RequestQueue();
$queue->importFromDatabase($data);
return $queue;
}
}

Wyświetl plik

@ -0,0 +1,8 @@
<?php
namespace OCA\Social\Exceptions;
class EmptyQueueException extends \Exception {
}

Wyświetl plik

@ -0,0 +1,8 @@
<?php
namespace OCA\Social\Exceptions;
class NoHighPriorityRequestException extends \Exception {
}

Wyświetl plik

@ -0,0 +1,8 @@
<?php
namespace OCA\Social\Exceptions;
class QueueStatusException extends \Exception {
}

Wyświetl plik

@ -36,7 +36,7 @@ use JsonSerializable;
use OCA\Social\Exceptions\ActivityCantBeVerifiedException; use OCA\Social\Exceptions\ActivityCantBeVerifiedException;
use OCA\Social\Exceptions\UrlCloudException; use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\InstancePath; use OCA\Social\Model\InstancePath;
use OCA\Social\Service\ICoreService; use OCA\Social\Service\ActivityPub\ICoreService;
abstract class ACore implements JsonSerializable { abstract class ACore implements JsonSerializable {

Wyświetl plik

@ -44,11 +44,17 @@ class InstancePath implements JsonSerializable {
use TArrayTools; use TArrayTools;
const TYPE_PUBLIC = 0; const TYPE_PUBLIC = 0;
const TYPE_INBOX = 1; const TYPE_INBOX = 1;
const TYPE_GLOBAL = 2; const TYPE_GLOBAL = 2;
const TYPE_FOLLOWERS = 3; const TYPE_FOLLOWERS = 3;
const PRIORITY_NONE = 0;
const PRIORITY_LOW = 1;
const PRIORITY_MEDIUM = 2;
const PRIORITY_HIGH = 3;
const PRIORITY_TOP = 4;
/** @var string */ /** @var string */
private $uri = ''; private $uri = '';
@ -56,19 +62,35 @@ class InstancePath implements JsonSerializable {
/** @var int */ /** @var int */
private $type = 0; private $type = 0;
/** @var int */
private $priority = 0;
/** /**
* InstancePath constructor. * InstancePath constructor.
* *
* @param string $uri * @param string $uri
* @param int $type * @param int $type
* @param int $priority
*/ */
public function __construct(string $uri = '', int $type = 0) { public function __construct(string $uri = '', int $type = 0, int $priority = 0) {
$this->uri = $uri; $this->uri = $uri;
$this->type = $type; $this->type = $type;
$this->priority = $priority;
} }
/**
* @param string $uri
*
* @return InstancePath
*/
public function setUri(string $uri): InstancePath {
$this->uri = $uri;
return $this;
}
/** /**
* @return string * @return string
*/ */
@ -77,6 +99,17 @@ class InstancePath implements JsonSerializable {
} }
/**
* @param int $type
*
* @return InstancePath
*/
public function setType(int $type): InstancePath {
$this->type = $type;
return $this;
}
/** /**
* @return int * @return int
*/ */
@ -85,11 +118,30 @@ class InstancePath implements JsonSerializable {
} }
/**
* @return int
*/
public function getPriority(): int {
return $this->priority;
}
/**
* @param int $priority
*
* @return InstancePath
*/
public function setPriority(int $priority): InstancePath {
$this->priority = $priority;
return $this;
}
/** /**
* @return string * @return string
*/ */
public function getAddress(): string { public function getAddress(): string {
$info = parse_url($this->uri); $info = parse_url($this->getUri());
return $this->get('host', $info, ''); return $this->get('host', $info, '');
} }
@ -99,15 +151,19 @@ class InstancePath implements JsonSerializable {
* @return string * @return string
*/ */
public function getPath(): string { public function getPath(): string {
$info = parse_url($this->uri); $info = parse_url($this->getUri());
return $this->get('path', $info, ''); return $this->get('path', $info, '');
} }
/**
* @param array $data
*/
public function import(array $data) { public function import(array $data) {
$this->setUri($this->get('uri', $data, ''));
$this->setType($this->getInt('type', $data, 0));
$this->setPriority($this->getInt('priority', $data, 0));
} }
@ -117,7 +173,8 @@ class InstancePath implements JsonSerializable {
public function jsonSerialize(): array { public function jsonSerialize(): array {
return [ return [
'uri' => $this->getUri(), 'uri' => $this->getUri(),
'type' => $this->getType() 'type' => $this->getType(),
'priority' => $this->getPriority()
]; ];
} }

Wyświetl plik

@ -0,0 +1,323 @@
<?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\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use JsonSerializable;
/**
* Class RequestQueue
*
* @package OCA\Social\Model
*/
class RequestQueue implements JsonSerializable {
use TArrayTools;
const STATUS_STANDBY = 0;
const STATUS_RUNNING = 1;
const STATUS_SUCCESS = 9;
/** @var integer */
private $id = 0;
/** @var string */
private $token = '';
/** @var string */
private $author = '';
/** @var string */
private $activity = '';
/** @var InstancePath */
private $instance;
/** @var int */
private $priority = 0;
/** @var int */
private $status = 0;
/** @var int */
private $tries = 0;
/** @var int */
private $last = 0;
/**
* RequestQueue constructor.
*
* @param string $activity
* @param InstancePath $instance
* @param string $author
*/
public function __construct(string $activity = '', $instance = null, string $author = '') {
$this->setActivity($activity);
if ($instance instanceof InstancePath) {
$this->setInstance($instance);
}
$this->setAuthor($author);
$this->resetToken();
}
/**
* @return int
*/
public function getId(): int {
return $this->id;
}
/**
* @param int $id
*
* @return RequestQueue
*/
public function setId(int $id): RequestQueue {
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getToken(): string {
return $this->token;
}
/**
* @param string $token
*
* @return RequestQueue
*/
public function setToken(string $token): RequestQueue {
$this->token = $token;
return $this;
}
/**
* @return string
*/
public function getAuthor(): string {
return $this->author;
}
/**
* @param string $author
*
* @return RequestQueue
*/
public function setAuthor(string $author): RequestQueue {
$this->author = $author;
return $this;
}
/**
* @return RequestQueue
*/
public function resetToken(): RequestQueue {
$uuid = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$this->setToken($uuid);
return $this;
}
/**
* @return string
*/
public function getActivity(): string {
return $this->activity;
}
/**
* @param string $activity
*
* @return RequestQueue
*/
public function setActivity(string $activity): RequestQueue {
$this->activity = $activity;
return $this;
}
/**
* @return InstancePath
*/
public function getInstance(): InstancePath {
return $this->instance;
}
/**
* @param InstancePath $instance
*
* @return RequestQueue
*/
public function setInstance(InstancePath $instance): RequestQueue {
$this->setPriority($instance->getPriority());
$this->instance = $instance;
return $this;
}
/**
* @return int
*/
public function getPriority(): int {
return $this->priority;
}
/**
* @param int $priority
*
* @return RequestQueue
*/
public function setPriority(int $priority): RequestQueue {
$this->priority = $priority;
return $this;
}
/**
* @return int
*/
public function getStatus(): int {
return $this->status;
}
/**
* @param int $status
*
* @return RequestQueue
*/
public function setStatus(int $status): RequestQueue {
$this->status = $status;
return $this;
}
/**
* @return int
*/
public function getTries(): int {
return $this->tries;
}
/**
* @param int $tries
*
* @return RequestQueue
*/
public function setTries(int $tries): RequestQueue {
$this->tries = $tries;
return $this;
}
/**
* @return int
*/
public function getLast(): int {
return $this->last;
}
/**
* @param int $last
*
* @return RequestQueue
*/
public function setLast(int $last): RequestQueue {
$this->last = $last;
return $this;
}
/**
* @param array $data
*/
public function importFromDatabase(array $data) {
$instance = new InstancePath();
$instance->import(json_decode($this->get('instance', $data, '{}'), true));
$this->setId($this->getInt('id', $data, 0));
$this->setToken($this->get('token', $data, ''));
$this->setAuthor($this->get('author', $data, ''));
$this->setInstance($instance);
$this->setPriority($this->getInt('priority', $data, 0));
$this->setActivity($this->get('activity', $data, ''));
$this->setStatus($this->getInt('status', $data, 0));
$this->setTries($this->getInt('tries', $data, 0));
$this->setLast($this->getInt('last', $data, 0));
}
/**
* @return array
*/
public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'token' => $this->getToken(),
'author' => $this->getAuthor(),
'instance' => $this->getInstance(),
'priority' => $this->getPriority(),
'status' => $this->getStatus(),
'tries' => $this->getTries(),
'last' => $this->getLast()
];
}
}

Wyświetl plik

@ -38,7 +38,6 @@ use OCA\Social\Exceptions\UnknownItemException;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Delete; use OCA\Social\Model\ActivityPub\Activity\Delete;
use OCA\Social\Service\ActivityService; use OCA\Social\Service\ActivityService;
use OCA\Social\Service\ICoreService;
use OCA\Social\Service\MiscService; use OCA\Social\Service\MiscService;

Wyświetl plik

@ -39,6 +39,7 @@ use OCA\Social\Exceptions\FollowDoesNotExistException;
use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Accept; use OCA\Social\Model\ActivityPub\Activity\Accept;
use OCA\Social\Model\ActivityPub\Activity\Reject; use OCA\Social\Model\ActivityPub\Activity\Reject;
@ -49,7 +50,6 @@ use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Model\InstancePath; use OCA\Social\Model\InstancePath;
use OCA\Social\Service\ActivityService; use OCA\Social\Service\ActivityService;
use OCA\Social\Service\ConfigService; use OCA\Social\Service\ConfigService;
use OCA\Social\Service\ICoreService;
use OCA\Social\Service\MiscService; use OCA\Social\Service\MiscService;
@ -103,6 +103,7 @@ class FollowService implements ICoreService {
* @throws SocialAppConfigException * @throws SocialAppConfigException
* @throws CacheActorDoesNotExistException * @throws CacheActorDoesNotExistException
* @throws InvalidResourceException * @throws InvalidResourceException
* @throws UrlCloudException
*/ */
public function followAccount(Person $actor, string $account) { public function followAccount(Person $actor, string $account) {
$remoteActor = $this->personService->getFromAccount($account); $remoteActor = $this->personService->getFromAccount($account);
@ -118,9 +119,11 @@ class FollowService implements ICoreService {
$this->followsRequest->save($follow); $this->followsRequest->save($follow);
$follow->addInstancePath( $follow->addInstancePath(
new InstancePath($remoteActor->getInbox(), InstancePath::TYPE_INBOX) new InstancePath(
$remoteActor->getInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_TOP
)
); );
$this->activityService->manageRequest($follow); $this->activityService->request($follow);
} }
} }
@ -132,6 +135,8 @@ class FollowService implements ICoreService {
* @throws CacheActorDoesNotExistException * @throws CacheActorDoesNotExistException
* @throws InvalidResourceException * @throws InvalidResourceException
* @throws RequestException * @throws RequestException
* @throws SocialAppConfigException
* @throws UrlCloudException
*/ */
public function unfollowAccount(Person $actor, string $account) { public function unfollowAccount(Person $actor, string $account) {
$remoteActor = $this->personService->getFromAccount($account); $remoteActor = $this->personService->getFromAccount($account);
@ -173,12 +178,14 @@ class FollowService implements ICoreService {
$accept->setObject($follow); $accept->setObject($follow);
$accept->addInstancePath( $accept->addInstancePath(
new InstancePath($remoteActor->getInbox(), InstancePath::TYPE_INBOX) new InstancePath(
$remoteActor->getInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_TOP
)
); );
$follow->setParent($accept); $follow->setParent($accept);
$this->activityService->manageRequest($accept); $this->activityService->request($accept);
$this->followsRequest->accepted($follow); $this->followsRequest->accepted($follow);
} catch (Exception $e) { } catch (Exception $e) {
} }

Wyświetl plik

@ -28,7 +28,7 @@ declare(strict_types=1);
*/ */
namespace OCA\Social\Service; namespace OCA\Social\Service\ActivityPub;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;

Wyświetl plik

@ -47,7 +47,6 @@ use OCA\Social\Service\ActivityService;
use OCA\Social\Service\ActorService; use OCA\Social\Service\ActorService;
use OCA\Social\Service\ConfigService; use OCA\Social\Service\ConfigService;
use OCA\Social\Service\CurlService; use OCA\Social\Service\CurlService;
use OCA\Social\Service\ICoreService;
use OCA\Social\Service\MiscService; use OCA\Social\Service\MiscService;
class NoteService implements ICoreService { class NoteService implements ICoreService {
@ -150,7 +149,7 @@ class NoteService implements ICoreService {
case self::TYPE_UNLISTED: case self::TYPE_UNLISTED:
$note->setTo($actor->getFollowers()); $note->setTo($actor->getFollowers());
$note->addInstancePath( $note->addInstancePath(
new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS) new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS, InstancePath::PRIORITY_LOW)
); );
$note->addCc(ActivityService::TO_PUBLIC); $note->addCc(ActivityService::TO_PUBLIC);
break; break;
@ -158,7 +157,7 @@ class NoteService implements ICoreService {
case self::TYPE_FOLLOWERS: case self::TYPE_FOLLOWERS:
$note->setTo($actor->getFollowers()); $note->setTo($actor->getFollowers());
$note->addInstancePath( $note->addInstancePath(
new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS) new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS, InstancePath::PRIORITY_LOW)
); );
break; break;
@ -169,7 +168,7 @@ class NoteService implements ICoreService {
$note->setTo(ActivityService::TO_PUBLIC); $note->setTo(ActivityService::TO_PUBLIC);
$note->addCc($actor->getFollowers()); $note->addCc($actor->getFollowers());
$note->addInstancePath( $note->addInstancePath(
new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS) new InstancePath($actor->getFollowers(), InstancePath::TYPE_FOLLOWERS, InstancePath::PRIORITY_LOW)
); );
break; break;
} }
@ -192,7 +191,9 @@ class NoteService implements ICoreService {
return; return;
} }
$instancePath = new InstancePath($actor->getInbox(), InstancePath::TYPE_INBOX, InstancePath::PRIORITY_MEDIUM);
if ($type === self::TYPE_DIRECT) { if ($type === self::TYPE_DIRECT) {
$instancePath->setPriority(InstancePath::PRIORITY_HIGH);
$note->addToArray($actor->getId()); $note->addToArray($actor->getId());
} else { } else {
$note->addCc($actor->getId()); $note->addCc($actor->getId());
@ -205,7 +206,7 @@ class NoteService implements ICoreService {
] ]
); );
$note->addInstancePath(new InstancePath($actor->getInbox(), InstancePath::TYPE_INBOX)); $note->addInstancePath($instancePath);
} }
@ -213,8 +214,6 @@ class NoteService implements ICoreService {
* @param Note $note * @param Note $note
* @param string $type * @param string $type
* @param array $accounts * @param array $accounts
*
* @throws RequestException
*/ */
public function addRecipients(Note $note, string $type, array $accounts) { public function addRecipients(Note $note, string $type, array $accounts) {
if ($accounts === []) { if ($accounts === []) {
@ -237,7 +236,8 @@ class NoteService implements ICoreService {
} }
$note->setInReplyTo($replyTo); $note->setInReplyTo($replyTo);
$note->addInstancePath(new InstancePath($replyTo)); // TODO - type can be NOT public !
$note->addInstancePath(new InstancePath($replyTo, InstancePath::TYPE_PUBLIC, InstancePath::PRIORITY_HIGH));
} }

Wyświetl plik

@ -44,7 +44,6 @@ use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Person; use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Service\ConfigService; use OCA\Social\Service\ConfigService;
use OCA\Social\Service\ICoreService;
use OCA\Social\Service\InstanceService; use OCA\Social\Service\InstanceService;
use OCA\Social\Service\MiscService; use OCA\Social\Service\MiscService;

Wyświetl plik

@ -33,7 +33,6 @@ namespace OCA\Social\Service\ActivityPub;
use Exception; use Exception;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Service\ICoreService;
use OCA\Social\Service\MiscService; use OCA\Social\Service\MiscService;

Wyświetl plik

@ -39,16 +39,21 @@ use OCA\Social\Db\ActorsRequest;
use OCA\Social\Db\FollowsRequest; use OCA\Social\Db\FollowsRequest;
use OCA\Social\Db\NotesRequest; use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\ActorDoesNotExistException; use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\EmptyQueueException;
use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\NoHighPriorityRequestException;
use OCA\Social\Exceptions\QueueStatusException;
use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SignatureException; use OCA\Social\Exceptions\SignatureException;
use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Create; use OCA\Social\Model\ActivityPub\Activity\Create;
use OCA\Social\Model\ActivityPub\Activity\Delete; use OCA\Social\Model\ActivityPub\Activity\Delete;
use OCA\Social\Model\ActivityPub\Activity\Tombstone; use OCA\Social\Model\ActivityPub\Activity\Tombstone;
use OCA\Social\Model\ActivityPub\Person; use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Model\InstancePath; use OCA\Social\Model\InstancePath;
use OCA\Social\Model\RequestQueue;
use OCA\Social\Service\ActivityPub\PersonService; use OCA\Social\Service\ActivityPub\PersonService;
use OCP\IRequest; use OCP\IRequest;
@ -68,6 +73,7 @@ class ActivityService {
const DATE_FORMAT = 'D, d M Y H:i:s T'; const DATE_FORMAT = 'D, d M Y H:i:s T';
const DATE_DELAY = 30; const DATE_DELAY = 30;
/** @var ActorsRequest */ /** @var ActorsRequest */
private $actorsRequest; private $actorsRequest;
@ -77,6 +83,9 @@ class ActivityService {
/** @var FollowsRequest */ /** @var FollowsRequest */
private $followsRequest; private $followsRequest;
/** @var QueueService */
private $queueService;
/** @var ActorService */ /** @var ActorService */
private $actorService; private $actorService;
@ -99,6 +108,7 @@ class ActivityService {
/** /**
* ActivityService constructor. * ActivityService constructor.
* *
* @param QueueService $queueService
* @param ActorsRequest $actorsRequest * @param ActorsRequest $actorsRequest
* @param NotesRequest $notesRequest * @param NotesRequest $notesRequest
* @param FollowsRequest $followsRequest * @param FollowsRequest $followsRequest
@ -111,15 +121,15 @@ class ActivityService {
*/ */
public function __construct( public function __construct(
ActorsRequest $actorsRequest, NotesRequest $notesRequest, FollowsRequest $followsRequest, ActorsRequest $actorsRequest, NotesRequest $notesRequest, FollowsRequest $followsRequest,
CurlService $curlService, ActorService $actorService, QueueService $queueService, CurlService $curlService, ActorService $actorService,
PersonService $personService, InstanceService $instanceService, PersonService $personService, InstanceService $instanceService,
ConfigService $configService, ConfigService $configService, MiscService $miscService
MiscService $miscService
) { ) {
$this->curlService = $curlService;
$this->actorsRequest = $actorsRequest; $this->actorsRequest = $actorsRequest;
$this->notesRequest = $notesRequest; $this->notesRequest = $notesRequest;
$this->followsRequest = $followsRequest; $this->followsRequest = $followsRequest;
$this->queueService = $queueService;
$this->curlService = $curlService;
$this->actorService = $actorService; $this->actorService = $actorService;
$this->personService = $personService; $this->personService = $personService;
$this->instanceService = $instanceService; $this->instanceService = $instanceService;
@ -133,13 +143,11 @@ class ActivityService {
* @param ACore $item * @param ACore $item
* @param ACore $activity * @param ACore $activity
* *
* @return array * @return string
* @throws RequestException * @throws Exception
* @throws SocialAppConfigException
* @throws ActorDoesNotExistException
*/ */
public function createActivity(Person $actor, ACore $item, ACore &$activity = null public function createActivity(Person $actor, ACore $item, ACore &$activity = null
): array { ): string {
$activity = new Create(); $activity = new Create();
$item->setParent($activity); $item->setParent($activity);
@ -158,20 +166,17 @@ class ActivityService {
$activity->setActor($actor); $activity->setActor($actor);
$result = $this->request($activity); return $this->request($activity);
return $result;
} }
/** /**
* @param ACore $item * @param ACore $item
* *
* @throws ActorDoesNotExistException * @return string
* @throws RequestException * @throws Exception
* @throws SocialAppConfigException
*/ */
public function deleteActivity(ACore $item) { public function deleteActivity(ACore $item): string {
$delete = new Delete(); $delete = new Delete();
$delete->setId($item->getId() . '#delete'); $delete->setId($item->getId() . '#delete');
$delete->setActorId($item->getActorId()); $delete->setActorId($item->getActorId());
@ -182,7 +187,7 @@ class ActivityService {
$delete->setObject($tombstone); $delete->setObject($tombstone);
$delete->addInstancePaths($item->getInstancePaths()); $delete->addInstancePaths($item->getInstancePaths());
$this->request($delete); return $this->request($delete);
} }
@ -217,61 +222,91 @@ class ActivityService {
/** /**
* @param ACore $activity * @param ACore $activity
* *
* @return string
* @throws Exception
*/
public function request(ACore $activity): string {
$this->setupCore($activity);
$author = $this->getAuthorFromItem($activity);
$instancePaths = $this->generateInstancePaths($activity);
$token = $this->queueService->generateRequestQueue($instancePaths, $activity, $author);
try {
$directRequest = $this->queueService->getPriorityRequest($token);
$this->manageRequest($directRequest);
} catch (NoHighPriorityRequestException $e) {
} catch (EmptyQueueException $e) {
return '';
}
$this->curlService->asyncWithToken($token);
return $token;
}
/**
* @param RequestQueue $queue
*
* @throws ActorDoesNotExistException
* @throws RequestException * @throws RequestException
* @throws SocialAppConfigException * @throws SocialAppConfigException
* @throws ActorDoesNotExistException
*/ */
public function manageRequest(ACore $activity) { public function manageRequest(RequestQueue $queue) {
$result = $this->request($activity);
$this->miscService->log('Activity: ' . json_encode($activity)); try {
$this->miscService->log('Result: ' . json_encode($result)); $this->queueService->initRequest($queue);
} catch (QueueStatusException $e) {
return;
}
$result = $this->generateRequest(
$queue->getInstance(), $queue->getActivity(), $queue->getAuthor()
);
try {
if ($this->getint('_code', $result, 500) === 202) {
$this->queueService->endRequest($queue, true);
} else {
$this->queueService->endRequest($queue, false);
}
} catch (QueueStatusException $e) {
}
} }
/** /**
* @param ACore $activity * @param ACore $activity
* *
* * @return InstancePath[]
* @return array
* @throws RequestException
* @throws SocialAppConfigException
* @throws ActorDoesNotExistException
*/ */
public function request(ACore &$activity) { private function generateInstancePaths(ACore $activity): array {
$this->setupCore($activity); $instancePaths = [];
// $hosts = $this->instanceService->getInstancesFromActivity($activity);
$result = [];
// foreach ($hosts as $host) {
// foreach ($host->getInstancePaths() as $path) {
foreach ($activity->getInstancePaths() as $instancePath) { foreach ($activity->getInstancePaths() as $instancePath) {
if ($instancePath->getType() === InstancePath::TYPE_FOLLOWERS) { if ($instancePath->getType() === InstancePath::TYPE_FOLLOWERS) {
$result = array_merge($result, $this->requestToFollowers($activity, $instancePath)); $instancePaths = array_merge(
$instancePaths, $this->generateInstancePathsFollowers($instancePath)
);
} else { } else {
$result[] = $this->generateRequest($instancePath, $activity); $instancePaths[] = $instancePath;
} }
} }
// } return $instancePaths;
return $result;
} }
/** /**
* @param ACore $activity
* @param InstancePath $instancePath * @param InstancePath $instancePath
* *
* @return array * @return InstancePath[]
* @throws ActorDoesNotExistException
* @throws RequestException
* @throws SocialAppConfigException
*/ */
private function requestToFollowers(ACore &$activity, InstancePath $instancePath): array { private function generateInstancePathsFollowers(InstancePath $instancePath): array {
$result = []; $follows = $this->followsRequest->getByFollowId($instancePath->getUri());
$sharedInboxes = []; $sharedInboxes = [];
$follows = $this->followsRequest->getByFollowId($instancePath->getUri()); $instancePaths = [];
foreach ($follows as $follow) { foreach ($follows as $follow) {
if (!$follow->gotActor()) { if (!$follow->gotActor()) {
// TODO - check if cache can be empty at this point ? // TODO - check if cache can be empty at this point ?
@ -285,28 +320,32 @@ class ActivityService {
} }
$sharedInboxes[] = $sharedInbox; $sharedInboxes[] = $sharedInbox;
$result[] = $this->generateRequest( $instancePaths[] = new InstancePath(
new InstancePath($sharedInbox, InstancePath::TYPE_GLOBAL), $activity $sharedInbox, InstancePath::TYPE_GLOBAL, $instancePath->getPriority()
); );
// $result[] = $this->generateRequest(
// new InstancePath($sharedInbox, InstancePath::TYPE_GLOBAL), $activity
// );
} }
return $result; return $instancePaths;
} }
/** /**
* @param InstancePath $path * @param InstancePath $path
* @param ACore $activity * @param string $activity
* @param string $author
* *
* @return Request[] * @return Request[]
* @throws ActorDoesNotExistException * @throws ActorDoesNotExistException
* @throws RequestException * @throws RequestException
* @throws SocialAppConfigException * @throws SocialAppConfigException
*/ */
public function generateRequest(InstancePath $path, ACore $activity): array { public function generateRequest(InstancePath $path, string $activity, string $author): array {
$document = json_encode($activity); // $document = json_encode($activity);
$date = gmdate(self::DATE_FORMAT); $date = gmdate(self::DATE_FORMAT);
$localActor = $this->getActorFromItem($activity); $localActor = $this->getActorFromAuthor($author);
$localActorLink = $localActorLink =
$this->configService->getUrlSocial() . '@' . $localActor->getPreferredUsername(); $this->configService->getUrlSocial() . '@' . $localActor->getPreferredUsername();
@ -332,7 +371,7 @@ class ActivityService {
$request->addHeader('Date: ' . $date); $request->addHeader('Date: ' . $date);
$request->addHeader('Signature: ' . $header); $request->addHeader('Signature: ' . $header);
$request->setDataJson($document); $request->setDataJson($activity);
$request->setAddress($path->getAddress()); $request->setAddress($path->getAddress());
return $this->curlService->request($request); return $this->curlService->request($request);
@ -362,18 +401,27 @@ class ActivityService {
/** /**
* @param ACore $activity * @param ACore $activity
* *
* @return Person * @return string
* @throws SocialAppConfigException
* @throws ActorDoesNotExistException
*/ */
private function getActorFromItem(Acore $activity): Person { private function getAuthorFromItem(Acore $activity): string {
if ($activity->gotActor()) { if ($activity->gotActor()) {
return $activity->getActor(); return $activity->getActor()
->getId();
} }
$actorId = $activity->getActorId(); return $activity->getActorId();
}
return $this->actorService->getActorById($actorId);
/**
* @param string $author
*
* @return Person
* @throws ActorDoesNotExistException
* @throws SocialAppConfigException
*/
private function getActorFromAuthor(string $author): Person {
return $this->actorService->getActorById($author);
} }
@ -456,8 +504,10 @@ class ActivityService {
* @param $keyId * @param $keyId
* *
* @return string * @return string
* @throws RequestException
* @throws InvalidResourceException * @throws InvalidResourceException
* @throws RequestException
* @throws SocialAppConfigException
* @throws UrlCloudException
*/ */
private function retrieveKey($keyId): string { private function retrieveKey($keyId): string {
$actor = $this->personService->getFromId($keyId); $actor = $this->personService->getFromId($keyId);
@ -467,6 +517,8 @@ class ActivityService {
/** /**
* @deprecated !??? - do we need this !?
*
* @param ACore $activity * @param ACore $activity
*/ */
private function setupCore(ACore $activity) { private function setupCore(ACore $activity) {

Wyświetl plik

@ -52,11 +52,18 @@ class ConfigService {
const SOCIAL_ADDRESS = 'address'; const SOCIAL_ADDRESS = 'address';
const SOCIAL_SERVICE = 'service';
const SOCIAL_MAX_SIZE = 'max_size'; const SOCIAL_MAX_SIZE = 'max_size';
const BACKGROUND_CRON = 1;
const BACKGROUND_ASYNC = 2;
const BACKGROUND_SERVICE = 3;
const BACKGROUND_FULL_SERVICE = 4;
/** @var array */ /** @var array */
public $defaults = [ public $defaults = [
self::SOCIAL_ADDRESS => '', self::SOCIAL_ADDRESS => '',
self::SOCIAL_SERVICE => 1,
self::SOCIAL_MAX_SIZE => 25 self::SOCIAL_MAX_SIZE => 25
]; ];

Wyświetl plik

@ -31,11 +31,24 @@ namespace OCA\Social\Service;
use daita\MySmallPhpTools\Model\Request; use daita\MySmallPhpTools\Model\Request;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
class CurlService { class CurlService {
use TArrayTools;
use TPathTools;
const ASYNC_TOKEN = '/async/token/{token}';
/** @var ConfigService */
private $configService;
/** @var MiscService */ /** @var MiscService */
private $miscService; private $miscService;
@ -43,9 +56,11 @@ class CurlService {
/** /**
* CurlService constructor. * CurlService constructor.
* *
* @param ConfigService $configService
* @param MiscService $miscService * @param MiscService $miscService
*/ */
public function __construct(MiscService $miscService) { public function __construct(ConfigService $configService, MiscService $miscService) {
$this->configService = $configService;
$this->miscService = $miscService; $this->miscService = $miscService;
} }
@ -89,6 +104,29 @@ class CurlService {
} }
/**
* @param string $token
*
* @throws SocialAppConfigException
*/
public function asyncWithToken(string $token) {
$address = $this->configService->getUrlSocial();
$parse = parse_url($address);
$host = $this->get('host', $parse, '');
$path = $this->withEndSlash($this->get('path', $parse, '')) . $this->withoutBeginSlash(
self::ASYNC_TOKEN
);
$path = str_replace('{token}', $token, $path);
$request = new Request($path, Request::TYPE_POST);
$request->setAddress($host);
try {
$this->request($request);
} catch (RequestException $e) {
}
}
/** /**
* @param Request $request * @param Request $request
* *

Wyświetl plik

@ -71,6 +71,7 @@ class ImportService {
/** @var DeleteService */ /** @var DeleteService */
private $deleteService; private $deleteService;
/** @var ConfigService */
private $configService; private $configService;
/** @var MiscService */ /** @var MiscService */

Wyświetl plik

@ -30,6 +30,7 @@ declare(strict_types=1);
namespace OCA\Social\Service; namespace OCA\Social\Service;
use Exception;
use OC\User\NoUserException; use OC\User\NoUserException;
use OCA\Social\Exceptions\ActorDoesNotExistException; use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException; use OCA\Social\Exceptions\RequestException;
@ -77,20 +78,20 @@ class PostService {
* @param Post $post * @param Post $post
* @param ACore $activity * @param ACore $activity
* *
* @return array * @return string
* @throws ActorDoesNotExistException * @throws ActorDoesNotExistException
* @throws NoUserException * @throws NoUserException
* @throws RequestException
* @throws SocialAppConfigException * @throws SocialAppConfigException
* @throws Exception
*/ */
public function createPost(Post $post, ACore &$activity = null) { public function createPost(Post $post, ACore &$activity = null): string {
$note = $note =
$this->noteService->generateNote( $this->noteService->generateNote(
$post->getUserId(), $post->getContent(), $post->getType() $post->getUserId(), $post->getContent(), $post->getType()
); );
$this->noteService->addRecipients($note, $post->getType(), $post->getTo());
$this->noteService->replyTo($note, $post->getReplyTo()); $this->noteService->replyTo($note, $post->getReplyTo());
$this->noteService->addRecipients($note, $post->getType(), $post->getTo());
$actor = $this->actorService->getActorFromUserId($post->getUserId()); $actor = $this->actorService->getActorFromUserId($post->getUserId());

Wyświetl plik

@ -0,0 +1,191 @@
<?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\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
use OCA\Social\Db\RequestQueueRequest;
use OCA\Social\Exceptions\EmptyQueueException;
use OCA\Social\Exceptions\NoHighPriorityRequestException;
use OCA\Social\Exceptions\QueueStatusException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\InstancePath;
use OCA\Social\Model\RequestQueue;
class QueueService {
use TArrayTools;
/** @var RequestQueueRequest */
private $requestQueueRequest;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/**
* QueueService constructor.
*
* @param RequestQueueRequest $requestQueueRequest
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
RequestQueueRequest $requestQueueRequest, ConfigService $configService,
MiscService $miscService
) {
$this->requestQueueRequest = $requestQueueRequest;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @param array $instancePaths
* @param ACore $item
*
* @return string
* @throws Exception
*/
public function generateRequestQueue(array $instancePaths, ACore $item, string $author
): string {
$activity = json_encode($item, JSON_UNESCAPED_SLASHES);
$token = '';
$requests = [];
foreach ($instancePaths as $instancePath) {
$request = new RequestQueue($activity, $instancePath, $author);
if ($token === '') {
$token = $request->getToken();
} else {
$request->setToken($token);
}
$requests[] = $request;
}
$this->requestQueueRequest->multiple($requests);
return $token;
}
/**
* @param string $token
*
* @return RequestQueue
* @throws EmptyQueueException
* @throws NoHighPriorityRequestException
*/
public function getPriorityRequest(string $token): RequestQueue {
$requests = $this->requestQueueRequest->getFromToken($token);
if (sizeof($requests) === 0) {
throw new EmptyQueueException();
}
$request = $requests[0];
switch ($request->getPriority()) {
case InstancePath::PRIORITY_TOP:
return $request;
case InstancePath::PRIORITY_HIGH:
if (sizeof($requests) === 1) {
return $request;
}
$next = $requests[1];
if ($next->getStatus() < InstancePath::PRIORITY_HIGH) {
return $request;
}
break;
case InstancePath::PRIORITY_MEDIUM:
if (sizeof($requests) === 1) {
return $request;
}
break;
}
throw new NoHighPriorityRequestException();
}
/**
* @param string $token
* @param int $status
*
* @return array
*/
public function getRequestFromToken(string $token, int $status = -1): array {
if ($token === '') {
return [];
}
return $this->requestQueueRequest->getFromToken($token, $status);
}
/**
* @param RequestQueue $queue
*
* @throws QueueStatusException
*/
public function initRequest(RequestQueue $queue) {
$this->requestQueueRequest->setAsRunning($queue);
}
/**
* @param RequestQueue $queue
* @param bool $success
*
* @throws QueueStatusException
*/
public function endRequest(RequestQueue $queue, bool $success) {
if ($success === true) {
$this->requestQueueRequest->setAsSuccess($queue);
} else {
$this->requestQueueRequest->setAsFailure($queue);
}
}
}