Merge pull request #84 from ArabCoders/dev
Dropped support for alternative database engines.
This commit is contained in:
49
README.md
49
README.md
@@ -1,22 +1,12 @@
|
||||
# Watch State Sync
|
||||
# WatchState
|
||||
|
||||
CLI based tool to sync watch state between different media servers.
|
||||
WatchState is a CLI based tool to sync your watch state between different media servers, without relying on 3rd parties
|
||||
services, like trakt.tv, This tool support `Plex Media Server`, `Emby` and `Jellyfin` out of the box currently, with
|
||||
plans for future expansion for other media servers.
|
||||
|
||||
# Introduction
|
||||
# Install
|
||||
|
||||
Ever wanted to sync your watch state without having to rely on 3rd party service like trakt.tv? then this tool is for
|
||||
you. I had multiple problems with Plex trakt.tv plugin which led to my account being banned at trakt.tv, and on top of
|
||||
that the plugin no longer supported. And I like to keep my own data locally if possible.
|
||||
|
||||
# Supported media servers.
|
||||
|
||||
* Plex
|
||||
* Emby
|
||||
* Jellyfin
|
||||
|
||||
## Install
|
||||
|
||||
create your `docker-compose.yaml` file
|
||||
create your `docker-compose.yaml` file:
|
||||
|
||||
```yaml
|
||||
version: '3.3'
|
||||
@@ -26,7 +16,7 @@ services:
|
||||
container_name: watchstate
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# For more ENV variables please read at the bottom of README.md
|
||||
# For more environment variables please read at the bottom of this page.
|
||||
WS_UID: ${UID:-1000} # Set container operation user id.
|
||||
WS_GID: ${GID:-1000} # Set container operation group id.
|
||||
ports:
|
||||
@@ -43,7 +33,7 @@ $ docker-compose up -d
|
||||
|
||||
# First time
|
||||
|
||||
Please run the following command to see all available commands you can also run help on each command to get more info.
|
||||
Run the following command to see all available commands you can also run help on each command to get more info.
|
||||
|
||||
```bash
|
||||
# Show all commands.
|
||||
@@ -153,7 +143,8 @@ valid cron expression. for example, `0 */3 * * *` it will run every three hours
|
||||
|
||||
By default, the official container includes a small http server exposed at port `80`, we officially don't support HTTPS
|
||||
inside the container for the HTTP server. However, for the adventurous people we expose port 443 as well, as such you
|
||||
can customize the Caddyfile to support SSL. and do the necessary adjustments. However, do not expect us to help with it.
|
||||
can customize the `docker/files/nginx.conf` to support SSL. and do the necessary adjustments. However, do not expect us
|
||||
to help with it.
|
||||
|
||||
#### Example nginx reverse proxy.
|
||||
|
||||
@@ -194,8 +185,8 @@ it's more secure that way.
|
||||
|
||||
#### [WEBHOOK_TOKEN]
|
||||
|
||||
Should match the server specific ``webhook.token`` value. in `server.yaml`. if the key does not exist please refer to
|
||||
the steps described at **Steps to enable webhook servers**.
|
||||
Should match the server specific ``webhook.token`` value. Refer to the steps described
|
||||
at **[Steps to enable webhook servers](#enable-webhooks-events-for-specific-server)**.
|
||||
|
||||
# Configuring Media servers to send webhook events.
|
||||
|
||||
@@ -272,8 +263,8 @@ Click `Save Changes`
|
||||
Does not send webhooks events for "marked as watched/unwatched", or you added more than 1 item at time i.e. folder
|
||||
import.
|
||||
|
||||
If you have multiuser setup, please will still report the admin account user_id as 1 even though when you get the list
|
||||
of users ids it shows completely different user ID, so when you initially set up your server for multiuser, select your
|
||||
If you have multi-user setup, Plex will still report the admin account user id as 1 even though when you get the list
|
||||
of users ids it shows completely different user ID, so when you initially set up your server for multi-user, select your
|
||||
admin account and after finishing you have to set the value manually to `1`. to do so please do the following
|
||||
|
||||
```bash
|
||||
@@ -289,15 +280,13 @@ Emby does not send webhooks events for newly added items.
|
||||
|
||||
# Jellyfin
|
||||
|
||||
None that we are aware of.
|
||||
If you don't select a user id there, sometimes the plugin will send itemAdd event without user info, and thus will fail
|
||||
the check if you happen to enable `match user id`.
|
||||
|
||||
# Globally supported environment variables.
|
||||
# Tool specific environment variables.
|
||||
|
||||
- (string) `WS_DATA_PATH` Where key data stored (config|db).
|
||||
- (string) `WS_TMP_DIR` Where temp data stored. (logs|cache). Defaults to `WS_DATA_PATH` if not set.
|
||||
- (string) `WS_STORAGE_PDO_DSN` PDO Data source Name, if you want to change from sqlite.
|
||||
- (string) `WS_STORAGE_PDO_USERNAME` PDO username
|
||||
- (string) `WS_STORAGE_PDO_PASSWORD` PDO password
|
||||
- (string) `WS_TZ` Set timezone for example, `UTC`
|
||||
- (bool) `WS_WEBHOOK_DEBUG` enable debug mode for webhook events.
|
||||
- (bool) `WS_REQUEST_DEBUG` enable debug mode for pre webhook request.
|
||||
@@ -327,7 +316,7 @@ None that we are aware of.
|
||||
follows php [strtotime](https://www.php.net/strtotime) function rules.
|
||||
- (bool) `WS_DEBUG_IMPORT` Whether to log invalid GUID items from server in `${WS_TMP_DIR}/debug`.
|
||||
|
||||
# Container specific environment variables
|
||||
# Container specific environment variables.
|
||||
|
||||
- (int) `WS_NO_CHOWN` do not change ownership needed paths inside container.
|
||||
- (int) `WS_DISABLE_HTTP` disable included HTTP Server.
|
||||
@@ -339,4 +328,4 @@ None that we are aware of.
|
||||
|
||||
# FAQ
|
||||
|
||||
For some common questions, Please take look at this [frequently asked questions](FAQ.md) page.
|
||||
For some common questions, Take look at this [frequently asked questions](FAQ.md) page.
|
||||
|
||||
@@ -19,7 +19,7 @@ return (function () {
|
||||
$config = [
|
||||
'name' => 'WatchState',
|
||||
'version' => 'v0.0.0',
|
||||
'tz' => env('WS_TS', 'UTC'),
|
||||
'tz' => env('WS_TZ', 'UTC'),
|
||||
'path' => fixPath(env('WS_DATA_PATH', fn() => env('IN_DOCKER') ? '/config' : realpath(__DIR__ . '/../var'))),
|
||||
'logs' => [
|
||||
'prune' => [
|
||||
@@ -31,18 +31,18 @@ return (function () {
|
||||
$config['tmpDir'] = fixPath(env('WS_TMP_DIR', fn() => ag($config, 'path')));
|
||||
|
||||
$config['storage'] = [
|
||||
'opts' => [
|
||||
'dsn' => env('WS_STORAGE_PDO_DSN', fn() => 'sqlite:' . ag($config, 'path') . '/db/watchstate.db'),
|
||||
'username' => env('WS_STORAGE_PDO_USERNAME', null),
|
||||
'password' => env('WS_STORAGE_PDO_PASSWORD', null),
|
||||
'exec' => [
|
||||
'sqlite' => [
|
||||
'PRAGMA journal_mode=MEMORY',
|
||||
'PRAGMA SYNCHRONOUS=OFF'
|
||||
],
|
||||
'pgsql' => [],
|
||||
'mysql' => [],
|
||||
],
|
||||
'dsn' => 'sqlite:' . ag($config, 'path') . '/db/watchstate.db',
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'options' => [
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
],
|
||||
'exec' => [
|
||||
'PRAGMA journal_mode=MEMORY',
|
||||
'PRAGMA SYNCHRONOUS=OFF'
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -51,9 +51,26 @@ return (function (): array {
|
||||
'shared' => false,
|
||||
],
|
||||
|
||||
PDO::class => [
|
||||
'class' => function (): PDO {
|
||||
$pdo = new PDO(dsn: Config::get('storage.dsn'), options: [
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
|
||||
foreach (Config::get('storage.exec', []) as $cmd) {
|
||||
$pdo->exec($cmd);
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
},
|
||||
],
|
||||
|
||||
StorageInterface::class => [
|
||||
'class' => function (LoggerInterface $logger): StorageInterface {
|
||||
$adapter = (new PDOAdapter($logger))->setUp(Config::get('storage.opts', []));
|
||||
'class' => function (LoggerInterface $logger, PDO $pdo): StorageInterface {
|
||||
$adapter = new PDOAdapter($logger, $pdo);
|
||||
|
||||
if (true !== $adapter->isMigrated()) {
|
||||
$adapter->migrations(StorageInterface::MIGRATE_UP);
|
||||
@@ -63,6 +80,7 @@ return (function (): array {
|
||||
},
|
||||
'args' => [
|
||||
LoggerInterface::class,
|
||||
PDO::class,
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -29,11 +29,10 @@ final class EnvCommand extends Command
|
||||
}
|
||||
|
||||
(new Table($output))->setStyle('box')
|
||||
->setHeaders(['Key', 'Value'])
|
||||
->setHeaders(['Environment Key', 'Environment Value'])
|
||||
->setRows($keys)
|
||||
->render();
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
-- # migrate_up
|
||||
|
||||
CREATE TABLE `state`
|
||||
(
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(50) NOT NULL,
|
||||
`updated` int(11) NOT NULL,
|
||||
`watched` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`meta` text DEFAULT NULL,
|
||||
`guid_plex` varchar(255) DEFAULT NULL,
|
||||
`guid_imdb` varchar(255) DEFAULT NULL,
|
||||
`guid_tvdb` varchar(255) DEFAULT NULL,
|
||||
`guid_tmdb` varchar(255) DEFAULT NULL,
|
||||
`guid_tvmaze` varchar(255) DEFAULT NULL,
|
||||
`guid_tvrage` varchar(255) DEFAULT NULL,
|
||||
`guid_anidb` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `type` (`type`),
|
||||
KEY `watched` (`watched`),
|
||||
KEY `updated` (`updated`),
|
||||
KEY `guid_plex` (`guid_plex`),
|
||||
KEY `guid_imdb` (`guid_imdb`),
|
||||
KEY `guid_tvdb` (`guid_tvdb`),
|
||||
KEY `guid_tmdb` (`guid_tmdb`),
|
||||
KEY `guid_tvmaze` (`guid_tvmaze`),
|
||||
KEY `guid_tvrage` (`guid_tvrage`),
|
||||
KEY `guid_anidb` (`guid_anidb`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- # migrate_down
|
||||
|
||||
DROP TABLE IF EXISTS `state`;
|
||||
@@ -1,35 +0,0 @@
|
||||
-- # migrate_up
|
||||
|
||||
CREATE SEQUENCE state_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
|
||||
|
||||
CREATE TABLE "state" (
|
||||
"id" integer DEFAULT nextval('state_id_seq') NOT NULL,
|
||||
"type" character varying NOT NULL,
|
||||
"updated" integer NOT NULL,
|
||||
"watched" smallint NOT NULL,
|
||||
"meta" json,
|
||||
"guid_plex" character varying,
|
||||
"guid_imdb" character varying,
|
||||
"guid_tvdb" character varying,
|
||||
"guid_tmdb" character varying,
|
||||
"guid_tvmaze" character varying,
|
||||
"guid_tvrage" character varying,
|
||||
"guid_anidb" character varying,
|
||||
CONSTRAINT "state_pkey" PRIMARY KEY ("id")
|
||||
) WITH (oids = false);
|
||||
|
||||
CREATE INDEX "state_guid_anidb" ON "state" USING btree ("guid_anidb");
|
||||
CREATE INDEX "state_guid_imdb" ON "state" USING btree ("guid_imdb");
|
||||
CREATE INDEX "state_guid_plex" ON "state" USING btree ("guid_plex");
|
||||
CREATE INDEX "state_guid_tmdb" ON "state" USING btree ("guid_tmdb");
|
||||
CREATE INDEX "state_guid_tvdb" ON "state" USING btree ("guid_tvdb");
|
||||
CREATE INDEX "state_guid_tvmaze" ON "state" USING btree ("guid_tvmaze");
|
||||
CREATE INDEX "state_guid_tvrage" ON "state" USING btree ("guid_tvrage");
|
||||
CREATE INDEX "state_type" ON "state" USING btree ("type");
|
||||
CREATE INDEX "state_updated" ON "state" USING btree ("updated");
|
||||
CREATE INDEX "state_watched" ON "state" USING btree ("watched");
|
||||
|
||||
-- # migrate_down
|
||||
|
||||
DROP TABLE IF EXISTS "state";
|
||||
DROP SEQUENCE IF EXISTS state_id_seq;
|
||||
@@ -18,13 +18,6 @@ use Psr\Log\LoggerInterface;
|
||||
|
||||
final class PDOAdapter implements StorageInterface
|
||||
{
|
||||
private array $supported = [
|
||||
'sqlite',
|
||||
'mysql',
|
||||
'pgsql'
|
||||
];
|
||||
|
||||
private PDO|null $pdo = null;
|
||||
private bool $viaCommit = false;
|
||||
|
||||
private bool $singleTransaction = false;
|
||||
@@ -39,57 +32,12 @@ final class PDOAdapter implements StorageInterface
|
||||
'update' => null,
|
||||
];
|
||||
|
||||
public function __construct(private LoggerInterface $logger)
|
||||
public function __construct(private LoggerInterface $logger, private PDO $pdo)
|
||||
{
|
||||
}
|
||||
|
||||
public function setUp(array $opts): StorageInterface
|
||||
{
|
||||
if (null === ($opts['dsn'] ?? null)) {
|
||||
throw new StorageException('No storage.opts.dsn (Data Source Name) was provided.', 10);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->pdo = new PDO(
|
||||
$opts['dsn'], $opts['username'] ?? null, $opts['password'] ?? null,
|
||||
array_replace_recursive(
|
||||
[
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
],
|
||||
$opts['options'] ?? []
|
||||
)
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
throw new StorageException(sprintf('Unable to connect to storage backend. \'%s\'.', $e->getMessage()));
|
||||
}
|
||||
|
||||
$driver = $this->getDriver();
|
||||
|
||||
if (!in_array($driver, $this->supported)) {
|
||||
throw new StorageException(sprintf('%s Driver is not supported.', $driver), 11);
|
||||
}
|
||||
|
||||
if (null !== ($exec = ag($opts, "exec.{$driver}")) && is_array($exec)) {
|
||||
foreach ($exec as $cmd) {
|
||||
$this->pdo->exec($cmd);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function insert(StateInterface $entity): StateInterface
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $entity->getAll();
|
||||
|
||||
@@ -146,13 +94,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function getAll(DateTimeInterface|null $date = null, StateInterface|null $class = null): array
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
$arr = [];
|
||||
|
||||
$sql = 'SELECT * FROM state';
|
||||
@@ -174,13 +115,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function update(StateInterface $entity): StateInterface
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $entity->getAll();
|
||||
|
||||
@@ -213,13 +147,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function matchAnyId(array $ids, StateInterface|null $class = null): StateInterface|null
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
if (null === $class) {
|
||||
$class = Container::get(StateInterface::class);
|
||||
}
|
||||
@@ -277,13 +204,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function remove(StateInterface $entity): bool
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
if (null === $entity->id && !$entity->hasGuids()) {
|
||||
return false;
|
||||
}
|
||||
@@ -309,13 +229,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function commit(array $entities): array
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
return $this->transactional(function () use ($entities) {
|
||||
$list = [
|
||||
StateInterface::TYPE_MOVIE => ['added' => 0, 'updated' => 0, 'failed' => 0],
|
||||
@@ -360,13 +273,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function migrations(string $dir, array $opts = []): mixed
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
$class = new PDOMigrations($this->pdo, $this->logger);
|
||||
|
||||
return match (strtolower($dir)) {
|
||||
@@ -378,13 +284,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function isMigrated(): bool
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
return (new PDOMigrations($this->pdo, $this->logger))->isMigrated();
|
||||
}
|
||||
|
||||
@@ -393,13 +292,6 @@ final class PDOAdapter implements StorageInterface
|
||||
*/
|
||||
public function makeMigration(string $name, array $opts = []): mixed
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new StorageException(
|
||||
afterLast(__CLASS__, '\\') . '->setUp(): method was not called.',
|
||||
StorageException::SETUP_NOT_CALLED
|
||||
);
|
||||
}
|
||||
|
||||
return (new PDOMigrations($this->pdo, $this->logger))->make($name);
|
||||
}
|
||||
|
||||
@@ -417,10 +309,6 @@ final class PDOAdapter implements StorageInterface
|
||||
|
||||
public function getPdo(): PDO
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
throw new \RuntimeException('PDO is not initialized yet.');
|
||||
}
|
||||
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
@@ -469,22 +357,6 @@ final class PDOAdapter implements StorageInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PDO Driver.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getDriver(): string
|
||||
{
|
||||
$driver = $this->pdo->getAttribute($this->pdo::ATTR_DRIVER_NAME);
|
||||
|
||||
if (empty($driver) || !is_string($driver)) {
|
||||
$driver = 'unknown';
|
||||
}
|
||||
|
||||
return strtolower($driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SQL Insert Statement.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Libs\Storage\PDO;
|
||||
|
||||
use App\Libs\Config;
|
||||
use App\Libs\Storage\StorageInterface;
|
||||
use Exception;
|
||||
use PDO;
|
||||
@@ -14,15 +13,13 @@ use RuntimeException;
|
||||
final class PDOMigrations
|
||||
{
|
||||
private string $path;
|
||||
private string $versionFile;
|
||||
private string $driver;
|
||||
private array $files = [];
|
||||
|
||||
public function __construct(private PDO $pdo, private LoggerInterface $logger)
|
||||
{
|
||||
$this->path = __DIR__ . '/Migrations';
|
||||
$this->path = __DIR__ . '/../../../../migrations';
|
||||
$this->driver = $this->getDriver();
|
||||
$this->versionFile = Config::get('path') . sprintf('/db/%s.migration', $this->driver);
|
||||
}
|
||||
|
||||
public function setLogger(LoggerInterface $logger): self
|
||||
@@ -142,24 +139,12 @@ final class PDOMigrations
|
||||
|
||||
private function getVersion(): int
|
||||
{
|
||||
if ('sqlite' === $this->driver) {
|
||||
return (int)$this->pdo->query('PRAGMA user_version')->fetchColumn();
|
||||
}
|
||||
|
||||
if (file_exists($this->versionFile)) {
|
||||
return (int)file_get_contents($this->versionFile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (int)$this->pdo->query('PRAGMA user_version')->fetchColumn();
|
||||
}
|
||||
|
||||
private function setVersion(int $version): void
|
||||
{
|
||||
if ('sqlite' === $this->driver) {
|
||||
$this->pdo->exec('PRAGMA user_version = ' . $version);
|
||||
} else {
|
||||
file_put_contents($this->versionFile, $version);
|
||||
}
|
||||
$this->pdo->exec('PRAGMA user_version = ' . $version);
|
||||
}
|
||||
|
||||
private function getDriver(): string
|
||||
|
||||
@@ -16,15 +16,6 @@ interface StorageInterface
|
||||
|
||||
public const MIGRATE_DOWN = 'down';
|
||||
|
||||
/**
|
||||
* Initiate Driver.
|
||||
*
|
||||
* @param array $opts
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUp(array $opts): self;
|
||||
|
||||
/**
|
||||
* Insert Entity immediately.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Libs\Extends\CliLogger;
|
||||
use App\Libs\Mappers\Export\ExportMapper;
|
||||
use App\Libs\Storage\PDO\PDOAdapter;
|
||||
use App\Libs\Storage\StorageInterface;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
@@ -31,8 +32,7 @@ class ExportMapperTest extends TestCase
|
||||
|
||||
$logger = new CliLogger($this->output);
|
||||
|
||||
$this->storage = new PDOAdapter($logger);
|
||||
$this->storage->setUp(['dsn' => 'sqlite::memory:']);
|
||||
$this->storage = new PDOAdapter($logger, new PDO('sqlite::memory:'));
|
||||
$this->storage->migrations('up');
|
||||
|
||||
$this->mapper = new ExportMapper($this->storage);
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Libs\Extends\CliLogger;
|
||||
use App\Libs\Mappers\Import\DirectMapper;
|
||||
use App\Libs\Storage\PDO\PDOAdapter;
|
||||
use App\Libs\Storage\StorageInterface;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
@@ -32,8 +33,7 @@ class DirectMapperTest extends TestCase
|
||||
|
||||
$logger = new CliLogger($this->output);
|
||||
|
||||
$this->storage = new PDOAdapter($logger);
|
||||
$this->storage->setUp(['dsn' => 'sqlite::memory:']);
|
||||
$this->storage = new PDOAdapter($logger, new PDO('sqlite::memory:'));
|
||||
$this->storage->migrations('up');
|
||||
|
||||
$this->mapper = new DirectMapper($logger, $this->storage);
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Libs\Extends\CliLogger;
|
||||
use App\Libs\Mappers\Import\MemoryMapper;
|
||||
use App\Libs\Storage\PDO\PDOAdapter;
|
||||
use App\Libs\Storage\StorageInterface;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
@@ -32,8 +33,7 @@ class MemoryMapperTest extends TestCase
|
||||
|
||||
$logger = new CliLogger($this->output);
|
||||
|
||||
$this->storage = new PDOAdapter($logger);
|
||||
$this->storage->setUp(['dsn' => 'sqlite::memory:']);
|
||||
$this->storage = new PDOAdapter($logger, new PDO('sqlite::memory:'));
|
||||
$this->storage->migrations('up');
|
||||
|
||||
$this->mapper = new MemoryMapper($logger, $this->storage);
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Libs\Storage\StorageException;
|
||||
use App\Libs\Storage\StorageInterface;
|
||||
use DateTimeImmutable;
|
||||
use Error;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
@@ -31,37 +32,10 @@ class PDOAdapterTest extends TestCase
|
||||
$this->testMovie = require __DIR__ . '/../Fixtures/MovieEntity.php';
|
||||
$this->testEpisode = require __DIR__ . '/../Fixtures/EpisodeEntity.php';
|
||||
|
||||
$this->storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$this->storage->setUp(['dsn' => 'sqlite::memory:']);
|
||||
$this->storage = new PDOAdapter(new CliLogger($this->output), new PDO('sqlite::memory:'));
|
||||
$this->storage->migrations('up');
|
||||
}
|
||||
|
||||
/** StorageInterface::setUp */
|
||||
public function test_setup_throw_exception_if_no_dsn(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(10);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->setUp([]);
|
||||
}
|
||||
|
||||
public function test_setup_throw_exception_if_invalid_dsn(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->setUp(['dsn' => 'not_real_driver::foo']);
|
||||
}
|
||||
|
||||
/** StorageInterface::insert */
|
||||
public function test_insert_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
|
||||
$storage->insert(new StateEntity([]));
|
||||
}
|
||||
|
||||
public function test_insert_throw_exception_if_has_id(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
@@ -77,15 +51,6 @@ class PDOAdapterTest extends TestCase
|
||||
$this->assertSame(1, $item->id);
|
||||
}
|
||||
|
||||
/** StorageInterface::get */
|
||||
public function test_get_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->get(new StateEntity([]));
|
||||
}
|
||||
|
||||
public function test_get_conditions(): void
|
||||
{
|
||||
$item = new StateEntity($this->testEpisode);
|
||||
@@ -102,15 +67,6 @@ class PDOAdapterTest extends TestCase
|
||||
$this->assertSame($modified->getAll(), $this->storage->get($modified)->getAll());
|
||||
}
|
||||
|
||||
/** StorageInterface::getAll */
|
||||
public function test_getAll_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->getAll();
|
||||
}
|
||||
|
||||
public function test_getAll_call_without_initialized_container(): void
|
||||
{
|
||||
$this->expectException(Error::class);
|
||||
@@ -132,15 +88,6 @@ class PDOAdapterTest extends TestCase
|
||||
$this->assertCount(0, $this->storage->getAll(date: new DateTimeImmutable('now'), class: $item));
|
||||
}
|
||||
|
||||
/** StorageInterface::update */
|
||||
public function test_update_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->update(new StateEntity([]));
|
||||
}
|
||||
|
||||
public function test_update_call_without_id_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
@@ -161,15 +108,6 @@ class PDOAdapterTest extends TestCase
|
||||
$this->assertSame($updatedItem->getAll(), $this->storage->get($item)->getAll());
|
||||
}
|
||||
|
||||
/** StorageInterface::update */
|
||||
public function test_matchAnyId_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->matchAnyId([]);
|
||||
}
|
||||
|
||||
public function test_matchAnyId_call_without_initialized_container(): void
|
||||
{
|
||||
$this->expectException(Error::class);
|
||||
@@ -209,15 +147,6 @@ class PDOAdapterTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/** StorageInterface::remove */
|
||||
public function test_remove_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->remove(new StateEntity([]));
|
||||
}
|
||||
|
||||
public function test_remove_conditions(): void
|
||||
{
|
||||
$item1 = new StateEntity($this->testEpisode);
|
||||
@@ -237,15 +166,6 @@ class PDOAdapterTest extends TestCase
|
||||
$this->assertFalse($this->storage->remove($item3));
|
||||
}
|
||||
|
||||
/** StorageInterface::commit */
|
||||
public function test_commit_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->commit([]);
|
||||
}
|
||||
|
||||
public function test_commit_conditions(): void
|
||||
{
|
||||
$item1 = new StateEntity($this->testEpisode);
|
||||
@@ -271,15 +191,6 @@ class PDOAdapterTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/** StorageInterface::migrations */
|
||||
public function test_migrations_call_without_setup_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
$this->expectExceptionCode(StorageException::SETUP_NOT_CALLED);
|
||||
$storage = new PDOAdapter(new CliLogger($this->output));
|
||||
$storage->migrations('f');
|
||||
}
|
||||
|
||||
public function test_migrations_call_with_wrong_direction_exception(): void
|
||||
{
|
||||
$this->expectException(StorageException::class);
|
||||
|
||||
Reference in New Issue
Block a user