Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: run-tests

on:
push:
pull_request:

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [8.3, 8.4]
laravel: [11.*, 12.*]
stability: [prefer-lowest, prefer-stable]
include:
- laravel: 11.*
testbench: ^9.9
carbon: ^2.63
- laravel: 12.*
testbench: 10.*
carbon: ^2.63|^3.0

name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo, swoole, openssl
coverage: pcov

- name: Setup problem matchers
run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" --no-interaction --no-update
composer update --${{ matrix.stability }} --prefer-dist --no-interaction

- name: List Installed Dependencies
run: composer show -D

- name: Execute tests
run: vendor/bin/pest --ci --bail --compact --memory
40 changes: 40 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<source>
<include>
<directory>./src</directory>
</include>
</source>
<php>
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
<env name="APP_ENV" value="testing"/>
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="testing"/>
<env name="MAIL_MAILER" value="array"/>
<env name="PULSE_ENABLED" value="false"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
</phpunit>
33 changes: 18 additions & 15 deletions src/DescribeFilamentResourceTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function extractRelationshipsInfo(Resource $resource): array
// Placeholder: Use manager class name as key.
$relationName = $manager->getRelationshipName();
$modelClass = $resource::getModel();
$modelInstance = new $modelClass();
$modelInstance = new $modelClass;
$relation = $modelInstance->$relationName();

$relationships[$relationName] = [
Expand Down Expand Up @@ -217,22 +217,20 @@ public function mapFilterType(BaseFilter $filter): string
};
}

public function mapFormComponent(Component $component, Resource $resource): ?array
public function mapFormComponent(Component $component, ?Resource $resource = null): ?array
{
$baseInfo = [
'name' => $component->getName(),
'type' => $this->mapComponentType($component),
'label' => $component->getLabel(),
'required' => method_exists($component, 'isRequired') ? $component->isRequired() : null,
'disabled' => method_exists($component, 'isDisabled') ? $component->isDisabled() : null,
// 'nullable' => method_exists($component, 'isNullable') ? $component->isNullable() : null, // Needs checking validation rules
];

if ($component instanceof TextInput) {
$baseInfo['maxLength'] = $component->getMaxLength();
}

if ($component instanceof Select && $component->getRelationshipName()) {
if ($resource && $component instanceof Select && $component->getRelationshipName()) {
$modelClass = $resource::getModel();
$modelInstance = app($modelClass);
$relationshipDefinition = $modelInstance->{$component->getRelationshipName()}();
Expand All @@ -241,25 +239,21 @@ public function mapFormComponent(Component $component, Resource $resource): ?arr
'type' => class_basename($relationshipDefinition), // e.g., BelongsTo
'model' => get_class($relationshipDefinition->getRelated()),
'displayColumn' => $component->getRelationshipTitleAttribute(),
'foreignKey' => $relationshipDefinition->getForeignKeyName(), // Might need adjustment based on relationship type
'foreignKey' => $relationshipDefinition->getForeignKeyName(),
];
}

// Add more specific component type mappings here if needed

return $baseInfo;
}

public function mapTableAction(Action|BulkAction $action): string
{
// Map common actions to simple strings, fallback to action name
$name = $action->getName();

return match ($name) {
'view', 'edit', 'delete', 'forceDelete', 'restore', 'replicate' => $name,
default => $name, // Return the action name itself
default => $name,
};
// Could potentially add more details like label, icon, color if needed
}

public function mapTableColumn(Column $column): array
Expand All @@ -283,17 +277,26 @@ public function mapTableFilter(BaseFilter $filter): array
'type' => $this->mapFilterType($filter),
];

if ($filter->hasFormSchema()) {
$baseInfo['usage'] = 'Please use the form schema to filter the data.';
$baseInfo['type'] = 'form';
$baseInfo['form'] = collect($filter->getFormSchema())
->reject(fn (Component $component) => $component instanceof Grid || $component instanceof Fieldset)
->map(fn (Component $component) => $this->mapFormComponent($component))
->filter()
->values()
->all();
}

if ($filter instanceof TernaryFilter) {
// Condition is implicit (true/false/all)
} elseif ($filter instanceof SelectFilter) {
$baseInfo['optionsSource'] = 'Dynamic/Callable'; // Getting exact source is complex
$baseInfo['options'] = 'Dynamic';

// Try to get options if they are simple array
if (method_exists($filter, 'getOptions') && is_array($options = $filter->getOptions())) {
$baseInfo['optionsSource'] = $options;
$baseInfo['options'] = $options;
}
}
// Add more specific filter type mappings here if needed

