Skip to content

Commit

Permalink
Agregado Patrón Adapter!'
Browse files Browse the repository at this point in the history
  • Loading branch information
sebacarrasco93 committed Aug 4, 2021
1 parent 32b9f02 commit 1a8e6cb
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ APP_URL=http://localhost

# WKHTML_PDF_BINARY='"C:\wkhtmltopdf\bin\wkhtmltopdf.exe"' 3 👈🏻 En Windows, especifica la ruta de wkhtmltopdf.exe

API_KEY_APILAYER=
API_KEY_ABSTRACTAPI=

LOG_CHANNEL=stack
LOG_LEVEL=debug

Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ Testeado en **Laravel 8** y **PHP 8**, pero puede servir en futuras versiones (y

## Importante, antes de usar:

Si quieres probar manualmente el patrón Adapter (que usa APIs externas para verificar si un email es válido) debes crear una cuenta en ambos proveedores.

- [API Layer (Mailbox Layer)](https://mailboxlayer.com/)
- [Abstract API](https://www.abstractapi.com/)

A continuación, debes generar tokens y copiarlos en el `.env`:

```dotenv
API_KEY_APILAYER=#{Tu token acá}
API_KEY_ABSTRACTAPI=#{Tu token acá}
```

Si usas Windows:
1. Instala [wkhtmltopdf](https://wkhtmltopdf.org/downloads.html), de preferencia en la ruta ```C:\wkhtmltopdf```
2. Abre el archivo `.env` y busca la llave `WKHTML_PDF_BINARY`. Pon la ruta completa del ejecutable de wkhtmltopdf (si instalaste en ```C:\wkhtmltopdf```, sólo debes descomentar esa línea, de lo contrario, ya sabes qué hacer: Escribirla completa)
Expand All @@ -21,7 +33,7 @@ Si usas Windows:
- [ ] Builder
- [ ] State
- [x] [Pipeline](https://github.com/sebacarrasco93/patrones-laravel/commit/51d2dfcb1f71e9cd76c9413a61ec84f9e6127235)
- [ ] Adapter
- [x] Adapter
- [ ] Strategy
- [ ] Chain of Responsability
- [x] [Command](https://github.com/sebacarrasco93/patrones-laravel/commit/22eedf535e40d23145250d62770f679f96b38ae0)
Expand Down
10 changes: 10 additions & 0 deletions app/Contracts/VerifiableAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Contracts;

interface VerifiableAdapter
{
public function verify(string $email) : bool;

public function checkResponse() : bool;
}
33 changes: 33 additions & 0 deletions app/Http/Controllers/AdapterController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Http\Controllers;

use App\Contracts\VerifiableAdapter;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AdapterController extends Controller
{
public function __invoke(Request $request, VerifiableAdapter $emailVerifier)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => ['required'],
]);

if (! $emailVerifier->verify($validated['email'])) {
return redirect()->back()->withErrors(['El email no es válido']);
}

$user = User::create($validated);

event(new Registered($user));

Auth::login($user);

return redirect()->back()->withSuccess('Sesión iniciada correctamente');
}
}
7 changes: 7 additions & 0 deletions app/Http/Controllers/PatronesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ public function pipeline()
return view('patrones.pipeline', compact('users'));
}

public function adapter()
{
$users = \App\Models\User::get();

return view('patrones.adapter', compact('users'));
}

public function command()
{
$users = \App\Models\User::get();
Expand Down
15 changes: 14 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace App\Providers;

use App\Contracts\VerifiableAdapter;
use App\Services\Adapters\AbstractApiAdapter;
use App\Services\Adapters\MailboxLayerAdapter;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
Expand All @@ -13,7 +16,17 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
//
/* Usamos Mailbox (ApiLayer) */
$this->app->bind(
VerifiableAdapter::class,
config('adapter.driver',MailboxLayerAdapter::class)
);

/* Y si falla o pasa algo, cambiamos acá, y seguimos usando AbstractApi */
// $this->app->bind(
// VerifiableAdapter::class,
// config('adapter.driver',AbstractApiAdapter::class)
// );
}

/**
Expand Down
36 changes: 36 additions & 0 deletions app/Services/Adapters/AbstractApiAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Services\Adapters;

use App\Contracts\VerifiableAdapter;
use Illuminate\Support\Facades\Http;

class AbstractApiAdapter implements VerifiableAdapter
{
private $client;
private $response;

public function __construct()
{
$this->client = Http::baseUrl('https://emailvalidation.abstractapi.com');
}

public function verify(string $email) : bool
{
$this->response = $this->client->get('v1', [
'api_key' => config('external.api_key_abstractapi'),
'email' => $email,
]);

return $this->checkResponse();
}

public function checkResponse() : bool
{
if (isset($this->response['error'])) {
throw new \Exception('Se produjo un error, revisa si tu API_KEY_ABSTRACTAPI es válida');
}

return (bool) $this->response['is_smtp_valid']['value'];
}
}
38 changes: 38 additions & 0 deletions app/Services/Adapters/MailboxLayerAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Services\Adapters;

use App\Contracts\VerifiableAdapter;
use Illuminate\Support\Facades\Http;

class MailboxLayerAdapter implements VerifiableAdapter
{
private $client;
private $response;

public function __construct()
{
$this->client = Http::baseUrl('http://apilayer.net');
}

public function verify(string $email) : bool
{
$this->response = $this->client->get('/api/check', [
'access_key' => config('external.api_key_apilayer'),
'email' => $email,
'smtp' => 1,
'format' => 1,
]);

return $this->checkResponse();
}

public function checkResponse() : bool
{
if (isset($this->response['error'])) {
throw new \Exception('Se produjo un error, revisa si tu API_KEY_APILAYER es válida');
}

return (bool) $this->response['mx_found'];
}
}
17 changes: 17 additions & 0 deletions config/external.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
|--------------------------------------------------------------------------
| Archivo para Credenciales
|--------------------------------------------------------------------------
|
| Modifica el .env con tus llaves de API
|
| API_KEY_APILAYER={tu propia api_key}
| API_KEY_ABSTRACTAPI={tu propia api_key}
|
*/
return [
'api_key_apilayer' => env('API_KEY_APILAYER'),
'api_key_abstractapi' => env('API_KEY_ABSTRACTAPI'),
];
1 change: 1 addition & 0 deletions resources/views/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<a href="{{ route('patrones.factory') }}">Factory</a>
<a href="{{ route('patrones.factoryMethod') }}">Factory Method</a>
<a href="{{ route('patrones.pipeline') }}">Pipeline</a>
<a href="{{ route('patrones.adapter') }}">Adapter</a>
<a href="{{ route('patrones.command') }}">Command</a>
@endsection
32 changes: 32 additions & 0 deletions resources/views/patrones/adapter.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@extends('layout')
@section('titulo', 'Patrón Adapter')

@section('contenido')
@foreach ($users as $user)
<ul>
<li>
{{ $user->name }}
</li>
</ul>
@endforeach

<form action="{{ route('adapter') }}" method="POST">
@csrf
<div>
<h2>Agregar nueva persona</h2>
</div>
<div>
<label for="name">Nombre:</label>
<input name="name" id="name" type="text" placeholder="Nombre">
</div>
<div>
<label for="email">Email:</label>
<input name="email" id="email" type="email" placeholder="Email">
</div>
<div>
<label for="password">Contraseña:</label>
<input name="password" id="password" type="password" placeholder="Contraseña">
</div>
<button>Agregar</button>
</form>
@endsection
3 changes: 3 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use App\Http\Controllers\AdapterController;
use App\Http\Controllers\CommandController;
use App\Http\Controllers\FactoryController;
use App\Http\Controllers\FactoryMethodController;
Expand All @@ -23,10 +24,12 @@
Route::get('factory', [PatronesController::class, 'factory'])->name('patrones.factory');
Route::get('factory-method', [PatronesController::class, 'factoryMethod'])->name('patrones.factoryMethod');
Route::get('pipeline', [PatronesController::class, 'pipeline'])->name('patrones.pipeline');
Route::get('adapter', [PatronesController::class, 'adapter'])->name('patrones.adapter');
Route::get('command', [PatronesController::class, 'command'])->name('patrones.command');

Route::post('factory/{report}', FactoryController::class)->name('factory');
Route::post('factory-method/{report}', FactoryMethodController::class)->name('factoryMethod');
Route::post('pipeline/{user}', PipelineController::class)->name('pipeline');
Route::post('adapter', AdapterController::class)->name('adapter');
Route::post('command/{user}', CommandController::class)->name('command');

98 changes: 98 additions & 0 deletions tests/Feature/AdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

namespace Tests\Feature;

use App\Contracts\VerifiableAdapter;
use App\Services\Adapters\AbstractApiAdapter;
use App\Services\Adapters\MailboxLayerAdapter;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class AdapterTest extends TestCase
{
use RefreshDatabase;

protected function forzarMailboxLayer($emailValido = true)
{
// 💡 Forzar el uso de MailboxLayer (ApiLayer), para simular funcionamiento de AppServiceProvider.php
app()->bind(VerifiableAdapter::class, config('adapter.driver',MailboxLayerAdapter::class));

// 😎 Mocking de solicitud Http
Http::fake([
'apilayer.net/*' => Http::response([
'mx_found' => $emailValido,
], 200),
]);
}

protected function forzarAbstractApi($emailValido = true)
{
// 💡 Forzar el uso de AbstractApi, para simular funcionamiento de AppServiceProvider.php
app()->bind(VerifiableAdapter::class, config('adapter.driver',AbstractApiAdapter::class));

// 😎 Mocking de solicitud Http
Http::fake([
'emailvalidation.abstractapi.com/*' => Http::response([
'is_smtp_valid' => ['value' => $emailValido]
], 200),
]);
}

/** @test */
function puede_crear_un_usuario_usando_el_proveedor_mailboxlayer_con_adapter()
{
$this->forzarMailboxLayer(true);

$data = ['name' => 'Nombre que funciona', 'email' => 'existente@dominio.cl', 'password' => 'password'];

$request = $this->post(route('adapter'), $data);

$response = $this->get(route('patrones.adapter'));

$response->assertSee('Nombre que funciona');
}

/** @test */
function puede_crear_un_usuario_usando_el_proveedor_abstractapi_con_adapter()
{
$this->forzarAbstractApi(true);

$data = ['name' => 'Nombre que funciona', 'email' => 'existente@dominio.cl', 'password' => 'password'];

$request = $this->post(route('adapter'), $data);

$response = $this->get(route('patrones.adapter'));

$response->assertSee('Nombre que funciona');
}

/** @test */
function no_puede_crear_un_usuario_usando_el_proveedor_mailboxlayer_con_adapter()
{
$this->forzarMailboxLayer(false);

$data = ['name' => 'Nombre que no debería aparecer', 'email' => 'repetido@aaaaaa.tk', 'password' => 'password'];

$request = $this->post(route('adapter'), $data);

$response = $this->get(route('patrones.adapter'));

$response->assertDontSee('Nombre que no debería aparecer');
}

/** @test */
function no_puede_crear_un_usuario_usando_el_proveedor_abstractapi_con_adapter()
{
$this->forzarAbstractApi(false);

$data = ['name' => 'Nombre que no debería aparecer', 'email' => 'repetido@aaaaaa.tk', 'password' => 'password'];

$request = $this->post(route('adapter'), $data);

$response = $this->get(route('patrones.adapter'));

$response->assertDontSee('Nombre que no debería aparecer');
}
}

0 comments on commit 1a8e6cb

Please sign in to comment.