Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
d8vjork committed Aug 16, 2024
1 parent 7840a23 commit 7fbd6c3
Show file tree
Hide file tree
Showing 28 changed files with 319 additions and 199 deletions.
9 changes: 2 additions & 7 deletions config/apiable.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,14 @@

return [

/**
* Resource type model map.
*
* @see https://docs.opensoutheners.com/laravel-apiable/guide/#getting-started
*/
'resource_type_map' => [],

/**
* Default options for request query filters, sorts, etc.
*
* @see https://docs.opensoutheners.com/laravel-apiable/guide/requests.html
*/
'requests' => [
'validate' => ! ((bool) env('APIABLE_DEV_MODE', false)),

'validate_params' => false,

'filters' => [
Expand Down
4 changes: 2 additions & 2 deletions src/Attributes/AppendsQueryParam.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace OpenSoutheners\LaravelApiable\Attributes;

use Attribute;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;
use OpenSoutheners\LaravelApiable\ServiceProvider;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class AppendsQueryParam extends QueryParam
Expand All @@ -19,6 +19,6 @@ public function getTypeAsResource(): string
return $this->type;
}

return Apiable::getResourceType($this->type);
return ServiceProvider::getTypeForModel($this->type);
}
}
4 changes: 2 additions & 2 deletions src/Attributes/FieldsQueryParam.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace OpenSoutheners\LaravelApiable\Attributes;

use Attribute;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;
use OpenSoutheners\LaravelApiable\ServiceProvider;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class FieldsQueryParam extends QueryParam
Expand All @@ -19,6 +19,6 @@ public function getTypeAsResource(): string
return $this->type;
}

return Apiable::getResourceType($this->type);
return ServiceProvider::getTypeForModel($this->type);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?php

namespace OpenSoutheners\LaravelApiable\Attributes;
namespace OpenSoutheners\LaravelApiable\Documentation\Attributes;

