Skip to content

Commit aee1373

Browse files
authored
add action and schema commit table to track all changes (#56)
* add action and schema commit table to track all changes * fix tests * add action and schema commit check * add action and schema commit table * skip commit for empty action config * add logic to persist action and schema config on operation update * use source if hash is not available * fix phpstan * add action and schema commit endpoint * fix phpstan * fix tests * improve fix action and schemas * remove not needed null check * split handle hash and add test case * add test cases * fix commit hash
1 parent 77319c6 commit aee1373

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2440
-127
lines changed

src/Action/Scheme.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,26 @@ public static function wrap(?string $actionName): ?string
5757

5858
/**
5959
* @param string $action
60-
* @return array{Scheme, string}
60+
* @return array{Scheme, string, ?string}
6161
*/
6262
public static function split(string $action): array
6363
{
64-
$pos = strpos($action, '://');
65-
if ($pos === false) {
66-
return [self::ACTION, $action];
64+
$scheme = self::ACTION->value;
65+
if (str_contains($action, '://')) {
66+
$parts = explode('://', $action);
67+
$scheme = $parts[0];
68+
$action = $parts[1];
6769
}
6870

69-
$scheme = substr($action, 0, $pos);
70-
$value = substr($action, $pos + 3);
71+
$name = $action;
72+
$hash = null;
73+
if (str_contains($name, '@')) {
74+
$parts = explode('@', $name);
75+
$name = $parts[0];
76+
$hash = $parts[1];
77+
}
7178

72-
return [self::from($scheme), $value];
79+
return [self::from($scheme), $name, $hash];
7380
}
7481

7582
private static function buildAction(string $actionName): string
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Backend\Action\Action\Commit;
22+
23+
use Fusio\Engine\ActionInterface;
24+
use Fusio\Engine\ContextInterface;
25+
use Fusio\Engine\ParametersInterface;
26+
use Fusio\Engine\RequestInterface;
27+
use Fusio\Impl\Backend\Filter\QueryFilter;
28+
use Fusio\Impl\Backend\View;
29+
use Fusio\Impl\Table;
30+
use PSX\Http\Exception\NotFoundException;
31+
32+
/**
33+
* GetAll
34+
*
35+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
36+
* @license http://www.apache.org/licenses/LICENSE-2.0
37+
* @link https://www.fusio-project.org
38+
*/
39+
readonly class GetAll implements ActionInterface
40+
{
41+
public function __construct(private Table\Action $actionTable, private View\Action\Commit $view)
42+
{
43+
}
44+
45+
public function handle(RequestInterface $request, ParametersInterface $configuration, ContextInterface $context): mixed
46+
{
47+
$action = $this->actionTable->findOneByIdentifier($context->getTenantId(), $context->getUser()->getCategoryId(), $request->get('action_id'));
48+
if (!$action instanceof Table\Generated\ActionRow) {
49+
throw new NotFoundException('Provided an invalid action id');
50+
}
51+
52+
return $this->view->getCollection(
53+
$action->getId(),
54+
QueryFilter::from($request),
55+
$context
56+
);
57+
}
58+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Backend\Action\Schema\Commit;
22+
23+
use Fusio\Engine\ActionInterface;
24+
use Fusio\Engine\ContextInterface;
25+
use Fusio\Engine\ParametersInterface;
26+
use Fusio\Engine\RequestInterface;
27+
use Fusio\Impl\Backend\Filter\QueryFilter;
28+
use Fusio\Impl\Backend\View;
29+
use Fusio\Impl\Table;
30+
use PSX\Http\Exception\NotFoundException;
31+
32+
/**
33+
* GetAll
34+
*
35+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
36+
* @license http://www.apache.org/licenses/LICENSE-2.0
37+
* @link https://www.fusio-project.org
38+
*/
39+
readonly class GetAll implements ActionInterface
40+
{
41+
public function __construct(private Table\Schema $schemaTable, private View\Schema\Commit $view)
42+
{
43+
}
44+
45+
public function handle(RequestInterface $request, ParametersInterface $configuration, ContextInterface $context): mixed
46+
{
47+
$schema = $this->schemaTable->findOneByIdentifier($context->getTenantId(), $context->getUser()->getCategoryId(), $request->get('schema_id'));
48+
if (!$schema instanceof Table\Generated\SchemaRow) {
49+
throw new NotFoundException('Provided an invalid schema id');
50+
}
51+
52+
return $this->view->getCollection(
53+
$schema->getId(),
54+
QueryFilter::from($request),
55+
$context
56+
);
57+
}
58+
}

src/Backend/View/Action/Commit.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Backend\View\Action;
22+
23+
use Fusio\Engine\ContextInterface;
24+
use Fusio\Impl\Backend\Filter\QueryFilter;
25+
use Fusio\Impl\Table;
26+
use PSX\Nested\Builder;
27+
use PSX\Nested\Reference;
28+
use PSX\Sql\OrderBy;
29+
use PSX\Sql\ViewAbstract;
30+
31+
/**
32+
* Commit
33+
*
34+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
35+
* @license http://www.apache.org/licenses/LICENSE-2.0
36+
* @link https://www.fusio-project.org
37+
*/
38+
class Commit extends ViewAbstract
39+
{
40+
public function getCollection(int $actionId, QueryFilter $filter, ContextInterface $context)
41+
{
42+
$startIndex = $filter->getStartIndex();
43+
$count = $filter->getCount();
44+
$sortBy = Table\Generated\ActionCommitColumn::tryFrom($filter->getSortBy(Table\Generated\ActionCommitTable::COLUMN_ID) ?? '');
45+
$sortOrder = $filter->getSortOrder(OrderBy::DESC);
46+
47+
$condition = $filter->getCondition([QueryFilter::COLUMN_SEARCH => Table\Generated\ActionCommitTable::COLUMN_CONFIG]);
48+
$condition->equals(Table\Generated\ActionCommitTable::COLUMN_ACTION_ID, $actionId);
49+
50+
$builder = new Builder($this->connection);
51+
52+
$definition = [
53+
'totalResults' => $this->getTable(Table\Action\Commit::class)->getCount($condition),
54+
'startIndex' => $startIndex,
55+
'itemsPerPage' => $count,
56+
'entry' => $builder->doCollection([$this->getTable(Table\Action\Commit::class), 'findAll'], [$condition, $startIndex, $count, $sortBy, $sortOrder], [
57+
'id' => $builder->fieldInteger(Table\Generated\ActionCommitTable::COLUMN_ID),
58+
'user' => $builder->doEntity([$this->getTable(Table\User::class), 'find'], [new Reference(Table\Generated\ActionCommitTable::COLUMN_USER_ID)], [
59+
'id' => $builder->fieldInteger(Table\Generated\UserTable::COLUMN_ID),
60+
'status' => $builder->fieldInteger(Table\Generated\UserTable::COLUMN_STATUS),
61+
'name' => Table\Generated\UserTable::COLUMN_NAME,
62+
]),
63+
'commitHash' => Table\Generated\ActionCommitTable::COLUMN_COMMIT_HASH,
64+
'config' => $builder->fieldJson(Table\Generated\ActionCommitTable::COLUMN_CONFIG),
65+
'insertDate' => $builder->fieldDateTime(Table\Generated\ActionCommitTable::COLUMN_INSERT_DATE),
66+
]),
67+
];
68+
69+
return $builder->build($definition);
70+
}
71+
}

src/Backend/View/Schema/Commit.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/*
3+
* Fusio - Self-Hosted API Management for Builders.
4+
* For the current version and information visit <https://www.fusio-project.org/>
5+
*
6+
* Copyright (c) Christoph Kappestein <christoph.kappestein@gmail.com>
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace Fusio\Impl\Backend\View\Schema;
22+
23+
use Fusio\Engine\ContextInterface;
24+
use Fusio\Impl\Backend\Filter\QueryFilter;
25+
use Fusio\Impl\Table;
26+
use PSX\Nested\Builder;
27+
use PSX\Nested\Reference;
28+
use PSX\Sql\OrderBy;
29+
use PSX\Sql\ViewAbstract;
30+
31+
/**
32+
* Commit
33+
*
34+
* @author Christoph Kappestein <christoph.kappestein@gmail.com>
35+
* @license http://www.apache.org/licenses/LICENSE-2.0
36+
* @link https://www.fusio-project.org
37+
*/
38+
class Commit extends ViewAbstract
39+
{
40+
public function getCollection(int $schemaId, QueryFilter $filter, ContextInterface $context)
41+
{
42+
$startIndex = $filter->getStartIndex();
43+
$count = $filter->getCount();
44+
$sortBy = Table\Generated\SchemaCommitColumn::tryFrom($filter->getSortBy(Table\Generated\SchemaCommitTable::COLUMN_ID) ?? '');
45+
$sortOrder = $filter->getSortOrder(OrderBy::DESC);
46+
47+
$condition = $filter->getCondition([QueryFilter::COLUMN_SEARCH => Table\Generated\SchemaCommitTable::COLUMN_SOURCE]);
48+
$condition->equals(Table\Generated\SchemaCommitTable::COLUMN_SCHEMA_ID, $schemaId);
49+
50+
$builder = new Builder($this->connection);
51+
52+
$definition = [
53+
'totalResults' => $this->getTable(Table\Schema\Commit::class)->getCount($condition),
54+
'startIndex' => $startIndex,
55+
'itemsPerPage' => $count,
56+
'entry' => $builder->doCollection([$this->getTable(Table\Schema\Commit::class), 'findAll'], [$condition, $startIndex, $count, $sortBy, $sortOrder], [
57+
'id' => $builder->fieldInteger(Table\Generated\SchemaCommitTable::COLUMN_ID),
58+
'user' => $builder->doEntity([$this->getTable(Table\User::class), 'find'], [new Reference(Table\Generated\SchemaCommitTable::COLUMN_USER_ID)], [
59+
'id' => $builder->fieldInteger(Table\Generated\UserTable::COLUMN_ID),
60+
'status' => $builder->fieldInteger(Table\Generated\UserTable::COLUMN_STATUS),
61+
'name' => Table\Generated\UserTable::COLUMN_NAME,
62+
]),
63+
'commitHash' => Table\Generated\SchemaCommitTable::COLUMN_COMMIT_HASH,
64+
'schema' => $builder->fieldJson(Table\Generated\SchemaCommitTable::COLUMN_SOURCE),
65+
'insertDate' => $builder->fieldDateTime(Table\Generated\SchemaCommitTable::COLUMN_INSERT_DATE),
66+
]),
67+
];
68+
69+
return $builder->build($definition);
70+
}
71+
}

src/Framework/Schema/Parser/Schema.php

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ public function __construct(Connection $connection, SchemaManagerInterface $sche
5656

5757
public function parse(string $schema, ?ContextInterface $context = null): SchemaInterface
5858
{
59+
$hash = null;
60+
if (str_contains($schema, '@')) {
61+
$hash = substr(strstr($schema, '@'), 1);
62+
$schema = strstr($schema, '@', true);
63+
}
64+
5965
$condition = Condition::withAnd();
6066
$condition->equals('sm.' . Table\Generated\SchemaTable::COLUMN_TENANT_ID, $this->frameworkConfig->getTenantId());
6167

@@ -67,21 +73,46 @@ public function parse(string $schema, ?ContextInterface $context = null): Schema
6773

6874
$queryBuilder = $this->connection->createQueryBuilder()
6975
->select([
76+
'sm.' . Table\Generated\SchemaTable::COLUMN_ID,
7077
'sm.' . Table\Generated\SchemaTable::COLUMN_SOURCE,
7178
])
7279
->from('fusio_schema', 'sm')
7380
->where($condition->getExpression($this->connection->getDatabasePlatform()))
7481
->setParameters($condition->getValues());
7582

76-
$source = $this->connection->fetchOne($queryBuilder->getSQL(), $queryBuilder->getParameters());
77-
if (empty($source)) {
83+
$row = $this->connection->fetchAssociative($queryBuilder->getSQL(), $queryBuilder->getParameters());
84+
if (empty($row)) {
7885
throw new ParserException('Could not find schema ' . $schema);
7986
}
8087

88+
$source = $row[Table\Generated\SchemaTable::COLUMN_SOURCE] ?? throw new ParserException('Could not fetch schema source');
89+
if ($hash !== null) {
90+
$source = $this->resolveSourceByHash((int) $row[Table\Generated\SchemaTable::COLUMN_ID], $hash) ?? $source;
91+
}
92+
8193
if (!str_contains($source, '{') && class_exists($source)) {
8294
return $this->popo->parse($source);
8395
} else {
8496
return $this->typeSchema->parse($source, $context);
8597
}
8698
}
99+
100+
private function resolveSourceByHash(int $schemaId, string $hash): ?string
101+
{
102+
$condition = Condition::withAnd();
103+
$condition->equals(Table\Generated\SchemaCommitTable::COLUMN_SCHEMA_ID, $schemaId);
104+
$condition->equals(Table\Generated\SchemaCommitTable::COLUMN_COMMIT_HASH, $hash);
105+
106+
$queryBuilder = $this->connection->createQueryBuilder()
107+
->select([
108+
Table\Generated\SchemaCommitTable::COLUMN_SOURCE,
109+
])
110+
->from('fusio_schema_commit', 'schema_commit')
111+
->where($condition->getExpression($this->connection->getDatabasePlatform()))
112+
->setParameters($condition->getValues());
113+
114+
$config = $this->connection->fetchOne($queryBuilder->getSQL(), $queryBuilder->getParameters());
115+
116+
return !empty($config) ? $config : null;
117+
}
87118
}

src/Framework/Schema/Scheme.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,25 @@ public static function wrap(?string $schemaName): ?string
5656

5757
/**
5858
* @param string $schema
59-
* @return array{Scheme, string}
59+
* @return array{Scheme, string, ?string}
6060
*/
6161
public static function split(string $schema): array
6262
{
63-
$pos = strpos($schema, '://');
64-
if ($pos === false) {
65-
return [self::SCHEMA, $schema];
63+
$scheme = self::SCHEMA->value;
64+
if (str_contains($schema, '://')) {
65+
$parts = explode('://', $schema);
66+
$scheme = $parts[0];
67+
$schema = $parts[1];
6668
}
6769

68-
$scheme = substr($schema, 0, $pos);
69-
$value = substr($schema, $pos + 3);
70+
$name = $schema;
71+
$hash = null;
72+
if (str_contains($name, '@')) {
73+
$parts = explode('@', $name);
74+
$name = $parts[0];
75+
$hash = $parts[1];
76+
}
7077

71-
return [self::from($scheme), $value];
78+
return [self::from($scheme), $name, $hash];
7279
}
7380
}

0 commit comments

Comments
 (0)