Bladeren bron

Merge pull request #57 from knuckleswtf/complete-notransaction-handling

Complete handling of no transaction support
Shalvah 4 jaren geleden
bovenliggende
commit
55c09c306e

+ 5 - 2
config/scribe.php

@@ -333,7 +333,10 @@ INTRO
     'routeMatcher' => \Knuckles\Scribe\Matching\RouteMatcher::class,
 
     /**
-     * [Advanced usage] If a database driver does not support transactions, you can list it here to allow it to run.
+     * [Advanced usage] If one of your app's database drivers does not support transactions,
+     * docs generation (instantiating Eloquent models and making response calls) will likely fail.
+     * To avoid that, you can add the driver class name here.
+     * Be warned: that means all database changes will persist.
      */
-    'run_without_database_transactions' => [],
+    'continue_without_database_transactions' => [],
 ];

+ 13 - 4
docs/config.md

@@ -205,9 +205,6 @@ When generating example requests, this package uses the fzanninoto/faker package
 .. Tip:: Alternatively, you can set example values for parameters when `documenting them <documenting.html>`_.
 ```
 
-### `routeMatcher`
-The route matcher class provides the algorithm that determines what routes should be documented. The default matcher used is the included `\Knuckles\Scribe\Matching\RouteMatcher::class`, and you can provide your own custom implementation if you wish to programmatically change the algorithm. The provided matcher should be an instance of `\Knuckles\Scribe\Matching\RouteMatcherInterface`.
-
 ### `fractal`
 This section only applies if you're using [transformers](https://fractal.thephpleague.com/transformers/) for your API (via the league/fractal package), and documenting responses with `@transformer` and `@transformerCollection`. Here, you configure how responses are transformed.
 
@@ -217,4 +214,16 @@ This section only applies if you're using [transformers](https://fractal.thephpl
   - `\League\Fractal\Serializer\JsonApiSerializer::class`
 
   Leave this as `null` to use no serializer or return a simple JSON.
-     
+
+
+### `routeMatcher`
+The route matcher class provides the algorithm that determines what routes should be documented. The default matcher used is the included `\Knuckles\Scribe\Matching\RouteMatcher::class`, and you can provide your own custom implementation if you wish to programmatically change the algorithm. The provided matcher should be an instance of `\Knuckles\Scribe\Matching\RouteMatcherInterface`.
+
+### `continue_without_database_transactions`
+By default, Scribe runs response calls and example model creation in a database transaction, and then rolls them back so no changes are persisted. If one of your database drivers does not support database transactions, Scribe will log an error and exit. If you would like Scribe to proceed (and persist the data), add the database driver class name to this array. For example:
+
+```php
+'continue_without_database_transactions' => [
+    Jenssegers\Mongodb\Connection::class,
+],
+```

+ 2 - 2
docs/documenting/documenting-endpoint-responses.md

@@ -122,7 +122,7 @@ This JSON string will be parsed and merged with the response from the file.
 If you don't specify an example response using any of the other means described in this document, Scribe will attempt to get a sample response by making a HTTP request to the local endpoint (known as a "response call").
 
 ```eval_rst
-.. Note:: Response calls are done within a database transaction and changes are rolled back afterwards, so no data is persisted.
+.. Note:: Response calls are done within a database transaction and changes are rolled back afterwards, so no data is persisted. If your database connection does not support transactions, you should add it to `continue_without_database_transactions`, but be warned that data from response calls will be persisted.
 ```
 
 The configuration for response calls is located in the `apply.response_calls` section for each route group in `config/scribe.php`. This means that You can apply different settings for different sets of routes. Here are some important things to note:
@@ -280,7 +280,7 @@ When generating responses from `@apiResource` and `@transformer` tags, Scribe ne
 1. First, it tries the Eloquent model factory: `factory(YourModel::class)->create()`. 
 
 ```eval_rst
