Skip to content

Commit 599c7fd

Browse files
Add custom field controllers
1 parent 73f2fae commit 599c7fd

17 files changed

+233
-24
lines changed

database/01-schema.sql

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
USE
2-
reconmap;
1+
USE reconmap;
32

43
SET
54
FOREIGN_KEY_CHECKS = 0;
@@ -450,6 +449,24 @@ CREATE TABLE report_configuration
450449
FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE
451450
) ENGINE = InnoDB;
452451

452+
DROP TABLE IF EXISTS custom_field;
453+
454+
CREATE TABLE custom_field (
455+
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
456+
insert_ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
457+
update_ts TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
458+
parent_type ENUM ('vulnerability') NOT NULL,
459+
name VARCHAR(100) NOT NULL,
460+
label VARCHAR(100) NOT NULL,
461+
kind ENUM('text', 'number') NOT NULL,
462+
config JSON NOT NULL,
463+
464+
PRIMARY KEY (id),
465+
UNIQUE KEY(parent_type, name)
466+
) ENGINE = InnoDB;
467+
468+
# INSERT INTO custom_field (parent_type, name, label, kind, config) VALUES ('vulnerability', 'cost_pounds', 'Cost in pounds', 'text', '{}');
469+
453470
DROP TABLE IF EXISTS document;
454471

455472
CREATE TABLE document
@@ -507,8 +524,7 @@ CREATE TABLE attachment
507524

508525
DROP FUNCTION IF EXISTS PARENT_CHILD_NAME;
509526

510-
DELIMITER
511-
$$
527+
DELIMITER $$
512528

