From e38d8bed8692741445258b36a5a016ee11b8d819 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 6 Dec 2024 15:18:51 -0400 Subject: [PATCH 1/3] Update test to support Variables Finder --- .../V1_1/ProcessVariableControllerTest.php | 123 +++++++++++++++++- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php index 0a4e10bcee..e125c72318 100644 --- a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php +++ b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php @@ -2,15 +2,26 @@ namespace Tests\Feature\Api\V1_1; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Schema; use ProcessMaker\Models\User; use ProcessMaker\Package\SavedSearch\Models\SavedSearch; use Tests\TestCase; use Tests\Feature\Shared\RequestHelper; +use ProcessMaker\Package\VariableFinder\Models\ProcessVariable; +use Illuminate\Support\Str; +use ProcessMaker\Http\Controllers\Api\V1_1\ProcessVariableController; +use ProcessMaker\Models\Process; +use ProcessMaker\Models\Screen; +use ProcessMaker\Package\VariableFinder\Models\AssetVariable; +use ProcessMaker\Package\VariableFinder\Models\VarFinderVariable; class ProcessVariableControllerTest extends TestCase { use RequestHelper; + private bool $isVariablesFinderEnabled; + /** * Set up test environment by creating a test user and authenticating as them * @@ -20,6 +31,19 @@ public function setupCreateUser() { $this->user = User::factory()->create(); $this->actingAs($this->user); + + // Check if the VariableFinder package is enabled + $this->isVariablesFinderEnabled = !class_exists(ProcessVariable::class) || !Schema::hasTable('process_variables'); + + // Create the processes variables + if (!$this->isVariablesFinderEnabled) { + // Mock the ProcessVariableController to use mock data instead of VariableFinder package + ProcessVariableController::mock(); + $this->mockVariableFinder([1, 2, 3], null); + $this->mockVariableFinder([1, 2], null); + } else { + $this->loadVariableFinderData([1, 2, 3]); + } } /** @@ -37,16 +61,10 @@ public function test_can_get_process_variables_with_pagination(): void '*' => [ 'id', 'process_id', - 'uuid', 'format', 'label', 'field', - 'asset' => [ - 'id', - 'type', - 'name', - 'uuid', - ], + 'default', 'created_at', 'updated_at', ] @@ -71,6 +89,94 @@ public function test_can_get_process_variables_with_pagination(): void $this->assertEquals(30, $responseData['meta']['total']); } + private function mockVariableFinder(array $processIds, $excludeSavedSearch) + { + // Create a cache key based on process IDs + $cacheKey = 'process_variables_' . implode('_', $processIds); + if ($excludeSavedSearch) { + $cacheKey .= '_exclude_saved_search_' . $excludeSavedSearch; + } + + // Try to get variables from cache first + $variables = Cache::remember($cacheKey, now()->addSeconds(60), function () use ($processIds) { + $variables = collect(); + + foreach ($processIds as $processId) { + // Generate 10 variables per process + for ($i = 1; $i <= 10; $i++) { + $variables->push([ + 'id' => $variables->count() + 1, + 'process_id' => $processId, + 'format' => $this->getRandomDataType(), + 'label' => "Variable {$i} for Process {$processId}", + 'field' => "data.var_{$processId}_{$i}", + 'default' => null, + 'created_at' => now()->toIso8601String(), + 'updated_at' => now()->toIso8601String(), + ]); + } + } + + return $variables; + }); + + return $variables; + } + + private function getRandomDataType(): string + { + return collect(['string', 'int', 'boolean', 'array'])->random(); + } + + private function getRandomAssetType(): string + { + return collect(['sensor', 'actuator', 'controller', 'device'])->random(); + } + + private function loadVariableFinderData(array $processIds) + { + foreach ($processIds as $processId) { + $process = Process::factory()->create([ + 'id' => $processId, + ]); + // 1. Create the AssetVariable record + $asset = [ + 'type' => $this->getRandomAssetType(), + 'uuid' => (string) Str::uuid(), + ]; + $assetVariable = AssetVariable::create([ + 'uuid' => $asset['uuid'], + 'asset_id' => 1, // Scren id=1 + 'asset_type' => Screen::class, + ]); + + // 2. Create the ProcessVariable record linking to the AssetVariable + ProcessVariable::create([ + 'uuid' => (string) Str::uuid(), + 'process_id' => $processId, + 'asset_variable_id' => $assetVariable->id, + ]); + + // Generate 10 variables per process + for ($i = 1; $i <= 10; $i++) { + + // Generate data similarly to mockVariableFinder + $format = $this->getRandomDataType(); + $label = "Variable {$i} for Process {$processId}"; + $field = "data.var_{$processId}_{$i}"; + + // 3. Create the VarFinderVariable record linked to the same AssetVariable + VarFinderVariable::create([ + 'uuid' => (string) Str::uuid(), + 'asset_variable_id' => $assetVariable->id, + 'data_type' => $format, + 'label' => $label, + 'field' => $field, + ]); + } + } + } + /** * Test validation for required processIds parameter */ @@ -159,6 +265,9 @@ public function test_saved_search_id_filtering(): void ]); // Make request with savedSearchId + if (!$this->isVariablesFinderEnabled) { + $this->mockVariableFinder([1], $savedSearch->id); + } $response = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1&savedSearchId=' . $savedSearch->id); $responseData = $response->json(); From 2c9e6c12bf7427e728258d1a1be5200f7224b570 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 6 Dec 2024 15:22:00 -0400 Subject: [PATCH 2/3] Iimplement data retrival from variables finder or saved search if not --- .../Api/V1_1/ProcessVariableController.php | 155 ++++++++++++------ 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index 16310e0c06..f7729443da 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -5,16 +5,20 @@ namespace ProcessMaker\Http\Controllers\Api\V1_1; use ProcessMaker\Http\Controllers\Controller; -use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Collection; -use Illuminate\Support\Str; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; use ProcessMaker\Package\SavedSearch\Models\SavedSearch; +use ProcessMaker\Package\VariableFinder\Models\ProcessVariable; class ProcessVariableController extends Controller { + + const CACHE_TTL = 60; + private static bool $mockData = false; + /** * @OA\Schema( * schema="Variable", @@ -113,24 +117,12 @@ public function index(Request $request) $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']); - }); + if (static::$mockData) { + $paginator = $this->getProcessesVariablesFromMock($processIds, $excludeSavedSearch, $page, $perPage, $request); + } else { + $paginator = $this->getProcessesVariables($processIds, $excludeSavedSearch, $page, $perPage, $request); } - // Create paginator - $paginator = new LengthAwarePaginator( - $mockData->forPage($page, $perPage), - $mockData->count(), - $perPage, - $page, - ['path' => $request->url()] - ); - return response()->json([ 'data' => array_values($paginator->items()), 'meta' => [ @@ -151,51 +143,108 @@ public function index(Request $request) ]); } - private function generateMockData(array $processIds): Collection + /** + * Retrieve process variables from a mock source. + * + * @param array $processIds An array of process IDs to retrieve variables for. + * @param bool $excludeSavedSearch Flag to determine whether to exclude saved searches. + * @param int $page The page number for pagination. + * @param int $perPage The number of items per page for pagination. + * @param \Illuminate\Http\Request $request The HTTP request instance. + * + * @return array The list of process variables. + */ + private function getProcessesVariablesFromMock(array $processIds, $excludeSavedSearch, $page, $perPage, $request) { - // Create a cache key based on process IDs $cacheKey = 'process_variables_' . implode('_', $processIds); + if ($excludeSavedSearch) { + $cacheKey .= '_exclude_saved_search_' . $excludeSavedSearch; + } - // Try to get variables from cache first - $variables = Cache::remember($cacheKey, now()->addSeconds(5), function () use ($processIds) { - $variables = collect(); - - foreach ($processIds as $processId) { - // Generate 10 variables per process - for ($i = 1; $i <= 10; $i++) { - $variables->push([ - 'id' => $variables->count() + 1, - 'process_id' => $processId, - 'uuid' => (string) Str::uuid(), - 'format' => $this->getRandomDataType(), - 'label' => "Variable {$i} for Process {$processId}", - '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(), - ]); - } + $mockData = Cache::remember($cacheKey, now()->addSeconds(self::CACHE_TTL), function () use ($excludeSavedSearch) { + if (!$excludeSavedSearch) { + return collect(); } - return $variables; + $savedSearch = SavedSearch::find($excludeSavedSearch); + return collect(array_values($savedSearch->data_columns->toArray())); }); - return $variables; + 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 + return new LengthAwarePaginator( + $mockData->forPage($page, $perPage), + $mockData->count(), + $perPage, + $page, + ['path' => $request->url()] + ); } - private function getRandomDataType(): string + /** + * Retrieve process variables for the given process IDs. + * + * @param array $processIds Array of process IDs to retrieve variables for. + * @param bool $excludeSavedSearch Flag to exclude saved searches. + * @param int $page The page number for pagination. + * @param int $perPage The number of items per page for pagination. + * @param \Illuminate\Http\Request $request The HTTP request instance. + * @return \Illuminate\Http\JsonResponse JSON response containing the process variables. + */ + public function getProcessesVariables(array $processIds, $excludeSavedSearch, $page, $perPage, $request) { - return collect(['string', 'number', 'boolean', 'array'])->random(); + // If the classes or tables do not exist, fallback to a saved search approach. + if (!class_exists(ProcessVariable::class) || !Schema::hasTable('process_variables')) { + return $this->getProcessesVariablesFromSavedSearch($processIds); + } + + // Determine which columns to exclude based on the saved search + $activeColumns = []; + if ($excludeSavedSearch) { + $savedSearch = SavedSearch::find($excludeSavedSearch); + if ($savedSearch && $savedSearch->current_columns) { + $activeColumns = $savedSearch->current_columns->pluck('field')->toArray(); + } + } + + // Build a single query that joins process_variables, asset_variables, and var_finder_variables + // and applies filtering for excluded fields. + $query = DB::table('var_finder_variables AS vfv') + ->join('asset_variables AS av', 'vfv.asset_variable_id', '=', 'av.id') + ->join('process_variables AS pv', 'av.id', '=', 'pv.asset_variable_id') + ->whereIn('pv.process_id', $processIds); + + if (!empty($activeColumns)) { + $query->whereNotIn('vfv.field', $activeColumns); + } + + // Paginate the query result directly + return $query->select( + 'vfv.id', + 'pv.process_id', + 'vfv.data_type AS format', + 'vfv.label', + 'vfv.field', + DB::raw('NULL AS `default`'), + 'vfv.created_at', + 'vfv.updated_at', + )->paginate($perPage, ['*'], 'page', $page); } - private function getRandomAssetType(): string + /** + * Change ProcessVariableController to use mock data + * + * @return void + */ + public static function mock() { - return collect(['sensor', 'actuator', 'controller', 'device'])->random(); + static::$mockData = true; } -} \ No newline at end of file +} From 1bb0b5083d630b513bd4a4878860f7eaa77062ee Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 6 Dec 2024 16:47:15 -0400 Subject: [PATCH 3/3] Fix logic to determine if Variables Finder is enabled --- tests/Feature/Api/V1_1/ProcessVariableControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php index e125c72318..00e286bc12 100644 --- a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php +++ b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php @@ -33,7 +33,7 @@ public function setupCreateUser() $this->actingAs($this->user); // Check if the VariableFinder package is enabled - $this->isVariablesFinderEnabled = !class_exists(ProcessVariable::class) || !Schema::hasTable('process_variables'); + $this->isVariablesFinderEnabled = class_exists(ProcessVariable::class) && Schema::hasTable('process_variables'); // Create the processes variables if (!$this->isVariablesFinderEnabled) {