Skip to content

Commit b177212

Browse files
authored
Merge pull request #49 from Lomkit/fix/memory-leak
Fix/memory leak
2 parents 9ea7968 + acf262c commit b177212

File tree

10 files changed

+126
-59
lines changed

10 files changed

+126
-59
lines changed

src/Console/stubs/action.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class {{ class }} extends RestAction
2626
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
2727
* @return array
2828
*/
29-
public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request)
29+
public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request): array
3030
{
3131
return [
3232
'id' => [

src/Console/stubs/instruction.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class {{ class }} extends RestInstruction
2424
* @param \Lomkit\Rest\Http\Requests\RestRequest $request
2525
* @return array
2626
*/
27-
public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request)
27+
public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request): array
2828
{
2929
return [
3030
'id' => [

src/Documentation/Schemas/RequestBody.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ public function generateSearch(Controller $controller): RequestBody
191191
],
192192
],
193193
],
194+
'gates' => [
195+
'create',
196+
],
194197
'page' => 2,
195198
'limit' => 10,
196199
]

src/Http/Requests/SearchRequest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function searchRules(Resource $resource, $prefix = '', $isRootSearchRules
5252
[
5353
'limit' => ['sometimes', 'integer', Rule::in($resource->getLimits($this))],
5454
'page' => ['sometimes', 'integer'],
55+
'gates' => ['sometimes', 'array', Rule::in(['viewAny', 'view', 'create', 'update', 'delete', 'restore', 'forceDelete'])],
5556
],
5657
$isRootSearchRules ? ['includes' => ['sometimes', 'array']] : [],
5758
$isRootSearchRules ? $this->includesRules($resource) : [],

src/Http/Resource.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public function getAuthorizationCacheKey(RestRequest $request, string $identifie
133133
/**
134134
* Determine for how much time the authorization cache should be keeped.
135135
*
136-
* @return \DateTimeInterface|\DateInterval|float|int|null
136+
* @return \DateTimeInterface|\DateInterval
137137
*/
138138
public function cacheAuthorizationFor()
139139
{
@@ -172,7 +172,7 @@ public function getResourceCacheKey(RestRequest $request, string $identifier)
172172
/**
173173
* Determine for how much time the resource cache should be keeped.
174174
*
175-
* @return \DateTimeInterface|\DateInterval|float|int|null
175+
* @return \DateTimeInterface|\DateInterval
176176
*/
177177
public function cacheResourceFor()
178178
{

src/Http/Response.php

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Illuminate\Database\Eloquent\Relations\Pivot;
88
use Illuminate\Pagination\LengthAwarePaginator;
99
use Illuminate\Support\Collection;
10-
use Illuminate\Support\Facades\Gate;
1110
use Illuminate\Support\Str;
1211
use Lomkit\Rest\Http\Requests\RestRequest;
1312
use Lomkit\Rest\Relations\Relation;
@@ -31,15 +30,15 @@ public function resource(Resource $resource)
3130
});
3231
}
3332

34-
protected function buildGatesForModel(Model $model)
33+
protected function buildGatesForModel(Model $model, Resource $resource, array $gates)
3534
{
36-
return [
37-
config('rest.automatic_gates.names.authorized_to_view') => Gate::allows('view', $model),
38-
config('rest.automatic_gates.names.authorized_to_update') => Gate::allows('update', $model),
39-
config('rest.automatic_gates.names.authorized_to_delete') => Gate::allows('delete', $model),
40-
config('rest.automatic_gates.names.authorized_to_restore') => Gate::allows('restore', $model),
41-
config('rest.automatic_gates.names.authorized_to_force_delete') => Gate::allows('forceDelete', $model),
42-
];
35+
return array_merge(
36+
in_array('view', $gates) ? [config('rest.automatic_gates.names.authorized_to_view') => $resource->authorizedTo('view', $model)] : [],
37+
in_array('update', $gates) ? [config('rest.automatic_gates.names.authorized_to_update') => $resource->authorizedTo('update', $model)] : [],
38+
in_array('delete', $gates) ? [config('rest.automatic_gates.names.authorized_to_delete') => $resource->authorizedTo('delete', $model)] : [],
39+
in_array('restore', $gates) ? [config('rest.automatic_gates.names.authorized_to_restore') => $resource->authorizedTo('restore', $model)] : [],
40+
in_array('forceDelete', $gates) ? [config('rest.automatic_gates.names.authorized_to_force_delete') => $resource->authorizedTo('forceDelete', $model)] : [],
41+
);
4342
}
4443

4544
public function modelToResponse(Model $model, Resource $resource, array $requestArray, Relation $relation = null)
@@ -54,26 +53,26 @@ public function modelToResponse(Model $model, Resource $resource, array $request
5453
collect($model->attributesToArray())
5554
->only(
5655
array_merge(
57-
isset($requestArray['selects']) ?
58-
collect($requestArray['selects'])->pluck('field')->toArray() :
56+
isset($currentRequestArray['selects']) ?
57+
collect($currentRequestArray['selects'])->pluck('field')->toArray() :
5958
$resource->getFields(app()->make(RestRequest::class)),
6059
// Here we add the aggregates
61-
collect($requestArray['aggregates'] ?? [])
60+
collect($currentRequestArray['aggregates'] ?? [])
6261
->map(function ($aggregate) {
6362
return Str::snake($aggregate['relation']).'_'.$aggregate['type'].(isset($aggregate['field']) ? '_'.$aggregate['field'] : '');
6463
})
6564
->toArray()
6665
)
6766
)
68-
->when($resource->isAutomaticGatingEnabled(), function ($attributes) use ($model) {
67+
->when($resource->isAutomaticGatingEnabled() && isset($currentRequestArray['gates']), function ($attributes) use ($currentRequestArray, $resource, $model) {
6968
return $attributes->put(
7069
config('rest.automatic_gates.key'),
71-
$this->buildGatesForModel($model)
70+
$this->buildGatesForModel($model, $resource, $currentRequestArray['gates'])
7271
);
7372
})
7473
->toArray(),
7574
collect($model->getRelations())
76-
->mapWithKeys(function ($modelRelation, $relationName) use ($requestArray, $relation, $resource) {
75+
->mapWithKeys(function ($modelRelation, $relationName) use ($requestArray, $currentRequestArray, $relation, $resource) {
7776
$key = Str::snake($relationName);
7877

7978
if (is_null($modelRelation)) {
@@ -90,7 +89,7 @@ public function modelToResponse(Model $model, Resource $resource, array $request
9089

9190
$relationConcrete = $resource->relation($relationName);
9291
$relationResource = $relationConcrete->resource();
93-
$requestArrayRelation = collect($requestArray['includes'])
92+
$requestArrayRelation = collect($currentRequestArray['includes'] ?? [])
9493
->first(function ($include) use ($relationName) {
9594
return preg_match('/(?:\.\b)?'.$relationName.'\b/', $include['relation']);
9695
});
@@ -128,9 +127,9 @@ public function toResponse($request)
128127
$this->responsable->perPage(),
129128
$this->responsable->currentPage(),
130129
$this->responsable->getOptions(),
131-
$this->resource->isAutomaticGatingEnabled() ? [
130+
$this->resource->isAutomaticGatingEnabled() && in_array('create', $request->input('gates', [])) ? [
132131
config('rest.automatic_gates.key') => [
133-
config('rest.automatic_gates.names.authorized_to_create') => Gate::allows('create', $this->resource::newModel()::class),
132+
config('rest.automatic_gates.names.authorized_to_create') => $this->resource->authorizedTo('create', $this->resource::newModel()::class),
134133
],
135134
] : []
136135
);
@@ -148,11 +147,13 @@ public function toResponse($request)
148147

149148
return [
150149
'data' => $data ?? $this->map($this->responsable, $this->modelToResponse($this->responsable, $this->resource, $request->input())),
151-
'meta' => [
152-
config('rest.automatic_gates.key') => [
153-
config('rest.automatic_gates.names.authorized_to_create') => Gate::allows('create', $this->resource::newModel()::class),
154-
],
155-
],
150+
'meta' => array_merge(
151+
$this->resource->isAutomaticGatingEnabled() && in_array('create', $request->input('gates', [])) ? [
152+
config('rest.automatic_gates.key') => [
153+
config('rest.automatic_gates.names.authorized_to_create') => $this->resource->authorizedTo('create', $this->resource::newModel()::class),
154+
],
155+
] : []
156+
),
156157
];
157158
}
158159

tests/Feature/Controllers/AutomaticGatingTest.php

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
use Lomkit\Rest\Http\Requests\RestRequest;
77
use Lomkit\Rest\Tests\Feature\TestCase;
88
use Lomkit\Rest\Tests\Support\Database\Factories\BelongsToManyRelationFactory;
9+
use Lomkit\Rest\Tests\Support\Database\Factories\HasManyRelationFactory;
910
use Lomkit\Rest\Tests\Support\Database\Factories\ModelFactory;
1011
use Lomkit\Rest\Tests\Support\Models\BelongsToManyRelation;
12+
use Lomkit\Rest\Tests\Support\Models\HasManyRelation;
1113
use Lomkit\Rest\Tests\Support\Models\Model;
1214
use Lomkit\Rest\Tests\Support\Policies\CreatePolicy;
1315
use Lomkit\Rest\Tests\Support\Policies\DeletePolicy;
@@ -32,7 +34,7 @@ public function test_searching_automatic_gated_resource(): void
3234
$response = $this->post(
3335
'/api/automatic-gating/search',
3436
[
35-
37+
'gates' => ['create', 'view', 'update', 'delete', 'forceDelete', 'restore'],
3638
],
3739
['Accept' => 'application/json']
3840
);
@@ -92,7 +94,7 @@ public function test_searching_automatic_gated_resource_with_create_policy(): vo
9294
$response = $this->post(
9395
'/api/automatic-gating/search',
9496
[
95-
97+
'gates' => ['create', 'view'],
9698
],
9799
['Accept' => 'application/json']
98100
);
@@ -105,10 +107,6 @@ public function test_searching_automatic_gated_resource_with_create_policy(): vo
105107
[
106108
'gates' => [
107109
'authorized_to_view' => false,
108-
'authorized_to_update' => false,
109-
'authorized_to_delete' => false,
110-
'authorized_to_restore' => false,
111-
'authorized_to_force_delete' => false,
112110
],
113111
],
114112
]
@@ -128,7 +126,7 @@ public function test_searching_automatic_gated_resource_with_view_policy(): void
128126
$response = $this->post(
129127
'/api/automatic-gating/search',
130128
[
131-
129+
'gates' => ['view', 'create'],
132130
],
133131
['Accept' => 'application/json']
134132
);
@@ -141,10 +139,6 @@ public function test_searching_automatic_gated_resource_with_view_policy(): void
141139
[
142140
'gates' => [
143141
'authorized_to_view' => true,
144-
'authorized_to_update' => false,
145-
'authorized_to_delete' => false,
146-
'authorized_to_restore' => false,
147-
'authorized_to_force_delete' => false,
148142
],
149143
],
150144
]
@@ -164,7 +158,7 @@ public function test_searching_automatic_gated_resource_with_update_policy(): vo
164158
$response = $this->post(
165159
'/api/automatic-gating/search',
166160
[
167-
161+
'gates' => ['update'],
168162
],
169163
['Accept' => 'application/json']
170164
);
@@ -176,17 +170,13 @@ public function test_searching_automatic_gated_resource_with_update_policy(): vo
176170
[
177171
[
178172
'gates' => [
179-
'authorized_to_view' => false,
180173
'authorized_to_update' => true,
181-
'authorized_to_delete' => false,
182-
'authorized_to_restore' => false,
183-
'authorized_to_force_delete' => false,
184174
],
185175
],
186176
]
187177
);
188178
$response->assertJson(
189-
['meta' => ['gates' => ['authorized_to_create' => false]]]
179+
['meta' => []]
190180
);
191181
}
192182

@@ -200,7 +190,7 @@ public function test_searching_automatic_gated_resource_with_delete_policy(): vo
200190
$response = $this->post(
201191
'/api/automatic-gating/search',
202192
[
203-
193+
'gates' => ['create', 'delete'],
204194
],
205195
['Accept' => 'application/json']
206196
);
@@ -212,11 +202,7 @@ public function test_searching_automatic_gated_resource_with_delete_policy(): vo
212202
[
213203
[
214204
'gates' => [
215-
'authorized_to_view' => false,
216-
'authorized_to_update' => false,
217205
'authorized_to_delete' => true,
218-
'authorized_to_restore' => false,
219-
'authorized_to_force_delete' => false,
220206
],
221207
],
222208
]
@@ -236,7 +222,7 @@ public function test_searching_automatic_gated_resource_with_restore_policy(): v
236222
$response = $this->post(
237223
'/api/automatic-gating/search',
238224
[
239-
225+
'gates' => ['restore', 'view'],
240226
],
241227
['Accept' => 'application/json']
242228
);
@@ -249,16 +235,13 @@ public function test_searching_automatic_gated_resource_with_restore_policy(): v
249235
[
250236
'gates' => [
251237
'authorized_to_view' => false,
252-
'authorized_to_update' => false,
253-
'authorized_to_delete' => false,
254238
'authorized_to_restore' => true,
255-
'authorized_to_force_delete' => false,
256239
],
257240
],
258241
]
259242
);
260243
$response->assertJson(
261-
['meta' => ['gates' => ['authorized_to_create' => false]]]
244+
['meta' => []]
262245
);
263246
}
264247

@@ -272,7 +255,7 @@ public function test_searching_automatic_gated_resource_with_force_delete_policy
272255
$response = $this->post(
273256
'/api/automatic-gating/search',
274257
[
275-
258+
'gates' => ['forceDelete', 'create'],
276259
],
277260
['Accept' => 'application/json']
278261
);
@@ -284,10 +267,6 @@ public function test_searching_automatic_gated_resource_with_force_delete_policy
284267
[
285268
[
286269
'gates' => [
287-
'authorized_to_view' => false,
288-
'authorized_to_update' => false,
289-
'authorized_to_delete' => false,
290-
'authorized_to_restore' => false,
291270
'authorized_to_force_delete' => true,
292271
],
293272
],
@@ -316,11 +295,15 @@ public function test_searching_automatic_gated_resource_with_belongs_to_many_rel
316295
'includes' => [
317296
[
318297
'relation' => 'belongsToManyRelation',
298+
'gates' => ['view'],
319299
],
320300
],
321301
'sorts' => [
322302
['field' => 'id', 'direction' => 'asc'],
323303
],
304+
'gates' => [
305+
'view', 'update', 'create', 'delete', 'restore', 'forceDelete',
306+
],
324307
],
325308
['Accept' => 'application/json']
326309
);
@@ -368,8 +351,44 @@ public function test_searching_automatic_gated_resource_with_belongs_to_many_rel
368351
],
369352
]
370353
);
354+
$this->assertArrayNotHasKey(
355+
'gates',
356+
$response->json('data.0.belongs_to_many_relation.0')
357+
);
371358
$response->assertJson(
372359
['meta' => ['gates' => ['authorized_to_create' => true]]]
373360
);
374361
}
362+
363+
public function test_searching_automatic_gated_resource_with_not_requested_includes_gates(): void
364+
{
365+
ModelFactory::new()
366+
->count(10)
367+
->has(
368+
HasManyRelationFactory::new()
369+
->count(10)
370+
)
371+
->create();
372+
373+
Gate::policy(Model::class, GreenPolicy::class);
374+
Gate::policy(HasManyRelation::class, GreenPolicy::class);
375+
376+
$response = $this->post(
377+
'/api/automatic-gating/search',
378+
[
379+
'includes' => [
380+
[
381+
'relation' => 'hasManyRelation',
382+
],
383+
],
384+
'gates' => ['view', 'create', 'update', 'delete', 'restore', 'forceDelete'],
385+
],
386+
['Accept' => 'application/json']
387+
);
388+
389+
$this->assertArrayNotHasKey(
390+
'gates',
391+
$response->json('data.0.has_many_relation.0')
392+
);
393+
}
375394
}

0 commit comments

Comments
 (0)