Skip to content

Commit

Permalink
refactor: Add better vendor-specific exceptions and replace uses
Browse files Browse the repository at this point in the history
Fixes #43
  • Loading branch information
ollieread committed Oct 25, 2024
1 parent 8abc4fd commit 5d305a5
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 75 deletions.
14 changes: 8 additions & 6 deletions src/Database/Eloquent/Concerns/IsTenantChild.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use RuntimeException;
use Sprout\Attributes\TenantRelation;
use Sprout\Contracts\Tenancy;
use Sprout\Database\Eloquent\Contracts\OptionalTenant;
use Sprout\Exceptions\TenantRelationException;
use Sprout\Managers\TenancyManager;

/**
Expand Down Expand Up @@ -115,6 +115,8 @@ public static function isTenantOptional(): bool
*
* @return string
*
* @throws \Sprout\Exceptions\TenantRelationException
*
* @see \Sprout\Attributes\TenantRelation
*/
private function findTenantRelationName(): string
Expand All @@ -127,25 +129,25 @@ private function findTenantRelationName(): string
->map(fn (ReflectionMethod $method) => $method->getName());

if ($methods->isEmpty()) {
throw new RuntimeException('No tenant relation found in model [' . static::class . ']');
throw TenantRelationException::missing(static::class);
}

if ($methods->count() > 1) {
throw new RuntimeException(
'Models can only have one tenant relation, [' . static::class . '] has ' . $methods->count()
);
throw TenantRelationException::tooMany(static::class, $methods->count());
}

return $methods->first();
} catch (ReflectionException $exception) {
throw new RuntimeException('Unable to find tenant relation for model [' . static::class . ']', previous: $exception); // @codeCoverageIgnore
throw TenantRelationException::missing(static::class, previous: $exception); // @codeCoverageIgnore
}
}

/**
* Get the name of the tenant relation
*
* @return string|null
*
* @throws \Sprout\Exceptions\TenantRelationException
*/
public function getTenantRelationName(): ?string
{
Expand Down
30 changes: 30 additions & 0 deletions src/Exceptions/CompatibilityException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);

namespace Sprout\Exceptions;

/**
* Compatibility Exception
*
* This exception is used when a component of Sprout is not compatible with
* another.
*
* @package Core
*/
final class CompatibilityException extends SproutException
{
/**
* Create the exception
*
* @param string $firstType
* @param string $firstName
* @param string $secondType
* @param string $secondName
*
* @return self
*/
public static function make(string $firstType, string $firstName, string $secondType, string $secondName): self

Check warning on line 26 in src/Exceptions/CompatibilityException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/CompatibilityException.php#L26

Added line #L26 was not covered by tests
{
return new self('Cannot use ' . $firstType . ' [' . $firstName . '] with ' . $secondType . ' [' . $secondName . ']');

Check warning on line 28 in src/Exceptions/CompatibilityException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/CompatibilityException.php#L28

Added line #L28 was not covered by tests
}
}
84 changes: 84 additions & 0 deletions src/Exceptions/MisconfigurationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);

namespace Sprout\Exceptions;

/**
* Misconfiguration Exception
*
* This exception is used when a component of Sprout does not have the correct
* configuration, whether that's through omissions or incorrect values.
* This also includes cases where instances of certain classes/interfaces is
* expected, but not provided.
*
* @package Core
*/
final class MisconfigurationException extends SproutException
{
/**
* Create an exception for when a config value is missing
*
* @param string $value
* @param string $type
* @param string $name
*
* @return self
*/
public static function missingConfig(string $value, string $type, string $name): self

Check warning on line 27 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L27

Added line #L27 was not covered by tests
{
return new self('The ' . $type . ' [' . $name . '] is missing a required value for \'' . $value . '\'');

Check warning on line 29 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L29

Added line #L29 was not covered by tests
}

/**
* Create an exception for when a config value is invalid
*
* @param string $value
* @param string $type
* @param string $name
*
* @return self
*/
public static function invalidConfig(string $value, string $type, string $name): self

Check warning on line 41 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L41

Added line #L41 was not covered by tests
{
return new self('The provided value for \'' . $value . '\' is not valid for' . $type . ' [' . $name . ']');

Check warning on line 43 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L43

Added line #L43 was not covered by tests
}

/**
* Create an exception for when something is misconfigured for a use
*
* @param string $type
* @param string $name
* @param string $subject
*
* @return self
*/
public static function misconfigured(string $type, string $name, string $subject): self
{
return new self('The current ' . $type . ' [' . $name . '] is not configured correctly for ' . $subject);
}

/**
* Create an exception for when an expected configuration is not found
*
* @param string $type
* @param string $name
*
* @return self
*/
public static function notFound(string $type, string $name): self

Check warning on line 68 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L68

Added line #L68 was not covered by tests
{
return new self('The ' . $type . ' for [' . $name . '] could not be found');

Check warning on line 70 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L70

Added line #L70 was not covered by tests
}

/**
* Create a new exception for when a default value is missing
*
* @param string $type
*
* @return self
*/
public static function noDefault(string $type): self

Check warning on line 80 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L80

Added line #L80 was not covered by tests
{
return new self('There is no default ' . $type . ' set');

Check warning on line 82 in src/Exceptions/MisconfigurationException.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/MisconfigurationException.php#L82

Added line #L82 was not covered by tests
}
}
27 changes: 27 additions & 0 deletions src/Exceptions/TenancyMissing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);

