diff --git a/.editorconfig b/.editorconfig old mode 100644 new mode 100755 diff --git a/.env.example b/.env.example old mode 100644 new mode 100755 diff --git a/.env.testing b/.env.testing new file mode 100755 index 0000000..1d45dbb --- /dev/null +++ b/.env.testing @@ -0,0 +1,36 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY=base64:vXZ/jB8d03n3McCvEFY3Po4Y9jqQDbXMapstnqHW6no= +APP_DEBUG=true +APP_URL=http://localhost:8080 + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=mysql +DB_PORT=3306 +DB_DATABASE=testing +DB_USERNAME=sail +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +L5_SWAGGER_CONST_HOST=${APP_URL}/api diff --git a/.gitattributes b/.gitattributes old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.rnd b/.rnd old mode 100644 new mode 100755 diff --git a/ANSWER.md b/ANSWER.md new file mode 100644 index 0000000..f3171d5 --- /dev/null +++ b/ANSWER.md @@ -0,0 +1,50 @@ +# DECISOES QUE TOMEI NO PROCESSO DE REFATORAÇÃO + +- API de usuários usei o padrão Repository com contrato, junto com um serviço para a lógica de negócio e controladores limpos, resulta em um código mais organizado, fácil de manter. + +- A separação das responsabilidades torna o código mais modular e fácil de manter. Alterações em uma camada (por exemplo, a lógica de negócio) não afetam diretamente outras camadas (por exemplo, o controlador). + +## Separação de Responsabilidades + +- InterfaceRepository: Define os métodos essenciais (all, find, create, update, delete) que qualquer repositório deve implementar. + +- AbstractRepository: Implementa a interface InterfaceRepository e fornece a lógica básica para manipulação de modelos. + +- UserRepository: Extende AbstractRepository e especifica que o modelo é User. + +## UserService Service para Lógica de Negócio: + +- UserService: Contém a lógica de negócio para manipulação dos usuários (busca, criação, atualização e deleção). O serviço interage com o repositório para realizar essas operações e formata a resposta. + +## UserController Limpo + +- UserController: Recebe as requisições, delega a lógica de negócio para o UserService e retorna as respostas formatadas. + +## ValidateUserRequest Validação Centralizada + +- ValidateUserRequest: Classe responsável pela validação dos dados de entrada para criação e atualização de usuários. Contém as regras e mensagens de validação. + +## UserControllerTest Testes Automatizados + +- Testes para garantir que os métodos do UserController funcionem conforme esperado. Isso inclui testar as operações de indexação, exibição, criação, atualização e deleção de usuários. + +## Legibilidade e Clareza: + +- A estrutura modular facilita a adição de novas funcionalidades ou a modificação das existentes sem causar grandes impactos na aplicação como um todo. + +## Mudança no makefile + +- A estrutura que estava como padrão não funcionava na minha maquina então adicionei uma condicional `||` para que funcione em outras versões inclusive a minha ex: +`docker-compose down || docker compose down` + +## Mudança no Readme.md + +- a porta da aplicaçaõ estava em `http://localhost:8000`. Fiz a mudança para `http://localhost:8080`. O mesmo para a documentação `http://localhost:8000/api/documentation`. Fiz a mudança para `http://localhost:8080/api/documentation`. + + +## Meio de Contato + +- whats: (98) 984242805 +- email: newtonplay007@gmail.com + +` Espero seu retorno ...` diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index fa48d7b..ccc341f --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ Ensure that docker is running on your local machine. make up ``` -The application will be accessible at `http://localhost:8000`. +The application will be accessible at `http://localhost:8080`. ### Documentation -The API is documented using Swagger. Ensure that your refactoring maintains or improves the clarity of the API documentation. The Swagger documentation can be accessed at `http://localhost:8000/api/documentation`. +The API is documented using Swagger. Ensure that your refactoring maintains or improves the clarity of the API documentation. The Swagger documentation can be accessed at `http://localhost:8080/api/documentation`. ### Useful Makefile Targets diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php old mode 100644 new mode 100755 diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php old mode 100644 new mode 100755 index 56af264..6dd33ec --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -2,8 +2,9 @@ namespace App\Exceptions; -use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Throwable; +use Illuminate\Validation\ValidationException; +use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { @@ -27,4 +28,23 @@ public function register(): void // }); } + + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * @param \Throwable $exception + * @return \Illuminate\Http\Response + */ + public function render($request, Throwable $exception) + { + if ($exception instanceof ValidationException) { + return response()->json([ + 'status' => false, + 'errors' => $exception->errors() + ], 422); + } + + return parent::render($request, $exception); + } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/Api/UserController.php old mode 100644 new mode 100755 similarity index 80% rename from app/Http/Controllers/UserController.php rename to app/Http/Controllers/Api/UserController.php index f4a5826..1a46501 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/Api/UserController.php @@ -1,17 +1,21 @@ user = $user; + $this->service = $service; } /** @@ -48,9 +52,10 @@ function __construct(User $user) * ) * ) */ - public function index(Request $request) + public function index(): JsonResponse { - return $this->user->get(); + $data = $this->service->getAllUser(); + return $this->generateResponse($data); } /** @@ -88,9 +93,10 @@ public function index(Request $request) * ) * ) */ - public function show(User $user) + public function show(int $id): JsonResponse { - return $user; + $data = $this->service->getUserById($id); + return $this->generateResponse($data); } /** @@ -126,15 +132,10 @@ public function show(User $user) * ) * ) */ - public function store(Request $request) + public function store(ValidateUserRequest $request): JsonResponse { - $data = $request->only([ - 'name', - 'email', - 'password', - ]); - - return $this->user->create($data); + $data = $this->service->createUser($request->all()); + return $this->generateResponse($data); } /** @@ -176,17 +177,10 @@ public function store(Request $request) * ) * ) */ - public function update(Request $request, User $user) + public function update(int $id, ValidateUserRequest $request): JsonResponse { - $data = $request->only([ - 'name', - 'email', - 'password', - ]); - - $user->update($data); - - return $user; + $data = $this->service->updateUser($id, $request->all()); + return $this->generateResponse($data); } /** @@ -224,11 +218,15 @@ public function update(Request $request, User $user) * ) * ) */ - public function destroy(User $user) + public function destroy(int $id): JsonResponse { - $user->delete(); + $data = $this->service->deleteUser($id); + return $this->generateResponse($data); + } - return $user; + private function generateResponse(array $data): JsonResponse + { + return response()->json($data['data'], $data['status']); } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php old mode 100644 new mode 100755 diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/ValidateSignature.php b/app/Http/Middleware/ValidateSignature.php old mode 100644 new mode 100755 diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php old mode 100644 new mode 100755 diff --git a/app/Http/Requests/ValidateUserRequest.php b/app/Http/Requests/ValidateUserRequest.php new file mode 100755 index 0000000..100d8fd --- /dev/null +++ b/app/Http/Requests/ValidateUserRequest.php @@ -0,0 +1,61 @@ +|string> + */ + public function rules(): array + { + if ($this->isMethod('post')) { + return [ + 'name' => 'required|string|max:255|min:3', + 'email' => 'required|email|unique:users,email', + 'password' => 'required|string|min:8', + ]; + } elseif ($this->isMethod('put') || $this->isMethod('patch')) { + return [ + 'name' => 'sometimes|string|max:255', + 'email' => 'sometimes|email|unique:users,email,' . $this->route('id'), + 'password' => 'sometimes|string|min:8', + ]; + } + + return []; + } + + /** + * Get the custom validation messages. + * + * @return array + */ + public function messages(): array + { + return [ + 'name.min' => 'O nome deve ter pelo menos 3 caracteres.', + 'name.required' => 'O nome é obrigatório.', + 'name.string' => 'O nome deve ser uma string.', + 'name.max' => 'O nome não pode ter mais de 255 caracteres.', + 'email.required' => 'O email é obrigatório.', + 'email.email' => 'O email deve ser um endereço de email válido.', + 'email.unique' => 'O email já está em uso.', + 'password.required' => 'A senha é obrigatória.', + 'password.string' => 'A senha deve ser uma string.', + 'password.min' => 'A senha deve ter pelo menos 8 caracteres.', + ]; + } +} diff --git a/app/Models/User.php b/app/Models/User.php old mode 100644 new mode 100755 diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php old mode 100644 new mode 100755 diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php old mode 100644 new mode 100755 diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php old mode 100644 new mode 100755 diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php old mode 100644 new mode 100755 diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php old mode 100644 new mode 100755 diff --git a/app/Repositories/Contract/AbstractRepository.php b/app/Repositories/Contract/AbstractRepository.php new file mode 100755 index 0000000..890cb0b --- /dev/null +++ b/app/Repositories/Contract/AbstractRepository.php @@ -0,0 +1,34 @@ +model->all(); + } + + public function find(int $id): ?object + { + return $this->model->find($id); + } + + public function create(array $data): object + { + return $this->model->create($data); + } + + public function update($data, array $request): object + { + $data->update($request); + return $data; + } + + public function delete(int $id): bool + { + return $this->model->find($id)->delete(); + } +} diff --git a/app/Repositories/Contract/InterfaceRepository.php b/app/Repositories/Contract/InterfaceRepository.php new file mode 100755 index 0000000..2a273e0 --- /dev/null +++ b/app/Repositories/Contract/InterfaceRepository.php @@ -0,0 +1,17 @@ +model = new User(); + } +} diff --git a/app/Services/UserService.php b/app/Services/UserService.php new file mode 100755 index 0000000..6cfda2b --- /dev/null +++ b/app/Services/UserService.php @@ -0,0 +1,78 @@ +repository = $repository; + } + + public function getAllUser(): array + { + $result = $this->repository->all(); + + if (empty($result)) { + return $this->formatResponse(400, 'User not found'); + } + + return $this->formatResponse(200, $result); + } + + public function getUserById(int $id): array + { + $result = $this->repository->find($id); + + if (empty($result)) { + return $this->formatResponse(400, 'User not found'); + } + + return $this->formatResponse(200, $result); + } + + public function createUser(array $data): array + { + $data['password'] = bcrypt($data['password']); + $result = $this->repository->create($data); + if (empty($result)) { + return $this->formatResponse(400, 'User not created'); + } + + return $this->formatResponse(201, $result); + } + + public function updateUser(int $id, array $data): array + { + $user = $this->repository->find($id); + if (empty($user)) { + return $this->formatResponse(400, 'User not found'); + } + + $result = $this->repository->update($user, $data); + return $this->formatResponse(200, $result); + } + + public function deleteUser(int $id): array + { + $user = $this->repository->find($id); + if (!$user) { + return $this->formatResponse(400, 'User not found'); + } + + $this->repository->delete($id); + return $this->formatResponse(200, 'User deleted successfully'); + } + + private function formatResponse(int $status, $data): array + { + return [ + 'status' => $status, + 'data' => $data + ]; + } +} diff --git a/bootstrap/app.php b/bootstrap/app.php old mode 100644 new mode 100755 diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 diff --git a/composer.lock b/composer.lock old mode 100644 new mode 100755 diff --git a/config/app.php b/config/app.php old mode 100644 new mode 100755 diff --git a/config/auth.php b/config/auth.php old mode 100644 new mode 100755 diff --git a/config/broadcasting.php b/config/broadcasting.php old mode 100644 new mode 100755 diff --git a/config/cache.php b/config/cache.php old mode 100644 new mode 100755 diff --git a/config/cors.php b/config/cors.php old mode 100644 new mode 100755 diff --git a/config/database.php b/config/database.php old mode 100644 new mode 100755 diff --git a/config/filesystems.php b/config/filesystems.php old mode 100644 new mode 100755 diff --git a/config/hashing.php b/config/hashing.php old mode 100644 new mode 100755 diff --git a/config/l5-swagger.php b/config/l5-swagger.php old mode 100644 new mode 100755 diff --git a/config/logging.php b/config/logging.php old mode 100644 new mode 100755 diff --git a/config/mail.php b/config/mail.php old mode 100644 new mode 100755 diff --git a/config/queue.php b/config/queue.php old mode 100644 new mode 100755 diff --git a/config/sanctum.php b/config/sanctum.php old mode 100644 new mode 100755 diff --git a/config/services.php b/config/services.php old mode 100644 new mode 100755 diff --git a/config/session.php b/config/session.php old mode 100644 new mode 100755 diff --git a/config/view.php b/config/view.php old mode 100644 new mode 100755 diff --git a/database/.gitignore b/database/.gitignore old mode 100644 new mode 100755 diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php old mode 100644 new mode 100755 diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php old mode 100644 new mode 100755 diff --git a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php old mode 100644 new mode 100755 diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php old mode 100644 new mode 100755 diff --git a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php old mode 100644 new mode 100755 diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php old mode 100644 new mode 100755 diff --git a/database/seeders/UserTableSeeder.php b/database/seeders/UserTableSeeder.php old mode 100644 new mode 100755 diff --git a/docker-compose.yml b/docker-compose.yml old mode 100644 new mode 100755 diff --git a/makefile b/makefile old mode 100644 new mode 100755 index 5d94f95..31683dc --- a/makefile +++ b/makefile @@ -4,67 +4,66 @@ sleep: sleep 3 ps: - docker-compose ps + docker-compose ps || docker compose ps up: - docker-compose up -d + docker-compose up -d || docker compose up -d up-recreate: - docker-compose up -d --force-recreate + docker-compose up -d --force-recreate || docker compose up -d --force-recreate down: - docker-compose down + docker-compose down || docker compose down forget: - docker-compose down --rmi all --volumes + docker-compose down --rmi all --volumes || docker compose down --rmi all --volumes docker volume rm backend-test_sail-mysql 2>/dev/null db-shell: mysql -h 127.0.0.1 -P 3306 -u sail -ppassword api-build: - USER_ID=$(shell id -u) GROUP_ID=$(shell id -g) docker-compose build --no-cache + USER_ID=$(shell id -u) GROUP_ID=$(shell id -g) docker-compose build --no-cache || docker compose build --no-cache api-db: - docker-compose exec -it api php /var/www/html/artisan migrate:fresh - docker-compose exec -it api php /var/www/html/artisan db:seed + docker-compose exec -it api php /var/www/html/artisan migrate:fresh --seed || docker compose exec -it api php /var/www/html/artisan migrate:fresh --seed api-key: - docker-compose exec -it api php /var/www/html/artisan key:generate + docker-compose exec -it api php /var/www/html/artisan key:generate || docker compose exec -it api php /var/www/html/artisan key:generate api-env: cp .env.example .env api-config-cache: - docker-compose exec -it api php /var/www/html/artisan config:cache + docker-compose exec -it api php /var/www/html/artisan config:cache || docker compose exec -it api php /var/www/html/artisan config:cache api-composer-install: composer install --ignore-platform-reqs api-shell: - docker-compose exec -it api bash -c 'su sail' + docker-compose exec -it api bash -c 'su sail' || docker compose exec -it api bash -c 'su sail' api-root-shell: - docker-compose exec -it api bash + docker-compose exec -it api bash || docker compose exec -it api bash api-test: - docker-compose exec -it api php /var/www/html/artisan test + docker-compose exec -it api php /var/www/html/artisan test || docker compose exec -it api php /var/www/html/artisan test api-test-feature: - docker-compose exec -it api php /var/www/html/artisan test --testsuite=Feature --stop-on-failure + docker-compose exec -it api php /var/www/html/artisan test --testsuite=Feature --stop-on-failure || docker compose exec -it api php /var/www/html/artisan test --testsuite=Feature --stop-on-failure api-test-php-unit: - docker-compose exec -it api php /var/www/html/artisan phpunit + docker-compose exec -it api php /var/www/html/artisan phpunit || docker compose exec -it api php /var/www/html/artisan phpunit api-build-swagger: - docker-compose exec -it api php /var/www/html/artisan l5-swagger:generate + docker-compose exec -it api php /var/www/html/artisan l5-swagger:generate || docker compose exec -it api php /var/www/html/artisan l5-swagger:generate api-passport-key: - docker-compose exec -it api php /var/www/html/artisan passport:keys --force + docker-compose exec -it api php /var/www/html/artisan passport:keys --force || docker compose exec -it api php /var/www/html/artisan passport:keys --force api-passport-generate: - docker-compose exec -it api php /var/www/html/artisan passport:client --password --name='Laravel Password Grant Client' --provider=users > .passport + docker-compose exec -it api php /var/www/html/artisan passport:client --password --name='Laravel Password Grant Client' --provider=users > .passport || docker compose exec -it api php /var/www/html/artisan passport:client --password --name='Laravel Password Grant Client' --provider=users > .passport cat .passport fix-permissions: - docker-compose exec -it api bash -c 'chmod -R 777 /var/www/html/storage/logs && chmod -R 777 /var/www/html/storage/framework' + docker-compose exec -it api bash -c 'chmod -R 777 /var/www/html/storage/logs && chmod -R 777 /var/www/html/storage/framework' || docker compose exec -it api bash -c 'chmod -R 777 /var/www/html/storage/logs && chmod -R 777 /var/www/html/storage/framework' diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/phpunit.xml b/phpunit.xml old mode 100644 new mode 100755 diff --git a/public/.htaccess b/public/.htaccess old mode 100644 new mode 100755 diff --git a/public/favicon.ico b/public/favicon.ico old mode 100644 new mode 100755 diff --git a/public/index.php b/public/index.php old mode 100644 new mode 100755 diff --git a/public/robots.txt b/public/robots.txt old mode 100644 new mode 100755 diff --git a/resources/css/app.css b/resources/css/app.css old mode 100644 new mode 100755 diff --git a/resources/js/app.js b/resources/js/app.js old mode 100644 new mode 100755 diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js old mode 100644 new mode 100755 diff --git a/resources/views/vendor/l5-swagger/.gitkeep b/resources/views/vendor/l5-swagger/.gitkeep old mode 100644 new mode 100755 diff --git a/resources/views/vendor/l5-swagger/index.blade.php b/resources/views/vendor/l5-swagger/index.blade.php old mode 100644 new mode 100755 diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php old mode 100644 new mode 100755 diff --git a/routes/api.php b/routes/api.php old mode 100644 new mode 100755 index 03a44e1..1111cae --- a/routes/api.php +++ b/routes/api.php @@ -1,7 +1,7 @@ get('/'); - - $response->assertStatus(200); - } -} diff --git a/tests/Feature/UserControllerTest.php b/tests/Feature/UserControllerTest.php new file mode 100644 index 0000000..4dbd716 --- /dev/null +++ b/tests/Feature/UserControllerTest.php @@ -0,0 +1,72 @@ +index(); + $this->assertEquals(200, $response->status()); + $this->assertJson($response->getContent()); + } + + public function test_show(): void + { + $user = User::factory()->create(); + $controller = App::make(UserController::class); + $response = $controller->show($user->id); + $this->assertEquals(200, $response->status()); + $this->assertJson($response->getContent()); + } + + public function test_store(): void + { + $data = [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'password' => 'password', + ]; + $response = $this->post('/api/users', $data); + $this->assertEquals(201, $response->status()); + $this->assertJson($response->getContent()); + } + + public function test_update(): void + { + $user = User::factory()->create(); + + $data = [ + 'name' => 'Jane Doe', + 'email' => 'jane@example.com', + ]; + + $response = $this->put('/api/users/' . $user->id, $data); + + $this->assertEquals(200, $response->status()); + $this->assertJson($response->getContent()); + } + + public function test_destroy(): void + { + $user = User::factory()->create(); + + $response = $this->delete('/api/users/' . $user->id); + + $this->assertEquals(200, $response->status()); + $this->assertJson($response->getContent()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php old mode 100644 new mode 100755 diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index 5773b0c..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertTrue(true); - } -}