513529
CREATE FUNCTION PARENT_CHILD_NAME(
514530
parent_name VARCHAR (100),

src/ApiRouter.php

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Controllers\ProjectCategories\ProjectCategoriesRouter,
2323
Controllers\Projects\ProjectsRouter,
2424
Controllers\Reports\ReportsRouter,
25+
Controllers\System\CustomFields\CustomFieldsRouter,
2526
Controllers\System\SystemRouter,
2627
Controllers\Targets\TargetsRouter,
2728
Controllers\Tasks\TasksRouter,
@@ -55,6 +56,7 @@ class ApiRouter extends Router
5556
ProjectCategoriesRouter::class,
5657
ReportsRouter::class,
5758
SystemRouter::class,
59+
CustomFieldsRouter::class,
5860
TargetsRouter::class,
5961
TasksRouter::class,
6062
UsersRouter::class,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Controllers\System\CustomFields;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
use Reconmap\Controllers\Controller;
8+
use Reconmap\Models\CustomField;
9+
use Reconmap\Repositories\CustomFieldRepository;
10+
11+
class CreateCustomFieldController extends Controller
12+
{
13+
public function __construct(private readonly CustomFieldRepository $clientContactRepository)
14+
{
15+
}
16+
17+
public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface
18+
{
19+
/** @var CustomField $customField */
20+
$customField = $this->getJsonBodyDecodedAsClass($request, new CustomField());
21+
$this->clientContactRepository->insert($customField);
22+
23+
return $this->createStatusCreatedResponse($customField);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Controllers\System\CustomFields;
4+
5+
use League\Route\RouteCollectionInterface;
6+
7+
class CustomFieldsRouter
8+
{
9+
10+
public function mapRoutes(RouteCollectionInterface $router): void
11+
{
12+
$router->get('/system/custom-fields', GetCustomFieldsController::class);
13+
$router->post('/system/custom-fields', CreateCustomFieldController::class);
14+
$router->delete('/system/custom-fields/{fieldId:number}', DeleteCustomFieldController::class);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Controllers\System\CustomFields;
4+
5+
use Reconmap\Controllers\DeleteEntityController;
6+
use Reconmap\Models\AuditActions\ContactAuditActions;
7+
use Reconmap\Repositories\CustomFieldRepository;
8+
use Reconmap\Services\ActivityPublisherService;
9+
use Reconmap\Services\Security\AuthorisationService;
10+
11+
class DeleteCustomFieldController extends DeleteEntityController
12+
{
13+
public function __construct(
14+
AuthorisationService $authorisationService,
15+
ActivityPublisherService $activityPublisherService,
16+
CustomFieldRepository $repository,
17+
)
18+
{
19+
parent::__construct(
20+
$authorisationService,
21+
$activityPublisherService,
22+
$repository,
23+
'custom_field',
24+
ContactAuditActions::DELETED,
25+
'fieldId'
26+
);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Controllers\System\CustomFields;
4+
5+
use Override;
6+
use Psr\Http\Message\ResponseInterface;
7+
use Reconmap\Controllers\ControllerV2;
8+
use Reconmap\Http\ApplicationRequest;
9+
use Reconmap\Repositories\CustomFieldRepository;
10+
11+
class GetCustomFieldsController extends ControllerV2
12+
{
13+
public function __construct(private readonly CustomFieldRepository $customFieldRepository)
14+
{
15+
}
16+
17+
#[Override]
18+
protected function process(ApplicationRequest $request): ResponseInterface
19+
{
20+
$fields = $this->customFieldRepository->findAll();
21+
22+
$response = $this->createOkResponse();
23+
$response->getBody()->write(json_encode($fields));
24+
25+
return $response;
26+
}
27+
}

src/Database/CommandTestDataGenerator.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
use Reconmap\Models\Command;
88
use Reconmap\Repositories\CommandRepository;
99

10-
class CommandTestDataGenerator
10+
readonly class CommandTestDataGenerator
1111
{
12-
public function __construct(private readonly CommandRepository $commandRepository)
12+
public function __construct(private CommandRepository $commandRepository)
1313
{
1414

1515
}

src/Database/ConnectionFactory.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ class ConnectionFactory
99

1010
static public function createConnection(ApplicationConfig $config): \mysqli
1111
{
12-
$driver = new \mysqli_driver();
13-
$driver->report_mode = MYSQLI_REPORT_ALL ^ MYSQLI_REPORT_INDEX;
12+
mysqli_report(MYSQLI_REPORT_ALL ^ MYSQLI_REPORT_INDEX);
1413

1514
$dbSettings = $config->getSettings('database');
1615
$conn = new \mysqli;

src/Database/UserTestDataGenerator.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
use Reconmap\Models\User;
66
use Reconmap\Repositories\UserRepository;
77

8-
class UserTestDataGenerator
8+
readonly class UserTestDataGenerator
99
{
10-
public function __construct(private readonly UserRepository $userRepository)
10+
public function __construct(private UserRepository $userRepository)
1111
{
1212
}
1313

src/Http/CorsResponseDecorator.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
use Psr\Http\Message\ServerRequestInterface;
88
use Reconmap\Services\ApplicationConfig;
99

10-
class CorsResponseDecorator
10+
readonly class CorsResponseDecorator
1111
{
12-
public function __construct(private readonly Logger $logger,
13-
private readonly ApplicationConfig $config)
12+
public function __construct(private Logger $logger,
13+
private ApplicationConfig $config)
1414
{
1515
}
1616

src/Models/CustomField.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Models;
4+
5+
/**
6+
* Autogenerated file, do not edit manually. @see https://github.com/reconmap/model-definitions
7+
*/
8+
class CustomField {
9+
10+
public ?int $id;
11+
public ?string $parent_type;
12+
public ?string $name;
13+
public ?string $label;
14+
public ?string $kind;
15+
public ?string $config;
16+
}

src/Repositories/AttachmentRepository.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
class AttachmentRepository extends MysqlRepository
1010
{
11-
private const TABLE_NAME = 'attachment';
11+
private const string TABLE_NAME = 'attachment';
1212

13-
public const UPDATABLE_COLUMNS_TYPES = [
13+
public const array UPDATABLE_COLUMNS_TYPES = [
1414
"submitter_uid" => "i",
1515
"client_file_name" => "s",
1616
"file_hash" => "s",
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Reconmap\Repositories;
4+
5+
use Ponup\SqlBuilders\SelectQueryBuilder;
6+
use Reconmap\Models\CustomField;
7+
8+
class CustomFieldRepository extends MysqlRepository implements Deletable
9+
{
10+
public const array UPDATABLE_COLUMNS_TYPES = [
11+
'parent_type' => 's',
12+
'name' => 's',
13+
'label' => 's',
14+
'kind' => 's',
15+
'config' => 's',
16+
];
17+
18+
public function findByParentId(string $parentType, ?int $parentId): array
19+
{
20+
$queryBuilder = $this->getBaseSelectQueryBuilder();
21+
if (is_null($parentId)) {
22+
$queryBuilder->setWhere('n.parent_type = ? AND n.parent_id IS NULL');
23+
} else {
24+
$queryBuilder->setWhere('n.parent_type = ? AND n.parent_id = ?');
25+
}
26+
27+
$stmt = $this->db->prepare($queryBuilder->toSql());
28+
if (is_null($parentId)) {
29+
$stmt->bind_param('s', $parentType);
30+
} else {
31+
$stmt->bind_param('si', $parentType, $parentId);
32+
}
33+
$stmt->execute();
34+
$result = $stmt->get_result();
35+
$documents = $result->fetch_all(MYSQLI_ASSOC);
36+
$stmt->close();
37+
38+
return $documents;
39+
}
40+
41+
public function findById(int $id): ?array
42+
{
43+
$queryBuilder = $this->getBaseSelectQueryBuilder();
44+
$queryBuilder->setWhere('n.id = ?');
45+
$stmt = $this->db->prepare($queryBuilder->toSql());
46+
$stmt->bind_param('i', $id);
47+
$stmt->execute();
48+
$result = $stmt->get_result();
49+
$document = $result->fetch_assoc();
50+
$stmt->close();
51+
52+
return $document;
53+
}
54+
55+
public function deleteById(int $id): bool
56+
{
57+
return $this->deleteByTableId('custom_field', $id);
58+
}
59+
60+
public function insert(CustomField $customField): bool
61+
{
62+
$stmt = $this->db->prepare('INSERT INTO custom_field (parent_type, name, label, kind, config) VALUES (?, ?, ?, ?, ?)');
63+
return $stmt->execute([$customField->parent_type, $customField->name, $customField->label, $customField->kind, $customField->config]);
64+
}
65+
66+
protected function getBaseSelectQueryBuilder(): SelectQueryBuilder
67+
{
68+
$queryBuilder = new SelectQueryBuilder('custom_field AS cf');
69+
$queryBuilder->setOrderBy('cf.insert_ts DESC');
70+
71+
return $queryBuilder;
72+
}
73+
74+
public function findAll(): array
75+
{
76+
$sql = $this->getBaseSelectQueryBuilder()->toSql();
77+
$resultSet = $this->db->query($sql);
78+
return $resultSet->fetch_all(MYSQLI_ASSOC);
79+
}
80+
81+
public function updateById(int $id, array $newColumnValues): bool
82+
{
83+
return $this->updateByTableId('custom_field', $id, $newColumnValues);
84+
}
85+
}

src/Repositories/MysqlRepository.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use Monolog\Logger;
66
use Ponup\SqlBuilders\DeleteQueryBuilder;
7-
use Ponup\SqlBuilders\QueryBuilder;
87
use Ponup\SqlBuilders\SearchCriteria;
98
use Ponup\SqlBuilders\SelectQueryBuilder;
109
use Ponup\SqlBuilders\UpdateQueryBuilder;
@@ -117,7 +116,7 @@ protected function searchAll(SelectQueryBuilder $queryBuilder, SearchCriteria $s
117116
return $result->fetch_all(MYSQLI_ASSOC);
118117
}
119118

120-
protected function countSearchResults(QueryBuilder $queryBuilder, SearchCriteria $searchCriteria): int
119+
protected function countSearchResults(SelectQueryBuilder $queryBuilder, SearchCriteria $searchCriteria): int
121120
{
122121
$queryBuilder->setColumns('COUNT(*) AS total');
123122
$results = $this->searchAll($queryBuilder, $searchCriteria);

src/Services/ObjectCaster.php

-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ static final public function cast(object $destObject, object $sourceObject): obj
1010
$destRef = new \ReflectionObject($destObject);
1111
$sourceProps = $sourceRef->getProperties();
1212
foreach ($sourceProps as $sourceProp) {
13-
$sourceProp->setAccessible(true);
1413
$propName = $sourceProp->getName();
1514
if ($sourceProp->isInitialized($sourceObject)) {
1615
$propValue = $sourceProp->getValue($sourceObject);
1716
if ($destRef->hasProperty($propName)) {
1817
$propDest = $destRef->getProperty($propName);
19-
$propDest->setAccessible(true);
2018
$propDest->setValue($destObject, $propValue);
2119
} else {
2220
$destObject->$propName = $propValue;

tests/Controllers/Auth/LoginControllerTest.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Reconmap\Controllers\Auth;
44

5-
use League\Container\Container;
65
use PHPUnit\Framework\TestCase;
76
use Psr\Http\Message\ServerRequestInterface;
87
use Reconmap\ConsecutiveParamsTrait;
@@ -12,6 +11,7 @@
1211
use Reconmap\Services\JwtPayloadCreator;
1312
use Reconmap\Services\RedisServer;
1413
use Reconmap\Services\Security\AuthorisationService;
14+
use Symfony\Component\DependencyInjection\Container;
1515
use Symfony\Component\HttpFoundation\Response;
1616

1717
class LoginControllerTest extends TestCase
@@ -62,7 +62,6 @@ public function testLogin()
6262
$mockRedisServer = $this->createMock(RedisServer::class);
6363

6464
$controller = new LoginController($mockUserRepository, $mockAuditLogService, $mockRedisServer);
65-
$controller->setContainer($mockContainer);
6665
$response = $controller($mockServerRequestInterface, []);
6766

6867
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());

0 commit comments

Comments
 (0)