return $baseInfo;
}
Expand Down
13 changes: 12 additions & 1 deletion src/GetFilamentResourceDataTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function build(): PrismTool
{
return app(PrismTool::class)
->as($this->getName())
->for('Gets the data for a given Filament resource, applying optional filters provided in the describe_filament_resource tool. Always call the describe_filament_resource tool before calling this tool. Try to use the available filters to get the data you need.')
->for('Gets the data for a given Filament resource, applying optional filters (try to use them). Always call the describe_filament_resource tool before calling this tool. Always try to use the available filters to get the data you need.')
->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.', required: true)
->withStringParameter('filters', 'JSON string of filters to apply (e.g., \'{"status": "published", "author_id": [1, 2]}\').', required: false)
->using(function (string $resource, ?string $filters = null) {
Expand All @@ -32,6 +32,8 @@ public function build(): PrismTool
try {
$listPageClass = $resource::getPages()['index'];
$component = $listPageClass->getPage();

/** @var InteractsWithTable $listPage */
$listPage = new $component;
$listPage->bootedInteractsWithTable();
$table = $listPage->getTable();
Expand All @@ -44,6 +46,8 @@ public function build(): PrismTool
$listPage->tableSearch = $filters[$column->getName()];
});

$listPage->resetTableFiltersForm();

foreach ($listPage->getTable()->getFilters() as $filter) {
if (method_exists($filter, 'isMultiple') && $filter->isMultiple()) {
$listPage->tableFilters[$filter->getName()] = [
Expand All @@ -55,6 +59,13 @@ public function build(): PrismTool
$listPage->tableFilters[$filter->getName()] = [
'value' => $filters[$filter->getName()] ?? null,
];

if ($filter->hasFormSchema()) {
foreach ($filter->getFormSchema() as $formSchema) {
$listPage->tableFilters[$filter->getName()][$formSchema->getName()] =
$filters[$formSchema->getName()] ?? null;
}
}
}
}

Expand Down
50 changes: 39 additions & 11 deletions tests/Feature/DescribeFilamentResourceToolTest.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<?php

use Tests\Feature\TestUserResource;
use Kirschbaum\Loop\Filament\DescribeFilamentResourceTool;
use Tests\Feature\TestPostWithRelationsResource;
use Tests\Feature\TestUserResource;
use Tests\Feature\TestUserWithRelationsResource;
use Kirschbaum\Loop\Filament\DescribeFilamentResourceTool;

it('can instantiate the describe filament resource tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;

expect($tool)->toBeInstanceOf(DescribeFilamentResourceTool::class);
expect($tool->getName())->toBe('describe_filament_resource');
});

it('can extract table schema using the tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;
$resource = app(TestUserResource::class);

$tableSchema = $tool->extractTableSchema($resource);
Expand All @@ -28,7 +28,7 @@
});

it('can extract column properties through the tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;
$resource = app(TestUserResource::class);

$tableSchema = $tool->extractTableSchema($resource);
Expand All @@ -45,8 +45,36 @@
->and($emailColumn['sortable'])->toBeTrue();
});

it('can extract filters through the tool', function () {
$tool = new DescribeFilamentResourceTool;
$resource = app(TestUserResource::class);

$tableSchema = $tool->extractTableSchema($resource);
$filters = $tableSchema['filters'];

$nameFilter = collect($filters)->firstWhere('name', 'name');
expect($nameFilter)->toBeArray()
->and($nameFilter['type'])->toBe('select')
->and($nameFilter['options'])->toBeArray();

$emailFilter = collect($filters)->firstWhere('name', 'email');
expect($emailFilter)->toBeArray()
->and($emailFilter['type'])->toBe('searchable_column');

$createdAtFilter = collect($filters)->firstWhere('name', 'created_at');
expect($createdAtFilter)->toBeArray()
->and($createdAtFilter['type'])->toBe('form')
->and($createdAtFilter['form'])->toBeArray()
->and($createdAtFilter['form'][0])->toBeArray()
->and($createdAtFilter['form'][0]['name'])->toBe('created_at_after')
->and($createdAtFilter['form'][0]['type'])->toBe('datetime')
->and($createdAtFilter['form'][1])->toBeArray()
->and($createdAtFilter['form'][1]['name'])->toBe('created_at_before')
->and($createdAtFilter['form'][1]['type'])->toBe('datetime');
});

it('can extract bulk actions through the tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;
$resource = app(TestUserResource::class);

$tableSchema = $tool->extractTableSchema($resource);
Expand All @@ -57,7 +85,7 @@
});

it('can extract resource relationships through the tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;
$resource = app(TestUserWithRelationsResource::class);

$relationships = $tool->extractRelationshipsInfo($resource);
Expand All @@ -75,7 +103,7 @@
});

it('can describe a complete resource using the tool', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;

$result = $tool->describe(TestUserWithRelationsResource::class);

Expand All @@ -95,7 +123,7 @@
});

it('can extract different relationship types including belongsTo', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;
$resource = app(TestPostWithRelationsResource::class);

$relationships = $tool->extractRelationshipsInfo($resource);
Expand All @@ -113,7 +141,7 @@
});

it('can describe a resource with mixed relationship types', function () {
$tool = new DescribeFilamentResourceTool();
$tool = new DescribeFilamentResourceTool;

$result = $tool->describe(TestPostWithRelationsResource::class);

Expand All @@ -127,4 +155,4 @@
expect($relationships)->toHaveKeys(['comments', 'category'])
->and($relationships['comments']['type'])->toBe('HasMany')
->and($relationships['category']['type'])->toBe('BelongsTo');
});
});
2 changes: 1 addition & 1 deletion tests/Feature/TestCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ public function posts(): HasMany
{
return $this->hasMany(TestPost::class);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestCategoryRelationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ public function table(Table $table): Table
]),
]);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestComment.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ public function user(): BelongsTo
{
return $this->belongsTo(TestUser::class);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestCommentsRelationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ public function table(Table $table): Table
]),
]);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestPost.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ public function category(): BelongsTo
{
return $this->belongsTo(TestCategory::class);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestPostWithRelationsResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ public static function getRelations(): array
TestCategoryRelationManager::class,
];
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestPostsRelationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ public function table(Table $table): Table
]),
]);
}
}
}
2 changes: 1 addition & 1 deletion tests/Feature/TestUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ public function comments(): HasMany
{
return $this->hasMany(TestComment::class);
}
}
}
Loading