Skip to content
This repository has been archived by the owner on Nov 19, 2022. It is now read-only.

Commit

Permalink
Merge pull request #15 from davorminchorov/setup-laravel-fortify-and-…
Browse files Browse the repository at this point in the history
…laravel-sanctum

[#2] Setup Laravel Sanctum and implement the authentication APIs with tests
  • Loading branch information
davorminchorov authored Sep 11, 2021
2 parents fcdb3db + e627a14 commit 73831c8
Show file tree
Hide file tree
Showing 55 changed files with 2,275 additions and 562 deletions.
21 changes: 17 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ LOG_CHANNEL=stack
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=davorminchorov_api
DB_USERNAME=root
DB_PASSWORD=
DB_DATABASE=davorminchorov
DB_USERNAME=davorminchorov
DB_PASSWORD=davorminchorov

DB_TEST_CONNECTION=mysql_testing
DB_TEST_HOST=mysql_testing
DB_TEST_PORT=3307
DB_TEST_DATABASE=davorminchorov_testing
DB_TEST_USERNAME=davorminchorov_testing
DB_TEST_PASSWORD=davorminchorov_testing

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync

SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_DOMAIN=davorminchorov.test

MEMCACHED_HOST=127.0.0.1

Expand Down Expand Up @@ -49,3 +58,7 @@ PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

OCTANE_SERVER=swoole

SANCTUM_STATEFUL_DOMAINS=
7 changes: 4 additions & 3 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
name: Merge me!
name: Auto Merge Dependabot Pull Requests

on:
workflow_run:
types:
- completed
workflows:
- 'Fix PHP Code Styles'
- 'Run PHPStan for Laravel'
- 'Run PHPStan Analysis'
- 'Run PHPUnit Tests'

jobs:
merge-me:
Expand All @@ -19,7 +20,7 @@ jobs:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
uses: ridedott/merge-me-action@v2
with:
# Depending on branch prodtection rules, a manually populated
# Depending on branch protection rules, a manually populated
# `GITHUB_TOKEN_WORKAROUND` secret with permissions to push to
# a protected branch must be used.
#
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run PHPStan for Laravel
name: Run PHPStan Analysis

on:
push:
Expand Down
51 changes: 51 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Run PHPUnit Tests

on:
push:
branches:
- main
pull_request:

jobs:
phpunit:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
BROADCAST_DRIVER: log
CACHE_DRIVER: file
QUEUE_CONNECTION: sync
SESSION_DRIVER: array
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PASSWORD: davorminchorov_testing
DB_USERNAME: davorminchorov_testing
DB_DATABASE: davorminchorov_testing
services:
mysql:
image: mysql:latest
ports:
- 3306:3306
env:
MYSQL_DATABASE: davorminchorov_testing
MYSQL_USER: davorminchorov_testing
MYSQL_PASSWORD: davorminchorov_testing
MYSQL_ROOT_PASSWORD: root
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
with:
php-version: 8.0
extensions: dom, curl, mbstring, zip, pcntl, intl
coverage: xdebug
- run: composer install --no-interaction --ignore-platform-reqs --no-scripts
- run: ./vendor/bin/phpunit --testdox --coverage-html=.coverage --coverage-text
- uses: actions/upload-artifact@v2
with:
name: Coverage
path: ./.coverage
- uses: actions/upload-artifact@v2
if: failure()
with:
name: Logs
path: ./storage/logs
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ yarn-error.log
/.idea
/.vscode
.php-cs-fixer.cache
.phpstorm.meta.php
_ide_helper.php
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Davor Minchorov API

[![Fix PHP Code Styles](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpcs.yml/badge.svg?branch=main)](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpcs.yml)
[![Run PHPStan Analysis](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpstan.yml/badge.svg?branch=main)](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpstan.yml)
[![Run PHPUnit Tests](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpunit.yml/badge.svg?branch=main)](https://github.com/davorminchorov/api.davorminchorov.com/actions/workflows/phpunit.yml)

The REST API for Davor Minchorov's personal website and blog.

For the front end application, please see: [davorminchorov/davorminchorov.com](https://github.com/davorminchorov/davorminchorov.com)
51 changes: 51 additions & 0 deletions app/Authentication/Actions/LoginAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace DavorMinchorov\Authentication\Actions;

use DavorMinchorov\Authentication\DataTransferObjects\LoginDataTransferObject;
use DavorMinchorov\Authentication\Rules\ValidateUserPasswordRule;
use DavorMinchorov\PersonalAccessTokens\Actions\CreateAccessTokenAction;
use DavorMinchorov\PersonalAccessTokens\Enums\AccessTokenName;
use DavorMinchorov\Users\Models\User;
use DavorMinchorov\Users\Queries\GetUserByEmailQuery;
use Illuminate\Validation\ValidationException;

class LoginAction
{
/**
* LoginAction constructor.
*
* @param GetUserByEmailQuery $getUserByEmailQuery
* @param ValidateUserPasswordRule $validateUserPasswordRule
* @param CreateAccessTokenAction $createAccessTokenAction
*/
public function __construct(
private GetUserByEmailQuery $getUserByEmailQuery,
private ValidateUserPasswordRule $validateUserPasswordRule,
private CreateAccessTokenAction $createAccessTokenAction,
) {

}

/**
* Log the administrator in the administration panel.
*
* @param LoginDataTransferObject $loginDataTransferObject
*
* @return User
* @throws ValidationException
*/
public function __invoke(LoginDataTransferObject $loginDataTransferObject): User
{
$user = ($this->getUserByEmailQuery)(email: $loginDataTransferObject->email);

($this->validateUserPasswordRule)(
password: $loginDataTransferObject->password,
userPassword: $user->password
);

$user->access_token = ($this->createAccessTokenAction)(user: $user, tokenName: AccessTokenName::API_AUTHENTICATION);

return $user;
}
}
37 changes: 37 additions & 0 deletions app/Authentication/Actions/LogoutAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace DavorMinchorov\Authentication\Actions;

use DavorMinchorov\PersonalAccessTokens\Actions\DeleteAccessTokenAction;
use DavorMinchorov\PersonalAccessTokens\Enums\AccessTokenName;
use DavorMinchorov\Users\Models\User;
use Illuminate\Auth\AuthManager;

class LogoutAction
{
/**
* LogoutAction constructor.
*
* @param AuthManager $authManager
* @param DeleteAccessTokenAction $deleteAccessTokenAction
*/
public function __construct(
private AuthManager $authManager,
private DeleteAccessTokenAction $deleteAccessTokenAction
) {

}

/**
* Log the administrator out of the administration panel.
*
* @return int|null
*/
public function __invoke(): int|null
{
/** @var User $user */
$user = $this->authManager->guard()->user();

return ($this->deleteAccessTokenAction)(tokenName: AccessTokenName::API_AUTHENTICATION, user: $user);
}
}
27 changes: 27 additions & 0 deletions app/Authentication/Api/V1/ApiResources/LoginJsonResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace DavorMinchorov\Authentication\Api\V1\ApiResources;

use Illuminate\Http\Resources\Json\JsonResource;

class LoginJsonResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function toArray($request): array
{
return [
'id' => $this->uuid,
'type' => 'users',
'attributes' => [
'name' => $this->name,
'accessToken' => $this->access_token,
],
];
}
}
38 changes: 38 additions & 0 deletions app/Authentication/Api/V1/Controllers/LoginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace DavorMinchorov\Authentication\Api\V1\Controllers;

use DavorMinchorov\Authentication\Actions\LoginAction;
use DavorMinchorov\Authentication\Api\V1\ApiResources\LoginJsonResource;
use DavorMinchorov\Authentication\Api\V1\FormRequests\LoginFormRequest;
use DavorMinchorov\Authentication\DataTransferObjects\LoginDataTransferObject;

class LoginController
{
/**
* LoginController constructor.
*
* @param LoginAction $loginAction
*/
public function __construct(private LoginAction $loginAction)
{
//
}

/**
* Log the administrator in the administration area of the application.
*
* @param LoginFormRequest $loginFormRequest
*
* @return LoginJsonResource
* @throws \Spatie\DataTransferObject\Exceptions\UnknownProperties
*/
public function __invoke(LoginFormRequest $loginFormRequest): LoginJsonResource
{
return new LoginJsonResource(
resource: ($this->loginAction)(
loginDataTransferObject: LoginDataTransferObject::fromRequest($loginFormRequest)
)
);
}
}
32 changes: 32 additions & 0 deletions app/Authentication/Api/V1/Controllers/LogoutController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace DavorMinchorov\Authentication\Api\V1\Controllers;

use DavorMinchorov\Authentication\Actions\LogoutAction;
use Illuminate\Auth\AuthManager;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class LogoutController
{

/**
* LogoutController constructor.
*
* @param LogoutAction $logoutAction
*/
public function __construct(private LogoutAction $logoutAction)
{

}

/**
* Logs the administrator out of the application.
*/
public function __invoke(): Response
{
($this->logoutAction)();

return new Response(content: null, status: SymfonyResponse::HTTP_NO_CONTENT);
}
}
29 changes: 29 additions & 0 deletions app/Authentication/Api/V1/FormRequests/LoginFormRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace DavorMinchorov\Authentication\Api\V1\FormRequests;

use Illuminate\Foundation\Http\FormRequest;

class LoginFormRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}

/**
* @return array
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string']
];
}
}
35 changes: 35 additions & 0 deletions app/Authentication/DataTransferObjects/LoginDataTransferObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace DavorMinchorov\Authentication\DataTransferObjects;

use DavorMinchorov\Authentication\Api\V1\FormRequests\LoginFormRequest;
use Spatie\DataTransferObject\DataTransferObject;

class LoginDataTransferObject extends DataTransferObject
{
/**
* @var string
*/
public string $email;

/**
* @var string
*/
public string $password;

/**
* Populates the properties of the data transfer object from the request object.
*
* @param LoginFormRequest $loginFormRequest
*
* @return LoginDataTransferObject
* @throws \Spatie\DataTransferObject\Exceptions\UnknownProperties
*/
public static function fromRequest(LoginFormRequest $loginFormRequest): self
{
return new self([
'email' => $loginFormRequest->input('email'),
'password' => $loginFormRequest->input('password'),
]);
}
}
Loading

0 comments on commit 73831c8

Please sign in to comment.