-.. Note:: Scribe uses :code:`create()` instead of :code:`make()` when calling the factory, but runs it in a database transaction which is rolled back afterwards, so no data is persisted.
+.. Note:: Scribe uses :code:`create()` instead of :code:`make()` when calling the factory, but runs it in a database transaction which is rolled back afterwards, so no data is persisted. If your database connection does not support transactions, you should add it to `continue_without_database_transactions`, but be warned that created models will be persisted.
 ```
 
 2. If that fails, Scribe calls `YourModel::first()` to retrieve the first model from the database. 

+ 18 - 0
src/Exceptions/DatabaseTransactionsNotSupported.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Knuckles\Scribe\Exceptions;
+
+use Knuckles\Scribe\Exceptions\ScribeException;
+use RuntimeException;
+
+class DatabaseTransactionsNotSupported extends RuntimeException implements ScribeException
+{
+    public static function create(string $connectionName, string $driverName)
+    {
+        return new self(
+            "Database Driver [{$driverName}] for connection [{$connectionName}] does not support transactions. " .
+            "Changes to your database will be persistent. " .
+            "To allow this, add \"{$driverName}\" to the \"continue_without_database_transactions\" config."
+        );
+    }
+}

+ 0 - 18
src/Exceptions/DbTransactionSupportException.php

@@ -1,18 +0,0 @@
-<?php
-
-namespace Knuckles\Scribe\Exceptions;
-
-use Knuckles\Scribe\Exceptions\ScribeException;
-use RuntimeException;
-
-class DbTransactionSupportException extends RuntimeException implements ScribeException
-{
-    public static function create(string $connection_name, string $driver_name)
-    {
-        return new self(
-            "Database Driver [{$driver_name}] for connection [{$connection_name}] does not support transactions. " .
-            "Changes to your database will be persistent. " .
-            "To allow this, add \"{$connection_name}\" to the \"run_without_database_transactions\" config."
-        );
-    }
-}

+ 20 - 33
src/Extracting/DatabaseTransactionHelpers.php

@@ -3,22 +3,20 @@
 namespace Knuckles\Scribe\Extracting;
 
 use Exception;
-use Knuckles\Scribe\Exceptions\DbTransactionSupportException;
+use Knuckles\Scribe\Exceptions\DatabaseTransactionsNotSupported;
 use Knuckles\Scribe\Exceptions\ScribeException;
 use Knuckles\Scribe\Tools\ConsoleOutputUtils as c;
+use Knuckles\Scribe\Tools\DocumentationConfig;
 
 trait DatabaseTransactionHelpers
 {
-    /**
-     * @return void
-     */
     private function startDbTransaction()
     {
         $connections = array_keys(config('database.connections', []));
 
-        foreach ($connections as $conn) {
+        foreach ($connections as $connection) {
             try {
-                $driver = app('db')->connection($conn);
+                $driver = app('db')->connection($connection);
 
                 if (self::driverSupportsTransactions($driver)) {
                     $driver->beginTransaction();
@@ -26,12 +24,13 @@ trait DatabaseTransactionHelpers
                     return;
                 }
 
-                if ($this->isNoTransactionSupportAllowed($conn)) {
-                    throw DbTransactionSupportException::create($conn, get_class($driver));
+                $driverClassName = get_class($driver);
+
+                if ($this->shouldAllowDatabasePersistence($driverClassName)) {
+                    throw DatabaseTransactionsNotSupported::create($connection, $driverClassName);
                 }
 
-                c::warn("Database driver for the connection [{$conn}] does not support transactions!");
-                c::warn("Any changes made to your database will persist!");
+                c::warn("Database driver [$driverClassName] for the connection [{$connection}] does not support transactions. Any changes made to your database will persist.");
             } catch (ScribeException $e) {
                 throw $e;
             } catch (Exception $e) {
@@ -46,9 +45,9 @@ trait DatabaseTransactionHelpers
     {
         $connections = array_keys(config('database.connections', []));
 
-        foreach ($connections as $conn) {
+        foreach ($connections as $connection) {
             try {
-                $driver = app('db')->connection($conn);
+                $driver = app('db')->connection($connection);
 
                 if (self::driverSupportsTransactions($driver)) {
                     $driver->rollBack();
@@ -56,23 +55,16 @@ trait DatabaseTransactionHelpers
                     return;
                 }
 
-                c::warn("Database driver for the connection [{$conn}] does not support transactions!");
-                c::warn("Any changes made to your database will persist!");
+                $driverClassName = get_class($driver);
+                c::warn("Database driver [$driverClassName] for the connection [{$connection}] does not support transactions. Any changes made to your database have been persisted.");
             } catch (Exception $e) {
             }
         }
     }
 
-    /**
-     * Assesses whether or not the "PDO" driver provided supports transactions
-     *
-     * @param mixed $driver Driver prodicted for particular connection
-     *
-     * @return boolean
-     */
-    private static function driverSupportsTransactions($driver)
+    private static function driverSupportsTransactions($driver): bool
     {
-        $methods = [ 'beginTransaction', 'rollback' ];
+        $methods = ['beginTransaction', 'rollback'];
 
         foreach ($methods as $method) {
             if (! method_exists($driver, $method)) {
@@ -86,21 +78,16 @@ trait DatabaseTransactionHelpers
     /**
      * Assesses whether drivers without transaction support can proceed
      *
-     * @param string $connection_name Name of the connection
+     * @param string $driverClassName
      *
-     * @return boolean
+     * @return bool
      */
-    private function isNoTransactionSupportAllowed(string $connection_name)
+    private function shouldAllowDatabasePersistence(string $driverClassName): bool
     {
         $config = $this->getConfig();
 
-        $allow_list = $config->get('run_without_database_transactions', false);
-
-        if (is_array($allow_list)) {
-            return in_array($connection_name, $allow_list);
-        }
-
-        return false;
+        $whitelistedDrivers = $config->get('continue_without_database_transactions', []);
+        return in_array($driverClassName, $whitelistedDrivers);
     }
 
     /**