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
57 changes: 57 additions & 0 deletions ProcessMaker/Http/Controllers/Api/V1_1/TaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use ProcessMaker\Models\ProcessRequest;
use ProcessMaker\Models\ProcessRequestToken;
use ProcessMaker\ProcessTranslations\TranslationManager;
use ProcessMaker\Repositories\TaskV11ShowFastRepository;

class TaskController extends Controller
{
Expand Down Expand Up @@ -92,11 +93,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.

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.

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