Skip to content

Commit 5ff813f

Browse files
authored
Merge pull request #18233 from grokability/develop
Merging from dev
2 parents 948b7cd + 69994e0 commit 5ff813f

File tree

6 files changed

+127
-16
lines changed

6 files changed

+127
-16
lines changed

app/Http/Controllers/Api/AssetsController.php

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,38 @@
33
namespace App\Http\Controllers\Api;
44

55
use App\Events\CheckoutableCheckedIn;
6-
use App\Http\Requests\StoreAssetRequest;
7-
use App\Http\Requests\UpdateAssetRequest;
8-
use App\Http\Traits\MigratesLegacyAssetLocations;
9-
use App\Http\Transformers\ComponentsTransformer;
10-
use App\Models\AccessoryCheckout;
11-
use App\Models\CheckoutAcceptance;
12-
use App\Models\LicenseSeat;
13-
use Illuminate\Database\Eloquent\Builder;
14-
use Illuminate\Http\JsonResponse;
15-
use Illuminate\Support\Facades\Crypt;
16-
use Illuminate\Support\Facades\Gate;
176
use App\Helpers\Helper;
187
use App\Http\Controllers\Controller;
198
use App\Http\Requests\AssetCheckoutRequest;
9+
use App\Http\Requests\FilterRequest;
10+
use App\Http\Requests\StoreAssetRequest;
11+
use App\Http\Requests\UpdateAssetRequest;
12+
use App\Http\Traits\MigratesLegacyAssetLocations;
2013
use App\Http\Transformers\AssetsTransformer;
14+
use App\Http\Transformers\ComponentsTransformer;
2115
use App\Http\Transformers\LicensesTransformer;
2216
use App\Http\Transformers\SelectlistTransformer;
17+
use App\Models\AccessoryCheckout;
2318
use App\Models\Asset;
2419
use App\Models\AssetModel;
20+
use App\Models\CheckoutAcceptance;
2521
use App\Models\Company;
2622
use App\Models\CustomField;
2723
use App\Models\License;
24+
use App\Models\LicenseSeat;
2825
use App\Models\Location;
2926
use App\Models\Setting;
3027
use App\Models\User;
28+
use App\View\Label;
3129
use Carbon\Carbon;
32-
use Illuminate\Support\Facades\DB;
30+
use Illuminate\Database\Eloquent\Builder;
31+
use Illuminate\Http\JsonResponse;
3332
use Illuminate\Http\Request;
33+
use Illuminate\Support\Facades\Crypt;
34+
use Illuminate\Support\Facades\DB;
35+
use Illuminate\Support\Facades\Gate;
3436
use Illuminate\Support\Facades\Log;
3537
use Illuminate\Support\Facades\Route;
36-
use App\View\Label;
3738
use Illuminate\Support\Facades\Storage;
3839
use Illuminate\Support\Facades\Validator;
3940

