diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index 4b57db4bac..c5cad7f2db 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -24,13 +24,16 @@ namespace OC\Notification; -use OC\RichObjectStrings\Validator; use OCP\Notification\IApp; use OCP\Notification\IManager; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\RichObjectStrings\IValidator; class Manager implements IManager { + /** @var IValidator */ + protected $validator; + /** @var IApp[] */ protected $apps; @@ -49,7 +52,13 @@ class Manager implements IManager { /** @var \Closure[] */ protected $notifiersInfoClosures; - public function __construct() { + /** + * Manager constructor. + * + * @param IValidator $validator + */ + public function __construct(IValidator $validator) { + $this->validator = $validator; $this->apps = []; $this->notifiers = []; $this->notifiersInfo = []; @@ -150,9 +159,7 @@ class Manager implements IManager { * @since 8.2.0 */ public function createNotification() { - return new Notification( - new Validator() - ); + return new Notification($this->validator); } /** @@ -214,7 +221,6 @@ class Manager implements IManager { /** * @param INotification $notification - * @return null */ public function markProcessed(INotification $notification) { $apps = $this->getApps(); diff --git a/lib/private/RichObjectStrings/Validator.php b/lib/private/RichObjectStrings/Validator.php index 2729b4a3f1..11d17fef64 100644 --- a/lib/private/RichObjectStrings/Validator.php +++ b/lib/private/RichObjectStrings/Validator.php @@ -22,6 +22,7 @@ namespace OC\RichObjectStrings; +use OCP\RichObjectStrings\Definitions; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; @@ -31,9 +32,9 @@ use OCP\RichObjectStrings\IValidator; * @package OCP\RichObjectStrings * @since 9.2.0 */ -class Validator implements IValidator { +class Validator implements IValidator { - /** @var array[] */ + /** @var Definitions */ protected $definitions; /** @var array[] */ @@ -41,9 +42,11 @@ class Validator implements IValidator { /** * Constructor + * + * @param Definitions $definitions */ - public function __construct() { - $this->definitions = json_decode(file_get_contents(__DIR__ . '/../../public/RichObjectStrings/definitions.json'), true); + public function __construct(Definitions $definitions) { + $this->definitions = $definitions; } /** @@ -76,11 +79,13 @@ class Validator implements IValidator { * @throws InvalidObjectExeption */ protected function validateParameter(array $parameter) { - if (!isset($parameter['type']) || !isset($this->definitions[$parameter['type']])) { + if (!isset($parameter['type'])) { throw new InvalidObjectExeption('Object type is undefined'); } - $requiredParameters = $this->getRequiredParameters($parameter['type']); + $definition = $this->definitions->getDefinition($parameter['type']); + $requiredParameters = $this->getRequiredParameters($parameter['type'], $definition); + $missingKeys = array_diff($requiredParameters, array_keys($parameter)); if (!empty($missingKeys)) { throw new InvalidObjectExeption('Object is invalid'); @@ -89,15 +94,16 @@ class Validator implements IValidator { /** * @param string $type + * @param array $definition * @return string[] */ - protected function getRequiredParameters($type) { + protected function getRequiredParameters($type, array $definition) { if (isset($this->requiredParameters[$type])) { return $this->requiredParameters[$type]; } $this->requiredParameters[$type] = []; - foreach ($this->definitions[$type]['parameters'] as $parameter => $data) { + foreach ($definition['parameters'] as $parameter => $data) { if ($data['required']) { $this->requiredParameters[$type][] = $parameter; } diff --git a/lib/private/Server.php b/lib/private/Server.php index 8f4e7d9ca2..79efeb18d1 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -72,6 +72,7 @@ use OC\Lock\NoopLockingProvider; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Notification\Manager; +use OC\RichObjectStrings\Validator; use OC\Security\Bruteforce\Throttler; use OC\Security\CertificateManager; use OC\Security\CSP\ContentSecurityPolicyManager; @@ -659,8 +660,10 @@ class Server extends ServerContainer implements IServerContainer { $c->getDatabaseConnection() ); }); - $this->registerService('NotificationManager', function () { - return new Manager(); + $this->registerService('NotificationManager', function (Server $c) { + return new Manager( + $c->query(Validator::class) + ); }); $this->registerService('CapabilitiesManager', function (Server $c) { $manager = new \OC\CapabilitiesManager($c->getLogger()); diff --git a/lib/public/RichObjectStrings/Definitions.php b/lib/public/RichObjectStrings/Definitions.php new file mode 100644 index 0000000000..d3e6b15cf9 --- /dev/null +++ b/lib/public/RichObjectStrings/Definitions.php @@ -0,0 +1,296 @@ + + * + * @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 . + * + */ + +namespace OCP\RichObjectStrings; + + +/** + * Class Definitions + * + * @package OCP\RichObjectStrings + * @since 9.2.0 + */ +class Definitions { + /** + * @var array + * @since 9.2.0 + */ + public $definitions = [ + 'addressbook' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the addressbook on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the addressbook which should be used in the visual representation', + 'example' => 'Contacts', + ], + ], + ], + 'addressbook-contact' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the contact on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the contact which should be used in the visual representation', + 'example' => 'John Doe', + ], + ], + ], + 'announcement' => [ + 'author' => 'Joas Schilling', + 'app' => 'announcementcenter', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, 'description' => 'The id used to identify the announcement on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The announcement subject which should be used in the visual representation', + 'example' => 'file.txt', + ], + 'link' => [ + 'since' => '9.2.0', + 'required' => false, + 'description' => 'The full URL to the file', + 'example' => 'http://localhost/index.php/apps/announcements/#23', + ], + ], + ], + 'calendar' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the calendar on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the calendar which should be used in the visual representation', + 'example' => 'Personal', + ], + ], + ], + 'calendar-event' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the event on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the event which should be used in the visual representation', + 'example' => 'Workout', + ], + ], + ], + 'email' => [ + 'author' => 'Nextcloud', + 'app' => 'sharebymail', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The mail-address used to identify the event on the instance', + 'example' => 'test@localhost', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of a matching contact or the email (fallback) which should be used in the visual representation', + 'example' => 'Foo Bar', + ], + ], + ], + 'file' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the file on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The file name which should be used in the visual representation', + 'example' => 'file.txt', + ], + 'path' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The full path of the file for the user', + 'example' => 'path/to/file.txt', + ], + 'link' => [ + 'since' => '9.2.0', + 'required' => false, + 'description' => 'The full URL to the file', + 'example' => 'http://localhost/index.php/f/42', + ], + ], + ], + 'pending-federated-share' => [ + 'author' => 'Nextcloud', + 'app' => 'dav', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the federated share on the instance', + 'example' => '42', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The name of the shared item which should be used in the visual representation', + 'example' => 'file.txt', + ], + ], + ], + 'systemtag' => [ + 'author' => 'Nextcloud', + 'app' => 'core', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the systemtag on the instance', + 'example' => '23', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the systemtag which should be used in the visual representation', + 'example' => 'Project 1', + ], + 'visibility' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'If the user can see the systemtag', + 'example' => '1', + ], + 'assignable' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'If the user can assign the systemtag', + 'example' => '0', + ], + ], + ], + 'user' => [ + 'author' => 'Nextcloud', + 'app' => 'core', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the user on the instance', + 'example' => 'johndoe', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the user which should be used in the visual representation', + 'example' => 'John Doe', + ], + 'server' => [ + 'since' => '9.2.0', + 'required' => false, + 'description' => 'The URL of the instance the user lives on', + 'example' => 'localhost', + ], + ], + ], + 'user-group' => [ + 'author' => 'Nextcloud', + 'app' => 'core', + 'since' => '9.2.0', + 'parameters' => [ + 'id' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The id used to identify the group on the instance', + 'example' => 'supportteam', + ], + 'name' => [ + 'since' => '9.2.0', + 'required' => true, + 'description' => 'The display name of the group which should be used in the visual representation', + 'example' => 'Support Team', + ], + ], + ], + ]; + + /** + * @param string $type + * @return array + * @throws InvalidObjectExeption + * @since 9.2.0 + */ + public function getDefinition($type) { + if (isset($this->definitions[$type])) { + return $this->definitions[$type]; + } + + throw new InvalidObjectExeption('Object type is undefined'); + } +} diff --git a/lib/public/RichObjectStrings/definitions.json b/lib/public/RichObjectStrings/definitions.json deleted file mode 100644 index 494e96c8f7..0000000000 --- a/lib/public/RichObjectStrings/definitions.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "addressbook": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the addressbook on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the addressbook which should be used in the visual representation", - "example": "Contacts" - } - } - }, - "addressbook-contact": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the contact on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the contact which should be used in the visual representation", - "example": "John Doe" - } - } - }, - "announcement": { - "author": "Joas Schilling", - "app": "announcementcenter", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the announcement on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The announcement subject which should be used in the visual representation", - "example": "file.txt" - }, - "link": { - "since": "9.2.0", - "required": false, - "description": "The full URL to the file", - "example": "http://localhost/index.php/apps/announcements/#23" - } - } - }, - "calendar": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the calendar on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the calendar which should be used in the visual representation", - "example": "Personal" - } - } - }, - "calendar-event": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the event on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the event which should be used in the visual representation", - "example": "Workout" - } - } - }, - "file": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the file on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The file name which should be used in the visual representation", - "example": "file.txt" - }, - "path": { - "since": "9.2.0", - "required": true, - "description": "The full path of the file for the user", - "example": "path/to/file.txt" - }, - "link": { - "since": "9.2.0", - "required": false, - "description": "The full URL to the file", - "example": "http://localhost/index.php/f/42" - } - } - }, - "pending-federated-share": { - "author": "Nextcloud", - "app": "dav", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the federated share on the instance", - "example": "42" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The name of the shared item which should be used in the visual representation", - "example": "file.txt" - } - } - }, - "systemtag": { - "author": "Nextcloud", - "app": "core", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the systemtag on the instance", - "example": "23" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the systemtag which should be used in the visual representation", - "example": "Project 1" - }, - "visibility": { - "since": "9.2.0", - "required": true, - "description": "If the user can see the systemtag", - "example": "1" - }, - "assignable": { - "since": "9.2.0", - "required": true, - "description": "If the user can assign the systemtag", - "example": "0" - } - } - }, - "user": { - "author": "Nextcloud", - "app": "core", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the user on the instance", - "example": "johndoe" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the user which should be used in the visual representation", - "example": "John Doe" - }, - "server": { - "since": "9.2.0", - "required": false, - "description": "The URL of the instance the user lives on", - "example": "localhost" - } - } - }, - "user-group": { - "author": "Nextcloud", - "app": "core", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The id used to identify the group on the instance", - "example": "supportteam" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of the group which should be used in the visual representation", - "example": "Support Team" - } - } - }, - "email": { - "author": "Nextcloud", - "app": "sharebymail", - "since": "9.2.0", - "parameters": { - "id": { - "since": "9.2.0", - "required": true, - "description": "The mail-address used to identify the event on the instance", - "example": "test@localhost" - }, - "name": { - "since": "9.2.0", - "required": true, - "description": "The display name of a matching contact or the email (fallback) which should be used in the visual representation", - "example": "Foo Bar" - } - } - } -} diff --git a/tests/lib/Notification/ManagerTest.php b/tests/lib/Notification/ManagerTest.php index 4410781bc3..e280838424 100644 --- a/tests/lib/Notification/ManagerTest.php +++ b/tests/lib/Notification/ManagerTest.php @@ -23,6 +23,7 @@ namespace Test\Notification; use OC\Notification\Manager; use OCP\Notification\IManager; +use OCP\RichObjectStrings\IValidator; use Test\TestCase; class ManagerTest extends TestCase { @@ -31,7 +32,8 @@ class ManagerTest extends TestCase { public function setUp() { parent::setUp(); - $this->manager = new Manager(); + $validator = $this->createMock(IValidator::class); + $this->manager = new Manager($validator); } public function testRegisterApp() { diff --git a/tests/lib/RichObjectStrings/DefinitionsTest.php b/tests/lib/RichObjectStrings/DefinitionsTest.php new file mode 100644 index 0000000000..e0d400bff2 --- /dev/null +++ b/tests/lib/RichObjectStrings/DefinitionsTest.php @@ -0,0 +1,93 @@ + + * + * @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 . + * + */ + +namespace Test\RichObjectStrings; + + +use OCP\RichObjectStrings\Definitions; +use Test\TestCase; + +class DefinitionsTest extends TestCase { + + public function dataGetDefinition() { + $definitions = new Definitions(); + $testsuite = []; + foreach ($definitions->definitions as $type => $definition) { + $testsuite[] = [$type, $definition]; + } + return $testsuite; + } + + /** + * @expectedException \OCP\RichObjectStrings\InvalidObjectExeption + * @expectedExceptionMessage Object type is undefined + */ + public function testGetDefinitionNotExisting() { + $definitions = new Definitions(); + $definitions->getDefinition('NotExistingType'); + } + + /** + * @dataProvider dataGetDefinition + * @param string $type + * @param array $expected + */ + public function testGetDefinition($type, array $expected) { + $definitions = new Definitions(); + $definition = $definitions->getDefinition($type); + + $this->assertEquals($expected, $definition); + $this->assertArrayHasKey('author', $definition); + $this->assertNotEquals('', $definition['author'], 'Author of definition must not be empty'); + $this->assertArrayHasKey('app', $definition); + $this->assertNotEquals('', $definition['app'], 'App of definition must not be empty'); + $this->assertArrayHasKey('since', $definition); + $this->assertNotEmpty($definition['since'], 'Since of definition must not be empty'); + $this->assertArrayHasKey('parameters', $definition); + $this->assertTrue(is_array($definition['parameters']), 'Parameters of definition must be of type array'); + $this->assertNotEmpty($definition['parameters'], 'Parameters of definition must not be empty'); + + + $this->assertArrayHasKey('id', $definition['parameters'], 'Parameter ID must be defined'); + $this->assertArrayHasKey('name', $definition['parameters'], 'Parameter name must be defined'); + + foreach ($definition['parameters'] as $parameter => $data) { + $this->validateParameter($parameter, $data); + } + } + + public function validateParameter($parameter, $data) { + $this->assertTrue(is_array($data), 'Parameter ' . $parameter . ' is invalid'); + $this->assertArrayHasKey('since', $data); + $this->assertNotEmpty($data['since'], 'Since of parameter ' . $parameter . ' must not be empty'); + $this->assertArrayHasKey('required', $data); + $this->assertTrue(is_bool($data['required']), 'Required of parameter ' . $parameter . ' must be a boolean'); + if ($parameter === 'id' || $parameter === 'name') { + $this->assertTrue($data['required'], 'Parameter ' . $parameter . ' must be required'); + } + + $this->assertArrayHasKey('description', $data); + $this->assertNotEquals('', $data['description'], 'Description of parameter ' . $parameter . ' must not be empty'); + $this->assertArrayHasKey('example', $data); + $this->assertNotEquals('', $data['example'], 'Example of parameter ' . $parameter . ' must not be empty'); + + } +} diff --git a/tests/lib/RichObjectStrings/ValidatorTest.php b/tests/lib/RichObjectStrings/ValidatorTest.php index b97388ab8e..f18d1bdd90 100644 --- a/tests/lib/RichObjectStrings/ValidatorTest.php +++ b/tests/lib/RichObjectStrings/ValidatorTest.php @@ -23,12 +23,13 @@ namespace Test\RichObjectStrings; use OC\RichObjectStrings\Validator; +use OCP\RichObjectStrings\Definitions; use Test\TestCase; class ValidatorTest extends TestCase { public function test() { - $v = new Validator(); + $v = new Validator(new Definitions()); $v->validate('test', []); $v->validate('test {string1} test {foo} test {bar}.', [ 'string1' => [