Refactor error handling internally to follow RFC7807 (#362)

* Refactor error handling internally to follow RFC7807

* style fixes
This commit is contained in:
Nabeel S
2019-08-21 08:17:44 -04:00
committed by GitHub
parent 91a5eb535d
commit 182aabf426
25 changed files with 692 additions and 144 deletions

View File

@@ -2,11 +2,49 @@
namespace App\Exceptions; namespace App\Exceptions;
use App\Models\Aircraft;
/** /**
* Class AircraftNotAtAirport * Class AircraftNotAtAirport
*/ */
class AircraftNotAtAirport extends InternalError class AircraftNotAtAirport extends HttpException
{ {
public const FIELD = 'aircraft_id';
public const MESSAGE = 'The aircraft is not at the departure airport'; public const MESSAGE = 'The aircraft is not at the departure airport';
private $aircraft;
public function __construct(Aircraft $aircraft)
{
$this->aircraft = $aircraft;
parent::__construct(
400,
static::MESSAGE
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'aircraft-not-at-airport';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'aircraft_id' => $this->aircraft->id,
];
}
} }

View File

@@ -2,11 +2,51 @@
namespace App\Exceptions; namespace App\Exceptions;
/** use App\Models\Aircraft;
* Class AircraftPermissionDenied use App\Models\User;
*/
class AircraftPermissionDenied extends InternalError class AircraftPermissionDenied extends HttpException
{ {
public const FIELD = 'aircraft_id';
public const MESSAGE = 'User is not allowed to fly this aircraft'; public const MESSAGE = 'User is not allowed to fly this aircraft';
private $aircraft;
private $user;
public function __construct(User $user, Aircraft $aircraft)
{
$this->aircraft = $aircraft;
$this->user = $user;
parent::__construct(
400,
static::MESSAGE
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'aircraft-permission-denied';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'aircraft_id' => $this->aircraft->id,
'user_id' => $this->user->id,
];
}
} }

View File

@@ -1,26 +0,0 @@
<?php
namespace App\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Class BidExists
*/
class BidExists extends HttpException
{
public function __construct(
string $message = null,
\Exception $previous = null,
int $code = 0,
array $headers = []
) {
parent::__construct(
409,
'A bid already exists for this flight',
$previous,
$headers,
$code
);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Exceptions;
use App\Models\Flight;
class BidExistsForFlight extends HttpException
{
private $flight;
public function __construct(Flight $flight)
{
$this->flight = $flight;
parent::__construct(
409,
'A bid already exists for this flight'
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'bid-exists';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'flight_id' => $this->flight->id,
];
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Exceptions\Converters;
use App\Exceptions\HttpException;
use Exception;
class GenericException extends HttpException
{
private $exception;
public function __construct(Exception $exception)
{
$this->exception = $exception;
parent::__construct(
503,
$exception->getMessage()
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'internal-error';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
// Only add trace if in dev
if (config('app.env') === 'dev') {
return [
'trace' => $this->exception->getTrace()[0],
];
}
return [];
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Exceptions\Converters;
use App\Exceptions\HttpException;
use Exception;
class NotFound extends HttpException
{
private $exception;
public function __construct(Exception $exception)
{
$this->exception = $exception;
parent::__construct(
404,
$exception->getMessage()
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'not-found';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [];
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Exceptions\Converters;
use App\Exceptions\HttpException;
use Symfony\Component\HttpKernel\Exception\HttpException as SymfonyHttpException;
class SymfonyException extends HttpException
{
private $exception;
public function __construct(SymfonyHttpException $exception)
{
$this->exception = $exception;
parent::__construct(
$exception->getStatusCode(),
$exception->getMessage()
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'internal-error';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
// Only add trace if in dev
if (config('app.env') === 'dev') {
return [
'trace' => $this->exception->getTrace()[0],
];
}
return [];
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Exceptions\Converters;
use App\Exceptions\HttpException;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
class ValidationException extends HttpException
{
private $validationException;
private $errorDetail;
private $errors;
public function __construct(IlluminateValidationException $validationException)
{
$this->validationException = $validationException;
$this->processValidationErrors();
parent::__construct(
400,
'Validation exception'
);
}
private function processValidationErrors()
{
$error_messages = [];
$this->errors = $this->validationException->errors();
foreach ($this->errors as $field => $error) {
$error_messages[] = implode(', ', $error);
}
$this->errorDetail = implode(', ', $error_messages);
// Log::error('Validation errors', $this->errors);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'validation-exception';
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorDetails(): string
{
return $this->errorDetail;
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'errors' => $this->errors,
];
}
}

View File

@@ -2,13 +2,21 @@
namespace App\Exceptions; namespace App\Exceptions;
use App\Exceptions\Converters\GenericException;
use App\Exceptions\Converters\NotFound;
use App\Exceptions\Converters\SymfonyException;
use App\Exceptions\Converters\ValidationException;
use Exception; use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException; use Illuminate\Http\Request;
use Log; use Illuminate\Session\TokenMismatchException;
use Symfony\Component\HttpKernel\Exception\HttpException; use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException as SymfonyHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -21,85 +29,56 @@ class Handler extends ExceptionHandler
* A list of the exception types that should not be reported. * A list of the exception types that should not be reported.
*/ */
protected $dontReport = [ protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class, AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class, AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class, HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class, IlluminateValidationException::class,
\Illuminate\Session\TokenMismatchException::class, ModelNotFoundException::class,
\Illuminate\Validation\ValidationException::class, SymfonyHttpException::class,
TokenMismatchException::class,
]; ];
/**
* Create an error message
*
* @param $status_code
* @param $message
*
* @return array
*/
protected function createError($status_code, $message)
{
return [
'error' => [
'status' => $status_code,
'message' => $message,
],
];
}
/** /**
* Render an exception into an HTTP response. * Render an exception into an HTTP response.
* *
* @param \Illuminate\Http\Request $request * @param Request $request
* @param \Exception $exception * @param Exception $exception
* *
* @return mixed * @return mixed
*/ */
public function render($request, Exception $exception) public function render($request, Exception $exception)
{ {
if ($request->is('api/*')) { if ($request->is('api/*')) {
$headers = [];
Log::error('API Error', $exception->getTrace()); Log::error('API Error', $exception->getTrace());
if ($exception instanceof HttpException) {
return $exception->getResponse();
}
/*
* Not of the HttpException abstract class. Map these into
*/
if ($exception instanceof ModelNotFoundException || if ($exception instanceof ModelNotFoundException ||
$exception instanceof NotFoundHttpException) { $exception instanceof NotFoundHttpException) {
$error = $this->createError(404, $exception->getMessage()); $error = new NotFound($exception);
return $error->getResponse();
} }
// Custom exceptions should be extending HttpException // Custom exceptions should be extending HttpException
elseif ($exception instanceof HttpException) { if ($exception instanceof SymfonyHttpException) {
$error = $this->createError( $error = new SymfonyException($exception);
$exception->getStatusCode(), return $error->getResponse();
$exception->getMessage()
);
$headers = $exception->getHeaders();
} }
// Create the detailed errors from the validation errors // Create the detailed errors from the validation errors
elseif ($exception instanceof ValidationException) { if ($exception instanceof IlluminateValidationException) {
$error_messages = []; $error = new ValidationException($exception);
$errors = $exception->errors(); return $error->getResponse();
foreach ($errors as $field => $error) {
$error_messages[] = implode(', ', $error);
}
$message = implode(', ', $error_messages);
$error = $this->createError(400, $message);
$error['error']['errors'] = $errors;
Log::error('Validation errors', $errors);
} else {
$error = $this->createError(400, $exception->getMessage());
} }
// Only add trace if in dev $error = new GenericException($exception);
if (config('app.env') === 'dev') { return $error->getResponse();
$error['error']['trace'] = $exception->getTrace()[0];
}
return response()->json($error, $error['error']['status'], $headers);
} }
if ($exception instanceof HttpException if ($exception instanceof HttpException
@@ -113,16 +92,16 @@ class Handler extends ExceptionHandler
/** /**
* Convert an authentication exception into an unauthenticated response. * Convert an authentication exception into an unauthenticated response.
* *
* @param \Illuminate\Http\Request $request * @param Request $request
* @param \Illuminate\Auth\AuthenticationException $exception * @param AuthenticationException $exception
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
protected function unauthenticated($request, AuthenticationException $exception) protected function unauthenticated($request, AuthenticationException $exception)
{ {
if ($request->expectsJson() || $request->is('api/*')) { if ($request->expectsJson() || $request->is('api/*')) {
$error = $this->createError(401, 'Unauthenticated'); $error = new Unauthenticated();
return response()->json($error, 401); return $error->getResponse();
} }
return redirect()->guest('login'); return redirect()->guest('login');
@@ -133,7 +112,7 @@ class Handler extends ExceptionHandler
* *
* @param HttpException $e * @param HttpException $e
* *
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response * @return \Illuminate\Http\Response|Response
*/ */
protected function renderHttpException(HttpExceptionInterface $e) protected function renderHttpException(HttpExceptionInterface $e)
{ {

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException as SymfonyHttpException;
abstract class HttpException extends SymfonyHttpException
{
/**
* Return the RFC 7807 error type (without the URL root)
*/
abstract public function getErrorType(): string;
/**
* Get the detailed error string
*/
abstract public function getErrorDetails(): string;
/**
* Return an array with the error details, merged with the RFC7807 response
*/
abstract public function getErrorMetadata(): array;
/**
* Return the error message as JSON
*/
public function getJson()
{
$response = [];
$response['type'] = config('phpvms.error_root').'/'.$this->getErrorType();
$response['title'] = $this->getMessage();
$response['details'] = $this->getErrorDetails();
// For backwards compatibility
$response['error'] = [
'status' => $this->getStatusCode(),
'message' => $this->getErrorDetails(),
];
return array_merge($response, $this->getErrorMetadata());
}
/**
* Return a response object that can be used by Laravel
*
* @return \Illuminate\Http\JsonResponse
*/
public function getResponse()
{
return response()
->json(
$this->getJson(),
$this->getStatusCode(),
[
'content-type' => 'application/problem+json',
]
);
}
}

View File

@@ -2,9 +2,9 @@
namespace App\Exceptions; namespace App\Exceptions;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Log;
use Validator;
/** /**
* Show an internal error, bug piggyback off of the validation * Show an internal error, bug piggyback off of the validation

View File

@@ -2,25 +2,38 @@
namespace App\Exceptions; namespace App\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException; use App\Models\Pirep;
/**
* Class PirepCancelled
*/
class PirepCancelled extends HttpException class PirepCancelled extends HttpException
{ {
public function __construct( private $pirep;
string $message = null,
\Exception $previous = null, public function __construct(Pirep $pirep)
int $code = 0, {
array $headers = [] $this->pirep = $pirep;
) {
parent::__construct( parent::__construct(
400, 400,
'PIREP has been cancelled, updates are not allowed', 'PIREP has been cancelled, updates are not allowed'
$previous,
$headers,
$code
); );
} }
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'pirep-cancelled';
}
public function getErrorDetails(): string
{
return $this->getMessage();
}
public function getErrorMetadata(): array
{
return [
'pirep_id' => $this->pirep->id,
];
}
} }

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Exceptions;
class Unauthenticated extends HttpException
{
public function __construct()
{
parent::__construct(
401,
'User not authenticated'
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'unauthenticated';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [];
}
}

View File

@@ -2,11 +2,51 @@
namespace App\Exceptions; namespace App\Exceptions;
/** use App\Models\Airport;
* Class UserNotAtAirport use App\Models\User;
*/
class UserNotAtAirport extends InternalError class UserNotAtAirport extends HttpException
{ {
public const FIELD = 'dpt_airport_id';
public const MESSAGE = 'Pilot is not at the departure airport'; public const MESSAGE = 'Pilot is not at the departure airport';
private $airport;
private $user;
public function __construct(User $user, Airport $airport)
{
$this->airport = $airport;
$this->user = $user;
parent::__construct(
400,
static::MESSAGE
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'user-not-at-airport';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'airport_id' => $this->airport->id,
'user_id' => $this->user->id,
];
}
} }

View File

@@ -2,8 +2,47 @@
namespace App\Exceptions; namespace App\Exceptions;
class UserPilotIdExists extends InternalError use App\Models\User;
class UserPilotIdExists extends HttpException
{ {
public const FIELD = 'pilot_id';
public const MESSAGE = 'A user with this pilot ID already exists'; public const MESSAGE = 'A user with this pilot ID already exists';
private $user;
public function __construct(User $user)
{
$this->user = $user;
parent::__construct(
400,
static::MESSAGE
);
}
/**
* Return the RFC 7807 error type (without the URL root)
*/
public function getErrorType(): string
{
return 'pilot-id-already-exists';
}
/**
* Get the detailed error string
*/
public function getErrorDetails(): string
{
return $this->getMessage();
}
/**
* Return an array with the error details, merged with the RFC7807 response
*/
public function getErrorMetadata(): array
{
return [
'user_id' => $this->user->id,
];
}
} }

View File

@@ -57,7 +57,7 @@ class AcarsController extends Controller
protected function checkCancelled(Pirep $pirep) protected function checkCancelled(Pirep $pirep)
{ {
if ($pirep->cancelled) { if ($pirep->cancelled) {
throw new PirepCancelled(); throw new PirepCancelled($pirep);
} }
} }

View File

@@ -221,20 +221,20 @@ class PirepController extends Controller
/* @noinspection NotOptimalIfConditionsInspection */ /* @noinspection NotOptimalIfConditionsInspection */
if (setting('pilots.only_flights_from_current') if (setting('pilots.only_flights_from_current')
&& $user->curr_airport_id !== $pirep->dpt_airport_id) { && $user->curr_airport_id !== $pirep->dpt_airport_id) {
throw new UserNotAtAirport(); throw new UserNotAtAirport($user, $pirep->dpt_airport);
} }
// See if this user is allowed to fly this aircraft // See if this user is allowed to fly this aircraft
if (setting('pireps.restrict_aircraft_to_rank', false) if (setting('pireps.restrict_aircraft_to_rank', false)
&& !$this->userSvc->aircraftAllowed($user, $pirep->aircraft_id)) { && !$this->userSvc->aircraftAllowed($user, $pirep->aircraft_id)) {
throw new AircraftPermissionDenied(); throw new AircraftPermissionDenied($user, $pirep->aircraft);
} }
// See if this aircraft is at the departure airport // See if this aircraft is at the departure airport
/* @noinspection NotOptimalIfConditionsInspection */ /* @noinspection NotOptimalIfConditionsInspection */
if (setting('pireps.only_aircraft_at_dpt_airport') if (setting('pireps.only_aircraft_at_dpt_airport')
&& $pirep->aircraft_id !== $pirep->dpt_airport_id) { && $pirep->aircraft_id !== $pirep->dpt_airport_id) {
throw new AircraftNotAtAirport(); throw new AircraftNotAtAirport($pirep->aircraft);
} }
// Find if there's a duplicate, if so, let's work on that // Find if there's a duplicate, if so, let's work on that
@@ -293,7 +293,7 @@ class PirepController extends Controller
) { ) {
$can_use_ac = $this->userSvc->aircraftAllowed($user, $pirep->aircraft_id); $can_use_ac = $this->userSvc->aircraftAllowed($user, $pirep->aircraft_id);
if (!$can_use_ac) { if (!$can_use_ac) {
throw new AircraftPermissionDenied(); throw new AircraftPermissionDenied($user, $pirep->aircraft);
} }
} }
@@ -335,7 +335,7 @@ class PirepController extends Controller
) { ) {
$can_use_ac = $this->userSvc->aircraftAllowed($user, $pirep->aircraft_id); $can_use_ac = $this->userSvc->aircraftAllowed($user, $pirep->aircraft_id);
if (!$can_use_ac) { if (!$can_use_ac) {
throw new AircraftPermissionDenied(); throw new AircraftPermissionDenied($user, $pirep->aircraft);
} }
} }

View File

@@ -98,7 +98,7 @@ class UserController extends Controller
* @param Request $request * @param Request $request
* *
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
* @throws \App\Exceptions\BidExists * @throws \App\Exceptions\BidExistsForFlight
* *
* @return mixed * @return mixed
*/ */

View File

@@ -3,7 +3,7 @@
namespace App\Services; namespace App\Services;
use App\Contracts\Service; use App\Contracts\Service;
use App\Exceptions\BidExists; use App\Exceptions\BidExistsForFlight;
use App\Models\Bid; use App\Models\Bid;
use App\Models\Flight; use App\Models\Flight;
use App\Models\FlightFieldValue; use App\Models\FlightFieldValue;
@@ -198,7 +198,7 @@ class FlightService extends Service
* @param Flight $flight * @param Flight $flight
* @param User $user * @param User $user
* *
* @throws \App\Exceptions\BidExists *@throws \App\Exceptions\BidExistsForFlight
* *
* @return mixed * @return mixed
*/ */
@@ -208,7 +208,7 @@ class FlightService extends Service
// bids // bids
$bids = Bid::where('user_id', $user->id)->get(); $bids = Bid::where('user_id', $user->id)->get();
if ($bids->count() > 0 && setting('bids.allow_multiple_bids') === false) { if ($bids->count() > 0 && setting('bids.allow_multiple_bids') === false) {
throw new BidExists('User "'.$user->ident.'" already has bids, skipping'); throw new BidExistsForFlight('User "'.$user->ident.'" already has bids, skipping');
} }
// Get all of the bids for this flight // Get all of the bids for this flight
@@ -230,11 +230,11 @@ class FlightService extends Service
// Check if the flight should be blocked off // Check if the flight should be blocked off
if (setting('bids.disable_flight_on_bid') === true) { if (setting('bids.disable_flight_on_bid') === true) {
throw new BidExists('Flight "'.$flight->ident.'" already has a bid, skipping'); throw new BidExistsForFlight($flight);
} }
if (setting('bids.allow_multiple_bids') === false) { if (setting('bids.allow_multiple_bids') === false) {
throw new BidExists('A bid already exists for this flight'); throw new BidExistsForFlight($flight);
} }
} else { } else {
/* @noinspection NestedPositiveIfStatementsInspection */ /* @noinspection NestedPositiveIfStatementsInspection */

View File

@@ -137,7 +137,7 @@ class UserService extends Service
if ($this->isPilotIdAlreadyUsed($pilot_id)) { if ($this->isPilotIdAlreadyUsed($pilot_id)) {
Log::error('User with id '.$pilot_id.' already exists'); Log::error('User with id '.$pilot_id.' already exists');
throw new UserPilotIdExists(); throw new UserPilotIdExists($user);
} }
$old_id = $user->pilot_id; $old_id = $user->pilot_id;

64
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "da494f087ae565d209fc971170f5a259", "content-hash": "4ff465ff2b45de41c61ce5a6f55db121",
"packages": [ "packages": [
{ {
"name": "akaunting/money", "name": "akaunting/money",
@@ -857,6 +857,66 @@
], ],
"time": "2019-05-27T17:52:04+00:00" "time": "2019-05-27T17:52:04+00:00"
}, },
{
"name": "crell/api-problem",
"version": "3.2",
"source": {
"type": "git",
"url": "https://github.com/Crell/ApiProblem.git",
"reference": "de9ab4d17f80b19af772f9906061863b296ab427"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Crell/ApiProblem/zipball/de9ab4d17f80b19af772f9906061863b296ab427",
"reference": "de9ab4d17f80b19af772f9906061863b296ab427",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "7.*",
"psr/http-factory": "^1.0",
"psr/http-message": "1.*",
"zendframework/zend-diactoros": "~1.0"
},
"suggest": {
"psr/http-factory": "Common interfaces for PSR-7 HTTP message factories",
"psr/http-message": "Common interface for HTTP messages"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Crell\\ApiProblem\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Larry Garfield",
"email": "larry@garfieldtech.com",
"homepage": "http://www.garfieldtech.com/"
}
],
"description": "PHP wrapper for the api-problem IETF specification",
"homepage": "https://github.com/Crell/ApiProblem",
"keywords": [
"api-problem",
"http",
"json",
"rest",
"xml"
],
"time": "2019-08-18T10:47:17+00:00"
},
{ {
"name": "doctrine/cache", "name": "doctrine/cache",
"version": "v1.8.0", "version": "v1.8.0",
@@ -9305,9 +9365,9 @@
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.2", "php": ">=7.2",
"ext-calendar": "*",
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-simplexml": "*",
"ext-pdo": "*" "ext-pdo": "*"
}, },
"platform-dev": [] "platform-dev": []

View File

@@ -15,6 +15,16 @@ return [
*/ */
'installed' => env('PHPVMS_INSTALLED', false), 'installed' => env('PHPVMS_INSTALLED', false),
/*
* Avatar resize settings
* feel free to edit the following lines.
* Both parameters are in px.
*/
'avatar' => [
'width' => '200',
'height' => '200',
],
/* /*
* Where to redirect after logging in * Where to redirect after logging in
*/ */
@@ -79,12 +89,8 @@ return [
], ],
/* /*
* Avatar resize settings * DO NOT CHANGE THIS. This is used to map error codes to the approriate
* feel free to edit the following lines. * RFC 7807 type, which can be used as a machine-readable error code/map
* Both parameters are in px.
*/ */
'avatar' => [ 'error_root' => 'https://phpvms.net/errors',
'width' => '200',
'height' => '200',
],
]; ];

View File

@@ -26,7 +26,7 @@
<env name="APP_ENV" value="unittest"/> <env name="APP_ENV" value="unittest"/>
<env name="APP_KEY" value="base64:ve66Z5Kt/zTN3p++0zOPu854PHfZkwJE5VuoFAlzHtI="/> <env name="APP_KEY" value="base64:ve66Z5Kt/zTN3p++0zOPu854PHfZkwJE5VuoFAlzHtI="/>
<env name="APP_DEBUG" value="true"/> <env name="APP_DEBUG" value="true"/>
<env name="APP_LOG_LEVEL" value="debug"/> <env name="APP_LOG_LEVEL" value="info"/>
<env name="DB_CONNECTION" value="memory"/> <env name="DB_CONNECTION" value="memory"/>
<env name="CACHE_DRIVER" value="array"/> <env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/>

View File

@@ -500,7 +500,7 @@ class FlightTest extends TestCase
$this->flightSvc->addBid($flight, $user1); $this->flightSvc->addBid($flight, $user1);
// Try adding again, should throw an exception // Try adding again, should throw an exception
$this->expectException(\App\Exceptions\BidExists::class); $this->expectException(\App\Exceptions\BidExistsForFlight::class);
$this->flightSvc->addBid($flight, $user2); $this->flightSvc->addBid($flight, $user2);
} }

View File

@@ -6,6 +6,7 @@ use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack; use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Response;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Tests\TestData; use Tests\TestData;
/** /**
@@ -38,6 +39,12 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
public function setUp() : void public function setUp() : void
{ {
parent::setUp(); parent::setUp();
// Don't throttle requests when running the tests
$this->withoutMiddleware(
ThrottleRequests::class
);
Artisan::call('database:create', ['--reset' => true]); Artisan::call('database:create', ['--reset' => true]);
Artisan::call('migrate:refresh', ['--env' => 'unittest']); Artisan::call('migrate:refresh', ['--env' => 'unittest']);
} }