From a50153d221ba613278a866483e8635a671507d5c Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 14 Apr 2018 22:17:47 +0100
Subject: [PATCH] Slimmed down testing DB sized and improved permission caching

---
 app/Chapter.php                       |  2 --
 app/Page.php                          |  1 -
 app/Services/PermissionService.php    | 40 +++++++++++++++------------
 database/seeds/DummyContentSeeder.php |  6 ++--
 database/seeds/LargeContentSeeder.php | 27 ++++++++++++++++++
 5 files changed, 52 insertions(+), 24 deletions(-)
 create mode 100644 database/seeds/LargeContentSeeder.php

diff --git a/app/Chapter.php b/app/Chapter.php
index 3726c57f4..88b4c134c 100644
--- a/app/Chapter.php
+++ b/app/Chapter.php
@@ -6,8 +6,6 @@ class Chapter extends Entity
 
     protected $fillable = ['name', 'description', 'priority', 'book_id'];
 
-    protected $with = ['book'];
-
     /**
      * Get the book this chapter is within.
      * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
diff --git a/app/Page.php b/app/Page.php
index e169a1959..38feb610d 100644
--- a/app/Page.php
+++ b/app/Page.php
@@ -6,7 +6,6 @@ class Page extends Entity
 
     protected $simpleAttributes = ['name', 'id', 'slug'];
 
-    protected $with = ['book'];
     public $textField = 'text';
 
     /**
diff --git a/app/Services/PermissionService.php b/app/Services/PermissionService.php
index e74801dc8..0dd316b34 100644
--- a/app/Services/PermissionService.php
+++ b/app/Services/PermissionService.php
@@ -67,13 +67,19 @@ class PermissionService
 
     /**
      * Prepare the local entity cache and ensure it's empty
+     * @param Entity[] $entities
      */
-    protected function readyEntityCache()
+    protected function readyEntityCache($entities = [])
     {
-        $this->entityCache = [
-            'books' => collect(),
-            'chapters' => collect()
-        ];
+        $this->entityCache = [];
+
+        foreach ($entities as $entity) {
+            $type = $entity->getType();
+            if (!isset($this->entityCache[$type])) {
+                $this->entityCache[$type] = collect();
+            }
+            $this->entityCache[$type]->put($entity->id, $entity);
+        }
     }
 
     /**
@@ -83,17 +89,14 @@ class PermissionService
      */
     protected function getBook($bookId)
     {
-        if (isset($this->entityCache['books']) && $this->entityCache['books']->has($bookId)) {
-            return $this->entityCache['books']->get($bookId);
+        if (isset($this->entityCache['book']) && $this->entityCache['book']->has($bookId)) {
+            return $this->entityCache['book']->get($bookId);
         }
 
         $book = $this->book->find($bookId);
         if ($book === null) {
             $book = false;
         }
-        if (isset($this->entityCache['books'])) {
-            $this->entityCache['books']->put($bookId, $book);
-        }
 
         return $book;
     }
@@ -105,17 +108,14 @@ class PermissionService
      */
     protected function getChapter($chapterId)
     {
-        if (isset($this->entityCache['chapters']) && $this->entityCache['chapters']->has($chapterId)) {
-            return $this->entityCache['chapters']->get($chapterId);
+        if (isset($this->entityCache['chapter']) && $this->entityCache['chapter']->has($chapterId)) {
+            return $this->entityCache['chapter']->get($chapterId);
         }
 
         $chapter = $this->chapter->find($chapterId);
         if ($chapter === null) {
             $chapter = false;
         }
-        if (isset($this->entityCache['chapters'])) {
-            $this->entityCache['chapters']->put($chapterId, $chapter);
-        }
 
         return $chapter;
     }
@@ -179,6 +179,7 @@ class PermissionService
      * @param Collection $books
      * @param array $roles
      * @param bool $deleteOld
+     * @throws \Throwable
      */
     protected function buildJointPermissionsForBooks($books, $roles, $deleteOld = false)
     {
@@ -250,7 +251,7 @@ class PermissionService
         $this->deleteManyJointPermissionsForRoles($roles);
 
         // Chunk through all books
-        $this->bookFetchQuery()->chunk(5, function ($books) use ($roles) {
+        $this->bookFetchQuery()->chunk(20, function ($books) use ($roles) {
             $this->buildJointPermissionsForBooks($books, $roles);
         });
     }
@@ -279,6 +280,7 @@ class PermissionService
     /**
      * Delete the entity jointPermissions for a particular entity.
      * @param Entity $entity
+     * @throws \Throwable
      */
     public function deleteJointPermissionsForEntity(Entity $entity)
     {
@@ -288,6 +290,7 @@ class PermissionService
     /**
      * Delete all of the entity jointPermissions for a list of entities.
      * @param Entity[] $entities
+     * @throws \Throwable
      */
     protected function deleteManyJointPermissionsForEntities($entities)
     {
@@ -314,10 +317,11 @@ class PermissionService
      * Create & Save entity jointPermissions for many entities and jointPermissions.
      * @param Collection $entities
      * @param array $roles
+     * @throws \Throwable
      */
     protected function createManyJointPermissions($entities, $roles)
     {
-        $this->readyEntityCache();
+        $this->readyEntityCache($entities);
         $jointPermissions = [];
 
         // Fetch Entity Permissions and create a mapping of entity restricted statuses
@@ -342,7 +346,7 @@ class PermissionService
         // Create a mapping of role permissions
         $rolePermissionMap = [];
         foreach ($roles as $role) {
-            foreach ($role->getRelationValue('permissions') as $permission) {
+            foreach ($role->permissions as $permission) {
                 $rolePermissionMap[$role->getRawAttribute('id') . ':' . $permission->getRawAttribute('name')] = true;
             }
         }
diff --git a/database/seeds/DummyContentSeeder.php b/database/seeds/DummyContentSeeder.php
index 15cf9b4de..41ac6650d 100644
--- a/database/seeds/DummyContentSeeder.php
+++ b/database/seeds/DummyContentSeeder.php
@@ -21,11 +21,11 @@ class DummyContentSeeder extends Seeder
         $role = \BookStack\Role::getRole('viewer');
         $viewerUser->attachRole($role);
 
-        factory(\BookStack\Book::class, 20)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
+        factory(\BookStack\Book::class, 5)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
             ->each(function($book) use ($editorUser) {
-                $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
+                $chapters = factory(\BookStack\Chapter::class, 3)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
                     ->each(function($chapter) use ($editorUser, $book){
-                        $pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'book_id' => $book->id]);
+                        $pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'book_id' => $book->id]);
                         $chapter->pages()->saveMany($pages);
                     });
                 $pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
diff --git a/database/seeds/LargeContentSeeder.php b/database/seeds/LargeContentSeeder.php
new file mode 100644
index 000000000..129ede997
--- /dev/null
+++ b/database/seeds/LargeContentSeeder.php
@@ -0,0 +1,27 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class LargeContentSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        // Create an editor user
+        $editorUser = factory(\BookStack\User::class)->create();
+        $editorRole = \BookStack\Role::getRole('editor');
+        $editorUser->attachRole($editorRole);
+
+        $largeBook = factory(\BookStack\Book::class)->create(['name' => 'Large book' . str_random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
+        $pages = factory(\BookStack\Page::class, 200)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
+        $chapters = factory(\BookStack\Chapter::class, 50)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
+        $largeBook->pages()->saveMany($pages);
+        $largeBook->chapters()->saveMany($chapters);
+        app(\BookStack\Services\PermissionService::class)->buildJointPermissions();
+        app(\BookStack\Services\SearchService::class)->indexAllEntities();
+    }
+}