Skip to content

Commit fdecca9

Browse files
committed
adjustments
1 parent 020db87 commit fdecca9

File tree

8 files changed

+172
-6
lines changed

8 files changed

+172
-6
lines changed

src/Concerns/Resource/Paginable.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Lomkit\Rest\Concerns\Resource;
44

5+
use Laravel\Scout\Builder;
56
use Lomkit\Rest\Http\Requests\RestRequest;
67

78
trait Paginable
@@ -16,6 +17,11 @@ trait Paginable
1617
*/
1718
public function paginate($query, RestRequest $request)
1819
{
20+
// In case we have a scout builder
21+
if ($query instanceof Builder) {
22+
return $query->paginate($request->input('search.limit', 50), 'page', $request->input('search.page', 1));
23+
}
24+
1925
return $query->paginate($request->input('search.limit', 50), ['*'], 'page', $request->input('search.page', 1));
2026
}
2127
}

src/Concerns/Resource/Scoutable.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Lomkit\Rest\Concerns\Resource;
44

55
use Lomkit\Rest\Http\Requests\RestRequest;
6+
use Lomkit\Rest\Instructions\Instruction;
67

78
trait Scoutable
89
{
@@ -13,6 +14,13 @@ trait Scoutable
1314
*/
1415
protected array $calculatedScoutFields;
1516

17+
/**
18+
* The calculated scout instructions if already done in this request.
19+
*
20+
* @var array
21+
*/
22+
protected array $calculatedScoutInstructions;
23+
1624
/**
1725
* The scout fields that could be provided.
1826
*
@@ -25,6 +33,18 @@ public function scoutFields(RestRequest $request): array
2533
return [];
2634
}
2735

36+
/**
37+
* The scout instructions that could be provided.
38+
*
39+
* @param RestRequest $request
40+
*
41+
* @return array
42+
*/
43+
public function scoutInstructions(RestRequest $request): array
44+
{
45+
return [];
46+
}
47+
2848
/**
2949
* Get the resource's scout fields.
3050
*
@@ -36,4 +56,39 @@ public function getScoutFields(\Lomkit\Rest\Http\Requests\RestRequest $request):
3656
{
3757
return $this->calculatedScoutFields ?? ($this->calculatedScoutFields = $this->scoutFields($request));
3858
}
59+
60+
/**
61+
* Get the resource's scout instructions.
62+
*
63+
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
64+
*
65+
* @return array
66+
*/
67+
public function getScoutInstructions(\Lomkit\Rest\Http\Requests\RestRequest $request): array
68+
{
69+
return $this->calculatedScoutInstructions ?? ($this->calculatedScoutInstructions = $this->scoutInstructions($request));
70+
}
71+
72+
/**
73+
* Retrieve a specific scout instruction by its key.
74+
*
75+
* @param RestRequest $request The REST request instance.
76+
* @param string $instructionKey The key of the instruction to retrieve.
77+
*
78+
* @return Instruction|null The instruction instance or null if not found.
79+
*/
80+
public function scoutInstruction(RestRequest $request, string $instructionKey)
81+
{
82+
$instruction = collect($this->getScoutInstructions($request))
83+
->first(function (Instruction $instruction) use ($instructionKey) {
84+
return $instruction->uriKey() === $instructionKey;
85+
});
86+
87+
if (!is_null($instruction)) {
88+
$instruction
89+
->resource($this);
90+
}
91+
92+
return $instruction;
93+
}
3994
}

