Skip to content
Open
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
58 changes: 57 additions & 1 deletion ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
use ProcessMaker\Models\ProcessRequestToken;
use ProcessMaker\Models\User;
use ProcessMaker\ProcessTranslations\TranslationManager;
use ProcessMaker\Repositories\TaskV11ShowFastRepository;
use ProcessMaker\Traits\TaskControllerIndexMethods;
use ProcessMaker\Services\TaskCompletionRawService;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;


class TaskController extends Controller
{
use TaskControllerIndexMethods;
Expand Down Expand Up @@ -163,11 +163,67 @@ private function addSubprocessTasks(Request $request, Builder &$q)

public function show(ProcessRequestToken $task)
{
if (config('app.optimized_tasks_start_enabled')) {
return $this->showFast($task);
}

$resource = TaskResource::preprocessInclude(request(), ProcessRequestToken::where('id', $task->id));

return $resource->toArray(request());
}

public function showFast(ProcessRequestToken $task)
{
$httpRequest = request();
$this->removeDataFromIncludeQuery($httpRequest);

$repository = app(TaskV11ShowFastRepository::class);
$token = $repository->findForShow($task->id);

if ($this->queryIncludesRequestData($httpRequest)) {
$repository->hydrateProcessRequestData($token);
}

$resource = TaskResource::fromTokenWithIncludes($token, $httpRequest);

return $resource->toArray($httpRequest);
}

private function removeDataFromIncludeQuery(Request $request): void
{
$include = $request->query('include');
if (!is_string($include) || $include === '') {
return;
}

$parts = array_values(array_filter(
array_map('trim', explode(',', $include)),
static fn (string $part) => $part !== '' && $part !== 'data'
));

if ($parts === []) {
$request->query->remove('include');
} else {
$request->query->set('include', implode(',', $parts));
}
}

private function queryIncludesRequestData(Request $request): bool
{
$include = $request->query('include', '');
if (!is_string($include) || $include === '') {
return false;
}

foreach (array_map('trim', explode(',', $include)) as $part) {
if ($part === 'requestData') {
return true;
}
}

return false;
}

public function showScreen($taskId)
{
$task = ProcessRequestToken::select(
Expand Down
39 changes: 39 additions & 0 deletions ProcessMaker/Http/Resources/V1_1/TaskResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,45 @@ public static function preprocessInclude(Request $request, Builder $query): self
return new static($model);
}

/**
* Build a resource for an already-loaded token (fast show path).
*/
public static function fromTokenWithIncludes(ProcessRequestToken $token, Request $request): self
{
$include = $request->query('include', []);
if ($include) {
$include = explode(',', $include);
}
$include = array_merge($include, self::$defaultIncludes);
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

foreach (self::$includeMethods as $key) {
if (!in_array($key, $include)) {
continue;
}

if ($key === 'user' && $token->relationLoaded('user')) {
continue;
}
if ($key === 'processRequest' && $token->relationLoaded('processRequest')) {
continue;
}
if ($key === 'process' && $token->relationLoaded('process')) {
continue;
}
if ($key === 'draft' && $token->relationLoaded('draft')) {
continue;
}
if ($key === 'requestor' && $token->relationLoaded('processRequest')
&& $token->processRequest->relationLoaded('user')) {
continue;
}

self::addRelationship($token, $key);
}

return new static($token);
}

private function processInclude(Request $request, array $array)
{
$include = $request->query('include', []);
Expand Down
91 changes: 91 additions & 0 deletions ProcessMaker/Repositories/TaskV11ShowFastRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace ProcessMaker\Repositories;

use Illuminate\Support\Facades\DB;
use ProcessMaker\Models\Process;
use ProcessMaker\Models\ProcessRequest;
use ProcessMaker\Models\ProcessRequestToken;
use ProcessMaker\Models\User;

class TaskV11ShowFastRepository
{
private const USER_COLUMNS = [
'id', 'uuid', 'email', 'firstname', 'lastname', 'username', 'status',
'address', 'city', 'state', 'postal', 'country', 'phone', 'fax', 'cell',
'title', 'birthdate', 'timezone', 'datetime_format', 'language', 'meta',
'is_administrator', 'expires_at', 'loggedin_at', 'created_at', 'updated_at',
'delegation_user_id', 'manager_id', 'schedule', 'avatar',
];

public function findForShow(int $taskId): ProcessRequestToken
{
$token = ProcessRequestToken::query()->where('id', $taskId)->firstOrFail();

$row = DB::selectOne(
'SELECT id, uuid, process_id, user_id, parent_request_id, status, do_not_sanitize, errors,
completed_at, initiated_at, created_at, updated_at, case_title, case_number, callable_id,
process_version_id, name
FROM process_requests
WHERE id = ?
LIMIT 1',
[$token->process_request_id]
);

$processRequest = new ProcessRequest();
$processRequest->setRawAttributes((array) $row, true);
$processRequest->exists = true;
$token->setRelation('processRequest', $processRequest);

if ($token->user_id) {
$user = User::query()->select(self::USER_COLUMNS)->find($token->user_id);
$token->setRelation('user', $user);
}

if ($processRequest->user_id) {
$requestor = User::query()->select(self::USER_COLUMNS)->find($processRequest->user_id);
$processRequest->setRelation('user', $requestor);
}
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

if ($token->process_id) {
$process = Process::query()->select(['id', 'name'])->find($token->process_id);
$token->setRelation('process', $process);
}

$token->load(['draft' => function ($query) {
$query->select(['id', 'task_id', 'data']);
}]);

return $token;
}

/**
* Load process_requests.data onto the lean processRequest relation (fast show only).
*/
public function hydrateProcessRequestData(ProcessRequestToken $token): void
{
$processRequest = $token->processRequest;
if (!$processRequest || !$processRequest->getKey()) {
return;
}

if (array_key_exists('data', $processRequest->getAttributes())) {
return;
}

$raw = DB::table('process_requests')
->where('id', $processRequest->getKey())
->value('data');

$data = [];
if (is_array($raw)) {
$data = $raw;
} elseif (is_string($raw)) {
$data = json_decode($raw, true) ?? [];
}

$processRequest->setAttribute('data', $data);
}
}
4 changes: 3 additions & 1 deletion ProcessMaker/Traits/TaskResourceIncludes.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ private function includeDefinition()

private function includeBpmnTagName()
{
return ['bpmn_tag_name' => $this->getBpmnDefinition()->localName];
$definition = $this->getBpmnDefinition();

return ['bpmn_tag_name' => $definition ? $definition->localName : null];
}

private function includeProcess()
Expand Down
3 changes: 3 additions & 0 deletions config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
// The timeout length for API calls, in milliseconds (0 for no timeout)
'api_timeout' => env('API_TIMEOUT', 5000),

// Optimized V1.1 task show (case start): lean load + skips duplicate request data include
'optimized_tasks_start_enabled' => env('OPTIMIZED_TASKS_START_ENABLED', false),

// Hide server headers for security (prevents information disclosure)
'hide_server_headers' => env('HIDE_SERVER_HEADERS', true),

Expand Down
Loading
Loading