Skip to content

Commit

Permalink
Merge branch 'vNext' into php-84-upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
shalvah authored Jan 18, 2025
2 parents 4674c2d + 8a2884e commit 115d495
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 11 deletions.
19 changes: 17 additions & 2 deletions src/Attributes/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,32 @@
class Group
{
public function __construct(
public string $name,
public mixed $name,
public ?string $description = '',
/** You can use the separate #[Authenticated] attribute, or pass authenticated: false to this. */
public ?bool $authenticated = null,
){
}

protected function getName(): string
{
if (is_string($this->name)) {
return $this->name;
}

if (interface_exists('BackedEnum') && is_a($this->name, 'BackedEnum')) {
return $this->name->value;
}

throw new \InvalidArgumentException(
'The name property of a group must be either a PHP Backed Enum or a string'
);
}

public function toArray()
{
$data = [
"groupName" => $this->name,
"groupName" => $this->getName(),
"groupDescription" => $this->description,
];

Expand Down
1 change: 1 addition & 0 deletions src/Attributes/ResponseFromApiResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct(
public ?int $simplePaginate = null,
public ?int $cursorPaginate = null,
public array $additional = [],
public array $withCount = [],
)
{
}
Expand Down
19 changes: 17 additions & 2 deletions src/Attributes/Subgroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,30 @@
class Subgroup
{
public function __construct(
public string $name,
public mixed $name,
public ?string $description = '',
){
}

protected function getName(): string
{
if (is_string($this->name)) {
return $this->name;
}

if (interface_exists('BackedEnum') && is_a($this->name, 'BackedEnum')) {
return $this->name->value;
}

throw new \InvalidArgumentException(
'The name property of a subgroup must be either a PHP Backed Enum or a string'
);
}

public function toArray()
{
return [
"subgroup" => $this->name,
"subgroup" => $this->getName(),
"subgroupDescription" => $this->description,
];
}
Expand Down
16 changes: 11 additions & 5 deletions src/Extracting/InstantiatesExampleModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait InstantiatesExampleModels
*/
protected function instantiateExampleModel(
?string $type = null, array $factoryStates = [],
array $relations = [], ?ReflectionFunctionAbstract $transformationMethod = null
array $relations = [], ?ReflectionFunctionAbstract $transformationMethod = null, array $withCount = [],
)
{
// If the API Resource uses an empty resource, there won't be an example model
Expand All @@ -42,7 +42,7 @@ protected function instantiateExampleModel(
$configuredStrategies = $this->config->get('examples.models_source', ['factoryCreate', 'factoryMake', 'databaseFirst']);

$strategies = [
'factoryCreate' => fn() => $this->getExampleModelFromFactoryCreate($type, $factoryStates, $relations),
'factoryCreate' => fn() => $this->getExampleModelFromFactoryCreate($type, $factoryStates, $relations, $withCount),
'factoryMake' => fn() => $this->getExampleModelFromFactoryMake($type, $factoryStates, $relations),
'databaseFirst' => fn() => $this->getExampleModelFromDatabaseFirst($type, $relations),
];
Expand All @@ -63,13 +63,19 @@ protected function instantiateExampleModel(
/**
* @param class-string $type
* @param string[] $factoryStates
* @param string[] $relations
* @param string[] $withCount
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
protected function getExampleModelFromFactoryCreate(string $type, array $factoryStates = [], array $relations = [])
protected function getExampleModelFromFactoryCreate(string $type, array $factoryStates = [], array $relations = [], array $withCount = [])
{
$factory = Utils::getModelFactory($type, $factoryStates, $relations);
return $factory->create()->refresh()->load($relations);
// Since $relations and $withCount refer to the same underlying relationships in the model,
// combining them ensures that all required relationships are initialized when passed to the factory.
$allRelations = array_unique(array_merge($relations, $withCount));

$factory = Utils::getModelFactory($type, $factoryStates, $allRelations);
return $factory->create()->refresh()->load($relations)->loadCount($withCount);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protected function getApiResourceResponse(ResponseFromApiResource $attributeInst
);
$modelInstantiator = null;
} else {
$modelInstantiator = fn() => $this->instantiateExampleModel($modelToBeTransformed, $attributeInstance->factoryStates, $attributeInstance->with);
$modelInstantiator = fn() => $this->instantiateExampleModel($modelToBeTransformed, $attributeInstance->factoryStates, $attributeInstance->with, null, $attributeInstance->withCount);
}

$pagination = [];
Expand Down
9 changes: 9 additions & 0 deletions tests/Fixtures/TestGroupBackedEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Knuckles\Scribe\Tests\Fixtures;

enum TestGroupBackedEnum: string
{
case Users = 'Users';
case Admins = 'Admins';
}
5 changes: 5 additions & 0 deletions tests/Fixtures/TestUserApiResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Knuckles\Scribe\Tests\Fixtures;

use Illuminate\Foundation\Application;
use Illuminate\Http\Resources\Json\JsonResource;

/**
Expand Down Expand Up @@ -34,6 +35,10 @@ public function toArray($request)
}),
];

if (version_compare(Application::VERSION, '9', '>=')) {
$result['children_count'] = $this->whenCounted('children_count');
}

if ($this['state1'] && $this['random-state']) {
$result['state1'] = $this['state1'];
$result['random-state'] = $this['random-state'];
Expand Down
23 changes: 23 additions & 0 deletions tests/Strategies/Metadata/GetFromMetadataAttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Knuckles\Scribe\Attributes\Subgroup;
use Knuckles\Scribe\Attributes\Unauthenticated;
use Knuckles\Scribe\Extracting\Strategies\Metadata\GetFromMetadataAttributes;
use Knuckles\Scribe\Tests\Fixtures\TestGroupBackedEnum;
use Knuckles\Scribe\Tools\DocumentationConfig;
use PHPUnit\Framework\TestCase;
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
Expand Down Expand Up @@ -84,6 +85,21 @@ public function can_fetch_from_authenticated_attribute_or_authenticated_paramete
"authenticated" => false,
], $results);

$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->controller = new ReflectionClass(MetadataAttributesTestController::class);
$e->method = $e->controller->getMethod('b2');
});
$results = $this->fetch($endpoint);
$this->assertArraySubset([
"groupName" => "Users",
"groupDescription" => "",
"subgroup" => "Admins",
"subgroupDescription" => "",
"title" => "Endpoint B2",
"description" => "",
"authenticated" => false,
], $results);

$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->controller = new ReflectionClass(MetadataAttributesTestController2::class);
$e->method = $e->controller->getMethod('c1');
Expand Down Expand Up @@ -159,6 +175,13 @@ public function a3()
public function b1()
{
}

#[Group(TestGroupBackedEnum::Users)]
#[Subgroup(TestGroupBackedEnum::Admins)]
#[Endpoint("Endpoint B2")]
public function b2()
{
}
}

#[Authenticated]
Expand Down
104 changes: 103 additions & 1 deletion tests/Strategies/Responses/UseResponseAttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Knuckles\Scribe\Tests\Strategies\Responses;

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Foundation\Application;
use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Schema;
use Knuckles\Camel\Extraction\ExtractedEndpointData;
use Knuckles\Scribe\Attributes\Response;
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
Expand Down Expand Up @@ -285,7 +287,95 @@ public function can_parse_apiresource_attributes_with_cursor_pagination()
], $results);
}

protected function fetch($endpoint): array
/** @test */
public function can_parse_apiresource_attributes_and_load_children_using_factory_create()
{
Schema::create('test_users', function (Blueprint $table) {
$table->id();
$table->string('first_name');
$table->string('last_name');
$table->string('email');
$table->integer('parent_id')->nullable();
});

$factory = app(\Illuminate\Database\Eloquent\Factory::class);
$factory->afterCreating(TestUser::class, function (TestUser $user, $faker) {
if ($user->id === 4) {
Utils::getModelFactory(TestUser::class)->create(['id' => 5, 'parent_id' => 4]);
}
});
$documentationConfig = ['examples' => ['models_source' => ['factoryCreate']]];

$results = $this->fetch($this->endpoint("apiResourceAttributesIncludeChildren"), $documentationConfig);
$this->assertArraySubset([
[
'status' => 200,
'content' => json_encode([
"data" => [
"id" => 4,
"name" => "Tested Again",
"email" => "[email protected]",
"children" => [
[
"id" => 5,
"name" => "Tested Again",
"email" => "[email protected]",
]
],
],
]),
],
], $results);
}


/** @test */
public function can_parse_apiresource_attributes_and_load_children_and_children_count_using_factory_create()
{
if (version_compare(Application::VERSION, '9', '<')) {
$this->markTestSkipped('The whenCounted method in JsonResource requires Laravel 9 or higher.');
}

Schema::create('test_users', function (Blueprint $table) {
$table->id();
$table->string('first_name');
$table->string('last_name');
$table->string('email');
$table->integer('parent_id')->nullable();
});

$factory = app(\Illuminate\Database\Eloquent\Factory::class);
$factory->afterCreating(TestUser::class, function (TestUser $user, $faker) {
if ($user->id === 4) {
Utils::getModelFactory(TestUser::class)->create(['id' => 5, 'parent_id' => 4]);
}
});
$documentationConfig = ['examples' => ['models_source' => ['factoryCreate']]];

$results = $this->fetch($this->endpoint("apiResourceAttributesIncludeChildrenAndChildrenCount"), $documentationConfig);
$this->assertArraySubset([
[
'status' => 200,
'content' => json_encode([
"data" => [
"id" => 4,
"name" => "Tested Again",
"email" => "[email protected]",
"children" => [
[
"id" => 5,
"name" => "Tested Again",
"email" => "[email protected]",
]
],
'children_count' => 1,
],
]),
],
], $results);
}

protected function fetch($endpoint, array $documentationConfig = []): array
{
$strategy = new UseResponseAttributes(new DocumentationConfig([]));
return $strategy($endpoint, []);
Expand Down Expand Up @@ -345,4 +435,16 @@ public function apiResourceAttributesWithCursorPaginate()
{

}

#[ResponseFromApiResource(TestUserApiResource::class, with: ['children'], withCount: ['children'])]
public function apiResourceAttributesIncludeChildrenAndChildrenCount()
{

}

#[ResponseFromApiResource(TestUserApiResource::class, with: ['children'])]
public function apiResourceAttributesIncludeChildren()
{

}
}

0 comments on commit 115d495

Please sign in to comment.