From 4034ac84ac50d15a151703ed6b1cfbc00c814f5e Mon Sep 17 00:00:00 2001 From: tomvvd Date: Thu, 16 May 2024 15:17:39 +0200 Subject: [PATCH] add classes and controllers for pipeline --- packages/core/classes/pipeline/Node.class.php | 67 +++++++++++ .../core/classes/pipeline/NodeLink.class.php | 40 +++++++ .../core/classes/pipeline/Parameter.class.php | 34 ++++++ .../core/classes/pipeline/Pipeline.class.php | 31 +++++ .../pipeline/PipelineExecution.class.php | 30 +++++ .../pipeline/PipelineNodeExecution.class.php | 40 +++++++ packages/core/data/check-pipeline.php | 96 ++++++++++++++++ packages/core/data/pipeline/test-divide.php | 49 ++++++++ packages/core/data/pipeline/test-sum-list.php | 42 +++++++ packages/core/data/pipeline/test-sum.php | 45 ++++++++ packages/core/data/run-pipeline.php | 107 ++++++++++++++++++ 11 files changed, 581 insertions(+) create mode 100644 packages/core/classes/pipeline/Node.class.php create mode 100644 packages/core/classes/pipeline/NodeLink.class.php create mode 100644 packages/core/classes/pipeline/Parameter.class.php create mode 100644 packages/core/classes/pipeline/Pipeline.class.php create mode 100644 packages/core/classes/pipeline/PipelineExecution.class.php create mode 100644 packages/core/classes/pipeline/PipelineNodeExecution.class.php create mode 100644 packages/core/data/check-pipeline.php create mode 100644 packages/core/data/pipeline/test-divide.php create mode 100644 packages/core/data/pipeline/test-sum-list.php create mode 100644 packages/core/data/pipeline/test-sum.php create mode 100644 packages/core/data/run-pipeline.php diff --git a/packages/core/classes/pipeline/Node.class.php b/packages/core/classes/pipeline/Node.class.php new file mode 100644 index 000000000..be917891d --- /dev/null +++ b/packages/core/classes/pipeline/Node.class.php @@ -0,0 +1,67 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU GPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class Node extends Model +{ + + public static function getColumns() + { + return [ + 'name' => [ + 'type' => 'string', + 'required' => true + ], + + 'description' => [ + 'type' => 'string' + ], + + 'pipeline_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Pipeline', + 'required' => true + ], + + 'out_links_ids' => [ + 'type' => 'one2many', + 'foreign_object' => 'core\pipeline\NodeLink', + 'foreign_field' => 'source_node_id' + ], + + 'in_links_ids' => [ + 'type' => 'one2many', + 'foreign_object' => 'core\pipeline\NodeLink', + 'foreign_field' => 'target_node_id' + ], + + 'operation_controller' => [ + 'type' => 'string' + ], + + 'operation_type' => [ + 'type' => 'string' + ], + + 'params_ids' => [ + 'type' => 'one2many', + 'foreign_object' => 'core\pipeline\Parameter', + 'foreign_field' => 'node_id' + ] + ]; + } + + public function getUnique() + { + return [ + ['name', 'pipeline_id'] + ]; + } +} diff --git a/packages/core/classes/pipeline/NodeLink.class.php b/packages/core/classes/pipeline/NodeLink.class.php new file mode 100644 index 000000000..551c7c82e --- /dev/null +++ b/packages/core/classes/pipeline/NodeLink.class.php @@ -0,0 +1,40 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU GPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class NodeLink extends Model +{ + + public static function getColumns() + { + return [ + 'reference_node_id' => [ + 'type' => 'integer', + 'required' => true + ], + + 'source_node_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Node', + 'required' => true + ], + + 'target_node_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Node', + 'required' => true + ], + + 'target_param' => [ + 'type' => 'string' + ] + ]; + } +} diff --git a/packages/core/classes/pipeline/Parameter.class.php b/packages/core/classes/pipeline/Parameter.class.php new file mode 100644 index 000000000..29ee3d10b --- /dev/null +++ b/packages/core/classes/pipeline/Parameter.class.php @@ -0,0 +1,34 @@ + + Some Rights Reserved, Yesbabylon SRL, 2020-2021 + Licensed under GNU AGPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class Parameter extends Model +{ + + public static function getColumns() + { + return [ + 'node_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Node' + ], + + 'value' => [ + 'type' => 'string', + 'required' => true + ], + + 'param' => [ + 'type' => 'string', + 'required' => true + ] + ]; + } +} diff --git a/packages/core/classes/pipeline/Pipeline.class.php b/packages/core/classes/pipeline/Pipeline.class.php new file mode 100644 index 000000000..08fdafbde --- /dev/null +++ b/packages/core/classes/pipeline/Pipeline.class.php @@ -0,0 +1,31 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU GPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class Pipeline extends Model +{ + + public static function getColumns() + { + return [ + 'nodes_ids' => [ + 'type' => 'one2many', + 'foreign_object' => 'core\pipeline\Node', + 'foreign_field' => 'pipeline_id' + ], + + 'name' => [ + 'type' => 'string', + 'required' => true, + 'unique' => true + ], + ]; + } +} diff --git a/packages/core/classes/pipeline/PipelineExecution.class.php b/packages/core/classes/pipeline/PipelineExecution.class.php new file mode 100644 index 000000000..b4e03b80b --- /dev/null +++ b/packages/core/classes/pipeline/PipelineExecution.class.php @@ -0,0 +1,30 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU GPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class PipelineExecution extends Model +{ + + public static function getColumns() + { + return [ + + 'pipeline_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Pipeline', + 'required' => true + ], + + 'status' => [ + 'type' => 'string' + ] + ]; + } +} diff --git a/packages/core/classes/pipeline/PipelineNodeExecution.class.php b/packages/core/classes/pipeline/PipelineNodeExecution.class.php new file mode 100644 index 000000000..2264b70e5 --- /dev/null +++ b/packages/core/classes/pipeline/PipelineNodeExecution.class.php @@ -0,0 +1,40 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU GPL 3 license +*/ + +namespace core\pipeline; + +use equal\orm\Model; + +class PipelineNodeExecution extends Model +{ + + public static function getColumns() + { + return [ + + 'pipeline_execution_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\PipelineExecution', + 'required' => true + ], + + 'node_id' => [ + 'type' => 'many2one', + 'foreign_object' => 'core\pipeline\Node', + 'required' => true + ], + + 'status' => [ + 'type' => 'string' + ], + + 'result' => [ + 'type' => 'string' + ] + ]; + } +} diff --git a/packages/core/data/check-pipeline.php b/packages/core/data/check-pipeline.php new file mode 100644 index 000000000..03b41322b --- /dev/null +++ b/packages/core/data/check-pipeline.php @@ -0,0 +1,96 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ + +use core\pipeline\Pipeline; + +list($params, $providers) = eQual::announce([ + 'description' => 'Run the given pipeline.', + 'params' => [ + 'pipeline_id' => [ + 'description' => 'Pipeline\'s id', + 'type' => 'integer' + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*', + 'schema' => [ + 'type' => '', + 'qty' => '' + ] + ], + 'access' => [ + 'visibility' => 'protected' + ], + 'providers' => ['context'] +]); + +/** + * @var \equal\php\Context $context + */ +$context = $providers['context']; + +$pipeline = Pipeline::id($params['pipeline_id']) + ->read([ + 'nodes_ids' => [ + 'id', + 'in_links_ids' => ['source_node_id'], + 'out_links_ids' => ['target_node_id'] + ] + ]) + ->first(); + +$pipeline_nodes = $pipeline['nodes_ids']->get(true); + +$graph = []; + +foreach ($pipeline_nodes as $node) { + $graph[$node['id']] = []; + foreach ($node['in_links_ids'] as $link) { + $graph[$node['id']][] = $link['source_node_id']; + } + foreach ($node['out_links_ids'] as $link) { + $graph[$node['id']][] = $link['target_node_id']; + } + $graph[$node['id']] = array_unique($graph[$node['id']]); +} + +if (!isGraphConnected($graph)) { + throw new Exception('non-compliant_pipeline', QN_ERROR_UNKNOWN); +} + +$context->httpResponse() + ->body(['success' => true]) + ->send(); + +function isGraphConnected($graph) +{ + $visited = []; + $start_node = array_key_first($graph); + + depthSearch($graph, $start_node, $visited); + + foreach ($graph as $node => $adjacent_nodes) { + if (!isset($visited[$node])) { + return false; + } + } + + return true; +}; + +function depthSearch($graph, $node, &$visited) +{ + $visited[$node] = true; + + foreach ($graph[$node] as $adjacent_node) { + if (!isset($visited[$adjacent_node])) { + depthSearch($graph, $adjacent_node, $visited); + } + } +}; diff --git a/packages/core/data/pipeline/test-divide.php b/packages/core/data/pipeline/test-divide.php new file mode 100644 index 000000000..d4dedfb27 --- /dev/null +++ b/packages/core/data/pipeline/test-divide.php @@ -0,0 +1,49 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ +list($params, $providers) = eQual::announce([ + 'description' => 'Returns the division of two values.', + 'params' => [ + 'numerator' => [ + 'description' => 'Numerator', + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'required' => true + ], + 'denominator' => [ + 'description' => 'Denominator', + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'required' => true + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*', + 'schema' => [ + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'qty' => 'one' + ] + ], + 'access' => [ + 'visibility' => 'public', + ], + 'providers' => ['context'] +]); + +list($context) = [$providers['context']]; + +$result = 0; + +if ($params['denominator'] != 0) { + $result = intdiv($params['numerator'], $params['denominator']); +} + +$context->httpResponse() + ->body($result) + ->send(); diff --git a/packages/core/data/pipeline/test-sum-list.php b/packages/core/data/pipeline/test-sum-list.php new file mode 100644 index 000000000..f3bded8d3 --- /dev/null +++ b/packages/core/data/pipeline/test-sum-list.php @@ -0,0 +1,42 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ +list($params, $providers) = eQual::announce([ + 'description' => 'Returns the sum of a list of integer', + 'params' => [ + 'list' => [ + 'description' => 'list of integer', + 'type' => 'array', + 'required' => true + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*', + 'schema' => [ + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'qty' => 'one' + ] + ], + 'access' => [ + 'visibility' => 'public', + ], + 'providers' => ['context'] +]); + +list($context) = [$providers['context']]; + +$result = 0; + +foreach ($params['list'] as $element) { + $result += $element; +} + +$context->httpResponse() + ->body($result) + ->send(); diff --git a/packages/core/data/pipeline/test-sum.php b/packages/core/data/pipeline/test-sum.php new file mode 100644 index 000000000..3960bd18e --- /dev/null +++ b/packages/core/data/pipeline/test-sum.php @@ -0,0 +1,45 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ +list($params, $providers) = eQual::announce([ + 'description' => 'Returns the sum of two values.', + 'params' => [ + 'first_value' => [ + 'description' => 'First value', + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'required' => true + ], + 'second_value' => [ + 'description' => 'Second value', + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'required' => true + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*', + 'schema' => [ + 'type' => 'integer', + 'usage' => 'numeric/integer', + 'qty' => 'one' + ] + ], + 'access' => [ + 'visibility' => 'public', + ], + 'providers' => ['context'] +]); + +list($context) = [$providers['context']]; + +$result = $params['first_value'] + $params['second_value']; + +$context->httpResponse() + ->body($result) + ->send(); diff --git a/packages/core/data/run-pipeline.php b/packages/core/data/run-pipeline.php new file mode 100644 index 000000000..8d929a24e --- /dev/null +++ b/packages/core/data/run-pipeline.php @@ -0,0 +1,107 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ + +use core\pipeline\Pipeline; + +list($params, $providers) = eQual::announce([ + 'description' => 'Run the given pipeline.', + 'params' => [ + 'pipeline_id' => [ + 'description' => 'Pipeline\'s id', + 'type' => 'integer' + ] + ], + 'response' => [ + 'content-type' => 'application/json', + 'charset' => 'UTF-8', + 'accept-origin' => '*', + 'schema' => [ + 'type' => '', + 'qty' => '' + ] + ], + 'access' => [ + 'visibility' => 'protected' + ], + 'providers' => ['context'] +]); + +/** + * @var \equal\php\Context $context + */ +$context = $providers['context']; + +eQual::run("get", "core_check-pipeline", $params, true); + +$pipeline = Pipeline::id($params['pipeline_id']) + ->read([ + 'nodes_ids' => [ + 'id', + 'name', + 'operation_controller', + 'operation_type', + 'in_links_ids' => ['reference_node_id', 'source_node_id', 'target_param'], + 'out_links_ids' => ['target_node_id'], + 'params_ids' => ['value', 'param'] + ] + ]) + ->first(); + +$pipeline_nodes = $pipeline['nodes_ids']->get(true); + +$count = 0; + +$result_map = $name_map = []; + +foreach ($pipeline_nodes as $node) { + if ($node['operation_type'] != null) { + $result_map[$node['id']] = null; + $name_map[$node['id']] = $node['name']; + if (empty($node['in_links_ids'])) { + $parameters = []; + foreach ($node['params_ids'] as $param) { + $parameters[$param['param']] = json_decode($param['value']); + } + $result_map[$node['id']] = eQual::run($node['operation_type'], $node['operation_controller'], $parameters, true); + $count++; + } + } +} + +while ($count != count($result_map)) { + foreach ($pipeline_nodes as $node) { + if ($node['operation_type'] != null && $result_map[$node['id']] == null) { + $is_computable = true; + $parameters = []; + foreach ($node['in_links_ids'] as $link) { + if ($result_map[$link['reference_node_id']] != null) { + $parameters[$link['target_param']] = $result_map[$link['reference_node_id']]; + } else { + $is_computable = false; + break; + } + } + if ($is_computable) { + foreach ($node['params_ids'] as $param) { + $parameters[$param['param']] = json_decode($param['value']); + } + $result_map[$node['id']] = eQual::run($node['operation_type'], $node['operation_controller'], $parameters, true); + $count++; + } + } + } +} + +$res = []; + +foreach ($name_map as $key => $value) { + $res[$value] = $result_map[$key]; +} + +$context->httpResponse() + ->body($res) + ->send();