src/Http/Resource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public function jsonSerialize(): mixed
162162
return [
163163
'actions' => collect($this->getActions($request))->map->jsonSerialize()->toArray(),
164164
'instructions' => collect($this->getInstructions($request))->map->jsonSerialize()->toArray(),
165+
'scout_instructions' => collect($this->getScoutInstructions($request))->map->jsonSerialize()->toArray(),
165166
'fields' => $this->getFields($request),
166167
'scout_fields' => $this->getScoutFields($request),
167168
'limits' => $this->getLimits($request),

src/Query/ScoutBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public function applySorts($sorts)
118118
*/
119119
public function instruction($name, $fields = [])
120120
{
121-
$this->resource->instruction(app(RestRequest::class), $name)
121+
$this->resource->scoutInstruction(app(RestRequest::class), $name)
122122
->handleScout(
123123
collect($fields)->mapWithKeys(function ($field) {return [$field['name'] => $field['value']]; })->toArray(),
124124
$this->queryBuilder

src/Rules/SearchRules.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ public function filtersRules(\Lomkit\Rest\Http\Resource $resource, string $prefi
193193
*/
194194
protected function scopesRules(\Lomkit\Rest\Http\Resource $resource, string $prefix)
195195
{
196+
if ($this->isScoutMode()) {
197+
return [
198+
$prefix => 'prohibited'
199+
];
200+
}
201+
196202
$rules = [
197203
$prefix.'.*.name' => [
198204
Rule::in($resource->getScopes($this->request)),
@@ -218,13 +224,20 @@ protected function scopesRules(\Lomkit\Rest\Http\Resource $resource, string $pre
218224
*/
219225
protected function instructionsRules(\Lomkit\Rest\Http\Resource $resource, string $prefix)
220226
{
227+
$instructionNames = Rule::in(
228+
collect(
229+
$this->isScoutMode() ?
230+
$resource->getScoutInstructions($this->request):
231+
$resource->getInstructions($this->request)
232+
)
233+
->map(function ($instruction) { return $instruction->uriKey(); })
234+
->toArray()
235+
);
236+
237+
221238
$rules = [
222239
$prefix.'.*.name' => [
223-
Rule::in(
224-
collect($resource->getInstructions($this->request))
225-
->map(function ($instruction) { return $instruction->uriKey(); })
226-
->toArray()
227-
),
240+
$instructionNames,
228241
'required',
229242
'string',
230243
],

tests/Feature/Controllers/SearchScoutOperationsTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,31 @@ public function test_getting_a_list_of_resources_with_not_allowed_sort_field():
116116
$response->assertJsonStructure(['message', 'errors' => ['search.sorts.0.field']]);
117117
}
118118

119+
public function test_getting_a_list_of_resources_with_not_allowed_instruction(): void
120+
{
121+
ModelFactory::new()->count(2)->create();
122+
123+
Gate::policy(Model::class, GreenPolicy::class);
124+
125+
$response = $this->post(
126+
'/api/searchable-models/search',
127+
[
128+
'search' => [
129+
'text' => [
130+
'value' => 'text',
131+
],
132+
'instructions' => [
133+
['name' => 'not_authorized_instruction'],
134+
],
135+
],
136+
],
137+
['Accept' => 'application/json']
138+
);
139+
140+
$response->assertStatus(422);
141+
$response->assertJsonStructure(['message', 'errors' => ['search.instructions.0.name']]);
142+
}
143+
119144
public function test_getting_a_list_of_resources_with_allowed_filter_field(): void
120145
{
121146
ModelFactory::new()->count(2)->create();
@@ -172,6 +197,34 @@ public function test_getting_a_list_of_resources_with_allowed_sort_field(): void
172197
);
173198
}
174199

200+
public function test_getting_a_list_of_resources_with_allowed_instruction(): void
201+
{
202+
ModelFactory::new()->count(2)->create();
203+
204+
Gate::policy(Model::class, GreenPolicy::class);
205+
206+
$response = $this->post(
207+
'/api/searchable-models/search',
208+
[
209+
'search' => [
210+
'text' => [
211+
'value' => 'text',
212+
],
213+
'instructions' => [
214+
['name' => 'numbered'],
215+
],
216+
],
217+
],
218+
['Accept' => 'application/json']
219+
);
220+
221+
$this->assertResourcePaginated(
222+
$response,
223+
[],
224+
new ModelResource()
225+
);
226+
}
227+
175228
public function test_getting_a_list_of_resources_with_not_allowed_filter_nested(): void
176229
{
177230
ModelFactory::new()->count(2)->create();
@@ -227,6 +280,31 @@ public function test_getting_a_list_of_resources_with_not_allowed_filter_type():
227280
$response->assertJsonStructure(['message', 'errors' => ['search.filters.0.type']]);
228281
}
229282

283+
public function test_getting_a_list_of_resources_with_not_allowed_scopes(): void
284+
{
285+
ModelFactory::new()->count(2)->create();
286+
287+
Gate::policy(Model::class, GreenPolicy::class);
288+
289+
$response = $this->post(
290+
'/api/searchable-models/search',
291+
[
292+
'search' => [
293+
'text' => [
294+
'value' => 'text',
295+
],
296+
'scopes' => [
297+
['name' => 'numbered'],
298+
],
299+
],
300+
],
301+
['Accept' => 'application/json']
302+
);
303+
304+
$response->assertStatus(422);
305+
$response->assertJsonStructure(['message', 'errors' => ['search.scopes']]);
306+
}
307+
230308
public function test_getting_a_list_of_resources_with_scout(): void
231309
{
232310
ModelFactory::new()->count(2)->create();

tests/Support/Rest/Instructions/NumberedInstruction.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public function handle(array $fields, \Illuminate\Database\Eloquent\Builder $que
1111
$query->where('number', '>', $fields['number'] ?? 0);
1212
}
1313

14+
public function handleScout(array $fields, \Laravel\Scout\Builder $query)
15+
{
16+
//
17+
}
18+
1419
public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request): array
1520
{
1621
return [

tests/Support/Rest/Resources/SearchableModelResource.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lomkit\Rest\Http\Requests\RestRequest;
77
use Lomkit\Rest\Http\Resource;
88
use Lomkit\Rest\Tests\Support\Models\SearchableModel;
9+
use Lomkit\Rest\Tests\Support\Rest\Instructions\NumberedInstruction;
910

1011
class SearchableModelResource extends Resource
1112
{
@@ -26,4 +27,11 @@ public function scoutFields(RestRequest $request): array
2627
'allowed_scout_field',
2728
];
2829
}
30+
31+
public function scoutInstructions(RestRequest $request): array
32+
{
33+
return [
34+
NumberedInstruction::make(),
35+
];
36+
}
2937
}

0 commit comments

Comments
 (0)