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
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => $baseDir . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php',
'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => $baseDir . '/../lib/Migration/Version2000Date20190808074233.php',
'OCA\\WorkflowEngine\\Migration\\Version2200Date20210805101925' => $baseDir . '/../lib/Migration/Version2200Date20210805101925.php',
'OCA\\WorkflowEngine\\Migration\\Version3000Date20260531150003' => $baseDir . '/../lib/Migration/Version3000Date20260531150003.php',
'OCA\\WorkflowEngine\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
'OCA\\WorkflowEngine\\Service\\Logger' => $baseDir . '/../lib/Service/Logger.php',
'OCA\\WorkflowEngine\\Service\\RuleMatcher' => $baseDir . '/../lib/Service/RuleMatcher.php',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ComposerStaticInitWorkflowEngine
'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => __DIR__ . '/..' . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php',
'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => __DIR__ . '/..' . '/../lib/Migration/Version2000Date20190808074233.php',
'OCA\\WorkflowEngine\\Migration\\Version2200Date20210805101925' => __DIR__ . '/..' . '/../lib/Migration/Version2200Date20210805101925.php',
'OCA\\WorkflowEngine\\Migration\\Version3000Date20260531150003' => __DIR__ . '/..' . '/../lib/Migration/Version3000Date20260531150003.php',
'OCA\\WorkflowEngine\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
'OCA\\WorkflowEngine\\Service\\Logger' => __DIR__ . '/..' . '/../lib/Service/Logger.php',
'OCA\\WorkflowEngine\\Service\\RuleMatcher' => __DIR__ . '/..' . '/../lib/Service/RuleMatcher.php',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public function show(string $id): DataResponse {
* @param string $operation Operation class to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow created
Expand All @@ -109,10 +110,11 @@ public function create(
string $operation,
string $entity,
array $events,
string $description = '',
): DataResponse {
$context = $this->getScopeContext();
try {
$operation = $this->manager->addOperation($class, $name, $checks, $operation, $context, $entity, $events);
$operation = $this->manager->addOperation($class, $name, $checks, $operation, $context, $entity, $events, $description);
$operation = $this->manager->formatOperation($operation);
return new DataResponse($operation);
} catch (\UnexpectedValueException $e) {
Expand All @@ -134,6 +136,7 @@ public function create(
* @param string $operation Operation action to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow updated
Expand All @@ -149,10 +152,11 @@ public function update(
string $operation,
string $entity,
array $events,
string $description = '',
): DataResponse {
try {
$context = $this->getScopeContext();
$operation = $this->manager->updateOperation($id, $name, $checks, $operation, $context, $entity, $events);
$operation = $this->manager->updateOperation($id, $name, $checks, $operation, $context, $entity, $events, $description);
$operation = $this->manager->formatOperation($operation);
return new DataResponse($operation);
} catch (\UnexpectedValueException $e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function show(string $id): DataResponse {
* @param string $operation Operation class to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow created
Expand All @@ -74,8 +75,8 @@ public function show(string $id): DataResponse {
#[\Override]
#[PasswordConfirmationRequired]
#[ApiRoute(verb: 'POST', url: '/api/v1/workflows/global')]
public function create(string $class, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
return parent::create($class, $name, $checks, $operation, $entity, $events);
public function create(string $class, string $name, array $checks, string $operation, string $entity, array $events, string $description = ''): DataResponse {
return parent::create($class, $name, $checks, $operation, $entity, $events, $description);
}

/**
Expand All @@ -87,6 +88,7 @@ public function create(string $class, string $name, array $checks, string $opera
* @param string $operation Operation action to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow updated
Expand All @@ -97,8 +99,8 @@ public function create(string $class, string $name, array $checks, string $opera
#[\Override]
#[PasswordConfirmationRequired]
#[ApiRoute(verb: 'PUT', url: '/api/v1/workflows/global/{id}')]
public function update(int $id, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
return parent::update($id, $name, $checks, $operation, $entity, $events);
public function update(int $id, string $name, array $checks, string $operation, string $entity, array $events, string $description = ''): DataResponse {
return parent::update($id, $name, $checks, $operation, $entity, $events, $description);
}

/**
Expand Down
10 changes: 6 additions & 4 deletions apps/workflowengine/lib/Controller/UserWorkflowsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public function show(string $id): DataResponse {
* @param string $operation Operation class to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow created
Expand All @@ -93,8 +94,8 @@ public function show(string $id): DataResponse {
#[NoAdminRequired]
#[PasswordConfirmationRequired]
#[ApiRoute(verb: 'POST', url: '/api/v1/workflows/user')]
public function create(string $class, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
return parent::create($class, $name, $checks, $operation, $entity, $events);
public function create(string $class, string $name, array $checks, string $operation, string $entity, array $events, string $description = ''): DataResponse {
return parent::create($class, $name, $checks, $operation, $entity, $events, $description);
}

/**
Expand All @@ -106,6 +107,7 @@ public function create(string $class, string $name, array $checks, string $opera
* @param string $operation Operation action to execute on match
* @param string $entity The matched entity
* @param list<class-string<IEntityEvent>> $events The list of events on which the rule should be validated
* @param string $description Optional free-text description of the workflow rule
* @return DataResponse<Http::STATUS_OK, WorkflowEngineRule, array{}>
*
* 200: Workflow updated
Expand All @@ -117,8 +119,8 @@ public function create(string $class, string $name, array $checks, string $opera
#[NoAdminRequired]
#[PasswordConfirmationRequired]
#[ApiRoute(verb: 'PUT', url: '/api/v1/workflows/user/{id}')]
public function update(int $id, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
return parent::update($id, $name, $checks, $operation, $entity, $events);
public function update(int $id, string $name, array $checks, string $operation, string $entity, array $events, string $description = ''): DataResponse {
return parent::update($id, $name, $checks, $operation, $entity, $events, $description);
}

/**
Expand Down
31 changes: 26 additions & 5 deletions apps/workflowengine/lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
* @psalm-import-type WorkflowEngineRule from ResponseDefinitions
*/
class Manager implements IManager {
public const MAX_NAME_BYTES = 256;
public const MAX_DESCRIPTION_BYTES = 4000;

/** @var array<string, array<string, array<int, WorkflowEngineCheck>>> */
protected array $operations = [];

Expand Down Expand Up @@ -320,12 +323,14 @@ protected function insertOperation(
string $operation,
string $entity,
array $events,
string $description = '',
): int {
$query = $this->connection->getQueryBuilder();
$query->insert('flow_operations')
->values([
'class' => $query->createNamedParameter($class),
'name' => $query->createNamedParameter($name),
'description' => $query->createNamedParameter($description),
'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
'operation' => $query->createNamedParameter($operation),
'entity' => $query->createNamedParameter($entity),
Expand Down Expand Up @@ -452,6 +457,7 @@ public function addRuntimeOperation(
* @param string $name
* @param list<WorkflowEngineCheck> $checks
* @param string $operation
* @param string $description optional free-text description shown in the UI
* @return array The added operation
* @throws \UnexpectedValueException
* @throws Exception
Expand All @@ -464,8 +470,9 @@ public function addOperation(
ScopeContext $scope,
string $entity,
array $events,
string $description = '',
) {
$this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
$this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events, $description);

$this->connection->beginTransaction();

Expand All @@ -475,7 +482,7 @@ public function addOperation(
$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
}

$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events, $description);
$this->addScope($id, $scope);

$this->connection->commit();
Expand Down Expand Up @@ -520,6 +527,7 @@ protected function canModify(int $id, ScopeContext $scopeContext):bool {
* @param string $name
* @param list<WorkflowEngineCheck> $checks
* @param string $operation
* @param string $description optional free-text description shown in the UI
* @return array The updated operation
* @throws \UnexpectedValueException
* @throws \DomainException
Expand All @@ -533,12 +541,13 @@ public function updateOperation(
ScopeContext $scopeContext,
string $entity,
array $events,
string $description = '',
): array {
if (!$this->canModify($id, $scopeContext)) {
throw new \DomainException('Target operation not within scope');
};
$row = $this->getOperation($id);
$this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
$this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events, $description);

$checkIds = [];
try {
Expand All @@ -550,6 +559,7 @@ public function updateOperation(
$query = $this->connection->getQueryBuilder();
$query->update('flow_operations')
->set('name', $query->createNamedParameter($name))
->set('description', $query->createNamedParameter($description))
->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
->set('operation', $query->createNamedParameter($operation))
->set('entity', $query->createNamedParameter($entity))
Expand Down Expand Up @@ -645,11 +655,19 @@ protected function validateEvents(string $entity, array $events, IOperation $ope
* @param array $events
* @throws \UnexpectedValueException
*/
public function validateOperation(string $class, string $name, array $checks, string $operation, ScopeContext $scope, string $entity, array $events): void {
public function validateOperation(string $class, string $name, array $checks, string $operation, ScopeContext $scope, string $entity, array $events, string $description = ''): void {
if (strlen($operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
}

if (strlen($name) > self::MAX_NAME_BYTES) {
throw new \UnexpectedValueException($this->l->t('The provided name is too long'));
}

if (strlen($description) > self::MAX_DESCRIPTION_BYTES) {
throw new \UnexpectedValueException($this->l->t('The provided description is too long'));
}

/** @psalm-suppress TaintedCallable newInstance is not called */
$reflection = new \ReflectionClass($class);
if ($class !== IOperation::class && !in_array(IOperation::class, $reflection->getInterfaceNames())) {
Expand Down Expand Up @@ -810,7 +828,7 @@ protected function addScope(int $operationId, ScopeContext $scope): void {
}

/**
* @param array{class: class-string<\OCP\WorkflowEngine\IOperation>, entity: class-string<\OCP\WorkflowEngine\IEntity>, checks: string, events: string, id: int, name: string, operation: string} $operation
* @param array{class: class-string<\OCP\WorkflowEngine\IOperation>, entity: class-string<\OCP\WorkflowEngine\IEntity>, checks: string, events: string, id: int, name: string, description: ?string, operation: string} $operation
* @return WorkflowEngineRule
*/
public function formatOperation(array $operation): array {
Expand All @@ -823,6 +841,9 @@ public function formatOperation(array $operation): array {
$events = json_decode($operation['events'], true) ?? [];
$operation['events'] = $events;

$operation['name'] = (string)($operation['name'] ?? '');
$operation['description'] = (string)($operation['description'] ?? '');

return $operation;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\WorkflowEngine\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;

/**
* Adds an optional description column to flow_operations
*/
class Version3000Date20260531150003 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
$schema = $schemaClosure();

if (!$schema->hasTable('flow_operations')) {
return null;
}

$table = $schema->getTable('flow_operations');
if (!$table->hasColumn('description')) {
$table->addColumn('description', Types::TEXT, [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the description is limited in length, shouldn't it be a Types::STRING with a 'length' => 4000 like the name column?

Copy link
Copy Markdown
Author

@letmefixthiscode letmefixthiscode Jun 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look here:

// Adjust STRING columns with a length higher than 4000 to TEXT (clob)
>4000 is converted to text (so yeah i have 4000 not >4000 - so should i use Types::STRING or remove the 4000 limit?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @Altahrim has an idea of what's the best solution

'notnull' => false,
'default' => null,
]);
}

return $schema;
}
}
1 change: 1 addition & 0 deletions apps/workflowengine/lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* id: int,
* class: class-string<IOperation>,
* name: string,
* description: string,
* checks: list<WorkflowEngineCheck>,
* operation: string,
* entity: class-string<IEntity>,
Expand Down
14 changes: 14 additions & 0 deletions apps/workflowengine/openapi-administration.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"id",
"class",
"name",
"description",
"checks",
"operation",
"entity",
Expand All @@ -106,6 +107,9 @@
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"checks": {
"type": "array",
"items": {
Expand Down Expand Up @@ -315,6 +319,11 @@
"type": "string",
"minLength": 1
}
},
"description": {
"type": "string",
"default": "",
"description": "Optional free-text description of the workflow rule"
}
}
}
Expand Down Expand Up @@ -640,6 +649,11 @@
"type": "string",
"minLength": 1
}
},
"description": {
"type": "string",
"default": "",
"description": "Optional free-text description of the workflow rule"
}
}
}
Expand Down
Loading