From 3ddbe29bd3632908adb3bd79bc4763316a4c1aa9 Mon Sep 17 00:00:00 2001 From: fokosun Date: Sat, 9 Sep 2023 19:06:50 -0400 Subject: [PATCH] refactor recipe controller --- .github/workflows/feature-test.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- .github/workflows/unit-test.yml | 2 +- app/Http/Controllers/RecipeController.php | 81 ++++++++++++++--------- app/Services/RecipeService.php | 64 +++++------------- tests/Feature/RecipeTest.php | 47 ++++++++++++- 6 files changed, 115 insertions(+), 83 deletions(-) diff --git a/.github/workflows/feature-test.yml b/.github/workflows/feature-test.yml index c4c714ff..c3bba071 100755 --- a/.github/workflows/feature-test.yml +++ b/.github/workflows/feature-test.yml @@ -1,4 +1,4 @@ -name: Feature test +name: Run Feature tests on: push: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 9c537192..f426c044 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -1,4 +1,4 @@ -name: Static analysis +name: Run Static analysis on: push: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 6b174ae4..760ebdb1 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -1,4 +1,4 @@ -name: Unit test +name: Run Unit tests on: push: diff --git a/app/Http/Controllers/RecipeController.php b/app/Http/Controllers/RecipeController.php index 703e2a15..2a94d6dc 100755 --- a/app/Http/Controllers/RecipeController.php +++ b/app/Http/Controllers/RecipeController.php @@ -4,19 +4,21 @@ namespace App\Http\Controllers; +use AllowDynamicProperties; use App\Http\Requests\RecipeStoreRequest; use App\Models\Recipe; use App\Services\RecipeService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Http\Response; use Illuminate\Support\Facades\Log; +use Symfony\Component\HttpFoundation\Response as ResponseAlias; +use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\JWT; /** * Class UserController */ -class RecipeController extends Controller +#[AllowDynamicProperties] class RecipeController extends Controller { protected RecipeService $service; @@ -33,26 +35,30 @@ public function __construct(RecipeService $service) } /** - * Get all recipes belonging to a user - * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ - public function index(): \Illuminate\Http\JsonResponse + public function index() { - return $this->service->index(); + return $this->successResponse(['data' => $this->service->index()]); } /** * @param $recipeId - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object + * @return JsonResponse * @throws \App\Exceptions\CookbookModelNotFoundException */ - public function show($recipeId) + public function show($recipeId): JsonResponse { - return $this->service->show($recipeId); + return $this->successResponse(['data' => $this->service->show($recipeId)]); } - public function addClap(Request $request) + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Exceptions\CookbookModelNotFoundException + * @throws \Illuminate\Validation\ValidationException + */ + public function addClap(Request $request): JsonResponse { $this->validate( $request, [ @@ -60,18 +66,28 @@ public function addClap(Request $request) ] ); - return $this->service->addClap($request->get('recipe_id')); + return ($recipe = $this->service->addClap($request->get('recipe_id'))) ? + $this->successResponse(['updated' => true, 'claps' => $recipe->claps]) : + $this->errorResponse(['error' => 'There was an error processing this request. Please try again.']); } - public function myRecipes(Request $request, JWT $jwtAuth): \Illuminate\Http\JsonResponse + /** + * @param Request $request + * @param JWT $jwtAuth + * @return JsonResponse + * @throws JWTException + */ + public function myRecipes(Request $request, JWT $jwtAuth): JsonResponse { if ($jwtAuth->parseToken()->check()) { - return $this->service->index($request->get('user_id')); + return $this->successResponse([ + 'data' => $this->service->index($request->get('user_id')) + ]); } return response()->json([ 'error', 'You are not authorized to access this resource.' - ], 401); + ], ResponseAlias::HTTP_UNAUTHORIZED); } public function store(RecipeStoreRequest $request, JWT $jwtAuth) @@ -80,16 +96,16 @@ public function store(RecipeStoreRequest $request, JWT $jwtAuth) $jwtAuth->parseToken()->check(); return $this->service->store($request); } catch (\Exception $exception) { - Log::debug('An error occured while creating this recipe', [ + Log::debug('An error occurred while creating this recipe', [ 'resource' => self::RECIPE_RESOURCE, 'exception' => $exception ]); $message = "There was an error processing this request, please try again later."; - $code = Response::HTTP_BAD_REQUEST; + $code = ResponseAlias::HTTP_BAD_REQUEST; if ($exception->getCode() == 401) { - $code = Response::HTTP_UNAUTHORIZED; + $code = ResponseAlias::HTTP_UNAUTHORIZED; $message = "You are not authorized to perform this action."; } @@ -101,33 +117,32 @@ public function store(RecipeStoreRequest $request, JWT $jwtAuth) public function update(Request $request, $recipeId, JWT $jwtAuth) { - if ( - $request->user()->ownRecipe($recipeId) - ) { - if ( - $jwtAuth->parseToken()->check() - ) { - return $this->service->update($request, $recipeId); + if ($jwtAuth->parseToken()->check() && $request->user()->ownRecipe($recipeId)) { + if ($this->service->update($request, $recipeId)) { + return $this->successResponse(['updated' => true]); } + + return $this->errorResponse(['updated' => false]); } return response()->json([ 'error' => 'You are not authorized to access this resource.' - ], 401); + ], ResponseAlias::HTTP_UNAUTHORIZED); } public function destroy(Request $request, $recipeId, JWT $jwtAuth) { - if ( - $jwtAuth->parseToken()->check() && - $request->user()->isSuper() - ) { - return $this->service->delete($request->user(), $recipeId); + if ($jwtAuth->parseToken()->check() && $request->user()->isSuper()) { + if ($this->service->delete($request->user(), $recipeId)) { + return $this->successResponse(['deleted' => true]); + } + + return $this->errorResponse(['deleted' => false]); } return response()->json([ 'error' => 'You are not authorized to perform this action.' - ], Response::HTTP_UNAUTHORIZED); + ], ResponseAlias::HTTP_UNAUTHORIZED); } public function report(Request $request, JWT $jwtAuth): JsonResponse @@ -155,6 +170,6 @@ public function report(Request $request, JWT $jwtAuth): JsonResponse return response()->json([ 'error' => 'You are not authorized to perform this action.' - ], Response::HTTP_UNAUTHORIZED); + ], ResponseAlias::HTTP_UNAUTHORIZED); } } diff --git a/app/Services/RecipeService.php b/app/Services/RecipeService.php index 6ed353e9..24c898e8 100755 --- a/app/Services/RecipeService.php +++ b/app/Services/RecipeService.php @@ -18,10 +18,13 @@ use Carbon\Carbon; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Routing\ResponseFactory; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Arr; use Illuminate\Support\Str; +use Symfony\Component\HttpFoundation\Response as ResponseAlias; /** * Class RecipeService @@ -33,11 +36,7 @@ public function __construct() $this->serviceModel = new Recipe(); } - /** - * @param $user_id - * @return \Illuminate\Http\JsonResponse - */ - public function index($user_id = null): \Illuminate\Http\JsonResponse + public function index($user_id = null): Collection { $recipes = Recipe::paginate(100); @@ -45,34 +44,17 @@ public function index($user_id = null): \Illuminate\Http\JsonResponse return !$recipe->is_draft; }); - if ($user_id) { - return response()->json( - [ - 'data' => $recipes->where('user_id', '=', $user_id), - ], Response::HTTP_OK - ); - } - - return response()->json(['data' => $recipes]); + return $user_id ? $recipes->where('user_id', '=', $user_id) : $recipes; } /** - * Retrieve one Recipe - * * @param $id - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model - * + * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null * @throws CookbookModelNotFoundException */ public function show($id) { - $recipe = $this->get($id); - - if (!$recipe) { - throw new CookbookModelNotFoundException(); - } - - return $recipe; + return $this->get($id) ?: null; } /** @@ -144,7 +126,7 @@ public function store($request) /** * @param Request $request * @param $id - * @return Application|ResponseFactory|\Illuminate\Foundation\Application|Response|void + * @return bool|int|void * @throws CookbookModelNotFoundException * @throws InvalidPayloadException */ @@ -172,18 +154,14 @@ public function update(Request $request, $id) ->getKey(); } - return response( - [ - 'updated' => $recipe->update($payload), - ], Response::HTTP_OK - ); + return $recipe->update($payload); } } /** * @param User $user * @param $id - * @return Application|ResponseFactory|\Illuminate\Foundation\Application|Response|void + * @return bool|mixed|void|null * @throws CookbookModelNotFoundException */ public function delete(User $user, $id) @@ -191,17 +169,13 @@ public function delete(User $user, $id) if ($user->isSuper()) { $recipe = $this->get($id); - return response( - [ - 'deleted' => $recipe->delete(), - ], Response::HTTP_ACCEPTED - ); + return $recipe->delete(); } } /** * @param $recipeId - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|Response + * @return false|\Illuminate\Database\Eloquent\Model * @throws CookbookModelNotFoundException */ public function addClap($recipeId) @@ -209,14 +183,12 @@ public function addClap($recipeId) $recipe = $this->get($recipeId); $recipe->claps = $recipe->claps + 1; - $recipe->save(); - - return response( - [ - 'updated' => true, - 'claps' => $recipe->refresh()->claps, - ], Response::HTTP_OK - ); + + if ($recipe->save()) { + return $recipe->refresh(); + } + + return false; } /** diff --git a/tests/Feature/RecipeTest.php b/tests/Feature/RecipeTest.php index 6d4a5460..e0fdcbb3 100755 --- a/tests/Feature/RecipeTest.php +++ b/tests/Feature/RecipeTest.php @@ -13,6 +13,7 @@ use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; +use Symfony\Component\HttpFoundation\Response as ResponseAlias; class RecipeTest extends \TestCase { @@ -166,6 +167,50 @@ public function it_cannot_clap_for_a_recipe_that_does_not_exist() ]); } + /** + * @test + */ + public function it_can_show_a_recipe_by_id_or_slug() + { + $user = User::factory()->make([ + 'email' => 'evan.reid@123.com', + 'password' => (new BcryptHasher)->make('pass123'), + ]); + $user->save(); + + $token = Auth::attempt([ + 'email' => 'evan.reid@123.com', + 'password' => 'pass123' + ]); + + $cookbook = Cookbook::factory()->make([ + 'user_id' => $user->getKey() + ]); + + $cookbook->save(); + + $recipe = Recipe::factory()->make([ + 'cookbook_id' => $cookbook->refresh()->getKey(), + 'user_id' => $user->getKey() + ]); + + $recipe->save(); + $recipe = $recipe->refresh(); + + $searchBy = ['id', 'slug']; + + foreach ($searchBy as $key) { + $this->json( + 'GET', + '/api/v1/recipes/' . $recipe->$key, + [], + [ + 'Authorization' => 'Bearer ' . $token + ] + )->assertStatus(ResponseAlias::HTTP_OK)->assertJsonStructure(['data']); + } + } + /** * @test */ @@ -366,7 +411,7 @@ public function only_supers_can_destroy_a_recipe() [ 'Authorization' => 'Bearer ' . $token ] - )->assertStatus(Response::HTTP_ACCEPTED) + )->assertStatus(ResponseAlias::HTTP_OK) ->assertExactJson([ "deleted" => true ]);