Skip to content

FOUR-20521 [ACTIVE COLUMNS] Optimize Rendering with Virtual Scrolling #7788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 4, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Support\Str;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;
use ProcessMaker\Package\SavedSearch\Models\SavedSearch;

class ProcessVariableController extends Controller
{
Expand All @@ -21,7 +22,7 @@ class ProcessVariableController extends Controller
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="process_id", type="integer", example=1),
* @OA\Property(property="uuid", type="string", format="uuid", example="550e8400-e29b-41d4-a716-446655440000"),
* @OA\Property(property="data_type", type="string", enum={"string", "number", "boolean", "array"}, example="string"),
* @OA\Property(property="field", type="string", enum={"string", "number", "boolean", "array"}, example="string"),
* @OA\Property(property="label", type="string", example="Variable 1 for Process 1"),
* @OA\Property(property="name", type="string", example="var_1_1"),
* @OA\Property(
Expand Down Expand Up @@ -95,22 +96,31 @@ class ProcessVariableController extends Controller
* )
* )
*/
public function index(Request $request): JsonResponse
public function index(Request $request)
{
// Validate request
$validated = $request->validate([
'processIds' => 'required|string',
'page' => 'sometimes|integer|min:1',
'per_page' => 'sometimes|integer|min:1|max:100',
'savedSearchId' => 'sometimes|string',
]);

// Parse process IDs
$processIds = array_map('intval', explode(',', $validated['processIds']));
$perPage = $validated['per_page'] ?? 20;
$page = $validated['page'] ?? 1;
$excludeSavedSearch = $validated['savedSearchId'] ?? 0;

// Generate mock data
$mockData = $this->generateMockData($processIds);
if ($excludeSavedSearch) {
$savedSearch = SavedSearch::find($excludeSavedSearch);
$columns = $savedSearch->current_columns;
$mockData = $mockData->filter(function ($variable) use ($columns) {
return !$columns->pluck('field')->contains($variable['field']);
});
}

// Create paginator
$paginator = new LengthAwarePaginator(
Expand All @@ -122,7 +132,7 @@ public function index(Request $request): JsonResponse
);

return response()->json([
'data' => $paginator->items(),
'data' => array_values($paginator->items()),
'meta' => [
'current_page' => $paginator->currentPage(),
'from' => $paginator->firstItem(),
Expand All @@ -147,7 +157,7 @@ private function generateMockData(array $processIds): Collection
$cacheKey = 'process_variables_' . implode('_', $processIds);

// Try to get variables from cache first
$variables = Cache::remember($cacheKey, now()->addHours(24), function () use ($processIds) {
$variables = Cache::remember($cacheKey, now()->addSeconds(5), function () use ($processIds) {
$variables = collect();

foreach ($processIds as $processId) {
Expand All @@ -157,15 +167,16 @@ private function generateMockData(array $processIds): Collection
'id' => $variables->count() + 1,
'process_id' => $processId,
'uuid' => (string) Str::uuid(),
'data_type' => $this->getRandomDataType(),
'format' => $this->getRandomDataType(),
'label' => "Variable {$i} for Process {$processId}",
'name' => "var_{$processId}_{$i}",
'field' => "data.var_{$processId}_{$i}",
'asset' => [
'id' => "asset_{$processId}_{$i}",
'type' => $this->getRandomAssetType(),
'name' => "Asset {$i} for Process {$processId}",
'uuid' => (string) Str::uuid(),
],
'default' => null,
'created_at' => now()->toIso8601String(),
'updated_at' => now()->toIso8601String(),
]);
Expand Down
53 changes: 46 additions & 7 deletions tests/Feature/Api/V1_1/ProcessVariableControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tests\Feature\Api\V1_1;

use ProcessMaker\Models\User;
use ProcessMaker\Package\SavedSearch\Models\SavedSearch;
use Tests\TestCase;
use Tests\Feature\Shared\RequestHelper;

Expand Down Expand Up @@ -37,9 +38,9 @@ public function test_can_get_process_variables_with_pagination(): void
'id',
'process_id',
'uuid',
'data_type',
'format',
'label',
'name',
'field',
'asset' => [
'id',
'type',
Expand All @@ -65,7 +66,7 @@ public function test_can_get_process_variables_with_pagination(): void
$responseData = $response->json();
$this->assertEquals(15, $responseData['meta']['per_page']);
$this->assertEquals(1, $responseData['meta']['current_page']);

// Since we're generating 10 variables per process (3 processes = 30 total)
$this->assertEquals(30, $responseData['meta']['total']);
}
Expand Down Expand Up @@ -108,7 +109,7 @@ public function test_pagination_consistency(): void
// Ensure no duplicate IDs between pages
$firstPageIds = collect($firstPage['data'])->pluck('id');
$secondPageIds = collect($secondPage['data'])->pluck('id');

$this->assertEquals(0, $firstPageIds->intersect($secondPageIds)->count());
}

Expand All @@ -118,9 +119,9 @@ public function test_pagination_consistency(): void
public function test_process_ids_filtering(): void
{
$response = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1,2&per_page=50');

$responseData = $response->json();

// Check that only requested process IDs are returned
$uniqueProcessIds = collect($responseData['data'])
->pluck('process_id')
Expand All @@ -129,8 +130,46 @@ public function test_process_ids_filtering(): void
->all();

$this->assertEquals([1, 2], $uniqueProcessIds);

// Since we generate 10 variables per process, total should be 20
$this->assertEquals(20, $responseData['meta']['total']);
}

/**
* Test filtering with savedSearchId parameter
*/
public function test_saved_search_id_filtering(): void
{
// Create a saved search with specific columns
$savedSearch = SavedSearch::factory()->create([
'meta' => [
'columns' => [
[
'label' => 'Variable 1',
'field' => 'data.var_1_1',
'default' => null,
],
[
'label' => 'Variable 2',
'field' => 'data.var_1_2',
'default' => null,
],
],
],
]);

// Make request with savedSearchId
$response = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1&savedSearchId=' . $savedSearch->id);

$responseData = $response->json();

// Check that the filtered variables do not include the fields from the saved search
$filteredFields = collect($responseData['data'])->pluck('field');

$this->assertFalse($filteredFields->contains('data.var_1_1'));
$this->assertFalse($filteredFields->contains('data.var_1_2'));

// Check that the total count is reduced by the number of excluded fields
$this->assertEquals(8, $responseData['meta']['total']); // 10 total - 2 excluded
}
}
Loading