@@ -56,7 +57,7 @@ class AssetsController extends Controller
5657
* @param int $assetId
5758
* @since [v4.0]
5859
*/
59-
public function index(Request $request, $action = null, $upcoming_status = null) : JsonResponse | array
60+
public function index(FilterRequest $request, $action = null, $upcoming_status = null) : JsonResponse | array
6061
{
6162

6263

app/Http/Controllers/Api/UsersController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Illuminate\Support\Facades\Log;
3232
use App\Http\Requests\DeleteUserRequest;
3333
use Illuminate\Http\JsonResponse;
34+
use App\Http\Requests\FilterRequest;
3435

3536
class UsersController extends Controller
3637
{
@@ -42,7 +43,7 @@ class UsersController extends Controller
4243
*
4344
* @return array
4445
*/
45-
public function index(Request $request) : array
46+
public function index(FilterRequest $request) : array
4647
{
4748
$this->authorize('view', User::class);
4849

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace App\Http\Requests;
4+
5+
use App\Rules\ValidJson;
6+
use Illuminate\Foundation\Http\FormRequest;
7+
8+
class FilterRequest extends FormRequest
9+
{
10+
/**
11+
* Determine if the user is authorized to make this request.
12+
*/
13+
public function authorize(): bool
14+
{
15+
return true;
16+
}
17+
18+
/**
19+
* Get the validation rules that apply to the request.
20+
*
21+
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
22+
*/
23+
public function rules(): array
24+
{
25+
return [
26+
'filter' => ['nullable', new ValidJson()],
27+
];
28+
}
29+
}

app/Rules/ValidJson.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Rules;
4+
5+
use Closure;
6+
use Illuminate\Contracts\Validation\ValidationRule;
7+
8+
class ValidJson implements ValidationRule
9+
{
10+
/**
11+
* Run the validation rule.
12+
*
13+
* @param \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString $fail
14+
*/
15+
public function validate(string $attribute, mixed $value, Closure $fail): void
16+
{
17+
if (!json_validate($value)) {
18+
$fail(trans('validation.json'));
19+
}
20+
}
21+
}

tests/Feature/Assets/Api/AssetIndexTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,33 @@ public function testAssetApiIndexAdheresToCompanyScoping()
171171
->assertResponseDoesNotContainInRows($assetA, 'asset_tag')
172172
->assertResponseContainsInRows($assetB, 'asset_tag');
173173
}
174+
175+
public function test_gracefully_handles_malformed_filter()
176+
{
177+
$this->actingAsForApi(User::factory()->viewAssets()->create())
178+
->getJson(route('api.assets.index', [
179+
// filter should be a json encoded array and not a string
180+
'filter' => 'asset_tag:12345',
181+
]))
182+
->assertStatusMessageIs('error')
183+
->assertJson(function (AssertableJson $json) {
184+
$json->has('messages.filter')->etc();
185+
});
186+
}
187+
188+
public function testReturnsResultViaFilter()
189+
{
190+
191+
Asset::factory()->count(3)->create(['name' => 'MY AWESOME ASSET NAME']);
192+
$this->actingAsForApi(User::factory()->viewAssets()->create())
193+
->getJson(route('api.assets.index', [
194+
'filter' => '{"name":"MY AWESOME ASSET NAME"}',
195+
]))
196+
->assertOk()
197+
->assertJsonStructure([
198+
'total',
199+
'rows',
200+
])
201+
->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc());
202+
}
174203
}

tests/Feature/Users/Api/IndexUsersTest.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,36 @@ public function test_gracefully_handles_malformed_filter()
6565
// filter should be a json encoded array and not a string
6666
'filter' => 'email:[email protected]',
6767
]))
68-
->assertOk();
68+
->assertStatusMessageIs('error')
69+
->assertJson(function (AssertableJson $json) {
70+
$json->has('messages.filter')->etc();
71+
});
72+
}
73+
74+
public function testReturnsResultViaFilter()
75+
{
76+
77+
User::factory()->count(3)->create(['first_name' => 'Awesome', 'last_name' => 'Admin', 'email' => '[email protected]']);
78+
$this->actingAsForApi(User::factory()->viewUsers()->create())
79+
->getJson(route('api.users.index', [
80+
'filter' => '{"first_name":"Awesome","last_name":"Admin","email":"[email protected]"}',
81+
]))
82+
->assertOk()
83+
->assertJsonStructure([
84+
'total',
85+
'rows',
86+
])
87+
->assertJson(fn(AssertableJson $json) => $json->has('rows', 3)->etc());
88+
89+
$this->actingAsForApi(User::factory()->viewUsers()->create())
90+
->getJson(route('api.users.index', [
91+
'filter' => '{"first_name":"Not Awesome"}',
92+
]))
93+
->assertOk()
94+
->assertJsonStructure([
95+
'total',
96+
'rows',
97+
])
98+
->assertJson(fn(AssertableJson $json) => $json->has('rows', 0)->etc());
6999
}
70100
}

0 commit comments

Comments
 (0)