From 3358e7d1ae80a21bcafeff67e66ae26710fbd312 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Sat, 11 Feb 2017 00:03:51 +0100 Subject: [PATCH 01/16] Toggles the disabled state of auto_increment_prefix To insert a prefix you had to toggle the checkbox, save the settings and reload. With this script it is immediate. Fixes #1390 --- resources/views/settings/edit.blade.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/views/settings/edit.blade.php b/resources/views/settings/edit.blade.php index 63f362a278cf..abf60a1a90ba 100755 --- a/resources/views/settings/edit.blade.php +++ b/resources/views/settings/edit.blade.php @@ -975,5 +975,11 @@ @stop From f7555ac829d434af39de125851b1849dddb0179f Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Sat, 11 Feb 2017 01:14:30 +0100 Subject: [PATCH 02/16] Delete asset image: made checkbox more visible Related to #3153 --- resources/views/hardware/edit.blade.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/resources/views/hardware/edit.blade.php b/resources/views/hardware/edit.blade.php index c83a42abf95c..f5e8b31f61c4 100755 --- a/resources/views/hardware/edit.blade.php +++ b/resources/views/hardware/edit.blade.php @@ -138,12 +138,16 @@ @if ($item->image)
- -
- {{ Form::checkbox('image_delete'),array('class' => 'minimal') }} - - {!! $errors->first('image_delete', ':message') !!} -
+ +
+ +
+ +
+
@endif From 8b74e32b500b1b6e868f88ace24edc572d64ab54 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 14:02:54 +0100 Subject: [PATCH 03/16] Added personal-access-token component --- resources/views/settings/api.blade.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/views/settings/api.blade.php b/resources/views/settings/api.blade.php index f176921fafc0..ae66bcfe43cf 100644 --- a/resources/views/settings/api.blade.php +++ b/resources/views/settings/api.blade.php @@ -11,6 +11,9 @@ @if (!config('app.lock_passwords')) + @if(env('APP_ENV') != 'production') + + @endif @else

{{ trans('general.feature_disabled') }}

