Skip to content
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ html
output
docs/node_modules
docs/.vuepress/dist
.phpunit.result.cache

1 change: 1 addition & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
Route::post('/{repository}', 'RepositoryStoreController@handle');
Route::get('/{repository}/{repositoryId}', 'RepositoryShowController@handle');
Route::patch('/{repository}/{repositoryId}', 'RepositoryUpdateController@handle');
Route::put('/{repository}/{repositoryId}', 'RepositoryUpdateController@handle');
Route::delete('/{repository}/{repositoryId}', 'RepositoryDestroyController@handle');
11 changes: 5 additions & 6 deletions src/Exceptions/RestifyHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Binaryk\LaravelRestify\Exceptions\UnauthorizedException as ActionUnauthorizedException;
use Binaryk\LaravelRestify\Restify;
use Closure;
use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
Expand Down Expand Up @@ -54,8 +53,8 @@ class RestifyHandler extends ExceptionHandler
/**
* Render an exception into an HTTP response.
*
* @param Request $request
* @param \Exception|Throwable $exception
* @param Request $request
* @param \Exception|Throwable $exception
*
* @return Response|\Symfony\Component\HttpFoundation\Response
* @throws \Illuminate\Contracts\Container\BindingResolutionException
Expand Down Expand Up @@ -94,13 +93,13 @@ public function render($request, $exception)
case $exception instanceof ValidationUnauthorized:
case $exception instanceof UnauthorizedHttpException:
case $exception instanceof UnauthenticateException:
case $exception instanceof ActionUnauthorizedException:
case $exception instanceof AuthorizationException:
case $exception instanceof GatePolicy:
case $exception instanceof AuthenticationException:
$response->addError($exception->getMessage())->auth();
break;

case $exception instanceof AuthorizationException:
case $exception instanceof ActionUnauthorizedException:
case $exception instanceof AccessDeniedHttpException:
case $exception instanceof InvalidSignatureException:
$response->addError($exception->getMessage())->forbidden();
Expand All @@ -121,7 +120,7 @@ public function render($request, $exception)
/**
* Report or log an exception.
*
* @param \Exception|Throwable $e
* @param \Exception|Throwable $e
* @return mixed
*
* @throws \Exception
Expand Down
6 changes: 3 additions & 3 deletions src/Fields/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public function getAttribute()

/**
* Validation rules for store.
* @param callable|array|string $rules
* @param $rules
* @return Field
*/
public function storingRules($rules)
Expand All @@ -166,7 +166,7 @@ public function storingRules($rules)
/**
* Validation rules for update.
*
* @param callable|array|string $rules
* @param $rules
* @return Field
*/
public function updatingRules($rules)
Expand All @@ -178,7 +178,7 @@ public function updatingRules($rules)

/**
* Validation rules for store.
* @param callable|array|string $rules
* @param $rules
* @return Field
*/
public function rules($rules)
Expand Down
55 changes: 55 additions & 0 deletions tests/Controllers/RepositoryDestroyControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Exceptions\RestifyHandler;
use Binaryk\LaravelRestify\Tests\Fixtures\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\PostPolicy;
use Binaryk\LaravelRestify\Tests\IntegrationTest;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Support\Facades\Gate;

/**
* @author Eduard Lupacescu <eduard.lupacescu@binarcode.com>
*/
class RepositoryDestroyControllerTest extends IntegrationTest
{
protected function setUp(): void
{
parent::setUp();
$this->authenticate();
$this->app->bind(ExceptionHandler::class, RestifyHandler::class);
}

public function test_destroy_works()
{
$post = factory(Post::class)->create(['user_id' => 1]);

$this->assertInstanceOf(Post::class, Post::find($post->id));

$this->withoutExceptionHandling()->delete('/restify-api/posts/'.$post->id, [
'title' => 'Updated title',
])
->assertStatus(204);

$this->assertNull(Post::find($post->id));
}

public function test_unathorized_to_destroy()
{
Gate::policy(Post::class, PostPolicy::class);

$post = factory(Post::class)->create(['user_id' => 1]);

$_SERVER['restify.post.deletable'] = false;

$this->delete('/restify-api/posts/'.$post->id, [
'title' => 'Updated title',
])->assertStatus(403)
->assertJson([
'errors' => ['This action is unauthorized.'],
]);

$this->assertInstanceOf(Post::class, $post->refresh());
}
}
68 changes: 68 additions & 0 deletions tests/Controllers/RepositoryUpdateControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Exceptions\RestifyHandler;
use Binaryk\LaravelRestify\Tests\Fixtures\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\PostPolicy;
use Binaryk\LaravelRestify\Tests\IntegrationTest;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Support\Facades\Gate;