namespace Sprout\Exceptions;

/**
* Tenancy Missing Exception
*
* This exception is used when there is no current tenancy, but one is
* expected/required.
*
* @package Core
*/
final class TenancyMissing extends SproutException
{
/**
* Create the exception
*
* @return self
*/
public static function make(): self

Check warning on line 21 in src/Exceptions/TenancyMissing.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/TenancyMissing.php#L21

Added line #L21 was not covered by tests
{
return new self(
'There is no current tenancy'
);

Check warning on line 25 in src/Exceptions/TenancyMissing.php

View check run for this annotation

Codecov / codecov/patch

src/Exceptions/TenancyMissing.php#L23-L25

Added lines #L23 - L25 were not covered by tests
}
}
43 changes: 43 additions & 0 deletions src/Exceptions/TenantRelationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);

namespace Sprout\Exceptions;

use Throwable;

/**
* Tenant Relation Exception
*
* This exception is used for issues that arise with tenant child/descendant
* relations.
*
* @package Database\Eloquent
*/
final class TenantRelationException extends SproutException
{
/**
* Create an exception for when the tenant relation is missing
*
* @param string $model
* @param \Throwable|null $previous
*
* @return self
*/
public static function missing(string $model, ?Throwable $previous = null): self
{
return new self('Cannot find tenant relation for model [' . $model . ']', previous: $previous);
}

/**
* Create an exception for when there are too many tenant relations
*
* @param string $model
* @param int $count
*
* @return self
*/
public static function tooMany(string $model, int $count): self
{
return new self('Expected one tenant relation, found ' . $count . ' in model [' . $model . ']');
}
}
6 changes: 4 additions & 2 deletions src/Http/Resolvers/SessionIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
use Illuminate\Routing\RouteRegistrar;
use RuntimeException;
use Sprout\Contracts\Tenancy;
use Sprout\Exceptions\CompatibilityException;
use Sprout\Http\Middleware\TenantRoutes;
use Sprout\Overrides\SessionOverride;
use Sprout\Support\BaseIdentityResolver;
Expand Down Expand Up @@ -90,11 +90,13 @@ public function getRequestSessionName(Tenancy $tenancy): string
* @param \Sprout\Contracts\Tenancy<TenantClass> $tenancy
*
* @return string|null
*
* @throws \Sprout\Exceptions\CompatibilityException
*/
public function resolveFromRequest(Request $request, Tenancy $tenancy): ?string
{
if (sprout()->hasOverride(SessionOverride::class)) {
throw new RuntimeException('Cannot use the session resolver for tenancy [' . $tenancy->getName() . '] and the session override');
throw CompatibilityException::make('resolver', $this->getName(), 'override', SessionOverride::class);

Check warning on line 99 in src/Http/Resolvers/SessionIdentityResolver.php

View check run for this annotation

Codecov / codecov/patch

src/Http/Resolvers/SessionIdentityResolver.php#L99

Added line #L99 was not covered by tests
}

/**
Expand Down
14 changes: 7 additions & 7 deletions src/Managers/IdentityResolverManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Sprout\Managers;

use InvalidArgumentException;
use Sprout\Exceptions\MisconfigurationException;
use Sprout\Http\Resolvers\CookieIdentityResolver;
use Sprout\Http\Resolvers\HeaderIdentityResolver;
use Sprout\Http\Resolvers\PathIdentityResolver;
Expand Down Expand Up @@ -54,13 +54,13 @@ protected function getConfigKey(string $name): string
* @phpstan-param array{domain?: string, pattern?: string|null, parameter?: string|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\SubdomainIdentityResolver
*
* @throws \Sprout\Exceptions\MisconfigurationException
*/
protected function createSubdomainResolver(array $config, string $name): SubdomainIdentityResolver
{
if (! isset($config['domain'])) {
throw new InvalidArgumentException(
'No domain provided for resolver [' . $name . ']'
);
throw MisconfigurationException::missingConfig('domain', 'resolver', $name);

Check warning on line 63 in src/Managers/IdentityResolverManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/IdentityResolverManager.php#L63

Added line #L63 was not covered by tests
}

return new SubdomainIdentityResolver(
Expand All @@ -81,15 +81,15 @@ protected function createSubdomainResolver(array $config, string $name): Subdoma
* @phpstan-param array{segment?: int|null, pattern?: string|null, parameter?: string|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\PathIdentityResolver
*
* @throws \Sprout\Exceptions\MisconfigurationException
*/
protected function createPathResolver(array $config, string $name): PathIdentityResolver
{
$segment = $config['segment'] ?? 1;

if ($segment < 1) {
throw new InvalidArgumentException(
'Invalid path segment [' . $segment . '], path segments should be 1 indexed'
);
throw MisconfigurationException::invalidConfig('segment', 'resolver', $name);

Check warning on line 92 in src/Managers/IdentityResolverManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/IdentityResolverManager.php#L92

Added line #L92 was not covered by tests
}

return new PathIdentityResolver(
Expand Down
28 changes: 11 additions & 17 deletions src/Managers/ProviderManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
namespace Sprout\Managers;

use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
use Sprout\Contracts\Tenant;
use Sprout\Exceptions\MisconfigurationException;
use Sprout\Providers\DatabaseTenantProvider;
use Sprout\Providers\EloquentTenantProvider;
use Sprout\Support\BaseFactory;
Expand Down Expand Up @@ -58,26 +58,24 @@ protected function getConfigKey(string $name): string
* @phpstan-param array{model?: class-string<TenantModel>} $config
*
* @phpstan-return \Sprout\Providers\EloquentTenantProvider<TenantModel>
*
* @throws \Sprout\Exceptions\MisconfigurationException
*/
protected function createEloquentProvider(array $config, string $name): EloquentTenantProvider
{
if (! isset($config['model'])) {
throw new InvalidArgumentException(
'No model provided for provider [' . $name . ']'
);
throw MisconfigurationException::missingConfig('model', 'provider', $name);

Check warning on line 67 in src/Managers/ProviderManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/ProviderManager.php#L67

Added line #L67 was not covered by tests
}

if (
! class_exists($config['model'])
|| ! is_subclass_of($config['model'], Model::class)
|| ! is_subclass_of($config['model'], Tenant::class)
) {
throw new InvalidArgumentException(
'Invalid model provided for provider [' . $name . ']'
);
throw MisconfigurationException::invalidConfig('model', 'provider', $name);

Check warning on line 75 in src/Managers/ProviderManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/ProviderManager.php#L75

Added line #L75 was not covered by tests
}

return new EloquentTenantProvider($name, $config['model']);
return new EloquentTenantProvider($name, $config['model']);
}

/**
Expand All @@ -93,6 +91,8 @@ protected function createEloquentProvider(array $config, string $name): Eloquent
* @phpstan-param array{entity?: class-string<TenantEntity>, table?: string|class-string<\Illuminate\Database\Eloquent\Model>, connection?: string} $config
*
* @phpstan-return \Sprout\Providers\DatabaseTenantProvider<TenantEntity>
*
* @throws \Sprout\Exceptions\MisconfigurationException
*/
protected function createDatabaseProvider(array $config, string $name): DatabaseTenantProvider
{
Expand All @@ -103,15 +103,11 @@ protected function createDatabaseProvider(array $config, string $name): Database
|| ! is_subclass_of($config['entity'], Tenant::class)
)
) {
throw new InvalidArgumentException(
'Invalid entity provided for provider [' . $name . ']'
);
throw MisconfigurationException::invalidConfig('entity', 'provider', $name);

Check warning on line 106 in src/Managers/ProviderManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/ProviderManager.php#L106

Added line #L106 was not covered by tests
}

if (! isset($config['table'])) {
throw new InvalidArgumentException(
'No table provided for provider [' . $name . ']'
);
throw MisconfigurationException::missingConfig('table', 'provider', $name);

Check warning on line 110 in src/Managers/ProviderManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/ProviderManager.php#L110

Added line #L110 was not covered by tests
}

// This allows users to provide a model name for retrieval of table and
Expand All @@ -120,9 +116,7 @@ protected function createDatabaseProvider(array $config, string $name): Database
// It's worth checking that the provided value is in fact a model,
// otherwise things are going to get awkward
if (! is_subclass_of($config['table'], Model::class)) {
throw new InvalidArgumentException(
'Invalid table provided for provider [' . $name . ']'
);
throw MisconfigurationException::invalidConfig('table', 'provider', $name);

Check warning on line 119 in src/Managers/ProviderManager.php

View check run for this annotation

Codecov / codecov/patch

src/Managers/ProviderManager.php#L119

Added line #L119 was not covered by tests
}

$model = new $config['table']();
Expand Down
Loading

0 comments on commit 5d305a5

Please sign in to comment.