class ResourceResponse
use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class EndpointResource
{
/**
* @param class-string<\Illuminate\Database\Eloquent\Model> $resource
Expand Down
53 changes: 45 additions & 8 deletions src/Documentation/Endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;
use OpenSoutheners\LaravelApiable\Http\JsonApiResponse;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
Expand Down Expand Up @@ -40,10 +46,38 @@ public static function fromMethodAttribute(ReflectionMethod $controllerMethod, R

$attribute = $documentedEndpointAttribute->newInstance();

return new self($resource, $route, $method, $attribute->title, $attribute->description);
return new self(
$resource,
$route,
$method,
$attribute->title,
$attribute->description ?: self::getDescriptionFromMethodDoc($controllerMethod->getDocComment())
);
}

public static function fromResourceAction(Resource $resource, Route $route, string $method): ?self
protected static function getDescriptionFromMethodDoc(string $comment): string
{
$lexer = new Lexer();
$constExprParser = new ConstExprParser();
$typeParser = new TypeParser($constExprParser);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser);

$tokens = new TokenIterator(
$lexer->tokenize($comment)
);

$description = '';

foreach ($phpDocParser->parse($tokens)->children as $node) {
if ($node instanceof PhpDocTextNode) {
$description .= (string) $node;
}
}

return $description;
}

public static function fromResourceAction(ReflectionMethod $controllerMethod, Resource $resource, Route $route, string $method): ?self
{
$endpointResource = $resource->getName();
$endpointResourcePlural = Str::plural($endpointResource);
Expand All @@ -59,6 +93,8 @@ public static function fromResourceAction(Resource $resource, Route $route, stri
default => ['', '']
};

$description = self::getDescriptionFromMethodDoc($controllerMethod) ?: $description;

return new self($resource, $route, $method, $title, $description);
}

Expand Down Expand Up @@ -95,10 +131,11 @@ public function toPostmanItem(): array
$postmanItem = [
'name' => $this->title,
'request' => [
'method' => $this->route->getActionMethod(),
'description' => $this->description,
'method' => $this->method,
'header' => [],
'url' => [],
],
'url' => [],
'response' => [],
];

Expand All @@ -122,11 +159,11 @@ public function toPostmanItem(): array
)->value()
);

$postmanItem['url']['raw'] = "{{base_url}}/{$routeUriString->join('/')}";
$postmanItem['url']['host'] = ['{{base_url}}'];
$postmanItem['url']['path'] = $routeUriString->toArray();
$postmanItem['request']['url']['raw'] = "{{base_url}}/{$routeUriString->join('/')}";
$postmanItem['request']['url']['host'] = ['{{base_url}}'];
$postmanItem['request']['url']['path'] = $routeUriString->toArray();

$postmanItem['url']['query'] = array_map(
$postmanItem['request']['url']['query'] = array_map(
fn (QueryParam $param): array => $param->toPostman(),
$this->query
);
Expand Down
2 changes: 1 addition & 1 deletion src/Documentation/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function generate(): self

foreach ($routeMethods as $routeMethod) {
$endpoint = Endpoint::fromMethodAttribute($method, $resource, $route, $routeMethod)
?? Endpoint::fromResourceAction($resource, $route, $routeMethod);
?? Endpoint::fromResourceAction($method, $resource, $route, $routeMethod);

$endpoint->getQueryFromAttributes($controller, $method);

Expand Down
1 change: 1 addition & 0 deletions src/Documentation/QueryParam.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public static function fromAttribute(Attributes\QueryParam $attribute): self
// $documentedRoute['name'] = $this->attribute->title;
// $documentedRoute['description'] = $this->attribute->description;
// },

Attributes\FilterQueryParam::class => static::fromFilterAttribute($attribute),
Attributes\FieldsQueryParam::class => static::fromFieldsAttribute($attribute),
Attributes\AppendsQueryParam::class => static::fromAppendsAttribute($attribute),
Expand Down
4 changes: 2 additions & 2 deletions src/Http/AllowedFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace OpenSoutheners\LaravelApiable\Http;

use Illuminate\Contracts\Support\Arrayable;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;
use OpenSoutheners\LaravelApiable\ServiceProvider;

class AllowedFields implements Arrayable
{
Expand All @@ -22,7 +22,7 @@ class AllowedFields implements Arrayable
*/
public function __construct(string $type, string|array $attributes)
{
$this->type = class_exists($type) ? Apiable::getResourceType($type) : $type;
$this->type = class_exists($type) ? ServiceProvider::getTypeForModel($type) : $type;
$this->attributes = (array) $attributes;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Http/AllowedFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public static function lowerOrEqualThan($attribute, $values = '*'): self
public static function scoped($attribute, $values = '1'): self
{
return new self(
Apiable::config('requests.filters.enforce_scoped_names') ? Apiable::scopedFilterSuffix($attribute) : $attribute,
Apiable::config('requests.filters.enforce_scoped_names') ? "{$attribute}_scoped" : $attribute,
static::SCOPE,
$values
);
Expand Down
3 changes: 2 additions & 1 deletion src/Http/ApplyFieldsToQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Closure;
use Illuminate\Database\Eloquent\Builder;
use OpenSoutheners\LaravelApiable\Contracts\HandlesRequestQueries;
use OpenSoutheners\LaravelApiable\ServiceProvider;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;

class ApplyFieldsToQuery implements HandlesRequestQueries
Expand Down Expand Up @@ -39,7 +40,7 @@ protected function applyFields(Builder $query, array $fields)
{
/** @var \OpenSoutheners\LaravelApiable\Contracts\JsonApiable|\Illuminate\Database\Eloquent\Model $mainQueryModel */
$mainQueryModel = $query->getModel();
$mainQueryResourceType = Apiable::getResourceType($mainQueryModel);
$mainQueryResourceType = ServiceProvider::getTypeForModel($mainQueryModel);

Check failure on line 43 in src/Http/ApplyFieldsToQuery.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #1 $model of static method OpenSoutheners\LaravelApiable\ServiceProvider::getTypeForModel() expects class-string<Illuminate\Database\Eloquent\Model>, Illuminate\Database\Eloquent\Model|OpenSoutheners\LaravelApiable\Contracts\JsonApiable given.
$queryEagerLoaded = $query->getEagerLoads();

// TODO: Move this to some class methods
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Concerns/AllowsAppends.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Exception;
use Illuminate\Database\Eloquent\Model;
use OpenSoutheners\LaravelApiable\Http\AllowedAppends;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;
use OpenSoutheners\LaravelApiable\ServiceProvider;

/**
* @mixin \OpenSoutheners\LaravelApiable\Http\RequestQueryObject
Expand Down Expand Up @@ -47,7 +47,7 @@ public function allowAppends(AllowedAppends|string $type, ?array $attributes = n
}

if (class_exists($type) && is_subclass_of($type, Model::class)) {
$type = Apiable::getResourceType($type);
$type = ServiceProvider::getTypeForModel($type);
}

$this->allowedAppends = array_merge($this->allowedAppends, [$type => (array) $attributes]);
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Concerns/AllowsFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Exception;
use Illuminate\Database\Eloquent\Model;
use OpenSoutheners\LaravelApiable\Http\AllowedFields;
use OpenSoutheners\LaravelApiable\Support\Facades\Apiable;
use OpenSoutheners\LaravelApiable\ServiceProvider;

/**
* @mixin \OpenSoutheners\LaravelApiable\Http\RequestQueryObject
Expand Down Expand Up @@ -48,7 +48,7 @@ public function allowFields($type, $attributes = null): self
}

if (class_exists($type) && is_subclass_of($type, Model::class)) {
$type = Apiable::getResourceType($type);
$type = ServiceProvider::getTypeForModel($type);
}

$this->allowedFields = array_merge($this->allowedFields, [$type => (array) $attributes]);
Expand Down
10 changes: 8 additions & 2 deletions src/Http/Concerns/IteratesResultsAfterQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use OpenSoutheners\LaravelApiable\Http\QueryParamsValidator;
use OpenSoutheners\LaravelApiable\Http\Resources\JsonApiCollection;
use OpenSoutheners\LaravelApiable\Http\Resources\JsonApiResource;
use OpenSoutheners\LaravelApiable\ServiceProvider;
use OpenSoutheners\LaravelApiable\Support\Apiable;

/**
Expand Down Expand Up @@ -96,15 +97,20 @@ protected function appendToApiResource(mixed $resource, array $appends): void

/** @var array<\OpenSoutheners\LaravelApiable\Http\Resources\JsonApiResource> $resourceIncluded */
$resourceIncluded = $resource->with['included'] ?? [];
$resourceType = Apiable::getResourceType($resource->resource);
$resourceType = ServiceProvider::getTypeForModel(
is_string($resource->resource) ? $resource->resource : get_class($resource->resource)
);

if ($appendsArr = $appends[$resourceType] ?? null) {
$resource->resource->makeVisible($appendsArr)->append($appendsArr);
}

foreach ($resourceIncluded as $included) {
$includedResourceType = Apiable::getResourceType($included->resource);
$includedResourceType = ServiceProvider::getTypeForModel(
is_string($included->resource) ? $included->resource : get_class($included->resource)
);

// dump($includedResourceType);
if ($appendsArr = $appends[$includedResourceType] ?? null) {
$included->resource->makeVisible($appendsArr)->append($appendsArr);
}
Expand Down
13 changes: 7 additions & 6 deletions src/Http/Concerns/ResolvesFromRouteAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use OpenSoutheners\LaravelApiable\Attributes\ForceAppendAttribute;
use OpenSoutheners\LaravelApiable\Attributes\IncludeQueryParam;
use OpenSoutheners\LaravelApiable\Attributes\QueryParam;
use OpenSoutheners\LaravelApiable\Attributes\ResourceResponse;
use OpenSoutheners\LaravelApiable\Documentation\Attributes\EndpointResource;
use OpenSoutheners\LaravelApiable\Attributes\SearchFilterQueryParam;
use OpenSoutheners\LaravelApiable\Attributes\SearchQueryParam;
use OpenSoutheners\LaravelApiable\Attributes\SortQueryParam;
Expand Down Expand Up @@ -53,10 +53,11 @@ protected function resolveFromRoute(): void
*/
protected function resolveAttributesFrom($reflected): void
{
$allowedQueryParams = array_filter($reflected->getAttributes(), function (ReflectionAttribute $attribute) {
return is_subclass_of($attribute->getName(), QueryParam::class)
|| in_array($attribute->getName(), [ApplyDefaultFilter::class, ApplyDefaultSort::class]);
});
$allowedQueryParams = array_filter(
$reflected->getAttributes(),
fn (ReflectionAttribute $attribute): bool => is_subclass_of($attribute->getName(), QueryParam::class)
|| in_array($attribute->getName(), [EndpointResource::class, ApplyDefaultFilter::class, ApplyDefaultSort::class])
);

foreach ($allowedQueryParams as $allowedQueryParam) {
$attributeInstance = $allowedQueryParam->newInstance();
Expand All @@ -72,7 +73,7 @@ protected function resolveAttributesFrom($reflected): void
AppendsQueryParam::class => $this->allowAppends($attributeInstance->type, $attributeInstance->attributes),
ApplyDefaultSort::class => $this->applyDefaultSort($attributeInstance->attribute, $attributeInstance->direction),
ApplyDefaultFilter::class => $this->applyDefaultFilter($attributeInstance->attribute, $attributeInstance->operator, $attributeInstance->values),
ResourceResponse::class => $this->using($attributeInstance->resource),
EndpointResource::class => $this->using($attributeInstance->resource),
default => null,
};
}
Expand Down
Loading

0 comments on commit 7fbd6c3

Please sign in to comment.