/**
* @author Eduard Lupacescu <eduard.lupacescu@binarcode.com>
*/
class RepositoryUpdateControllerTest extends IntegrationTest
{
protected function setUp(): void
{
parent::setUp();
$this->authenticate();
}

public function test_basic_update_works()
{
$post = factory(Post::class)->create(['user_id' => 1]);

$this->withoutExceptionHandling()->patch('/restify-api/posts/'.$post->id, [
'title' => 'Updated title',
])
->assertStatus(200);

$updatedPost = Post::find($post->id);

$this->assertEquals($updatedPost->title, 'Updated title');
}

public function test_put_works()
{
$post = factory(Post::class)->create(['user_id' => 1]);

$this->withoutExceptionHandling()->put('/restify-api/posts/'.$post->id, [
'title' => 'Updated title',
])
->assertStatus(200);

$updatedPost = Post::find($post->id);

$this->assertEquals($updatedPost->title, 'Updated title');
}

public function test_unathorized_to_update()
{
$this->app->bind(ExceptionHandler::class, RestifyHandler::class);

Gate::policy(Post::class, PostPolicy::class);

$post = factory(Post::class)->create(['user_id' => 1]);

$_SERVER['restify.post.updateable'] = false;

$this->patch('/restify-api/posts/'.$post->id, [
'title' => 'Updated title',
])->assertStatus(403)
->assertJson([
'errors' => ['This action is unauthorized.'],
]);
}
}
10 changes: 10 additions & 0 deletions tests/Fixtures/PostPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,14 @@ public function store($user)
{
return $_SERVER['restify.post.creatable'] ?? true;
}

public function update($user, $post)
{
return $_SERVER['restify.post.updateable'] ?? true;
}

public function delete($user, $post)
{
return $_SERVER['restify.post.deletable'] ?? true;
}
}
13 changes: 13 additions & 0 deletions tests/HandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Binaryk\LaravelRestify\Exceptions\Guard\GatePolicy;
use Binaryk\LaravelRestify\Exceptions\RestifyHandler;
use Binaryk\LaravelRestify\Exceptions\UnauthenticateException;
use Binaryk\LaravelRestify\Restify;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Testing\Concerns\InteractsWithContainer;
Expand Down Expand Up @@ -150,4 +151,16 @@ public function test_default_unhandled_exception_production()
$this->assertObjectHasAttribute('errors', $response->getData());
$this->assertEquals($response->getData()->errors[0], __('messages.something_went_wrong'));
}

public function test_can_inject_custom_handler_but_handler_will_continue_handle()
{
Restify::exceptionHandler(function ($request, $exception) {
$this->assertInstanceOf(NotFoundHttpException::class, $exception);
});

$response = $this->handler->render($this->request, new NotFoundHttpException('This message is not visible'));
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertEquals($response->getData()->errors[0], __('messages.not_found'));
$this->assertEquals($response->getStatusCode(), 404);
}
}
29 changes: 25 additions & 4 deletions tests/RepositoryWithRoutesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ protected function setUp(): void
WithCustomPrefix::class,
WithCustomMiddleware::class,
WithCustomNamespace::class,
WithoutGroup::class,
]);

parent::setUp();
Expand Down Expand Up @@ -62,15 +63,27 @@ public function test_can_use_custom_namespace()
],
]);
}

public function test_routes_default_wrapped()
{
$this->withoutExceptionHandling()->getJson(route('no.group.default.options'))
->assertStatus(200)
->assertJson([
'meta' => [
'message' => 'From the sayHello method',
],
]);
}
}

class RepositoryWithRoutes extends Repository
{
/**
* @param Router $router
* @param array $attributes
* @param bool $wrap
*/
public static function routes(Router $router, $attributes)
public static function routes(Router $router, $attributes, $wrap = false)
{
$router->group($attributes, function ($router) {
$router->get('/main-testing', function () {
Expand All @@ -89,7 +102,7 @@ public static function uriKey()

class WithCustomPrefix extends RepositoryWithRoutes
{
public static function routes(Router $router, $attributes)
public static function routes(Router $router, $attributes, $wrap = false)
{
$attributes['prefix'] = 'custom-prefix';

Expand All @@ -115,7 +128,7 @@ public function handle($request, $next)

class WithCustomMiddleware extends RepositoryWithRoutes
{
public static function routes(Router $router, $options)
public static function routes(Router $router, $options, $wrap = false)
{
$options['middleware'] = [MiddlewareFail::class];

Expand All @@ -131,7 +144,7 @@ public static function routes(Router $router, $options)

class WithCustomNamespace extends RepositoryWithRoutes
{
public static function routes(Router $router, $options)
public static function routes(Router $router, $options, $wrap = false)
{
$options['namespace'] = 'Binaryk\LaravelRestify\Tests';

Expand All @@ -141,6 +154,14 @@ public static function routes(Router $router, $options)
}
}

class WithoutGroup extends RepositoryWithRoutes
{
public static function routes(Router $router, $options = [], $wrap = true)
{
$router->get('default-options', '\\'.HandleController::class.'@sayHello')->name('no.group.default.options');
}
}

class HandleController extends RestController
{
/**
Expand Down