From 168923d3da50949733ab32b4e9f3a7c9677fed5a Mon Sep 17 00:00:00 2001 From: Victor Gonzalex Date: Tue, 17 Mar 2020 16:31:17 -0400 Subject: [PATCH 1/4] wip getting the Storage driver to work for remote json files --- src/Updater.php | 64 ++++++++++--- tests/Unit/UpdaterRemoteTest.php | 157 +++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 tests/Unit/UpdaterRemoteTest.php diff --git a/src/Updater.php b/src/Updater.php index 15a81f9..59c8893 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -2,6 +2,7 @@ namespace nullthoughts\LaravelDataSync; +use Illuminate\Support\Facades\Storage; use nullthoughts\LaravelDataSync\Exceptions\ErrorUpdatingModelException; use nullthoughts\LaravelDataSync\Exceptions\FileDirectoryNotFoundException; use nullthoughts\LaravelDataSync\Exceptions\NoCriteriaException; @@ -13,18 +14,44 @@ use stdClass; class Updater { + /** + * @var string + */ + private $directory; + + /** + * @var \Illuminate\Support\Collection + */ + private $files; + + /** + * @var bool + */ + private $remote; + + /** + * @var string + */ + private $disk; + /** * Get files in sync directory. * - * @param string|null $path - * @param string|null $model + * @param string|null $path + * @param string|null $model + * + * @param bool $remote + * @param string $disk * * @throws \nullthoughts\LaravelDataSync\Exceptions\FileDirectoryNotFoundException */ - public function __construct($path = null, $model = null) + public function __construct($path = null, $model = null, $remote = false, $disk = 's3') { - $directory = $this->getDirectory($path); - $this->files = $this->getFiles($directory, $model); + $this->remote = $remote; + $this->disk = $disk; + + $this->directory = $this->getDirectory($path); + $this->files = $this->getFiles($this->directory, $model); } /** @@ -50,11 +77,11 @@ class Updater /** * Parse each record for criteria/values and update/create model. * - * @param string $file - * - * @throws \nullthoughts\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException + * @param string $file * * @return \Illuminate\Support\Collection + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @throws \nullthoughts\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException */ protected function syncModel(string $file) { @@ -89,7 +116,7 @@ class Updater { $directory = $path ?? config('data-sync.path', base_path('sync')); - if (!file_exists($directory)) { + if (!file_exists($directory) && !$this->remote) { throw new FileDirectoryNotFoundException(); } @@ -110,10 +137,17 @@ class Updater return Collection::wrap($directory.'/'.$model.'.json'); } - return collect(File::files($directory)) + $files = ($this->remote) ? Storage::disk($this->disk)->files($directory) : File::files($directory); + + return collect($files) ->filter(function ($file) { return pathinfo($file, PATHINFO_EXTENSION) == 'json'; })->map(function ($path) { + + if (is_string($path)) { + return $path; + } + return $path->getPathname(); }); } @@ -204,15 +238,17 @@ class Updater /** * Parses JSON from file and returns collection. * - * @param string $file - * - * @throws \nullthoughts\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException + * @param string $file * * @return \Illuminate\Support\Collection + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @throws \nullthoughts\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException */ protected function getRecords(string $file) { - $records = collect(json_decode(File::get($file))); + $fetchedFile = ($this->remote) ? Storage::disk($this->disk)->get($file) : File::get($file); + + $records = collect(json_decode($fetchedFile)); if ($records->isEmpty()) { throw new NoRecordsInvalidJSONException($file); diff --git a/tests/Unit/UpdaterRemoteTest.php b/tests/Unit/UpdaterRemoteTest.php new file mode 100644 index 0000000..fb50d86 --- /dev/null +++ b/tests/Unit/UpdaterRemoteTest.php @@ -0,0 +1,157 @@ +put('test-data/roles.json', File::get(__DIR__.'/../test-data/roles.json')); + foreach (File::directories(__DIR__.'/../test-data/') as $directory) { + $files = File::files($directory); + + foreach ($files as $file) { + Storage::disk('s3')->put('test-data/'.basename($directory).'/'.$file->getRelativePathname(), File::get($file->getPathname())); + } + } + } + + /** @test @group current */ + public function it_adds_roles_to_the_database() + { + $updater = new UpdaterFake('/test-data', 'roles', true, 's3'); + + $updater->run(); + + $this->assertDatabaseHas('roles', ['slug' => 'update-student-records']); + $this->assertDatabaseHas('roles', ['slug' => 'borrow-ferrari']); + $this->assertDatabaseHas('roles', ['slug' => 'destroy-ferrari']); + } + + /** @test */ + public function it_can_default_to_configuration() + { + config()->set('data-sync.path', 'test-data'); + + $updater = new UpdaterFake(null, null, true, 's3'); + + $updater->run(); + + $this->assertDatabaseHas('roles', ['slug' => 'update-student-records']); + $this->assertDatabaseHas('roles', ['slug' => 'borrow-ferrari']); + $this->assertDatabaseHas('roles', ['slug' => 'destroy-ferrari']); + } + + /** @test */ + public function it_can_update_an_existing_record() + { + config()->set('data-sync.path', 'test-data'); + (new UpdaterFake(null, null, true, 's3'))->run(); + + config()->set('data-sync.path', 'test-data/valid'); + (new UpdaterFake(null, null, true, 's3'))->run(); + + $this->assertDatabaseHas('roles', ['category' => 'changed']); + $this->assertDatabaseHas('roles', ['category' => 'changed']); + $this->assertDatabaseHas('roles', ['category' => 'changed']); + } + + /** @test */ + public function it_can_update_the_relationship() + { + $supervisor = Supervisor::create([ + 'name' => 'CEO', + ]); + + config()->set('data-sync.path', 'test-data/relationship'); + (new UpdaterFake(null, null, true, 's3'))->run(); + + $this->assertEquals($supervisor->id, Roles::first()->supervisor_id); + $this->assertTrue($supervisor->is(Roles::first()->supervisor)); + } + + /** @test */ + public function exception_is_thrown_if_the_directory_does_not_exists() + { + try { + new UpdaterFake(null, null, true, 's3'); + + $this->fail('exception was thrown'); + } catch (Exception $e) { + $this->assertEquals('Specified sync file directory does not exist', $e->getMessage()); + } + } +// +// /** @test */ +// public function invalid_json_throws_an_exception() +// { +// try { +// $updater = new UpdaterFake(__DIR__.'/../test-data/invalid-json'); +// $updater->run(); +// +// $this->fail('exception was thrown'); +// } catch (Exception $e) { +// $this->assertStringContainsString('No records or invalid JSON for', $e->getMessage()); +// } +// } +// +// /** @test */ +// public function the_json_must_contain_a_key_with_an_underscore() +// { +// try { +// $updater = new UpdaterFake(__DIR__.'/../test-data/no-criteria'); +// $updater->run(); +// +// $this->fail('exception was thrown'); +// } catch (Exception $e) { +// $this->assertEquals('No criteria/attributes detected', $e->getMessage()); +// } +// } +// +// /** @test */ +// public function order_of_imports_can_be_defined_in_config() +// { +// config()->set('data-sync.order', [ +// 'Supervisor', +// 'Roles', +// ]); +// +// $updater = new UpdaterFake(__DIR__.'/../test-data/ordered'); +// $updater->run(); +// +// $this->assertDatabaseHas('roles', ['slug' => 'update-student-records']); +// $this->assertDatabaseHas('supervisors', ['name' => 'CEO']); +// } +// +// /** @test */ +// public function exception_is_thrown_if_imports_are_in_incorrect_order() +// { +// config()->set('data-sync.order', [ +// 'Roles', +// 'Supervisor', +// ]); +// +// $this->expectException(ErrorUpdatingModelException::class); +// +// $updater = new UpdaterFake(__DIR__.'/../test-data/ordered'); +// $updater->run(); +// } +// +// /** @test */ +// public function it_ignores_non_json_files() +// { +// $updater = new UpdaterFake(__DIR__.'/../test-data/not-json'); +// $updater->run(); +// +// $this->assertDatabaseMissing('roles', ['slug' => 'update-student-records']); +// } +} From 20b07aeea84b1dabdd1f48ff3ffec4a2dec04d97 Mon Sep 17 00:00:00 2001 From: Victor Gonzalex Date: Wed, 18 Mar 2020 07:30:40 -0400 Subject: [PATCH 2/4] fix broken tests --- tests/Unit/UpdaterRemoteTest.php | 139 ++++++++++++++++--------------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/tests/Unit/UpdaterRemoteTest.php b/tests/Unit/UpdaterRemoteTest.php index fb50d86..a5b9a6f 100644 --- a/tests/Unit/UpdaterRemoteTest.php +++ b/tests/Unit/UpdaterRemoteTest.php @@ -25,7 +25,7 @@ class UpdaterRemoteTest extends TestCase } } - /** @test @group current */ + /** @test */ public function it_adds_roles_to_the_database() { $updater = new UpdaterFake('/test-data', 'roles', true, 's3'); @@ -79,79 +79,82 @@ class UpdaterRemoteTest extends TestCase $this->assertTrue($supervisor->is(Roles::first()->supervisor)); } +// /** +// * @test +// * @group current +// */ +// public function exception_is_thrown_if_the_directory_does_not_exists() +// { +// try { +// new UpdaterFake(null, null, true, 's3'); +// +// $this->fail('exception was thrown'); +// } catch (Exception $e) { +// $this->assertEquals('Specified sync file directory does not exist', $e->getMessage()); +// } +// } + /** @test */ - public function exception_is_thrown_if_the_directory_does_not_exists() + public function invalid_json_throws_an_exception() { try { - new UpdaterFake(null, null, true, 's3'); + $updater = new UpdaterFake('test-data/invalid-json', null, true, 's3'); + $updater->run(); $this->fail('exception was thrown'); } catch (Exception $e) { - $this->assertEquals('Specified sync file directory does not exist', $e->getMessage()); + $this->assertStringContainsString('No records or invalid JSON for', $e->getMessage()); } } -// -// /** @test */ -// public function invalid_json_throws_an_exception() -// { -// try { -// $updater = new UpdaterFake(__DIR__.'/../test-data/invalid-json'); -// $updater->run(); -// -// $this->fail('exception was thrown'); -// } catch (Exception $e) { -// $this->assertStringContainsString('No records or invalid JSON for', $e->getMessage()); -// } -// } -// -// /** @test */ -// public function the_json_must_contain_a_key_with_an_underscore() -// { -// try { -// $updater = new UpdaterFake(__DIR__.'/../test-data/no-criteria'); -// $updater->run(); -// -// $this->fail('exception was thrown'); -// } catch (Exception $e) { -// $this->assertEquals('No criteria/attributes detected', $e->getMessage()); -// } -// } -// -// /** @test */ -// public function order_of_imports_can_be_defined_in_config() -// { -// config()->set('data-sync.order', [ -// 'Supervisor', -// 'Roles', -// ]); -// -// $updater = new UpdaterFake(__DIR__.'/../test-data/ordered'); -// $updater->run(); -// -// $this->assertDatabaseHas('roles', ['slug' => 'update-student-records']); -// $this->assertDatabaseHas('supervisors', ['name' => 'CEO']); -// } -// -// /** @test */ -// public function exception_is_thrown_if_imports_are_in_incorrect_order() -// { -// config()->set('data-sync.order', [ -// 'Roles', -// 'Supervisor', -// ]); -// -// $this->expectException(ErrorUpdatingModelException::class); -// -// $updater = new UpdaterFake(__DIR__.'/../test-data/ordered'); -// $updater->run(); -// } -// -// /** @test */ -// public function it_ignores_non_json_files() -// { -// $updater = new UpdaterFake(__DIR__.'/../test-data/not-json'); -// $updater->run(); -// -// $this->assertDatabaseMissing('roles', ['slug' => 'update-student-records']); -// } + + /** @test */ + public function the_json_must_contain_a_key_with_an_underscore() + { + try { + $updater = new UpdaterFake('test-data/no-criteria', null, true, 's3'); + $updater->run(); + + $this->fail('exception was thrown'); + } catch (Exception $e) { + $this->assertEquals('No criteria/attributes detected', $e->getMessage()); + } + } + + /** @test */ + public function order_of_imports_can_be_defined_in_config() + { + config()->set('data-sync.order', [ + 'Supervisor', + 'Roles', + ]); + + $updater = new UpdaterFake('test-data/ordered', null, true, 's3'); + $updater->run(); + + $this->assertDatabaseHas('roles', ['slug' => 'update-student-records']); + $this->assertDatabaseHas('supervisors', ['name' => 'CEO']); + } + + /** @test */ + public function exception_is_thrown_if_imports_are_in_incorrect_order() + { + config()->set('data-sync.order', [ + 'Roles', + 'Supervisor', + ]); + + $this->expectException(ErrorUpdatingModelException::class); + + $updater = new UpdaterFake('test-data/ordered', null, true, 's3'); + $updater->run(); + } + + /** @test */ + public function it_ignores_non_json_files() + { + $updater = new UpdaterFake(__DIR__.'/../test-data/not-json'); + $updater->run(); + + $this->assertDatabaseMissing('roles', ['slug' => 'update-student-records']); + } } From 163be72b863c9bb31967b4e6e6311dcea37d6132 Mon Sep 17 00:00:00 2001 From: Victor Gonzalex Date: Wed, 18 Mar 2020 07:57:45 -0400 Subject: [PATCH 3/4] fixes all tests --- src/Updater.php | 28 ++++++++++++++++++++++++---- tests/Unit/UpdaterRemoteTest.php | 28 ++++++++++++++-------------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/Updater.php b/src/Updater.php index 59c8893..63c6d22 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -2,14 +2,14 @@ namespace nullthoughts\LaravelDataSync; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; use nullthoughts\LaravelDataSync\Exceptions\ErrorUpdatingModelException; use nullthoughts\LaravelDataSync\Exceptions\FileDirectoryNotFoundException; use nullthoughts\LaravelDataSync\Exceptions\NoCriteriaException; use nullthoughts\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\File; -use Illuminate\Support\Str; use stdClass; class Updater @@ -116,7 +116,7 @@ class Updater { $directory = $path ?? config('data-sync.path', base_path('sync')); - if (!file_exists($directory) && !$this->remote) { + if ($this->directoryMissingLocally($directory) || $this->directoryMissingRemotely($directory)) { throw new FileDirectoryNotFoundException(); } @@ -309,4 +309,24 @@ class Updater return [$key => $value]; })->toArray(); } + + /** + * @param \Illuminate\Config\Repository $directory + * + * @return bool + */ + protected function directoryMissingLocally($directory) + { + return !$this->remote && !file_exists($directory); + } + + /** + * @param \Illuminate\Config\Repository $directory + * + * @return bool + */ + protected function directoryMissingRemotely($directory) + { + return $this->remote && !Storage::disk($this->disk)->exists($directory); + } } diff --git a/tests/Unit/UpdaterRemoteTest.php b/tests/Unit/UpdaterRemoteTest.php index a5b9a6f..f27ebb1 100644 --- a/tests/Unit/UpdaterRemoteTest.php +++ b/tests/Unit/UpdaterRemoteTest.php @@ -79,20 +79,20 @@ class UpdaterRemoteTest extends TestCase $this->assertTrue($supervisor->is(Roles::first()->supervisor)); } -// /** -// * @test -// * @group current -// */ -// public function exception_is_thrown_if_the_directory_does_not_exists() -// { -// try { -// new UpdaterFake(null, null, true, 's3'); -// -// $this->fail('exception was thrown'); -// } catch (Exception $e) { -// $this->assertEquals('Specified sync file directory does not exist', $e->getMessage()); -// } -// } + /** + * @test + * @group current + */ + public function exception_is_thrown_if_the_directory_does_not_exists() + { + try { + new UpdaterFake(null, null, true, 's3'); + + $this->fail('exception was thrown'); + } catch (Exception $e) { + $this->assertEquals('Specified sync file directory does not exist', $e->getMessage()); + } + } /** @test */ public function invalid_json_throws_an_exception() From 0ab4a4f05393dd10b44471101288548d5ba8a7e2 Mon Sep 17 00:00:00 2001 From: Victor Gonzalex Date: Wed, 18 Mar 2020 07:59:34 -0400 Subject: [PATCH 4/4] change tests name to be unique for remote test --- tests/Unit/UpdaterRemoteTest.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Unit/UpdaterRemoteTest.php b/tests/Unit/UpdaterRemoteTest.php index f27ebb1..a6c9186 100644 --- a/tests/Unit/UpdaterRemoteTest.php +++ b/tests/Unit/UpdaterRemoteTest.php @@ -26,9 +26,9 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function it_adds_roles_to_the_database() + public function it_adds_roles_to_the_database_in_remote() { - $updater = new UpdaterFake('/test-data', 'roles', true, 's3'); + $updater = new UpdaterFake('test-data', 'roles', true, 's3'); $updater->run(); @@ -38,7 +38,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function it_can_default_to_configuration() + public function it_can_default_to_configuration_in_remote() { config()->set('data-sync.path', 'test-data'); @@ -52,7 +52,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function it_can_update_an_existing_record() + public function it_can_update_an_existing_record_in_remote() { config()->set('data-sync.path', 'test-data'); (new UpdaterFake(null, null, true, 's3'))->run(); @@ -66,7 +66,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function it_can_update_the_relationship() + public function it_can_update_the_relationship_in_remote() { $supervisor = Supervisor::create([ 'name' => 'CEO', @@ -95,7 +95,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function invalid_json_throws_an_exception() + public function invalid_json_throws_an_exception_in_remote() { try { $updater = new UpdaterFake('test-data/invalid-json', null, true, 's3'); @@ -108,7 +108,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function the_json_must_contain_a_key_with_an_underscore() + public function the_json_must_contain_a_key_with_an_underscore_in_remote() { try { $updater = new UpdaterFake('test-data/no-criteria', null, true, 's3'); @@ -121,7 +121,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function order_of_imports_can_be_defined_in_config() + public function order_of_imports_can_be_defined_in_config_in_remote() { config()->set('data-sync.order', [ 'Supervisor', @@ -136,7 +136,7 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function exception_is_thrown_if_imports_are_in_incorrect_order() + public function exception_is_thrown_if_imports_are_in_incorrect_order_in_remote() { config()->set('data-sync.order', [ 'Roles', @@ -150,9 +150,9 @@ class UpdaterRemoteTest extends TestCase } /** @test */ - public function it_ignores_non_json_files() + public function it_ignores_non_json_files_in_remote() { - $updater = new UpdaterFake(__DIR__.'/../test-data/not-json'); + $updater = new UpdaterFake('test-data/not-json', null, true, 's3'); $updater->run(); $this->assertDatabaseMissing('roles', ['slug' => 'update-student-records']);