From f273bfd13ddd82d38d668843b11b13063672c08a Mon Sep 17 00:00:00 2001 From: Dawid Date: Sun, 16 Sep 2018 15:58:02 +0200 Subject: [PATCH 1/9] User creation --- data/stilus.db | Bin 0 -> 20480 bytes src/api/Exception/Code.php | 12 +++ src/api/Exception/DomainException.php | 7 ++ src/api/Exception/RuntimeException.php | 4 +- .../Platform/Controller/GetPlatformStatus.php | 1 + src/api/Platform/Exception/UserException.php | 14 +++ src/api/Platform/Platform.php | 11 +++ src/api/Platform/User.php | 83 ++++++++++++++++++ tests/api/Unit/Platform/UserTest.php | 14 +++ 9 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 data/stilus.db create mode 100644 src/api/Exception/Code.php create mode 100644 src/api/Exception/DomainException.php create mode 100644 src/api/Platform/Exception/UserException.php create mode 100644 src/api/Platform/Platform.php create mode 100644 src/api/Platform/User.php create mode 100644 tests/api/Unit/Platform/UserTest.php diff --git a/data/stilus.db b/data/stilus.db new file mode 100644 index 0000000000000000000000000000000000000000..0bfd336de4ad4ae40abd8256d95f9c0015700aef GIT binary patch literal 20480 zcmeI(Z)?*)90%~by17y4+6^Q=+ffi#@g$dA+w{S}OPYvSr`8JVqY_<)g>Ka*oj%Qc zGrkmGmY2BVEOkQgftK%;yZq_zcjP|DJvc&Mzv_*Xh@DPnXJNu@QX`a-$BYp|Hm}u^ zKHt@KL)S&2i<29Uo8-~mZy$@#xmepV{`%j;%{*XQMhwSLZfE~T* z^&aM7(OEdI9@EP!0FeV&t`m5w?Q&bWl1m|d-Vwgwj?;8HrjmBIwFF?>G6-bLDxSFn zP`sl80Fx^!VL6Q@pATUif11t~m@6XUaFQ(Y&7bL&azk(P9vFr}FPpjQJdS2@-jsfw z?0KBZ-K{+G$)6&17yr%x8+*5nLxYm>`APIS{y5P)=TSIMrup?KAL>Zthg*8de$AF` zdZ)H+r1$k=`%PByi64pLx!K-+JA1xL*3_~F1p*L&00bZa0SG_<0uX=z1Rwx`brmRQ z1N*W6&))H)KmY;|fB*y_009U<00Izz00bbgh5~s1zlM{FAwd8F5P$##AOHafKmY;| qfB*zm3uNE&U;nm$brkMG00Izz00bZa0SG_<0uX=z1R(HV3Va7}r3N?v literal 0 HcmV?d00001 diff --git a/src/api/Exception/Code.php b/src/api/Exception/Code.php new file mode 100644 index 0000000..2bc96cd --- /dev/null +++ b/src/api/Exception/Code.php @@ -0,0 +1,12 @@ +platformService = $platformService; } public function __invoke(ServerRequestInterface $request): ResponseInterface diff --git a/src/api/Platform/Exception/UserException.php b/src/api/Platform/Exception/UserException.php new file mode 100644 index 0000000..b02d4a9 --- /dev/null +++ b/src/api/Platform/Exception/UserException.php @@ -0,0 +1,14 @@ +id = new Uuid(); + $this->email = $email; + $this->password = $password; + $this->validate(); + } + + public function getId(): Id + { + return $this->id; + } + + public function getEmail(): string + { + return $this->email; + } + + public function validatePassword(string $password): bool + { + return $this->password === $this->generateHash($password); + } + + public function createPassword(string $password): void + { + $this->salt = bin2hex(random_bytes(10)); + $this->password = $this->generateHash($password); + } + + private function generateHash(string $password): string + { + return password_hash($password, PASSWORD_BCRYPT, ['salt' => $this->salt]); + } + + private function validate() + { + if (!Constraint::email()->validate($this->email)) { + throw UserException::forUserCreation(); + } + } +} diff --git a/tests/api/Unit/Platform/UserTest.php b/tests/api/Unit/Platform/UserTest.php new file mode 100644 index 0000000..f633eef --- /dev/null +++ b/tests/api/Unit/Platform/UserTest.php @@ -0,0 +1,14 @@ + Date: Sun, 16 Sep 2018 17:19:54 +0200 Subject: [PATCH 2/9] User Repository --- src/api/Platform/User.php | 21 +++++------------- src/api/Platform/UserRepository.php | 32 ++++++++++++++++++++++++++++ tests/api/Unit/Platform/UserTest.php | 21 ++++++++++++++++++ 3 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 src/api/Platform/UserRepository.php diff --git a/src/api/Platform/User.php b/src/api/Platform/User.php index 0ae7d85..20b6ea0 100644 --- a/src/api/Platform/User.php +++ b/src/api/Platform/User.php @@ -7,9 +7,10 @@ use Igni\Storage\Mapping\Annotation as Storage; use Igni\Storage\Storable; use Igni\Validation\Constraint; +use Stilus\Platform\Exception\UserException; use function password_hash; -use Stilus\Platform\Exception\UserException; +use function password_verify; /** * @Storage\Entity(source="users") @@ -34,17 +35,11 @@ final class User implements Storable */ private $password; - /** - * @var - * @Storage\Property\Text() - */ - private $salt; - public function __construct(string $email, string $password) { $this->id = new Uuid(); $this->email = $email; - $this->password = $password; + $this->createPassword($password); $this->validate(); } @@ -60,18 +55,12 @@ public function getEmail(): string public function validatePassword(string $password): bool { - return $this->password === $this->generateHash($password); + return password_verify($password, $this->password); } public function createPassword(string $password): void { - $this->salt = bin2hex(random_bytes(10)); - $this->password = $this->generateHash($password); - } - - private function generateHash(string $password): string - { - return password_hash($password, PASSWORD_BCRYPT, ['salt' => $this->salt]); + $this->password = password_hash($password, PASSWORD_BCRYPT); } private function validate() diff --git a/src/api/Platform/UserRepository.php b/src/api/Platform/UserRepository.php new file mode 100644 index 0000000..401aa40 --- /dev/null +++ b/src/api/Platform/UserRepository.php @@ -0,0 +1,32 @@ +query( + 'SELECT *FROM users WHERE email = :email LIMIT 1', + [ + 'email' => $email, + ] + ); + $cursor->hydrateWith($this->hydrator); + $user = $cursor->current(); + $cursor->close(); + + if ($user === null) { + + } + + return $user; + } + + public static function getEntityClass(): string + { + return User::class; + } +} diff --git a/tests/api/Unit/Platform/UserTest.php b/tests/api/Unit/Platform/UserTest.php index f633eef..7860bc7 100644 --- a/tests/api/Unit/Platform/UserTest.php +++ b/tests/api/Unit/Platform/UserTest.php @@ -3,6 +3,7 @@ namespace StilusTests\Unit\Platform; use PHPUnit\Framework\TestCase; +use Stilus\Platform\Exception\UserException; use Stilus\Platform\User; final class UserTest extends TestCase @@ -11,4 +12,24 @@ public function testCanInstantiate(): void { self::assertInstanceOf(User::class, new User('some@email.com', 'password')); } + + public function testFailOnInvalidEmailAddress(): void + { + $this->expectException(UserException::class); + new User('invalidemail', 'aa'); + } + + public function testCreatePassword(): void + { + $user = new User('test@email.com', 'password'); + self::assertTrue($user->validatePassword('password')); + } + + public function testVerifyPassword(): void + { + $user = new User('test@email.com', 'password'); + self::assertTrue($user->validatePassword('password')); + self::assertFalse($user->validatePassword('invalid')); + self::assertFalse($user->validatePassword('error')); + } } From 8391c0648e4ac21c293ae65e646244d98bfc91af Mon Sep 17 00:00:00 2001 From: Dawid Date: Tue, 18 Sep 2018 09:34:51 +0200 Subject: [PATCH 3/9] user creation --- .stilus.yml | 1 + composer.json | 11 +- composer.lock | 136 +++++++++++++++++- data/stilus.db | Bin 20480 -> 0 bytes logo/stilus.svg | 15 -- phpunit.xml | 2 +- src/api/Exception/BootException.php | 11 +- src/api/Exception/EntityNotFound.php | 6 + .../Exception/{Code.php => ExceptionCode.php} | 6 +- src/api/Exception/ExceptionTrait.php | 23 --- src/api/Exception/RuntimeException.php | 1 - .../Platform/Controller/CreatePlatform.php | 10 +- src/api/Platform/Exception/UserException.php | 14 +- src/api/Platform/PlatformService.php | 21 +++ src/api/Platform/User.php | 13 +- src/api/Platform/UserRepository.php | 22 ++- src/api/Stilus.php | 19 ++- src/dashboard/i18n/en_EN.json | 3 + tests/api/Fixtures/.gitkeep | 0 .../Platform/UserRepositoryTest.php | 68 +++++++++ tests/api/StorageTestTrait.php | 35 +++++ tests/api/Unit/Platform/UserTest.php | 4 +- tests/api/bootstrap.php | 9 +- tests/api/temp/.gitkeep | 0 24 files changed, 363 insertions(+), 67 deletions(-) delete mode 100644 data/stilus.db delete mode 100644 logo/stilus.svg create mode 100644 src/api/Exception/EntityNotFound.php rename src/api/Exception/{Code.php => ExceptionCode.php} (66%) delete mode 100644 src/api/Exception/ExceptionTrait.php create mode 100644 src/dashboard/i18n/en_EN.json create mode 100644 tests/api/Fixtures/.gitkeep create mode 100644 tests/api/Functional/Platform/UserRepositoryTest.php create mode 100644 tests/api/StorageTestTrait.php create mode 100644 tests/api/temp/.gitkeep diff --git a/.stilus.yml b/.stilus.yml index 93c28f3..80bd45b 100644 --- a/.stilus.yml +++ b/.stilus.yml @@ -15,6 +15,7 @@ api: log_file: "./data/stilus.log" dashboard: + language: en_EN web_server: host: "0.0.0.0" port: 8090 diff --git a/composer.json b/composer.json index 0c97d2c..6c5dc80 100644 --- a/composer.json +++ b/composer.json @@ -17,18 +17,23 @@ "igniphp/validation": "^1.1.0", "symfony/yaml": "^4.1", "zendframework/zend-mail": "^2.10", - "zendframework/zend-crypt": "^3.3" + "zendframework/zend-crypt": "^3.3", + "league/flysystem": "^1.0" }, "scripts": { - "start": "php src/api/Stilus.php", + "start": "php src/Stilus.php", "stop": "kill $(cat ./data/stilus.pid)" }, "require-dev": { "phpunit/phpunit": ">=5.7.0", "mockery/mockery": ">=0.9.4", - "phpunit/php-code-coverage": ">=4.0.0" + "phpunit/php-code-coverage": ">=4.0.0", + "fzaninotto/faker": "^1.8" }, "autoload": { + "exclude-from-classmap": [ + "src/api/Stilus.php" + ], "psr-4": { "Stilus\\": "src/api/" } diff --git a/composer.lock b/composer.lock index 26a09fd..f11f621 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4a32763295390bfef6319ecbfa848c3e", + "content-hash": "fab1a9bf9659244a76404e380e77b66f", "packages": [ { "name": "cache/adapter-common", @@ -1002,6 +1002,90 @@ ], "time": "2018-03-20T17:20:48+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.47", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "a11e4a75f256bdacf99d20780ce42d3b8272975c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a11e4a75f256bdacf99d20780ce42d3b8272975c", + "reference": "a11e4a75f256bdacf99d20780ce42d3b8272975c", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7.10" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2018-09-14T15:30:29+00:00" + }, { "name": "paragonie/random_compat", "version": "v2.0.17", @@ -2300,6 +2384,56 @@ ], "time": "2017-07-22T11:58:36+00:00" }, + { + "name": "fzaninotto/faker", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/fzaninotto/Faker.git", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "ext-intl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7", + "squizlabs/php_codesniffer": "^1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "time": "2018-07-12T10:23:15+00:00" + }, { "name": "hamcrest/hamcrest-php", "version": "v2.0.0", diff --git a/data/stilus.db b/data/stilus.db deleted file mode 100644 index 0bfd336de4ad4ae40abd8256d95f9c0015700aef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI(Z)?*)90%~by17y4+6^Q=+ffi#@g$dA+w{S}OPYvSr`8JVqY_<)g>Ka*oj%Qc zGrkmGmY2BVEOkQgftK%;yZq_zcjP|DJvc&Mzv_*Xh@DPnXJNu@QX`a-$BYp|Hm}u^ zKHt@KL)S&2i<29Uo8-~mZy$@#xmepV{`%j;%{*XQMhwSLZfE~T* z^&aM7(OEdI9@EP!0FeV&t`m5w?Q&bWl1m|d-Vwgwj?;8HrjmBIwFF?>G6-bLDxSFn zP`sl80Fx^!VL6Q@pATUif11t~m@6XUaFQ(Y&7bL&azk(P9vFr}FPpjQJdS2@-jsfw z?0KBZ-K{+G$)6&17yr%x8+*5nLxYm>`APIS{y5P)=TSIMrup?KAL>Zthg*8de$AF` zdZ)H+r1$k=`%PByi64pLx!K-+JA1xL*3_~F1p*L&00bZa0SG_<0uX=z1Rwx`brmRQ z1N*W6&))H)KmY;|fB*y_009U<00Izz00bbgh5~s1zlM{FAwd8F5P$##AOHafKmY;| qfB*zm3uNE&U;nm$brkMG00Izz00bZa0SG_<0uX=z1R(HV3Va7}r3N?v diff --git a/logo/stilus.svg b/logo/stilus.svg deleted file mode 100644 index edf060d..0000000 --- a/logo/stilus.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - logo - - - - - - diff --git a/phpunit.xml b/phpunit.xml index e7c9331..fc8cc21 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,7 +7,7 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - bootstrap="tests/bootstrap.php" + bootstrap="tests/api/bootstrap.php" > diff --git a/src/api/Exception/BootException.php b/src/api/Exception/BootException.php index 72ff5cb..f662884 100644 --- a/src/api/Exception/BootException.php +++ b/src/api/Exception/BootException.php @@ -8,29 +8,30 @@ class BootException extends RuntimeException { public static function forInvalidPHPVersion(string $currentVersion): self { - return self::withMessage("Stilus requires PHP 7.1.0 or higher, you are running PHP {$currentVersion}."); + return new self("Stilus requires PHP 7.1.0 or higher, you are running PHP {$currentVersion}."); } public static function forMissingComposer(): self { - return self::withMessage('`vendor` dir is missing. Did you forgot to run `composer install?`'); + return new self('`vendor` dir is missing. Did you forgot to run `composer install?`'); } public static function forMissingBaseConfiguration(): self { - return self::withMessage('`.stilus.yml` file is missing. Did you deleted it by accident?'); + return new self('`.stilus.yml` file is missing. Did you deleted it by accident?'); } public static function forInvalidBaseConfiguration(Throwable $previous): self { - return self::withPrevious( + return new self( 'There was a problem with parsing `.stilus.yml` file. Please check the config file.', + $previous->getCode(), $previous ); } public static function forMissingConfigurationOption(string $name): self { - return self::withMessage("`{$name}`` configuration option is missing."); + return new self("`{$name}`` configuration option is missing."); } } diff --git a/src/api/Exception/EntityNotFound.php b/src/api/Exception/EntityNotFound.php new file mode 100644 index 0000000..2343e82 --- /dev/null +++ b/src/api/Exception/EntityNotFound.php @@ -0,0 +1,6 @@ +getCode(), $previous); - } -} diff --git a/src/api/Exception/RuntimeException.php b/src/api/Exception/RuntimeException.php index 96d8bc2..3c977d6 100644 --- a/src/api/Exception/RuntimeException.php +++ b/src/api/Exception/RuntimeException.php @@ -6,5 +6,4 @@ class RuntimeException extends PhpRuntimeException implements StilusException { - use ExceptionTrait; } diff --git a/src/api/Platform/Controller/CreatePlatform.php b/src/api/Platform/Controller/CreatePlatform.php index 1ac2d01..85d8716 100644 --- a/src/api/Platform/Controller/CreatePlatform.php +++ b/src/api/Platform/Controller/CreatePlatform.php @@ -6,12 +6,20 @@ use Igni\Network\Http\Route; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Stilus\Platform\PlatformService; final class CreatePlatform implements Controller { - public function __invoke(ServerRequestInterface $request): ResponseInterface + private $platformService; + + public function __construct(PlatformService $platformService) { + $this->platformService = $platformService; + } + public function __invoke(ServerRequestInterface $request): ResponseInterface + { + $this->platformService->install(); } public static function getRoute(): Route diff --git a/src/api/Platform/Exception/UserException.php b/src/api/Platform/Exception/UserException.php index b02d4a9..1d51a71 100644 --- a/src/api/Platform/Exception/UserException.php +++ b/src/api/Platform/Exception/UserException.php @@ -2,13 +2,19 @@ namespace Stilus\Platform\Exception; -use Stilus\Exception\Code; +use Stilus\Exception\EntityNotFound; +use Stilus\Exception\ExceptionCode; use Stilus\Exception\DomainException; -final class UserException extends DomainException +class UserException extends DomainException { - public static function forUserCreation(): self + public static function forCreationFailure(): self { - throw new self('Could not create user', Code::INVALID_USER_EMAIL); + throw new self('Could not create user', ExceptionCode::INVALID_USER_EMAIL); + } + + public static function forNotFound(): self + { + return new class("User not found", ExceptionCode::USER_NOT_FOUND) extends UserException implements EntityNotFound {}; } } diff --git a/src/api/Platform/PlatformService.php b/src/api/Platform/PlatformService.php index 85d83a2..cbcd2fe 100644 --- a/src/api/Platform/PlatformService.php +++ b/src/api/Platform/PlatformService.php @@ -16,6 +16,27 @@ public function __construct(Config $paths) $this->config = $paths; } + public function setLanguage(): void + { + + } + + public function setupDatabase(string $host, string $name, string $username, string $password): void + { + + } + + public function createAdmin(string $email, string $password): void + { + + } + + public function postInstall(): void + { + + } + + public function getStatus() { diff --git a/src/api/Platform/User.php b/src/api/Platform/User.php index 20b6ea0..de64bb8 100644 --- a/src/api/Platform/User.php +++ b/src/api/Platform/User.php @@ -63,10 +63,21 @@ public function createPassword(string $password): void $this->password = password_hash($password, PASSWORD_BCRYPT); } + public function changePassword(string $oldPassword, string $newPassword): bool + { + if (!$this->validatePassword($oldPassword)) { + return false; + } + + $this->createPassword($newPassword); + + return true; + } + private function validate() { if (!Constraint::email()->validate($this->email)) { - throw UserException::forUserCreation(); + throw UserException::forCreationFailure(); } } } diff --git a/src/api/Platform/UserRepository.php b/src/api/Platform/UserRepository.php index 401aa40..e8192c9 100644 --- a/src/api/Platform/UserRepository.php +++ b/src/api/Platform/UserRepository.php @@ -3,13 +3,31 @@ namespace Stilus\Platform; use Igni\Storage\Driver\Pdo\Repository; +use Stilus\Platform\Exception\UserException; class UserRepository extends Repository { + public function createSchema(): void + { + $cursor = $this->connection->execute('CREATE TABLE IF NOT EXISTS "users" ( + "id" char(22) PRIMARY KEY NOT NULL, + "email" char(128) NOT NULL, + "password" char(128) NOT NULL + );' + ); + $cursor->execute(); + } + + public function dropSchema(): void + { + $cursor = $this->connection->execute('DROP TABLE IF EXISTS "users"'); + $cursor->execute(); + } + public function findUserByEmail(string $email): User { $cursor = $this->query( - 'SELECT *FROM users WHERE email = :email LIMIT 1', + 'SELECT * FROM users WHERE email = :email LIMIT 1', [ 'email' => $email, ] @@ -19,7 +37,7 @@ public function findUserByEmail(string $email): User $cursor->close(); if ($user === null) { - + throw UserException::forNotFound(); } return $user; diff --git a/src/api/Stilus.php b/src/api/Stilus.php index 8177778..92383c9 100644 --- a/src/api/Stilus.php +++ b/src/api/Stilus.php @@ -1,17 +1,23 @@ ')) { throw BootException::forInvalidPHPVersion(PHP_VERSION); @@ -23,6 +29,10 @@ const STILUS_DIR = __DIR__ . '/../..'; +const STILUS_DATA_DIR = STILUS_DIR . '/data'; + +const STILUS_DB_PATH = STILUS_DATA_DIR . '/stilus.db'; + const STILUS_BASE_CONFIG = STILUS_DIR . '/.stilus.yml'; const STILUS_VENDOR_DIR = __DIR__ . '/../../vendor'; @@ -98,6 +108,9 @@ public function main(): void 'dir.themes', realpath(STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['themes']), ]); }); + + ConnectionManager::register('default', new Connection('sqlite:' . STILUS_DB_PATH)); + $application = new HttpApplication($container); foreach (STILUS_MODULES as $module) { diff --git a/src/dashboard/i18n/en_EN.json b/src/dashboard/i18n/en_EN.json new file mode 100644 index 0000000..0db3279 --- /dev/null +++ b/src/dashboard/i18n/en_EN.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/api/Fixtures/.gitkeep b/tests/api/Fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/api/Functional/Platform/UserRepositoryTest.php b/tests/api/Functional/Platform/UserRepositoryTest.php new file mode 100644 index 0000000..8eb87dc --- /dev/null +++ b/tests/api/Functional/Platform/UserRepositoryTest.php @@ -0,0 +1,68 @@ +createConnection(); + $this->faker = FakerFactory::create(); + } + + public function testFindByEmail(): void + { + $repository = $this->getUserRepository(); + $this->createTestUsers($repository); + + $email = 'test@user.com'; + $repository->create(new User($email, 'test')); + + $user = $repository->findUserByEmail($email); + self::assertInstanceOf(User::class, $user); + self::assertSame($email, $user->getEmail()); + self::assertTrue($user->validatePassword('test')); + } + + public function testFailFindByEmail(): void + { + $this->expectException(EntityNotFound::class); + $repository = $this->getUserRepository(); + $this->createTestUsers($repository); + + $repository->findUserByEmail('test'); + } + + private function getUserRepository(): UserRepository + { + $entityManager = new EntityManager(); + $repository = new UserRepository($entityManager, $this->connection); + $repository->dropSchema(); + $repository->createSchema(); + + return $repository; + } + + private function createTestUsers(UserRepository $repository, int $amount = 5) + { + for ($i = 0; $i < $amount; $i++) { + $user = new User($this->faker->email, $this->faker->password); + $repository->create($user); + } + } +} diff --git a/tests/api/StorageTestTrait.php b/tests/api/StorageTestTrait.php new file mode 100644 index 0000000..4ba7b3a --- /dev/null +++ b/tests/api/StorageTestTrait.php @@ -0,0 +1,35 @@ +connection = new Connection('sqlite:' . STILUS_TEST_FIXTURE_DIR . '/test.db'); + ConnectionManager::register($name, $this->connection); + + return $this->connection; + } + + public function createEntityManager(): EntityManager + { + $this->entityManager = new EntityManager(); + + return $this->entityManager; + } + + public function createStorage(): Storage + { + $this->storage = new Storage($this->entityManager ?? $this->createEntityManager()); + } +} diff --git a/tests/api/Unit/Platform/UserTest.php b/tests/api/Unit/Platform/UserTest.php index 7860bc7..7b754d9 100644 --- a/tests/api/Unit/Platform/UserTest.php +++ b/tests/api/Unit/Platform/UserTest.php @@ -1,8 +1,9 @@ expectException(UserException::class); + $this->expectExceptionCode(ExceptionCode::INVALID_USER_EMAIL); new User('invalidemail', 'aa'); } diff --git a/tests/api/bootstrap.php b/tests/api/bootstrap.php index 58ce587..09b24b2 100644 --- a/tests/api/bootstrap.php +++ b/tests/api/bootstrap.php @@ -1,4 +1,7 @@ - Date: Mon, 24 Sep 2018 09:43:40 +0200 Subject: [PATCH 4/9] Refactor + migration manager bootstrap --- composer.json | 25 ++++- composer.lock | 12 +-- src/api/Kernel/MigrationSynchronizer.php | 35 +++++++ src/api/Kernel/System.php | 91 +++++++++++++++++++ .../Platform/{ => Persistence}/Platform.php | 2 +- src/api/Platform/{ => Persistence}/User.php | 2 +- .../{ => Persistence}/UserRepository.php | 13 +-- src/api/Platform/Persistence/UserSchema.php | 45 +++++++++ .../Persistence/UserSchemaFactory.php | 14 +++ src/api/Platform/PlatformModule.php | 7 ++ src/api/Platform/PlatformService.php | 2 +- src/api/Stilus.php | 77 +++------------- 12 files changed, 237 insertions(+), 88 deletions(-) create mode 100644 src/api/Kernel/MigrationSynchronizer.php create mode 100644 src/api/Kernel/System.php rename src/api/Platform/{ => Persistence}/Platform.php (72%) rename src/api/Platform/{ => Persistence}/User.php (97%) rename src/api/Platform/{ => Persistence}/UserRepository.php (71%) create mode 100644 src/api/Platform/Persistence/UserSchema.php create mode 100644 src/api/Platform/Persistence/UserSchemaFactory.php diff --git a/composer.json b/composer.json index 6c5dc80..dc4c4d3 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,5 @@ { "name": "igniphp/stilus", - "version": "0.0.1", "description": "", "keywords": [], "license": "BSD-3-Clause", @@ -13,7 +12,7 @@ "require": { "php": ">=7.1.0", "igniphp/framework": "^2.0", - "igniphp/storage": "^0.4.2", + "igniphp/storage": "^0.5.0", "igniphp/validation": "^1.1.0", "symfony/yaml": "^4.1", "zendframework/zend-mail": "^2.10", @@ -21,12 +20,30 @@ "league/flysystem": "^1.0" }, "scripts": { + "post-install-cmd": [ + + ], + "post-update-cmd": [ + "Stilus\\Kernel\\MigrationSynchronizer::synchronize" + ], + "ci": [ + "composer validate --no-check-all --strict", + "@phpcs", + "@test-coverage" + ], "start": "php src/Stilus.php", - "stop": "kill $(cat ./data/stilus.pid)" + "stop": "kill $(cat ./data/stilus.pid)", + "phpcs": "phpcs --standard=PSR2 src", + "test": "phpunit", + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" + }, + "scripts-descriptions": { + "phpcs": "Checks that the application code conforms to coding standard", + "test-coverage": "Launches the preconfigured PHPUnit with coverage", + "ci": "Continues integration checks" }, "require-dev": { "phpunit/phpunit": ">=5.7.0", - "mockery/mockery": ">=0.9.4", "phpunit/php-code-coverage": ">=4.0.0", "fzaninotto/faker": "^1.8" }, diff --git a/composer.lock b/composer.lock index f11f621..7fbb2c2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fab1a9bf9659244a76404e380e77b66f", + "content-hash": "97c604c395f61645f5a9ff11b1bfc34c", "packages": [ { "name": "cache/adapter-common", @@ -843,16 +843,16 @@ }, { "name": "igniphp/storage", - "version": "0.4.2", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/igniphp/storage.git", - "reference": "475b44b9b530a1737f4293b0c3cd3bb9fca99334" + "reference": "6ee58943a600c0941ede63867a8e46fdd41d88e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igniphp/storage/zipball/475b44b9b530a1737f4293b0c3cd3bb9fca99334", - "reference": "475b44b9b530a1737f4293b0c3cd3bb9fca99334", + "url": "https://api.github.com/repos/igniphp/storage/zipball/6ee58943a600c0941ede63867a8e46fdd41d88e6", + "reference": "6ee58943a600c0941ede63867a8e46fdd41d88e6", "shasum": "" }, "require": { @@ -911,7 +911,7 @@ "sqlite", "unit of work" ], - "time": "2018-07-16T07:58:44+00:00" + "time": "2018-09-23T11:24:48+00:00" }, { "name": "igniphp/uuid", diff --git a/src/api/Kernel/MigrationSynchronizer.php b/src/api/Kernel/MigrationSynchronizer.php new file mode 100644 index 0000000..f84d028 --- /dev/null +++ b/src/api/Kernel/MigrationSynchronizer.php @@ -0,0 +1,35 @@ +createConnection()); + + } + + private static function loadModules(Config $config, ServiceLocator $locator): void + { + $modules = System::STILUS_MODULES; + + foreach ($modules as $module) { + if (class_exists($module)) { + $module = new $module; + } + } + } +} diff --git a/src/api/Kernel/System.php b/src/api/Kernel/System.php new file mode 100644 index 0000000..06989d5 --- /dev/null +++ b/src/api/Kernel/System.php @@ -0,0 +1,91 @@ +')) { + throw BootException::forInvalidPHPVersion(PHP_VERSION); + } + + if (!file_exists(self::STILUS_VENDOR_AUTOLOADER)) { + throw BootException::forMissingComposer(); + } + + require_once self::STILUS_VENDOR_AUTOLOADER; + } + + public function createConnection(): Connection + { + if (!ConnectionManager::has('default')) { + ConnectionManager::register('default', new PdoConnection('sqlite:' . self::STILUS_DB_PATH)); + } + + + return ConnectionManager::get('default'); + } + + public function getBaseConfig(): Config + { + if ($this->config instanceof Config) { + return $this->config; + } + + $config = $this->loadBaseConfig(); + return $this->config = new Config([ + 'dir.basedir', System::STILUS_DIR, + 'dir.config' => realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['config']), + 'dir.database', realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['database']), + 'dir.themes', realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['themes']), + ]); + } + + public function loadBaseConfig(): array + { + if (!is_readable(self::STILUS_BASE_CONFIG)) { + throw BootException::forMissingBaseConfiguration(); + } + + try { + return $configuration = Yaml::parseFile(self::STILUS_BASE_CONFIG); + } catch (Throwable $throwable) { + throw BootException::forInvalidBaseConfiguration($throwable); + } + } + + public function createServiceLocator(): ServiceLocator + { + $container = new ServiceLocator(); + $container->set(Config::class, $this->getBaseConfig()); + + return $container; + } +} diff --git a/src/api/Platform/Platform.php b/src/api/Platform/Persistence/Platform.php similarity index 72% rename from src/api/Platform/Platform.php rename to src/api/Platform/Persistence/Platform.php index e397218..a25a8b2 100644 --- a/src/api/Platform/Platform.php +++ b/src/api/Platform/Persistence/Platform.php @@ -1,6 +1,6 @@ connection->execute('CREATE TABLE IF NOT EXISTS "users" ( - "id" char(22) PRIMARY KEY NOT NULL, - "email" char(128) NOT NULL, - "password" char(128) NOT NULL - );' - ); - $cursor->execute(); - } - public function dropSchema(): void { $cursor = $this->connection->execute('DROP TABLE IF EXISTS "users"'); diff --git a/src/api/Platform/Persistence/UserSchema.php b/src/api/Platform/Persistence/UserSchema.php new file mode 100644 index 0000000..330efcc --- /dev/null +++ b/src/api/Platform/Persistence/UserSchema.php @@ -0,0 +1,45 @@ +connection = $connection; + } + + public function up(): void + { + $cursor = $this->connection->execute('CREATE TABLE IF NOT EXISTS "users" ( + "id" char(22) PRIMARY KEY NOT NULL, + "email" char(128) NOT NULL, + "password" char(128) NOT NULL + );' + ); + $cursor->execute(); + } + + public function down(): void + { + $cursor = $this->connection->execute('DROP TABLE IF EXISTS "users"'); + $cursor->execute(); + } + + public function getVersion(): Version + { + return Version::fromString('1.0.0'); + } + + public static function create(ContainerInterface $container): self + { + return new self($container->get(Connection::class)); + } +} diff --git a/src/api/Platform/Persistence/UserSchemaFactory.php b/src/api/Platform/Persistence/UserSchemaFactory.php new file mode 100644 index 0000000..f1a6718 --- /dev/null +++ b/src/api/Platform/Persistence/UserSchemaFactory.php @@ -0,0 +1,14 @@ +get(Connection::class)); + } +} diff --git a/src/api/Platform/PlatformModule.php b/src/api/Platform/PlatformModule.php index 779ee75..56885bf 100644 --- a/src/api/Platform/PlatformModule.php +++ b/src/api/Platform/PlatformModule.php @@ -6,9 +6,12 @@ use Igni\Application\Providers\ControllerProvider; use Igni\Application\Providers\ServiceProvider; use Igni\Container\ServiceLocator; +use Igni\Storage\Driver\Pdo\Connection; +use Igni\Storage\MigrationManager; use Psr\Container\ContainerInterface; use Stilus\Platform\Controller\CreatePlatform; use Stilus\Platform\Controller\GetPlatformStatus; +use Stilus\Platform\Persistence\UserSchema; class PlatformModule implements ControllerProvider, ServiceProvider { @@ -24,5 +27,9 @@ public function provideControllers(ControllerAggregator $controllers): void public function provideServices(ContainerInterface $container): void { $container->share(PlatformService::class); + + $container + ->get(MigrationManager::class) + ->register(UserSchema::create($container)); } } diff --git a/src/api/Platform/PlatformService.php b/src/api/Platform/PlatformService.php index cbcd2fe..ca8161b 100644 --- a/src/api/Platform/PlatformService.php +++ b/src/api/Platform/PlatformService.php @@ -21,7 +21,7 @@ public function setLanguage(): void } - public function setupDatabase(string $host, string $name, string $username, string $password): void + public function setupDatabase(): void { } diff --git a/src/api/Stilus.php b/src/api/Stilus.php index 92383c9..4450456 100644 --- a/src/api/Stilus.php +++ b/src/api/Stilus.php @@ -1,17 +1,11 @@ ')) { - throw BootException::forInvalidPHPVersion(PHP_VERSION); -} - -const STILUS_MODULES = [ - PlatformModule::class -]; - -const STILUS_DIR = __DIR__ . '/../..'; - -const STILUS_DATA_DIR = STILUS_DIR . '/data'; - -const STILUS_DB_PATH = STILUS_DATA_DIR . '/stilus.db'; - -const STILUS_BASE_CONFIG = STILUS_DIR . '/.stilus.yml'; - -const STILUS_VENDOR_DIR = __DIR__ . '/../../vendor'; - -const STILUS_VENDOR_AUTOLOADER = __DIR__ . '/../../vendor/autoload.php'; +$system = new System(); // Bootstrap -(new class { +(new class($system) { - private function setupAutoload(): void - { - if (!file_exists(STILUS_VENDOR_AUTOLOADER)) { - throw BootException::forMissingComposer(); - } + private $system; - require STILUS_VENDOR_AUTOLOADER; - } - - private function loadBootstrapConfig(): array + public function __construct(System $system) { - if (!is_readable(STILUS_BASE_CONFIG)) { - throw BootException::forMissingBaseConfiguration(); - } - - try { - return $configuration = Yaml::parseFile(STILUS_BASE_CONFIG); - } catch (Throwable $throwable) { - throw BootException::forInvalidBaseConfiguration($throwable); - } + $this->system = $system; } private function setupServer(array $config): HttpServer @@ -96,24 +57,14 @@ private function setupServer(array $config): HttpServer public function main(): void { - $this->setupAutoload(); - - $config = $this->loadBootstrapConfig(); - $container = new ServiceLocator(); - $container->share(Config::class, function() use ($config) { - return new Config([ - 'dir.basedir', STILUS_DIR, - 'dir.config' => realpath(STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['config']), - 'dir.database', realpath(STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['database']), - 'dir.themes', realpath(STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['themes']), - ]); - }); - - ConnectionManager::register('default', new Connection('sqlite:' . STILUS_DB_PATH)); + $config = $this->system->getBaseConfig(); + $connection = $this->system->createConnection(); - $application = new HttpApplication($container); + $serviceLocator = $this->system->createServiceLocator(); + $serviceLocator->set(Connection::class, $connection); + $application = new HttpApplication($serviceLocator); - foreach (STILUS_MODULES as $module) { + foreach (System::STILUS_MODULES as $module) { $application->extend($module); } From a291f8d4a63486f5a1c3aec0f6daa146e8b11ab1 Mon Sep 17 00:00:00 2001 From: Dawid Date: Tue, 25 Sep 2018 08:54:32 +0200 Subject: [PATCH 5/9] Migration manager --- src/api/Kernel/Migration/MigrationService.php | 38 ++++++++++ .../Kernel/Migration/VersionSynchronizer.php | 75 +++++++++++++++++++ src/api/Kernel/MigrationSynchronizer.php | 35 --------- src/api/Kernel/System.php | 15 ++-- src/api/Platform/Persistence/UserSchema.php | 4 +- .../Persistence/UserSchemaFactory.php | 14 ---- src/api/Platform/PlatformModule.php | 3 +- src/api/Stilus.php | 2 +- .../Kernel/Migration/MigrationServiceTest.php | 10 +++ .../Migration/VersionSynchronizerTest.php | 40 ++++++++++ .../Platform/UserRepositoryTest.php | 13 ++-- tests/api/StorageTestTrait.php | 9 ++- tests/api/Unit/Platform/UserTest.php | 2 +- 13 files changed, 192 insertions(+), 68 deletions(-) create mode 100644 src/api/Kernel/Migration/MigrationService.php create mode 100644 src/api/Kernel/Migration/VersionSynchronizer.php delete mode 100644 src/api/Kernel/MigrationSynchronizer.php delete mode 100644 src/api/Platform/Persistence/UserSchemaFactory.php create mode 100644 tests/api/Functional/Kernel/Migration/MigrationServiceTest.php create mode 100644 tests/api/Functional/Kernel/Migration/VersionSynchronizerTest.php diff --git a/src/api/Kernel/Migration/MigrationService.php b/src/api/Kernel/Migration/MigrationService.php new file mode 100644 index 0000000..0d73e86 --- /dev/null +++ b/src/api/Kernel/Migration/MigrationService.php @@ -0,0 +1,38 @@ +createServiceLocator(); + $connection = $system->createDatabaseConnection(); + + $versionSynchronizer = new VersionSynchronizer($connection); + $migrationManager = new MigrationManager($versionSynchronizer); + $container->set(MigrationManager::class, $migrationManager); + $container->set(Connection::class, $connection); + + self::loadModules($container); + $migrationManager->migrate(); + } + + private static function loadModules(ServiceLocator $locator): void + { + $modules = System::STILUS_MODULES; + + foreach ($modules as $module) { + if (!class_exists($module)) { + continue; + } + $module = new $module; + } + } +} diff --git a/src/api/Kernel/Migration/VersionSynchronizer.php b/src/api/Kernel/Migration/VersionSynchronizer.php new file mode 100644 index 0000000..a3bfcdd --- /dev/null +++ b/src/api/Kernel/Migration/VersionSynchronizer.php @@ -0,0 +1,75 @@ +connection = $connection; + + $this->prepareMigrationTable(); + } + + private function prepareMigrationTable(): void + { + $cursor = $this->connection->execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'" + ); + + $tableExists = $cursor->current(); + + if (!$tableExists) { + $this->createMigrationTable(); + } + } + + private function createMigrationTable(): void + { + $cursor = $this->connection->execute( + "CREATE TABLE migrations ( + major INTEGER NOT NULL DEFAULT 0, + minor INTEGER NOT NULL DEFAULT 0, + patch INTEGER NOT NULL DEFAULT 0 + )" + ); + + $cursor->execute(); + } + + public function getVersion(): Version + { + $cursor = $this->connection->execute( + 'SELECT major, minor, patch FROM migrations ORDER BY major DESC, minor DESC, patch DESC' + ); + + $current = $cursor->current(); + if ($current === null) { + $current = Version::fromString('0.0.0'); + } else { + $current = Version::fromString(implode('.', $current)); + } + + return $current; + } + + public function setVersion(Version $version): void + { + $cursor = $this->connection->execute( + 'INSERT INTO migrations (major, minor, patch) VALUES (:major, :minor, :patch)', + [ + $version->getMajor(), + $version->getMinor(), + $version->getPatch() + ] + ); + + $cursor->execute(); + } +} diff --git a/src/api/Kernel/MigrationSynchronizer.php b/src/api/Kernel/MigrationSynchronizer.php deleted file mode 100644 index f84d028..0000000 --- a/src/api/Kernel/MigrationSynchronizer.php +++ /dev/null @@ -1,35 +0,0 @@ -createConnection()); - - } - - private static function loadModules(Config $config, ServiceLocator $locator): void - { - $modules = System::STILUS_MODULES; - - foreach ($modules as $module) { - if (class_exists($module)) { - $module = new $module; - } - } - } -} diff --git a/src/api/Kernel/System.php b/src/api/Kernel/System.php index 06989d5..e1146e0 100644 --- a/src/api/Kernel/System.php +++ b/src/api/Kernel/System.php @@ -6,6 +6,7 @@ use Igni\Container\ServiceLocator; use Igni\Storage\Driver\Connection; use Igni\Storage\Driver\ConnectionManager; +use Psr\Container\ContainerInterface; use Stilus\Exception\BootException; use Stilus\Platform\PlatformModule; use Symfony\Component\Yaml\Yaml; @@ -28,7 +29,7 @@ final class System /** @var Config */ private $config; - private $serviceLocator; + private $container; public function __construct() { @@ -43,7 +44,7 @@ public function __construct() require_once self::STILUS_VENDOR_AUTOLOADER; } - public function createConnection(): Connection + public function createDatabaseConnection(): Connection { if (!ConnectionManager::has('default')) { ConnectionManager::register('default', new PdoConnection('sqlite:' . self::STILUS_DB_PATH)); @@ -68,7 +69,7 @@ public function getBaseConfig(): Config ]); } - public function loadBaseConfig(): array + private function loadBaseConfig(): array { if (!is_readable(self::STILUS_BASE_CONFIG)) { throw BootException::forMissingBaseConfiguration(); @@ -83,9 +84,11 @@ public function loadBaseConfig(): array public function createServiceLocator(): ServiceLocator { - $container = new ServiceLocator(); - $container->set(Config::class, $this->getBaseConfig()); + if (!$this->container instanceof ContainerInterface) { + $this->container = new ServiceLocator(); + $this->container->set(Config::class, $this->getBaseConfig()); + } - return $container; + return $this->container; } } diff --git a/src/api/Platform/Persistence/UserSchema.php b/src/api/Platform/Persistence/UserSchema.php index 330efcc..add1ff4 100644 --- a/src/api/Platform/Persistence/UserSchema.php +++ b/src/api/Platform/Persistence/UserSchema.php @@ -38,8 +38,8 @@ public function getVersion(): Version return Version::fromString('1.0.0'); } - public static function create(ContainerInterface $container): self + public static function factory(ContainerInterface $container): self { - return new self($container->get(Connection::class)); + return new self($container->get(\Igni\Storage\Driver\Connection::class)); } } diff --git a/src/api/Platform/Persistence/UserSchemaFactory.php b/src/api/Platform/Persistence/UserSchemaFactory.php deleted file mode 100644 index f1a6718..0000000 --- a/src/api/Platform/Persistence/UserSchemaFactory.php +++ /dev/null @@ -1,14 +0,0 @@ -get(Connection::class)); - } -} diff --git a/src/api/Platform/PlatformModule.php b/src/api/Platform/PlatformModule.php index 56885bf..0ae8310 100644 --- a/src/api/Platform/PlatformModule.php +++ b/src/api/Platform/PlatformModule.php @@ -6,7 +6,6 @@ use Igni\Application\Providers\ControllerProvider; use Igni\Application\Providers\ServiceProvider; use Igni\Container\ServiceLocator; -use Igni\Storage\Driver\Pdo\Connection; use Igni\Storage\MigrationManager; use Psr\Container\ContainerInterface; use Stilus\Platform\Controller\CreatePlatform; @@ -30,6 +29,6 @@ public function provideServices(ContainerInterface $container): void $container ->get(MigrationManager::class) - ->register(UserSchema::create($container)); + ->register(UserSchema::factory($container)); } } diff --git a/src/api/Stilus.php b/src/api/Stilus.php index 4450456..5103537 100644 --- a/src/api/Stilus.php +++ b/src/api/Stilus.php @@ -58,7 +58,7 @@ private function setupServer(array $config): HttpServer public function main(): void { $config = $this->system->getBaseConfig(); - $connection = $this->system->createConnection(); + $connection = $this->system->createDatabaseConnection(); $serviceLocator = $this->system->createServiceLocator(); $serviceLocator->set(Connection::class, $connection); diff --git a/tests/api/Functional/Kernel/Migration/MigrationServiceTest.php b/tests/api/Functional/Kernel/Migration/MigrationServiceTest.php new file mode 100644 index 0000000..440a30b --- /dev/null +++ b/tests/api/Functional/Kernel/Migration/MigrationServiceTest.php @@ -0,0 +1,10 @@ +createConnection(); + $this->connection->execute('DROP TABLE migrations')->execute(); + parent::setUp(); + } + + public function testCanInstantiate(): void + { + $synchronizer = new VersionSynchronizer($this->connection); + self::assertInstanceOf(VersionSynchronizer::class, $synchronizer); + } + + public function testSetAndGetVersion(): void + { + $synchronizer = new VersionSynchronizer($this->connection); + $synchronizer->setVersion(Version::fromString('1.0.0')); + $synchronizer->setVersion(Version::fromString('1.2.0')); + + $synchronizer = new VersionSynchronizer($this->connection); + + self::assertTrue($synchronizer->getVersion()->equalsLiteral('1.2.0')); + + $cursor = $this->connection->execute('SELECT *FROM migrations'); + self::assertCount(2, $cursor->toArray()); + } +} diff --git a/tests/api/Functional/Platform/UserRepositoryTest.php b/tests/api/Functional/Platform/UserRepositoryTest.php index 8eb87dc..b828127 100644 --- a/tests/api/Functional/Platform/UserRepositoryTest.php +++ b/tests/api/Functional/Platform/UserRepositoryTest.php @@ -7,9 +7,10 @@ use Igni\Storage\EntityManager; use PHPUnit\Framework\TestCase; use Stilus\Exception\EntityNotFound; -use Stilus\Platform\User; -use Stilus\Platform\UserRepository; +use Stilus\Platform\Persistence\User; +use Stilus\Platform\Persistence\UserRepository; use Stilus\Tests\StorageTestTrait; +use Stilus\Platform\Persistence\UserSchema; final class UserRepositoryTest extends TestCase { @@ -20,9 +21,9 @@ final class UserRepositoryTest extends TestCase public function setUp() { - parent::setUp(); $this->createConnection(); $this->faker = FakerFactory::create(); + parent::setUp(); } public function testFindByEmail(): void @@ -52,9 +53,9 @@ private function getUserRepository(): UserRepository { $entityManager = new EntityManager(); $repository = new UserRepository($entityManager, $this->connection); - $repository->dropSchema(); - $repository->createSchema(); - + $schema = new UserSchema($this->connection); + $schema->down(); + $schema->up(); return $repository; } diff --git a/tests/api/StorageTestTrait.php b/tests/api/StorageTestTrait.php index 4ba7b3a..a747e69 100644 --- a/tests/api/StorageTestTrait.php +++ b/tests/api/StorageTestTrait.php @@ -9,12 +9,19 @@ trait StorageTestTrait { + /** @var Connection */ private $connection; + /** @var EntityManager */ private $entityManager; + /** @var Storage */ private $storage; - public function createConnection(string $name = 'test'): Connection + public function createConnection(string $name = 'default'): Connection { + if (ConnectionManager::has($name)) { + return $this->connection = ConnectionManager::get($name); + } + $this->connection = new Connection('sqlite:' . STILUS_TEST_FIXTURE_DIR . '/test.db'); ConnectionManager::register($name, $this->connection); diff --git a/tests/api/Unit/Platform/UserTest.php b/tests/api/Unit/Platform/UserTest.php index 7b754d9..eb97c84 100644 --- a/tests/api/Unit/Platform/UserTest.php +++ b/tests/api/Unit/Platform/UserTest.php @@ -5,7 +5,7 @@ use PHPUnit\Framework\TestCase; use Stilus\Exception\ExceptionCode; use Stilus\Platform\Exception\UserException; -use Stilus\Platform\User; +use Stilus\Platform\Persistence\User; final class UserTest extends TestCase { From 4a35df9891b86faf74c2c884a97d09154175ed89 Mon Sep 17 00:00:00 2001 From: Dawid Date: Tue, 25 Sep 2018 11:16:20 +0200 Subject: [PATCH 6/9] Migration Service --- src/api/Kernel/Migration/MigrationService.php | 21 ++++++++++++++++--- src/api/Kernel/System.php | 3 +-- src/api/Platform/Persistence/UserSchema.php | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/api/Kernel/Migration/MigrationService.php b/src/api/Kernel/Migration/MigrationService.php index 0d73e86..3a290cf 100644 --- a/src/api/Kernel/Migration/MigrationService.php +++ b/src/api/Kernel/Migration/MigrationService.php @@ -2,6 +2,9 @@ namespace Stilus\Kernel\Migration; +use Igni\Application\Config; +use Igni\Application\Providers\ConfigProvider; +use Igni\Application\Providers\ServiceProvider; use Igni\Container\ServiceLocator; use Igni\Storage\Driver\Connection; use Igni\Storage\MigrationManager; @@ -26,13 +29,25 @@ public static function synchronize(System $system = null): void private static function loadModules(ServiceLocator $locator): void { - $modules = System::STILUS_MODULES; + $modules = []; - foreach ($modules as $module) { + foreach (System::STILUS_MODULES as $module) { if (!class_exists($module)) { continue; } - $module = new $module; + $modules[] = new $module; + } + + foreach ($modules as $module) { + if ($module instanceof ConfigProvider) { + $module->provideConfig($locator->get(Config::class)); + } + } + + foreach ($modules as $module) { + if ($module instanceof ServiceProvider) { + $module->provideServices($locator); + } } } } diff --git a/src/api/Kernel/System.php b/src/api/Kernel/System.php index e1146e0..33c3da4 100644 --- a/src/api/Kernel/System.php +++ b/src/api/Kernel/System.php @@ -49,8 +49,7 @@ public function createDatabaseConnection(): Connection if (!ConnectionManager::has('default')) { ConnectionManager::register('default', new PdoConnection('sqlite:' . self::STILUS_DB_PATH)); } - - + return ConnectionManager::get('default'); } diff --git a/src/api/Platform/Persistence/UserSchema.php b/src/api/Platform/Persistence/UserSchema.php index add1ff4..47bf9e5 100644 --- a/src/api/Platform/Persistence/UserSchema.php +++ b/src/api/Platform/Persistence/UserSchema.php @@ -40,6 +40,6 @@ public function getVersion(): Version public static function factory(ContainerInterface $container): self { - return new self($container->get(\Igni\Storage\Driver\Connection::class)); + return new self($container->get(Connection::class)); } } From db3cefdc5aced47c07ececd47366eec0249774be Mon Sep 17 00:00:00 2001 From: Dawid Date: Tue, 25 Sep 2018 13:19:33 +0200 Subject: [PATCH 7/9] Migration Command --- .gitignore | 1 + composer.json | 8 +- composer.lock | 116 +----------------- ...rationService.php => MigrationCommand.php} | 16 ++- src/api/Kernel/System.php | 7 +- 5 files changed, 22 insertions(+), 126 deletions(-) rename src/api/Kernel/Migration/{MigrationService.php => MigrationCommand.php} (78%) diff --git a/.gitignore b/.gitignore index c0ad1d9..efd9f46 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ yarn-error.log* *.sln *.sw* /data/stilus.log +/coverage.clover diff --git a/composer.json b/composer.json index dc4c4d3..0972dbd 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,9 @@ "post-install-cmd": [ ], - "post-update-cmd": [ - "Stilus\\Kernel\\MigrationSynchronizer::synchronize" + "post-update-cmd": [], + "migrate": [ + "Stilus\\Kernel\\Migration\\MigrationCommand::synchronize" ], "ci": [ "composer validate --no-check-all --strict", @@ -40,7 +41,8 @@ "scripts-descriptions": { "phpcs": "Checks that the application code conforms to coding standard", "test-coverage": "Launches the preconfigured PHPUnit with coverage", - "ci": "Continues integration checks" + "ci": "Continues integration checks", + "migrate": "Runs migrations, example usage: composer migration 1.0.0" }, "require-dev": { "phpunit/phpunit": ">=5.7.0", diff --git a/composer.lock b/composer.lock index 7fbb2c2..1c557e2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "97c604c395f61645f5a9ff11b1bfc34c", + "content-hash": "4962ffe985eaf2b5c9d1a793169fb870", "packages": [ { "name": "cache/adapter-common", @@ -2434,120 +2434,6 @@ ], "time": "2018-07-12T10:23:15+00:00" }, - { - "name": "hamcrest/hamcrest-php", - "version": "v2.0.0", - "source": { - "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", - "shasum": "" - }, - "require": { - "php": "^5.3|^7.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" - }, - "require-dev": { - "phpunit/php-file-iterator": "1.3.3", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "hamcrest" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD" - ], - "description": "This is the PHP port of Hamcrest Matchers", - "keywords": [ - "test" - ], - "time": "2016-01-20T08:20:44+00:00" - }, - { - "name": "mockery/mockery", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "99e29d3596b16dabe4982548527d5ddf90232e99" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/99e29d3596b16dabe4982548527d5ddf90232e99", - "reference": "99e29d3596b16dabe4982548527d5ddf90232e99", - "shasum": "" - }, - "require": { - "hamcrest/hamcrest-php": "~2.0", - "lib-pcre": ">=7.0", - "php": ">=5.6.0" - }, - "require-dev": { - "phpdocumentor/phpdocumentor": "^2.9", - "phpunit/phpunit": "~5.7.10|~6.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Mockery": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" - } - ], - "description": "Mockery is a simple yet flexible PHP mock object framework", - "homepage": "https://github.com/mockery/mockery", - "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", - "test", - "test double", - "testing" - ], - "time": "2018-05-08T08:54:48+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.8.1", diff --git a/src/api/Kernel/Migration/MigrationService.php b/src/api/Kernel/Migration/MigrationCommand.php similarity index 78% rename from src/api/Kernel/Migration/MigrationService.php rename to src/api/Kernel/Migration/MigrationCommand.php index 3a290cf..9d9ae24 100644 --- a/src/api/Kernel/Migration/MigrationService.php +++ b/src/api/Kernel/Migration/MigrationCommand.php @@ -7,24 +7,32 @@ use Igni\Application\Providers\ServiceProvider; use Igni\Container\ServiceLocator; use Igni\Storage\Driver\Connection; +use Igni\Storage\Migration\Version; use Igni\Storage\MigrationManager; use Stilus\Kernel\System; +use Composer\Script\Event; -final class MigrationService +final class MigrationCommand { - public static function synchronize(System $system = null): void + public static function synchronize(Event $event, System $system = null): void { $system = $system ?? new System(); $container = $system->createServiceLocator(); $connection = $system->createDatabaseConnection(); + $container->set(Connection::class, $connection); $versionSynchronizer = new VersionSynchronizer($connection); $migrationManager = new MigrationManager($versionSynchronizer); $container->set(MigrationManager::class, $migrationManager); - $container->set(Connection::class, $connection); self::loadModules($container); - $migrationManager->migrate(); + $arguments = $event->getArguments(); + + if (isset($arguments[0])) { + $migrationManager->migrate(Version::fromString($arguments[0])); + } else { + $migrationManager->migrate(); + } } private static function loadModules(ServiceLocator $locator): void diff --git a/src/api/Kernel/System.php b/src/api/Kernel/System.php index 33c3da4..803c40b 100644 --- a/src/api/Kernel/System.php +++ b/src/api/Kernel/System.php @@ -4,14 +4,13 @@ use Igni\Application\Config; use Igni\Container\ServiceLocator; -use Igni\Storage\Driver\Connection; use Igni\Storage\Driver\ConnectionManager; +use Igni\Storage\Driver\Pdo\Connection; use Psr\Container\ContainerInterface; use Stilus\Exception\BootException; use Stilus\Platform\PlatformModule; use Symfony\Component\Yaml\Yaml; use Throwable; -use Igni\Storage\Driver\Pdo\Connection as PdoConnection; final class System { @@ -47,9 +46,9 @@ public function __construct() public function createDatabaseConnection(): Connection { if (!ConnectionManager::has('default')) { - ConnectionManager::register('default', new PdoConnection('sqlite:' . self::STILUS_DB_PATH)); + ConnectionManager::register('default', new Connection('sqlite:' . self::STILUS_DB_PATH)); } - + return ConnectionManager::get('default'); } From f562b264e7d2bbc7b1f5b1a209aa1496d82f3df5 Mon Sep 17 00:00:00 2001 From: Dawid Date: Thu, 27 Sep 2018 08:43:03 +0200 Subject: [PATCH 8/9] Migrated to new version of igni/storage --- composer.json | 2 +- composer.lock | 12 ++++++------ .../Kernel/Migration/VersionSynchronizer.php | 8 ++++---- .../Platform/Persistence/UserRepository.php | 6 ------ src/api/Platform/Persistence/UserSchema.php | 19 ++++++++++--------- .../Migration/VersionSynchronizerTest.php | 4 ++-- 6 files changed, 23 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index 0972dbd..ea54a09 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "require": { "php": ">=7.1.0", "igniphp/framework": "^2.0", - "igniphp/storage": "^0.5.0", + "igniphp/storage": "^0.6.0", "igniphp/validation": "^1.1.0", "symfony/yaml": "^4.1", "zendframework/zend-mail": "^2.10", diff --git a/composer.lock b/composer.lock index 1c557e2..b497d89 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4962ffe985eaf2b5c9d1a793169fb870", + "content-hash": "4fd5a2c04cb0024fbbe9742a325534d2", "packages": [ { "name": "cache/adapter-common", @@ -843,16 +843,16 @@ }, { "name": "igniphp/storage", - "version": "0.5.0", + "version": "0.6.0", "source": { "type": "git", "url": "https://github.com/igniphp/storage.git", - "reference": "6ee58943a600c0941ede63867a8e46fdd41d88e6" + "reference": "dbfa7f0a29a67f02b113efe448346f195180bcf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igniphp/storage/zipball/6ee58943a600c0941ede63867a8e46fdd41d88e6", - "reference": "6ee58943a600c0941ede63867a8e46fdd41d88e6", + "url": "https://api.github.com/repos/igniphp/storage/zipball/dbfa7f0a29a67f02b113efe448346f195180bcf8", + "reference": "dbfa7f0a29a67f02b113efe448346f195180bcf8", "shasum": "" }, "require": { @@ -911,7 +911,7 @@ "sqlite", "unit of work" ], - "time": "2018-09-23T11:24:48+00:00" + "time": "2018-09-27T06:35:43+00:00" }, { "name": "igniphp/uuid", diff --git a/src/api/Kernel/Migration/VersionSynchronizer.php b/src/api/Kernel/Migration/VersionSynchronizer.php index a3bfcdd..46e4251 100644 --- a/src/api/Kernel/Migration/VersionSynchronizer.php +++ b/src/api/Kernel/Migration/VersionSynchronizer.php @@ -19,7 +19,7 @@ public function __construct(Connection $connection) private function prepareMigrationTable(): void { - $cursor = $this->connection->execute( + $cursor = $this->connection->createCursor( "SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'" ); @@ -32,7 +32,7 @@ private function prepareMigrationTable(): void private function createMigrationTable(): void { - $cursor = $this->connection->execute( + $cursor = $this->connection->createCursor( "CREATE TABLE migrations ( major INTEGER NOT NULL DEFAULT 0, minor INTEGER NOT NULL DEFAULT 0, @@ -45,7 +45,7 @@ private function createMigrationTable(): void public function getVersion(): Version { - $cursor = $this->connection->execute( + $cursor = $this->connection->createCursor( 'SELECT major, minor, patch FROM migrations ORDER BY major DESC, minor DESC, patch DESC' ); @@ -61,7 +61,7 @@ public function getVersion(): Version public function setVersion(Version $version): void { - $cursor = $this->connection->execute( + $cursor = $this->connection->createCursor( 'INSERT INTO migrations (major, minor, patch) VALUES (:major, :minor, :patch)', [ $version->getMajor(), diff --git a/src/api/Platform/Persistence/UserRepository.php b/src/api/Platform/Persistence/UserRepository.php index f9afb47..514e165 100644 --- a/src/api/Platform/Persistence/UserRepository.php +++ b/src/api/Platform/Persistence/UserRepository.php @@ -7,12 +7,6 @@ class UserRepository extends Repository { - public function dropSchema(): void - { - $cursor = $this->connection->execute('DROP TABLE IF EXISTS "users"'); - $cursor->execute(); - } - public function findUserByEmail(string $email): User { $cursor = $this->query( diff --git a/src/api/Platform/Persistence/UserSchema.php b/src/api/Platform/Persistence/UserSchema.php index 47bf9e5..27c4a4e 100644 --- a/src/api/Platform/Persistence/UserSchema.php +++ b/src/api/Platform/Persistence/UserSchema.php @@ -18,19 +18,20 @@ public function __construct(Connection $connection) public function up(): void { - $cursor = $this->connection->execute('CREATE TABLE IF NOT EXISTS "users" ( - "id" char(22) PRIMARY KEY NOT NULL, - "email" char(128) NOT NULL, - "password" char(128) NOT NULL - );' - ); - $cursor->execute(); + $this->connection + ->createCursor('CREATE TABLE IF NOT EXISTS "users" ( + "id" char(22) PRIMARY KEY NOT NULL, + "email" char(128) NOT NULL, + "password" char(128) NOT NULL + )') + ->execute(); } public function down(): void { - $cursor = $this->connection->execute('DROP TABLE IF EXISTS "users"'); - $cursor->execute(); + $this->connection + ->createCursor('DROP TABLE IF EXISTS "users"') + ->execute(); } public function getVersion(): Version diff --git a/tests/api/Functional/Kernel/Migration/VersionSynchronizerTest.php b/tests/api/Functional/Kernel/Migration/VersionSynchronizerTest.php index ea99553..d9362d6 100644 --- a/tests/api/Functional/Kernel/Migration/VersionSynchronizerTest.php +++ b/tests/api/Functional/Kernel/Migration/VersionSynchronizerTest.php @@ -14,7 +14,7 @@ final class VersionSynchronizerTest extends TestCase public function setUp() { $this->createConnection(); - $this->connection->execute('DROP TABLE migrations')->execute(); + $this->connection->createCursor('DROP TABLE IF EXISTS migrations')->execute(); parent::setUp(); } @@ -34,7 +34,7 @@ public function testSetAndGetVersion(): void self::assertTrue($synchronizer->getVersion()->equalsLiteral('1.2.0')); - $cursor = $this->connection->execute('SELECT *FROM migrations'); + $cursor = $this->connection->createCursor('SELECT *FROM migrations'); self::assertCount(2, $cursor->toArray()); } } From e98419c6a4f9fe185d48cc868854c1cb05957be1 Mon Sep 17 00:00:00 2001 From: Dawid Date: Sun, 14 Oct 2018 16:46:58 +0200 Subject: [PATCH 9/9] OpenAPI docs for platform --- composer.json | 3 +- composer.lock | 114 +++++++++++++++++- src/api/Kernel/Installer.php | 25 ++++ src/api/Kernel/Migration/MigrationCommand.php | 2 +- src/api/Kernel/Module.php | 10 ++ src/api/Kernel/System.php | 33 +++-- ...CreatePlatform.php => InstallPlatform.php} | 21 +++- .../Platform/Exception/PlartformException.php | 9 ++ src/api/Platform/Exception/UserException.php | 2 +- src/api/Platform/Persistence/Platform.php | 17 +++ src/api/Platform/PlatformModule.php | 16 +-- src/api/Stilus.php | 18 ++- tests/api/Fixtures/test.db | Bin 0 -> 16384 bytes 13 files changed, 239 insertions(+), 31 deletions(-) create mode 100644 src/api/Kernel/Installer.php create mode 100644 src/api/Kernel/Module.php rename src/api/Platform/Controller/{CreatePlatform.php => InstallPlatform.php} (53%) create mode 100644 src/api/Platform/Exception/PlartformException.php create mode 100644 tests/api/Fixtures/test.db diff --git a/composer.json b/composer.json index ea54a09..8e39062 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "symfony/yaml": "^4.1", "zendframework/zend-mail": "^2.10", "zendframework/zend-crypt": "^3.3", - "league/flysystem": "^1.0" + "league/flysystem": "^1.0", + "zircote/swagger-php": "^3.0" }, "scripts": { "post-install-cmd": [ diff --git a/composer.lock b/composer.lock index b497d89..3c22999 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4fd5a2c04cb0024fbbe9742a325534d2", + "content-hash": "29fad14a5be7546f891c0ebf94758a92", "packages": [ { "name": "cache/adapter-common", @@ -1533,6 +1533,55 @@ ], "time": "2017-10-23T01:57:42+00:00" }, + { + "name": "symfony/finder", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2018-10-03T08:47:56+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.9.0", @@ -2327,6 +2376,69 @@ "zf2" ], "time": "2018-02-01T17:05:33+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf", + "reference": "8fc3bc059a7f71b3f100bcfd84a96b5b8fcf6fcf", + "shasum": "" + }, + "require": { + "doctrine/annotations": "*", + "php": ">=7.0", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=2.8" + }, + "require-dev": { + "phpunit/phpunit": ">=6.3", + "squizlabs/php_codesniffer": ">=3.3", + "zendframework/zend-form": "<2.8" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "autoload": { + "psr-4": { + "OpenApi\\": "src" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com", + "homepage": "http://www.zircote.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "http://bfanger.nl" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "time": "2018-09-30T12:19:07+00:00" } ], "packages-dev": [ diff --git a/src/api/Kernel/Installer.php b/src/api/Kernel/Installer.php new file mode 100644 index 0000000..de16fd5 --- /dev/null +++ b/src/api/Kernel/Installer.php @@ -0,0 +1,25 @@ +loadBaseConfig(); return $this->config = new Config([ - 'dir.basedir', System::STILUS_DIR, - 'dir.config' => realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['config']), - 'dir.database', realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['database']), - 'dir.themes', realpath(System::STILUS_DIR . DIRECTORY_SEPARATOR . $config['paths']['themes']), + 'dir.basedir' => System::DIR, + 'dir.config' => realpath(System::DIR . DIRECTORY_SEPARATOR . $config['paths']['config']), + 'dir.database' => realpath(System::DIR . DIRECTORY_SEPARATOR . $config['paths']['database']), + 'dir.themes' => realpath(System::DIR . DIRECTORY_SEPARATOR . $config['paths']['themes']), ]); } private function loadBaseConfig(): array { - if (!is_readable(self::STILUS_BASE_CONFIG)) { + if (!is_readable(self::BASE_CONFIG)) { throw BootException::forMissingBaseConfiguration(); } try { - return $configuration = Yaml::parseFile(self::STILUS_BASE_CONFIG); + return $configuration = Yaml::parseFile(self::BASE_CONFIG); } catch (Throwable $throwable) { throw BootException::forInvalidBaseConfiguration($throwable); } diff --git a/src/api/Platform/Controller/CreatePlatform.php b/src/api/Platform/Controller/InstallPlatform.php similarity index 53% rename from src/api/Platform/Controller/CreatePlatform.php rename to src/api/Platform/Controller/InstallPlatform.php index 85d8716..015705e 100644 --- a/src/api/Platform/Controller/CreatePlatform.php +++ b/src/api/Platform/Controller/InstallPlatform.php @@ -4,11 +4,30 @@ use Igni\Application\Http\Controller; use Igni\Network\Http\Route; +use OpenApi\Annotations as Doc; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Stilus\Platform\PlatformService; -final class CreatePlatform implements Controller +/** + * @Doc\Post( + * path="/platform", + * summary="Performs platform installation", + * tags={"platform"}, + * operationId="platformInstall", + * @Doc\Response( + * response=201, + * description="Platform successfully installed", + * @Doc\Schema(ref="#/components/schemas/Platform") + * ), + * @Doc\Response( + * response="default", + * description="Unexpected error", + * @Doc\Schema(ref="#/components/schemas/Error") + * ) + * ) + */ +final class InstallPlatform implements Controller { private $platformService; diff --git a/src/api/Platform/Exception/PlartformException.php b/src/api/Platform/Exception/PlartformException.php new file mode 100644 index 0000000..f9ed1ce --- /dev/null +++ b/src/api/Platform/Exception/PlartformException.php @@ -0,0 +1,9 @@ +register(CreatePlatform::class); + $controllers->register(InstallPlatform::class); $controllers->register(GetPlatformStatus::class); } @@ -26,9 +25,10 @@ public function provideControllers(ControllerAggregator $controllers): void public function provideServices(ContainerInterface $container): void { $container->share(PlatformService::class); + } + + public static function install(ContainerInterface $container) + { - $container - ->get(MigrationManager::class) - ->register(UserSchema::factory($container)); } } diff --git a/src/api/Stilus.php b/src/api/Stilus.php index 5103537..fb54c2b 100644 --- a/src/api/Stilus.php +++ b/src/api/Stilus.php @@ -6,6 +6,7 @@ use Stilus\Exception\BootException; use Stilus\Kernel\System; use Igni\Storage\Driver\Connection; +use OpenApi\Annotations as Doc; // Composer is autoloading this file even when test are run, this hack stops from // excecution while tests are runnnig @@ -13,6 +14,18 @@ return; } +const STILUS_VERSION = "1.0.0"; + +/** + * @Doc\Info( + * version=STILUS_VERSION, + * title="Stilus API", + * @Doc\License( + * name="BSD-3-Clause", + * url="https://www.opensource.org/licenses/BSD-3-Clause" + * ) + * ) + */ $system = new System(); // Bootstrap @@ -69,7 +82,10 @@ public function main(): void } $server = null; - if (isset($config['api']) && + + // Server should be only available in sapi mode and when proper config is set + if (php_sapi_name() == "cli" && + isset($config['api']) && isset($config['api']['http_server']) && isset($config['api']['http_server']['enable']) && $config['api']['http_server']['enable'] diff --git a/tests/api/Fixtures/test.db b/tests/api/Fixtures/test.db new file mode 100644 index 0000000000000000000000000000000000000000..8b17d50b328232cf71906450a418e951f24fb4e5 GIT binary patch literal 16384 zcmeI%%Wk7q7yw|fO(q#BrqWch+Qo>8qRv>F`hqW*#Yi^zehpv;XEqTAYzP=^E;07b z^8&N#dvx7b=sQ&ErY|tNu8OUslXR>|v#MJCM*_~_KZnEr*_(3^xUTN0#ME}?l1Jo_ zZx9qk$^?NR$cylZg~xq43M&uW=zZXc>kB0H?eC}fOC%b9jo?4TU&lW^(HlY_00JNY z0w4eaAOHd&00JNY0{^zawiS7nNF>k?OHZ1Zs_&|fyFW!)zE&C5h+&24)`+wHy))v? zbK)Q6OjphbS(BVMB$*^G_*SpNUlE zU?F+3_QVVio}WAovy0%L5d6>Z1O)^@00ck)1V8`;KmY_l00ck)1VG^bB7j9=C+Kk` zhQ>~gk9X^T?DRJT|A?Q*e~CB3Dija^0T2KI5C8!X009sH0T2Lz|D3=K!{{HkSi6(E zxa_mzq16{loEY?~!{U7CNWSWnRY#diR!X+#=j8f4n>jb=mZw|{S6-TuGZSk0zDP2> zn&Lf|@ddihT((J}K`Pp2OxcF0E(1Q(8!TfOUD;xS;Bf6(Gw3QV>n|II%ZY|&%%xzZ znr15eTJCA(g=1@yYe)~K+{o_D;F8IUR@J_31q#E^{lK>dn}Rqkkfg~>Te(@otFL{| z5DIK5b7}VkEsD|UEml`l&Ni$%GZs4);kMCVvT}B#>82u;a%I^z)s${c?Yrdkpj5S3 zCllnv!KArbar{bcq>5>m?&xHeHeG8lZWcs?oAA__8W#j5t%`aCqqAF#xv)IFNKq4I zS?8(IDol`fo9aZBjnL8YT~{?z6Mb_x)k5m9)lndE`Kyu5&M50<)NlLQl$+ytUuPMz zCM-9-QhU~FufzGohA(Ly8^!2%TWnk$H>Q4RG2y7DMls`jo78i@Z<>xSYbjS(9Y-n~ zrm8>u;ppjXePfr_`P{HYwtC&wVq~V&VzILx$s(I;aNT@*5mY98hI0DZ-fhQb$IIv8 ze;UI#2>vVnGrqxp!v6>xP(T0#KmY_l00ck)1V8`;KmY_l00h29;QJ_zcHaB+hNLT6 uPRuioM%@M^-y9w7LWehIuwCfzR!VFaI=tl(-GvVCQAECr(kF-a6#fEB4{O8# literal 0 HcmV?d00001