@endif From 2447fc7a4f48aaf4db990a6e46d886fb962f20ec Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 14:03:54 +0100 Subject: [PATCH 04/16] Created basic API testing configuration --- tests/_support/ApiTester.php | 26 ++++++++++++++++++++++++++ tests/_support/Helper/Api.php | 10 ++++++++++ tests/api.suite.yml | 13 +++++++++++++ tests/api/_bootstrap.php | 2 ++ 4 files changed, 51 insertions(+) create mode 100644 tests/_support/ApiTester.php create mode 100644 tests/_support/Helper/Api.php create mode 100644 tests/api.suite.yml create mode 100644 tests/api/_bootstrap.php diff --git a/tests/_support/ApiTester.php b/tests/_support/ApiTester.php new file mode 100644 index 000000000000..aba673611c24 --- /dev/null +++ b/tests/_support/ApiTester.php @@ -0,0 +1,26 @@ + Date: Mon, 20 Feb 2017 14:16:17 +0100 Subject: [PATCH 05/16] First version of /components endpoind cest --- tests/api/ApiComponentsCest.php | 137 ++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/api/ApiComponentsCest.php diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php new file mode 100644 index 000000000000..1f3384a4c1d5 --- /dev/null +++ b/tests/api/ApiComponentsCest.php @@ -0,0 +1,137 @@ +faker = \Faker\Factory::create(); + $this->user = \App\Models\User::find(1); + + $token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjRjMDMyMDYyN2FkNTg1YzZiYTVkZTAzOThlMGMzYmQyNzZlNzA2YzAxMDk4ODA2NzE4YjcyNWIzNDkzODc3YzQzNWQ0Zjg1MjAxYWU1MzMzIn0.eyJhdWQiOiIxIiwianRpIjoiNGMwMzIwNjI3YWQ1ODVjNmJhNWRlMDM5OGUwYzNiZDI3NmU3MDZjMDEwOTg4MDY3MThiNzI1YjM0OTM4NzdjNDM1ZDRmODUyMDFhZTUzMzMiLCJpYXQiOjE0ODc1ODc0NzUsIm5iZiI6MTQ4NzU4NzQ3NSwiZXhwIjoxODAzMTIwMjc1LCJzdWIiOiIxMiIsInNjb3BlcyI6W119.MrRaor9nSKmndbOBjpGtAzUmk3WGfRKD4LUJTr3eepj-c5gv2A5bOO6pJ47YLZ5tqGyt7jgxErW5J3OOogSRipuIn-eRzW_pSgGj3qbOXDdEGU5HXHFGhoEpKW8Uk00eNbl1Mt-Dl-tj02rOxslhDYrXv3rZzcreeT9HJKZGZAkaRQZPWPmuAiYF06YBf4bKVogrtlG4LOFjhGnl9rsYygmylfzGhHlfq0jH9R9vNurWbQ2JEJVFFyDMK7_95jCvWrlXNQ5KncUSDrHzV3TI2UM0LWMYrTEF8CC0Em5UwUNEnG7BBpf2f-QWwt7KSR4NVdUn6hVADWWD1c1iRNbvlVehPm4fpmQ_QpODE-fg6v7cu58loePTPTd5BpXxDKXERRKxuQ47v-9iesc0YqsHFj15yuKrHodge96zIsbWasNw8gZ5q9WN7HOjK4OKXpnyHSx1s_U_XrQKZz_a6GgagQ21WABoOgBPozlbtlCVSc1uGwHnThVZKiJ4qIy1-8NDiObi6E3ncC_e3zvGRHreqZ7H5t_lCK45aEvJDxKTCgHPhNXEMvYmp5dclhhpNgybreNrVH7S2yCCkY5vdjMqjej0HIhpHBTWfvOIC7Kjh1PQkg_u7KkXUDXQw_VTAIOO7WaO5FouAMsUWnuMk88eU3kjGFHtW83wCuo6pdA73x8'; + + $I->amBearerAuthenticated($token); + } + + /** @test */ + public function indexComponents(ApiTester $I) + { + $I->wantTo('Get a list of components'); + $I->sendGET('/components'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + } + + /** @test */ + public function createComponent(ApiTester $I) + { + $I->wantTo('Create a new component'); + + // setup + $category = factory(\App\Models\Category::class, 'category')->create(['user_id' => $this->user->id]); + $location = factory(\App\Models\Location::class, 'location')->create(['user_id' => $this->user->id]); + $company = factory(\App\Models\Company::class, 'company')->create(); + + $data = [ + 'category_id' => $category->id, + 'company_id' => $company->id, + 'location_id' => $location->id, + 'name' => $this->faker->sentence(3), + 'purchase_cost' => $this->faker->randomFloat(2, 0), + 'purchase_date' => $this->faker->dateTime->format('Y-m-d'), + 'qty' => rand(1, 10), + ]; + + // create + $I->sendPOST('/components', $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $id = $response->payload->id; + + $I->assertEquals('success', $response->status); + + // verify + $I->sendGET('/components/' . $id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + 'id' => $id, + 'category' => [ + 'id' => $data['category_id'], + 'name' => $category->name, + ], + 'company' => [ + 'id' => $data['company_id'], + 'name' => $company->name, + ], + 'location' => [ + 'id' => $data['location_id'], + 'name' => $location->name, + ], + 'name' => $data['name'], + 'qty' => $data['qty'], + 'purchase_cost' => $data['purchase_cost'], + 'purchase_date' => $data['purchase_date'], + ]); + } + + /** @test */ + public function updateComponent(ApiTester $I) + { + $I->wantTo('Update a component'); + + // create + $component = factory(\App\Models\Component::class, 'component')->create(); + $I->assertInstanceOf(\App\Models\Component::class, $component); + + $data = [ + 'name' => $this->faker->word, + ]; + + $I->assertNotEquals($component->name, $data['name']); + + // update + $I->sendPATCH('/components/' . $component->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + + // verify + $I->sendGET('/components/' . $component->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + 'name' => $data['name'], + 'id' => $component->id, + ]); + + } + + /** @test */ + public function deleteComponentTest(ApiTester $I) + { + $I->wantTo('Delete a component'); + + // create + $component = factory(\App\Models\Component::class, 'component')->create(); + $I->assertInstanceOf(\App\Models\Component::class, $component); + + // delete + $I->sendDELETE('/components/' . $component->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + // verify, expect a 404 + $I->sendGET('/components/' . $component->id); + // $I->seeResponseIsJson(); // @todo: response is not JSON + $I->seeResponseCodeIs(404); + } +} From 27537d6eed693e3e68b9945f1558a9ead525504d Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 15:10:50 +0100 Subject: [PATCH 06/16] On-the-fly bearer token generation --- tests/_support/ApiTester.php | 20 ++++++++++++++++++++ tests/api/ApiComponentsCest.php | 6 ++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/_support/ApiTester.php b/tests/_support/ApiTester.php index aba673611c24..c53fda8f8acd 100644 --- a/tests/_support/ApiTester.php +++ b/tests/_support/ApiTester.php @@ -23,4 +23,24 @@ class ApiTester extends \Codeception\Actor /** * Define custom actions here */ + + public function getToken(\App\Models\User $user) + { + $client_repository = new \Laravel\Passport\ClientRepository(); + $client = $client_repository->createPersonalAccessClient($user->id, 'Codeception API Test Client', + 'http://localhost/'); + + \Illuminate\Support\Facades\DB::table('oauth_personal_access_clients')->insert([ + 'client_id' => $client->id, + 'created_at' => new DateTime, + 'updated_at' => new DateTime, + ]); + + $user->permissions = json_encode(['superuser' => true]); + $user->save(); + + $token = $user->createToken('CodeceptionAPItestToken')->accessToken; + + return $token; + } } diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php index 1f3384a4c1d5..3ee692ad30c6 100644 --- a/tests/api/ApiComponentsCest.php +++ b/tests/api/ApiComponentsCest.php @@ -12,9 +12,7 @@ public function _before(ApiTester $I) $this->faker = \Faker\Factory::create(); $this->user = \App\Models\User::find(1); - $token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjRjMDMyMDYyN2FkNTg1YzZiYTVkZTAzOThlMGMzYmQyNzZlNzA2YzAxMDk4ODA2NzE4YjcyNWIzNDkzODc3YzQzNWQ0Zjg1MjAxYWU1MzMzIn0.eyJhdWQiOiIxIiwianRpIjoiNGMwMzIwNjI3YWQ1ODVjNmJhNWRlMDM5OGUwYzNiZDI3NmU3MDZjMDEwOTg4MDY3MThiNzI1YjM0OTM4NzdjNDM1ZDRmODUyMDFhZTUzMzMiLCJpYXQiOjE0ODc1ODc0NzUsIm5iZiI6MTQ4NzU4NzQ3NSwiZXhwIjoxODAzMTIwMjc1LCJzdWIiOiIxMiIsInNjb3BlcyI6W119.MrRaor9nSKmndbOBjpGtAzUmk3WGfRKD4LUJTr3eepj-c5gv2A5bOO6pJ47YLZ5tqGyt7jgxErW5J3OOogSRipuIn-eRzW_pSgGj3qbOXDdEGU5HXHFGhoEpKW8Uk00eNbl1Mt-Dl-tj02rOxslhDYrXv3rZzcreeT9HJKZGZAkaRQZPWPmuAiYF06YBf4bKVogrtlG4LOFjhGnl9rsYygmylfzGhHlfq0jH9R9vNurWbQ2JEJVFFyDMK7_95jCvWrlXNQ5KncUSDrHzV3TI2UM0LWMYrTEF8CC0Em5UwUNEnG7BBpf2f-QWwt7KSR4NVdUn6hVADWWD1c1iRNbvlVehPm4fpmQ_QpODE-fg6v7cu58loePTPTd5BpXxDKXERRKxuQ47v-9iesc0YqsHFj15yuKrHodge96zIsbWasNw8gZ5q9WN7HOjK4OKXpnyHSx1s_U_XrQKZz_a6GgagQ21WABoOgBPozlbtlCVSc1uGwHnThVZKiJ4qIy1-8NDiObi6E3ncC_e3zvGRHreqZ7H5t_lCK45aEvJDxKTCgHPhNXEMvYmp5dclhhpNgybreNrVH7S2yCCkY5vdjMqjej0HIhpHBTWfvOIC7Kjh1PQkg_u7KkXUDXQw_VTAIOO7WaO5FouAMsUWnuMk88eU3kjGFHtW83wCuo6pdA73x8'; - - $I->amBearerAuthenticated($token); + $I->amBearerAuthenticated($I->getToken($this->user)); } /** @test */ @@ -91,7 +89,7 @@ public function updateComponent(ApiTester $I) $I->assertInstanceOf(\App\Models\Component::class, $component); $data = [ - 'name' => $this->faker->word, + 'name' => $this->faker->sentence(3), ]; $I->assertNotEquals($component->name, $data['name']); From eb4a6f13fc46a828fd2adf4ef15a0d28fdbd5f41 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 15:26:31 +0100 Subject: [PATCH 07/16] Completed testing of PATCH and PUT methods --- tests/api/ApiComponentsCest.php | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php index 3ee692ad30c6..4aef9adc65dc 100644 --- a/tests/api/ApiComponentsCest.php +++ b/tests/api/ApiComponentsCest.php @@ -19,9 +19,21 @@ public function _before(ApiTester $I) public function indexComponents(ApiTester $I) { $I->wantTo('Get a list of components'); + + // setup + $components = factory(\App\Models\Component::class, 'component', 10)->create(['user_id' => $this->user->id]); + + // call $I->sendGET('/components'); $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); + + // sample verify + $component = $components->random(); + $I->seeResponseContainsJson([ + 'name' => $component->name, + 'qty' => $component->qty, + ]); } /** @test */ @@ -80,9 +92,9 @@ public function createComponent(ApiTester $I) } /** @test */ - public function updateComponent(ApiTester $I) + public function updateComponentWithPatch(ApiTester $I) { - $I->wantTo('Update a component'); + $I->wantTo('Update a component with PATCH'); // create $component = factory(\App\Models\Component::class, 'component')->create(); @@ -90,6 +102,7 @@ public function updateComponent(ApiTester $I) $data = [ 'name' => $this->faker->sentence(3), + 'qty' => $this->faker->randomDigit, ]; $I->assertNotEquals($component->name, $data['name']); @@ -109,8 +122,42 @@ public function updateComponent(ApiTester $I) $I->seeResponseContainsJson([ 'name' => $data['name'], 'id' => $component->id, + 'qty' => $data['qty'], ]); + } + + /** @test */ + public function updateComponentWithPut(ApiTester $I) + { + $I->wantTo('Update a component with PUT'); + + // create + $component = factory(\App\Models\Component::class, 'component')->create(); + $I->assertInstanceOf(\App\Models\Component::class, $component); + + $data = [ + 'name' => $this->faker->sentence(3), + ]; + $I->assertNotEquals($component->name, $data['name']); + + // update + $I->sendPUT('/components/' . $component->id, $data); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + + $response = json_decode($I->grabResponse()); + $I->assertEquals('success', $response->status); + + // verify + $I->sendGET('/components/' . $component->id); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $I->seeResponseContainsJson([ + 'name' => $data['name'], + 'id' => $component->id, + 'qty' => $component->qty, + ]); } /** @test */ @@ -129,7 +176,7 @@ public function deleteComponentTest(ApiTester $I) // verify, expect a 404 $I->sendGET('/components/' . $component->id); - // $I->seeResponseIsJson(); // @todo: response is not JSON $I->seeResponseCodeIs(404); + // $I->seeResponseIsJson(); // @todo: response is not JSON } } From 1aea9ac25f38059fa994116a8d1cd59b7f539ec0 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 19:10:13 +0100 Subject: [PATCH 08/16] Added /components/{id}/assets route with tests --- .../Controllers/Api/ComponentsController.php | 23 ++++++++ routes/api.php | 5 ++ tests/api/ApiComponentsAssetsCest.php | 54 +++++++++++++++++++ tests/api/ApiComponentsCest.php | 2 +- 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/api/ApiComponentsAssetsCest.php diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index 3194d40c77f8..b8a6cdc78cbd 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -4,6 +4,7 @@ use Illuminate\Http\Request; use App\Http\Controllers\Controller; +use App\Http\Transformers\AssetsTransformer; use App\Http\Transformers\ComponentsTransformer; use App\Models\Component; use App\Models\Company; @@ -131,4 +132,26 @@ public function destroy($id) return response()->json(Helper::formatStandardApiResponse('success', null, trans('admin/components/message.delete.success'))); } + /** + * Display all assets attached to a component + * + * @author [A. Bergamasco] [@vjandrea] + * @since [v4.0] + * @param Request $request + * @param int $id + * @return \Illuminate\Http\Response + */ + public function getAssets(Request $request, $id) + { + $this->authorize('index', Asset::class); + + $component = Component::findOrFail($id); + $assets = $component->assets(); + + $offset = request('offset', 0); + $limit = $request->input('limit', 50); + $total = $assets->count(); + $assets = $assets->skip($offset)->take($limit)->get(); + return (new AssetsTransformer)->transformAssets($assets, $total); + } } diff --git a/routes/api.php b/routes/api.php index de210c132590..63518ef61f9e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -142,6 +142,11 @@ ] ); + Route::get('components/{id}/assets', [ + 'name' =>'api.components.assets', + 'uses' => 'ComponentsController@getAssets', + ]); + Route::resource('suppliers', 'SuppliersController', ['names' => diff --git a/tests/api/ApiComponentsAssetsCest.php b/tests/api/ApiComponentsAssetsCest.php new file mode 100644 index 000000000000..6610b12a19d4 --- /dev/null +++ b/tests/api/ApiComponentsAssetsCest.php @@ -0,0 +1,54 @@ +faker = \Faker\Factory::create(); + $this->user = \App\Models\User::find(1); + + $I->amBearerAuthenticated($I->getToken($this->user)); + } + + /** @test */ + public function indexComponentsAssets(ApiTester $I) + { + $I->wantTo('Get a list of assets related to a component'); + + // generate + $component = factory(\App\Models\Component::class, 'component') + ->create(['user_id' => $this->user->id, 'qty' => 20]); + + $assets = factory(\App\Models\Asset::class, 'asset', 2) + ->create(['user_id' => $this->user->id]) + ->each(function ($asset) use ($component) { + $component->assets()->attach($component->id, [ + 'component_id' => $component->id, + 'user_id' => $this->user->id, + 'created_at' => date('Y-m-d H:i:s'), + 'assigned_qty' => 2, + 'asset_id' => $asset->id + ]); + }); + + $I->sendGET('/components/' . $component->id . '/assets/'); + $I->seeResponseIsJson(); + $I->seeResponseCodeIs(200); + $response = json_decode($I->grabResponse()); + + $I->assertEquals(2, $response->total); + $I->assertInstanceOf(\Illuminate\Database\Eloquent\Collection::class, $assets); + + $random_asset = $assets->random(); + $I->seeResponseContainsJson([ + 'name' => $random_asset->name, + 'asset_tag' => $random_asset->asset_tag, + 'serial' => $random_asset->serial, + 'last_checkout' => $random_asset->last_checkout, + 'notes' => $random_asset->notes, + ]); + } +} diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php index 4aef9adc65dc..1bce4a84540c 100644 --- a/tests/api/ApiComponentsCest.php +++ b/tests/api/ApiComponentsCest.php @@ -102,7 +102,7 @@ public function updateComponentWithPatch(ApiTester $I) $data = [ 'name' => $this->faker->sentence(3), - 'qty' => $this->faker->randomDigit, + 'qty' => $this->faker->randomDigit + 1, ]; $I->assertNotEquals($component->name, $data['name']); From d6ed677130ef473619cf8365e66179dd61ae1a62 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 21:24:09 +0100 Subject: [PATCH 09/16] Updated route and dataTable in view --- .../Controllers/Api/ComponentsController.php | 3 +- .../ComponentsAssetsTransformer.php | 54 +++++++++++++++++++ resources/views/components/view.blade.php | 2 +- routes/api.php | 2 +- tests/api/ApiComponentsCest.php | 6 +-- 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 app/Http/Transformers/ComponentsAssetsTransformer.php diff --git a/app/Http/Controllers/Api/ComponentsController.php b/app/Http/Controllers/Api/ComponentsController.php index b8a6cdc78cbd..e10f1c7b7fee 100644 --- a/app/Http/Controllers/Api/ComponentsController.php +++ b/app/Http/Controllers/Api/ComponentsController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Http\Transformers\AssetsTransformer; use App\Http\Transformers\ComponentsTransformer; +use App\Http\Transformers\ComponentsAssetsTransformer; use App\Models\Component; use App\Models\Company; use App\Helpers\Helper; @@ -152,6 +153,6 @@ public function getAssets(Request $request, $id) $limit = $request->input('limit', 50); $total = $assets->count(); $assets = $assets->skip($offset)->take($limit)->get(); - return (new AssetsTransformer)->transformAssets($assets, $total); + return (new ComponentsAssetsTransformer)->transformAssets($assets, $total); } } diff --git a/app/Http/Transformers/ComponentsAssetsTransformer.php b/app/Http/Transformers/ComponentsAssetsTransformer.php new file mode 100644 index 000000000000..65054b4209fe --- /dev/null +++ b/app/Http/Transformers/ComponentsAssetsTransformer.php @@ -0,0 +1,54 @@ +transformDatatables($array, $total); + } + + + public function transformAsset (Asset $asset) + { + $array = [ + 'id' => $asset->id, + 'name' => e($asset->name), + 'created_at' => $asset->created_at->format('Y-m-d'), + 'qty' => $asset->components()->count(), + 'can_checkout' => $asset->availableForCheckout(), + ]; + + $permissions_array['available_actions'] = [ + 'checkout' => Gate::allows('checkout', Asset::class) ? true : false, + 'checkin' => Gate::allows('checkin', Asset::class) ? true : false, + 'update' => Gate::allows('update', Asset::class) ? true : false, + 'delete' => Gate::allows('delete', Asset::class) ? true : false, + ]; + + $array += $permissions_array; + + if ($asset->model->fieldset) { + foreach ($asset->model->fieldset->fields as $field) { + $fields_array = [$field->name => $asset->{$field->convertUnicodeDbSlug()}]; + $array += $fields_array; + } + } + + return $array; + } + + public function transformAssetsDatatable ($assets) { + return (new DatatablesTransformer)->transformDatatables($assets); + } +} diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index 18c327db0d28..08e7e1fab909 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -49,7 +49,7 @@ name="component_users" class="table table-striped snipe-table" id="table" - data-url="{{route('api.components.show', $component->id)}}" + data-url="{{route('api.components.assets', $component->id)}}" data-cookie="true" data-click-to-select="true" data-cookie-id-table="componentDetailTable-{{ config('version.hash_version') }}" diff --git a/routes/api.php b/routes/api.php index 63518ef61f9e..3e534d76ad68 100644 --- a/routes/api.php +++ b/routes/api.php @@ -143,7 +143,7 @@ ); Route::get('components/{id}/assets', [ - 'name' =>'api.components.assets', + 'as' =>'api.components.assets', 'uses' => 'ComponentsController@getAssets', ]); diff --git a/tests/api/ApiComponentsCest.php b/tests/api/ApiComponentsCest.php index 1bce4a84540c..a0b311142dd0 100644 --- a/tests/api/ApiComponentsCest.php +++ b/tests/api/ApiComponentsCest.php @@ -154,9 +154,9 @@ public function updateComponentWithPut(ApiTester $I) $I->seeResponseIsJson(); $I->seeResponseCodeIs(200); $I->seeResponseContainsJson([ - 'name' => $data['name'], - 'id' => $component->id, - 'qty' => $component->qty, + 'name' => e($data['name']), + 'id' => e($component->id), + 'qty' => e($component->qty), ]); } From 77f0de83d0dba7ddf45ee3dfec048eb877f6c933 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 21:31:47 +0100 Subject: [PATCH 10/16] Completed test assertion --- tests/api/ApiComponentsAssetsCest.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/api/ApiComponentsAssetsCest.php b/tests/api/ApiComponentsAssetsCest.php index 6610b12a19d4..48e0d3e604ae 100644 --- a/tests/api/ApiComponentsAssetsCest.php +++ b/tests/api/ApiComponentsAssetsCest.php @@ -42,13 +42,18 @@ public function indexComponentsAssets(ApiTester $I) $I->assertEquals(2, $response->total); $I->assertInstanceOf(\Illuminate\Database\Eloquent\Collection::class, $assets); - $random_asset = $assets->random(); - $I->seeResponseContainsJson([ - 'name' => $random_asset->name, - 'asset_tag' => $random_asset->asset_tag, - 'serial' => $random_asset->serial, - 'last_checkout' => $random_asset->last_checkout, - 'notes' => $random_asset->notes, + $I->seeResponseContainsJson(['rows' => [ + 0 => [ + 'name' => $assets[0]->name, + 'id' => $assets[0]->id, + 'created_at' => $assets[0]->created_at->format('Y-m-d'), + ], + 1 => [ + 'name' => $assets[1]->name, + 'id' => $assets[1]->id, + 'created_at' => $assets[1]->created_at->format('Y-m-d'), + ], + ] ]); } } From 71b28898d73b496508ac7a2da68d249ebdf0d9a4 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 22:27:19 +0100 Subject: [PATCH 11/16] Added links to assets in ComponentsAssets view --- resources/views/components/view.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/components/view.blade.php b/resources/views/components/view.blade.php index 08e7e1fab909..5b1f96198dcd 100644 --- a/resources/views/components/view.blade.php +++ b/resources/views/components/view.blade.php @@ -56,7 +56,7 @@ class="table table-striped snipe-table" > - {{ trans('general.asset') }} + {{ trans('general.asset') }} {{ trans('general.qty') }} {{ trans('general.date') }} From 37d0db082436fbb980834967f3df9dfa19cc1c47 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Mon, 20 Feb 2017 22:54:20 +0100 Subject: [PATCH 12/16] Linked Company in AssetView page --- resources/views/hardware/view.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/hardware/view.blade.php b/resources/views/hardware/view.blade.php index 2812cebcd379..ad7b2795fb89 100755 --- a/resources/views/hardware/view.blade.php +++ b/resources/views/hardware/view.blade.php @@ -84,7 +84,7 @@ @if ($asset->company) {{ trans('general.company') }} - {{ $asset->company->name }} + {{ $asset->company->name }} @endif From 6eb5624c8ded1ff62f116bfc3bea163a577a2bdd Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Tue, 21 Feb 2017 00:35:26 +0100 Subject: [PATCH 13/16] Bugfixes based on functional tests --- app/Http/Controllers/CategoriesController.php | 2 +- app/Http/Controllers/GroupsController.php | 2 +- app/Http/Controllers/LocationsController.php | 12 +++--- resources/views/groups/edit.blade.php | 2 +- tests/functional/AccessoriesCest.php | 7 +++- tests/functional/CategoriesCest.php | 6 +-- tests/functional/GroupsCest.php | 38 ++++++++++++++----- tests/functional/LicensesCest.php | 2 +- 8 files changed, 48 insertions(+), 23 deletions(-) diff --git a/app/Http/Controllers/CategoriesController.php b/app/Http/Controllers/CategoriesController.php index f13520353bb4..a9d475167713 100755 --- a/app/Http/Controllers/CategoriesController.php +++ b/app/Http/Controllers/CategoriesController.php @@ -173,7 +173,7 @@ public function destroy($categoryId) $category->delete(); // Redirect to the locations management page - return redirect()->to('admin/settings/categories')->with('success', trans('admin/categories/message.delete.success')); + return redirect()->to(route('categories.index'))->with('success', trans('admin/categories/message.delete.success')); } diff --git a/app/Http/Controllers/GroupsController.php b/app/Http/Controllers/GroupsController.php index 1140310b7ad8..0514eaa81b7d 100755 --- a/app/Http/Controllers/GroupsController.php +++ b/app/Http/Controllers/GroupsController.php @@ -72,7 +72,7 @@ public function postCreate() if ($group->save()) { return redirect()->route("groups.index")->with('success', trans('admin/groups/message.success.create')); } - return redirect()->back()->withInput()->withErrors($group->getErrors()); + return redirect(route('groups.create'))->withInput()->withErrors($group->getErrors()); } /** diff --git a/app/Http/Controllers/LocationsController.php b/app/Http/Controllers/LocationsController.php index 0277460cdab3..7851719daec8 100755 --- a/app/Http/Controllers/LocationsController.php +++ b/app/Http/Controllers/LocationsController.php @@ -208,21 +208,21 @@ public function destroy($locationId) // Check if the location exists if (is_null($location = Location::find($locationId))) { // Redirect to the blogs management page - return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.not_found')); + return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.not_found')); } if ($location->users->count() > 0) { - return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_users')); + return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_users')); } elseif ($location->childLocations->count() > 0) { - return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_child_loc')); + return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_child_loc')); } elseif ($location->assets->count() > 0) { - return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_assets')); + return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets')); } elseif ($location->assignedassets->count() > 0) { - return redirect()->to('admin/settings/locations')->with('error', trans('admin/locations/message.assoc_assets')); + return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.assoc_assets')); } else { $location->delete(); - return redirect()->to('admin/settings/locations')->with('success', trans('admin/locations/message.delete.success')); + return redirect()->to(route('locations.index'))->with('success', trans('admin/locations/message.delete.success')); } } diff --git a/resources/views/groups/edit.blade.php b/resources/views/groups/edit.blade.php index 38d137fed1e8..2678761e9e1f 100755 --- a/resources/views/groups/edit.blade.php +++ b/resources/views/groups/edit.blade.php @@ -4,7 +4,7 @@ 'helpTitle' => trans('admin/groups/general.about_groups_title'), 'helpText' => trans('admin/groups/general.about_groups_text'), 'item' => $group, - 'formAction' => ($group) ? route('groups.update', ['accessory' => $group->id]) : route('groups.store'), + 'formAction' => ($group->id) ? route('groups.update', ['accessory' => $group->id]) : route('groups.store'), ]) @section('content') diff --git a/tests/functional/AccessoriesCest.php b/tests/functional/AccessoriesCest.php index bdbd66154ddf..961874eca771 100644 --- a/tests/functional/AccessoriesCest.php +++ b/tests/functional/AccessoriesCest.php @@ -9,14 +9,16 @@ public function _before(FunctionalTester $I) $I->fillField('username', 'snipeit'); $I->fillField('password', 'snipeit'); $I->click('Login'); + $I->seeAuthentication(); } // tests - public function tryToTest(FunctionalTester $I) + public function loadsFormWithoutErrors(FunctionalTester $I) { $I->wantTo('ensure that the create accessories form loads without errors'); $I->lookForwardTo('seeing it load without errors'); $I->amOnPage('/accessories/create'); + $I->seeResponseCodeIs(200); $I->dontSee('Create Accessory', '.page-header'); $I->see('Create Accessory', 'h1.pull-left'); } @@ -25,6 +27,7 @@ public function failsEmptyValidation(FunctionalTester $I) { $I->wantTo("Test Validation Fails with blank elements"); $I->amOnPage('/accessories/create'); + $I->seeResponseCodeIs(200); $I->click('Save'); $I->seeElement('.alert-danger'); $I->see('The name field is required.', '.alert-msg'); @@ -36,6 +39,7 @@ public function failsShortValidation(FunctionalTester $I) { $I->wantTo("Test Validation Fails with short name"); $I->amOnPage('/accessories/create'); + $I->seeResponseCodeIs(200); $I->fillField('name', 't2'); $I->fillField('qty', '-15'); $I->fillField('min_amt', '-15'); @@ -65,6 +69,7 @@ public function passesCorrectValidation(FunctionalTester $I) $I->wantTo("Test Validation Succeeds"); $I->amOnPage('/accessories/create'); + $I->seeResponseCodeIs(200); $I->submitForm('form#create-form', $values); $I->seeRecord('accessories', $values); diff --git a/tests/functional/CategoriesCest.php b/tests/functional/CategoriesCest.php index f51e0e4c81ea..bea932aa08aa 100644 --- a/tests/functional/CategoriesCest.php +++ b/tests/functional/CategoriesCest.php @@ -1,7 +1,7 @@ make(); + $category = factory(App\Models\Category::class, 'category')->make(); $values = [ 'name' => $category->name, 'category_type' => $category->category_type, @@ -55,7 +55,7 @@ public function passesCorrectValidation(FunctionalTester $I) public function allowsDelete(FunctionalTester $I) { $I->wantTo('Ensure I can delete a category'); - $category = factory(App\Models\Category::class, 'asset-category')->create(); + $category = factory(App\Models\Category::class, 'category')->create(); $I->sendDelete(route('categories.destroy', $category->id), ['_token' => csrf_token()]); $I->seeResponseCodeIs(200); } diff --git a/tests/functional/GroupsCest.php b/tests/functional/GroupsCest.php index cab4752b9f37..6ec115d20fba 100644 --- a/tests/functional/GroupsCest.php +++ b/tests/functional/GroupsCest.php @@ -14,11 +14,12 @@ public function _before(FunctionalTester $I) } // tests - public function tryToTest(FunctionalTester $I) + public function loadsFormWithoutErrors(FunctionalTester $I) { $I->wantTo('ensure that the create groups form loads without errors'); $I->lookForwardTo('seeing it load without errors'); - $I->amOnPage(route('create/group')); + $I->amOnPage(route('groups.create')); + $I->seeResponseCodeIs(200); $I->dontSee('Create New Group', '.page-header'); $I->see('Create New Group', 'h1.pull-left'); } @@ -26,7 +27,8 @@ public function tryToTest(FunctionalTester $I) public function failsEmptyValidation(FunctionalTester $I) { $I->wantTo("Test Validation Fails with blank elements"); - $I->amOnPage(route('create/group')); + $I->amOnPage(route('groups.create')); + $I->seeResponseCodeIs(200); $I->click('Save'); $I->seeElement('.alert-danger'); $I->see('The name field is required.', '.alert-msg'); @@ -35,7 +37,8 @@ public function failsEmptyValidation(FunctionalTester $I) public function failsShortValidation(FunctionalTester $I) { $I->wantTo("Test Validation Fails with short name"); - $I->amOnPage(route('create/group')); + $I->amOnPage(route('groups.create')); + $I->seeResponseCodeIs(200); $I->fillField('name', 't2'); $I->click('Save'); $I->seeElement('.alert-danger'); @@ -45,19 +48,36 @@ public function failsShortValidation(FunctionalTester $I) public function passesCorrectValidation(FunctionalTester $I) { $I->wantTo("Test Validation Succeeds"); - $I->amOnPage(route('create/group')); + $I->amOnPage(route('groups.create')); + $I->seeResponseCodeIs(200); $I->fillField('name', 'TestGroup'); $I->click('Save'); $I->dontSee('<span class="'); $I->seeElement('.alert-success'); } - public function allowsDelete(FunctionalTester $I) + public function allowsDelete(FunctionalTester $I, $scenario) { - $I->wantTo("Fix this test to generate a group for deletes"); + $scenario->incomplete('Test not implemented yet'); $I->wantTo('Ensure I can delete a group'); -// $I->amOnPage(route('delete/group', Group::doesntHave('users')->first()->id)); -// $I->seeElement('.alert-success'); + + // create a group + $I->amOnPage(route('groups.create')); + $I->seeResponseCodeIs(200); + $I->fillField('name', 'TestGroup'); + $I->click('Save'); + $I->dontSee('<span class="'); + $I->seeElement('.alert-success'); + + // delete it + $I->amOnPage(route('groups.delete', Group::doesntHave('users')->first()->id)); + $I->seeResponseCodeIs(200); + $I->seeElement('.alert-success'); + // $I->seeResponseCodeIs(200); } + public function allowsEditing(FunctionalTester $I, $scenario) + { + $scenario->incomplete('Test not implemented yet'); + } } diff --git a/tests/functional/LicensesCest.php b/tests/functional/LicensesCest.php index 15bca2d13d61..d5c735e9a712 100644 --- a/tests/functional/LicensesCest.php +++ b/tests/functional/LicensesCest.php @@ -3,7 +3,7 @@ use App\Models\License; -class licensesCest +class LicensesCest { public function _before(FunctionalTester $I) { From 375ac50ec05d0881323f5b7673e8b90192e9cd6d Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Tue, 21 Feb 2017 09:12:50 +0100 Subject: [PATCH 14/16] Removed unused function --- tests/functional/_bootstrap.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/functional/_bootstrap.php b/tests/functional/_bootstrap.php index f7cbed7bb28e..8a885558065a 100644 --- a/tests/functional/_bootstrap.php +++ b/tests/functional/_bootstrap.php @@ -1,6 +1,2 @@ Date: Wed, 22 Feb 2017 10:05:59 +0100 Subject: [PATCH 15/16] Marked tests as incomplete --- tests/functional/GroupsCest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/GroupsCest.php b/tests/functional/GroupsCest.php index 6ec115d20fba..76a18971d3d2 100644 --- a/tests/functional/GroupsCest.php +++ b/tests/functional/GroupsCest.php @@ -58,7 +58,7 @@ public function passesCorrectValidation(FunctionalTester $I) public function allowsDelete(FunctionalTester $I, $scenario) { - $scenario->incomplete('Test not implemented yet'); + $scenario->incomplete('Fix this test to generate a group for deletes'); $I->wantTo('Ensure I can delete a group'); // create a group @@ -78,6 +78,7 @@ public function allowsDelete(FunctionalTester $I, $scenario) public function allowsEditing(FunctionalTester $I, $scenario) { - $scenario->incomplete('Test not implemented yet'); + $scenario->incomplete('Fix this test to generate a group for editing'); + $I->wantTo('Ensure i can edit a group'); } } From 6ee5004062c5a3793fcdbd37703c7e99a5b89f54 Mon Sep 17 00:00:00 2001 From: Andrea Bergamasco Date: Wed, 22 Feb 2017 21:46:31 +0100 Subject: [PATCH 16/16] Added check for existence in groups/edit.blade.php --- resources/views/groups/edit.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/groups/edit.blade.php b/resources/views/groups/edit.blade.php index 2678761e9e1f..f8e63795d5ed 100755 --- a/resources/views/groups/edit.blade.php +++ b/resources/views/groups/edit.blade.php @@ -4,7 +4,7 @@ 'helpTitle' => trans('admin/groups/general.about_groups_title'), 'helpText' => trans('admin/groups/general.about_groups_text'), 'item' => $group, - 'formAction' => ($group->id) ? route('groups.update', ['accessory' => $group->id]) : route('groups.store'), + 'formAction' => ($group !== null && $group->id !== null) ? route('groups.update', ['accessory' => $group->id]) : route('groups.store'), ]) @section('content')