From 67a6425d6e117dc819d67a05526a82683968c7c6 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Wed, 27 Nov 2024 09:32:52 -0400 Subject: [PATCH 1/5] Add some cursor rules --- .cursorrules | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 .cursorrules diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000000..cec4248a17 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,115 @@ +Vue 2 and Laravel Best Practices and Guidelines + +Vue 2 Rules + +You are an expert in TypeScript, Node.js, NuxtJS, Vue 2, Shadcn Vue, Radix Vue, VueUse, and Tailwind CSS. + +Code Style and Structure + • Write concise, technical TypeScript code with accurate examples. + • Use Vue 2’s Options API for component definitions; prioritize composition patterns using plugins like vue-composition-api where necessary. + • Prefer iteration and modularization over duplication. + • Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError). + • Structure files: exported component, helpers, static content, and types. + +Naming Conventions + • Use lowercase with dashes for directories (e.g., components/auth-wizard). + • Use PascalCase for component names (e.g., AuthWizard.vue). + • Use camelCase for composables and helpers (e.g., useAuthState.ts). + +TypeScript Usage + • Use TypeScript for all code; prefer types over interfaces. + • Avoid enums; use const objects instead. + • Use Vue 2 with TypeScript leveraging class-style components or vue-property-decorator. + +Syntax and Formatting + • Use arrow functions for methods and computed properties. + • Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements. + • Use template syntax for declarative rendering. + +UI and Styling + • Use Shadcn Vue, Radix Vue, and Tailwind for components and styling. + • Implement responsive design with Tailwind CSS using a mobile-first approach. + +Performance Optimization + • Leverage Nuxt 2’s built-in performance optimizations. + • Use dynamic imports and lazy loading for routes and components. + • Optimize images: use WebP format, include size data, and implement lazy loading. + +Key Conventions + • Use VueUse for common composables and utilities. + • Use Vuex for state management in Vue 2. + • Optimize Web Vitals (LCP, CLS, FID). + • Utilize Nuxt’s auto-imports feature for components and utilities. + +Nuxt-Specific Guidelines + • Follow Nuxt 2 directory structure (e.g., pages/, components/, plugins/). + • Use Nuxt’s built-in features: + • File-based routing in the pages/ directory. + • Plugins for global functionality. + • Modules for third-party integration. + • Use asyncData and fetch hooks for data fetching. + • Implement SEO best practices using Nuxt’s head() method or vue-meta. + +Laravel Rules + +You are an expert in Laravel, PHP, and related web development technologies. + +Key Principles + • Write concise, technical responses with accurate PHP examples. + • Follow Laravel best practices and conventions. + • Use object-oriented programming with a focus on SOLID principles. + • Prefer iteration and modularization over duplication. + • Use descriptive variable and method names. + • Use lowercase with dashes for directories (e.g., app/Http/Controllers). + • Favor dependency injection and service containers. + +PHP/Laravel Practices + • Use PHP 8.1+ features when appropriate (e.g., typed properties, match expressions). + • Follow PSR-12 coding standards. + • Use strict typing: declare(strict_types=1); + • Utilize Laravel’s built-in features and helpers. + • Implement error handling with: + • Laravel’s exception handling and logging. + • Custom exceptions where necessary. + • try-catch blocks for expected exceptions. + • Use Laravel’s validation for form and request inputs. + • Implement middleware for request filtering and modification. + • Utilize Laravel’s Eloquent ORM for database operations. + • Use Laravel’s query builder for complex queries. + • Implement proper database migrations and seeders. + +Dependencies + • Laravel (latest stable version). + • Composer for dependency management. + +Best Practices + 1. Use Eloquent ORM instead of raw SQL queries where feasible. + 2. Implement Repository pattern for the data access layer. + 3. Use Laravel’s authentication and authorization features. + 4. Utilize caching for improved performance. + 5. Implement job queues for long-running tasks. + 6. Use Laravel’s testing tools (PHPUnit, Dusk) for unit and feature tests. + 7. Implement API versioning for public APIs. + 8. Use localization for multi-language support. + 9. Ensure proper CSRF protection and security measures. + 10. Use Laravel Mix for asset compilation. + 11. Index databases for improved query performance. + 12. Use pagination for data-heavy pages. + +Key Laravel Conventions + • Follow MVC architecture. + • Use Laravel’s routing for endpoints. + • Implement request validation using FormRequest. + • Use Blade templating for views. + • Implement relationships using Eloquent. + • Use built-in authentication scaffolding. + • Apply API resource transformations. + • Use event and listener systems for decoupled logic. + • Implement database transactions for data integrity. + • Use Laravel’s scheduling features for recurring tasks. + +PHP tests + • Use PHPUnit for unit testing. + • Use Laravel's testing tools (PHPUnit, Dusk) for unit and feature tests. + • If the test requires a user, create a user using the factory ProcessMaker\Models\User and authenticate as them. + • Use `Tests\Feature\Shared\RequestHelper` for common request setup, and use `apiCall($method, $url, $data)` for API requests. From 8b2603eeb3b06a98da33211102095e3e77ba2b8d Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Wed, 27 Nov 2024 09:33:16 -0400 Subject: [PATCH 2/5] Test process variables mocked endpoint --- .../V1_1/ProcessVariableControllerTest.php | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/Feature/Api/V1_1/ProcessVariableControllerTest.php diff --git a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php new file mode 100644 index 0000000000..9de1ba228d --- /dev/null +++ b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php @@ -0,0 +1,137 @@ +user = User::factory()->create(); + $this->actingAs($this->user); + } + + + /** + * Test successful variables retrieval with pagination + */ + public function test_can_get_process_variables_with_pagination(): void + { + // Make request to the endpoint + $response = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1,2,3&page=1&per_page=15'); + + // Assert response structure and status + $response->assertStatus(200) + ->assertJsonStructure([ + 'data' => [ + '*' => [ + 'id', + 'process_id', + 'uuid', + 'data_type', + 'label', + 'name', + 'asset' => [ + 'id', + 'type', + 'name', + 'uuid', + ], + 'created_at', + 'updated_at', + ] + ], + 'meta' => [ + 'current_page', + 'from', + 'last_page', + 'path', + 'per_page', + 'to', + 'total', + ] + ]); + + // Assert pagination works correctly + $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']); + } + + /** + * Test validation for required processIds parameter + */ + public function test_process_ids_are_required(): void + { + $response = $this->apiCall('GET', '/api/1.1/processes/variables'); + + $response->assertStatus(422) + ->assertJsonValidationErrors(['processIds']); + } + + /** + * Test validation for per_page parameter + */ + public function test_per_page_validation(): void + { + $response = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1&per_page=101'); + + $response->assertStatus(422) + ->assertJsonValidationErrors(['per_page']); + } + + /** + * Test data consistency across pages + */ + public function test_pagination_consistency(): void + { + // Get first page + $firstPage = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1&page=1&per_page=5') + ->json(); + + // Get second page + $secondPage = $this->apiCall('GET', '/api/1.1/processes/variables?processIds=1&page=2&per_page=5') + ->json(); + + // 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()); + } + + /** + * Test that process IDs filter works correctly + */ + 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') + ->unique() + ->values() + ->all(); + + $this->assertEquals([1, 2], $uniqueProcessIds); + + // Since we generate 10 variables per process, total should be 20 + $this->assertEquals(20, $responseData['meta']['total']); + } +} From bcc23f56f7701b00f0271e1e0d115d32073395dc Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Wed, 27 Nov 2024 09:43:42 -0400 Subject: [PATCH 3/5] Implement process variables preliminar endpoint --- .../Api/V1_1/ProcessVariableController.php | 148 ++++++++++++++++++ routes/v1_1/api.php | 8 + 2 files changed, 156 insertions(+) create mode 100644 ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php new file mode 100644 index 0000000000..98ec59e5a0 --- /dev/null +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -0,0 +1,148 @@ +validate([ + 'processIds' => 'required|string', + 'page' => 'sometimes|integer|min:1', + 'per_page' => 'sometimes|integer|min:1|max:100', + ]); + + // Parse process IDs + $processIds = array_map('intval', explode(',', $validated['processIds'])); + $perPage = $validated['per_page'] ?? 20; + $page = $validated['page'] ?? 1; + + // Generate mock data + $mockData = $this->generateMockData($processIds); + + // Create paginator + $paginator = new LengthAwarePaginator( + $mockData->forPage($page, $perPage), + $mockData->count(), + $perPage, + $page, + ['path' => $request->url()] + ); + + return response()->json([ + 'data' => $paginator->items(), + 'meta' => [ + 'current_page' => $paginator->currentPage(), + 'from' => $paginator->firstItem(), + 'last_page' => $paginator->lastPage(), + 'path' => $paginator->path(), + 'per_page' => $paginator->perPage(), + 'to' => $paginator->lastItem(), + 'total' => $paginator->total(), + 'links' => [ + 'first' => $paginator->url(1), + 'last' => $paginator->url($paginator->lastPage()), + 'prev' => $paginator->previousPageUrl(), + 'next' => $paginator->nextPageUrl(), + ] + ] + ]); + } + + private function generateMockData(array $processIds): Collection + { + // Create a cache key based on process IDs + $cacheKey = 'process_variables_' . implode('_', $processIds); + + // Try to get variables from cache first + $variables = Cache::remember($cacheKey, now()->addHours(24), 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(), + 'data_type' => $this->getRandomDataType(), + 'label' => "Variable {$i} for Process {$processId}", + 'name' => "var_{$processId}_{$i}", + 'asset' => [ + 'id' => "asset_{$processId}_{$i}", + 'type' => $this->getRandomAssetType(), + 'name' => "Asset {$i} for Process {$processId}", + 'uuid' => (string) Str::uuid(), + ], + 'created_at' => now()->toIso8601String(), + 'updated_at' => now()->toIso8601String(), + ]); + } + } + + return $variables; + }); + + return $variables; + } + + private function getRandomDataType(): string + { + return collect(['string', 'number', 'boolean', 'array'])->random(); + } + + private function getRandomAssetType(): string + { + return collect(['sensor', 'actuator', 'controller', 'device'])->random(); + } +} \ No newline at end of file diff --git a/routes/v1_1/api.php b/routes/v1_1/api.php index a2b2328dc9..d3d914039f 100644 --- a/routes/v1_1/api.php +++ b/routes/v1_1/api.php @@ -3,6 +3,7 @@ use Illuminate\Support\Facades\Route; use ProcessMaker\Http\Controllers\Api\V1_1\CaseController; use ProcessMaker\Http\Controllers\Api\V1_1\ClipboardController; +use ProcessMaker\Http\Controllers\Api\V1_1\ProcessVariableController; use ProcessMaker\Http\Controllers\Api\V1_1\TaskController; // Define the prefix and name for version 1.1 of the API routes @@ -60,4 +61,11 @@ Route::post('/create_or_update', [ClipboardController::class, 'createOrUpdateForUser']) ->name('clipboard.createOrUpdateForUser'); }); + + // Process Variables Endpoints + Route::name('process_variables.')->prefix('processes/variables')->group(function () { + // Route to list process variables + Route::get('/', [ProcessVariableController::class, 'index']) + ->name('index'); + }); }); From a807fe142dc5755f68f1449f9f8616d795e224b3 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Wed, 27 Nov 2024 09:45:53 -0400 Subject: [PATCH 4/5] Remove extra line --- tests/Feature/Api/V1_1/ProcessVariableControllerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php index 9de1ba228d..f0bc648f5c 100644 --- a/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php +++ b/tests/Feature/Api/V1_1/ProcessVariableControllerTest.php @@ -21,7 +21,6 @@ public function setupCreateUser() $this->actingAs($this->user); } - /** * Test successful variables retrieval with pagination */ From 7a521946e32d98a6b1d019719735f03114351328 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Thu, 28 Nov 2024 10:01:55 -0400 Subject: [PATCH 5/5] Fix swagger definition for endpoint --- .../Api/V1_1/ProcessVariableController.php | 46 ++++++++++++++++++- config/l5-swagger.php | 1 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index 98ec59e5a0..52b5e937c9 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -15,10 +15,52 @@ class ProcessVariableController extends Controller { /** + * @OA\Schema( + * schema="Variable", + * type="object", + * @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="label", type="string", example="Variable 1 for Process 1"), + * @OA\Property(property="name", type="string", example="var_1_1"), + * @OA\Property( + * property="asset", + * type="object", + * @OA\Property(property="id", type="string", example="asset_1_1"), + * @OA\Property(property="type", type="string", enum={"sensor", "actuator", "controller", "device"}, example="sensor"), + * @OA\Property(property="name", type="string", example="Asset 1 for Process 1"), + * @OA\Property(property="uuid", type="string", format="uuid", example="550e8400-e29b-41d4-a716-446655440000") + * ), + * @OA\Property(property="created_at", type="string", format="date-time"), + * @OA\Property(property="updated_at", type="string", format="date-time") + * ) + * @OA\Schema( + * schema="PaginationMeta", + * type="object", + * @OA\Property(property="current_page", type="integer", example=1), + * @OA\Property(property="from", type="integer", example=1), + * @OA\Property(property="last_page", type="integer", example=5), + * @OA\Property(property="path", type="string", example="http://processmaker.com/processes/variables"), + * @OA\Property(property="per_page", type="integer", example=20), + * @OA\Property(property="to", type="integer", example=20), + * @OA\Property(property="total", type="integer", example=100), + * @OA\Property( + * property="links", + * type="object", + * @OA\Property(property="first", type="string", example="http://processmaker.com/processes/variables?page=1"), + * @OA\Property(property="last", type="string", example="http://processmaker.com/processes/variables?page=5"), + * @OA\Property(property="prev", type="string", nullable=true), + * @OA\Property(property="next", type="string", example="http://processmaker.com/processes/variables?page=2") + * ) + * ) * @OA\Get( - * path="/api/1.1/processes/variables", + * path="/processes/variables", * summary="Get variables for multiple processes with pagination", - * tags={"Processes"}, + * servers={ + * @OA\Server(url=L5_SWAGGER_API_V1_1, description="API v1.1 Server") + * }, + * tags={"Processes Variables"}, * @OA\Parameter( * name="processIds", * in="query", diff --git a/config/l5-swagger.php b/config/l5-swagger.php index de1a79a3a8..22df63a6cc 100644 --- a/config/l5-swagger.php +++ b/config/l5-swagger.php @@ -266,6 +266,7 @@ */ 'constants' => [ 'L5_SWAGGER_CONST_HOST' => env('L5_SWAGGER_CONST_HOST', 'http://my-default-host.com'), + 'L5_SWAGGER_API_V1_1' => env('APP_URL', 'http://localhost') . '/api/1.1', ], ], ];