feat(helpdesk): refactor multi-tenant BC settings and risk radar improvements
Restructure helpdesk tenant settings into per-connection config with improved token handling. Update risk radar data endpoint and UI. Clean up ImageUploadTrait and update architecture contract tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,21 +9,24 @@ use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface;
|
||||
*
|
||||
* Wraps the tenant settings repository and handles encryption/decryption
|
||||
* of secrets (basic_password, oauth_client_secret) via SettingsCryptoGateway.
|
||||
*
|
||||
* Uses a per-request row cache to avoid N+1 queries when multiple getters
|
||||
* are called for the same tenant (e.g. during settings resolution).
|
||||
*/
|
||||
class HelpdeskTenantSettingsGateway
|
||||
{
|
||||
/** @var array<int, array<string, mixed>|false> Cache: tenantId → row or false (not found) */
|
||||
private array $rowCache = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly HelpdeskTenantSettingsRepository $repository,
|
||||
private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an active override exists for the given tenant.
|
||||
*/
|
||||
public function isOverrideEnabled(int $tenantId): bool
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
if ($row === null) {
|
||||
return false;
|
||||
}
|
||||
@@ -36,12 +39,12 @@ class HelpdeskTenantSettingsGateway
|
||||
*/
|
||||
public function findByTenantId(int $tenantId): ?array
|
||||
{
|
||||
return $this->repository->findByTenantId($tenantId);
|
||||
return $this->resolveRow($tenantId);
|
||||
}
|
||||
|
||||
public function getAuthMode(int $tenantId): string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['auth_mode'] ?? ''));
|
||||
|
||||
return $value === HelpdeskSettingsGateway::AUTH_MODE_OAUTH2
|
||||
@@ -51,7 +54,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getODataBaseUrl(int $tenantId): string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['odata_base_url'] ?? ''));
|
||||
|
||||
return $value !== '' ? rtrim($value, '/') : HelpdeskSettingsGateway::DEFAULT_ODATA_BASE_URL;
|
||||
@@ -59,7 +62,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getCompanyName(int $tenantId): string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['company_name'] ?? ''));
|
||||
|
||||
return $value !== '' ? $value : HelpdeskSettingsGateway::DEFAULT_COMPANY_NAME;
|
||||
@@ -67,7 +70,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getBasicUser(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['basic_user'] ?? ''));
|
||||
|
||||
return $value !== '' ? $value : null;
|
||||
@@ -75,7 +78,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getBasicPassword(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$encrypted = trim((string) ($row['basic_password_enc'] ?? ''));
|
||||
if ($encrypted === '') {
|
||||
return null;
|
||||
@@ -94,7 +97,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getOAuthTenantId(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['oauth_tenant_id'] ?? ''));
|
||||
|
||||
return $value !== '' ? $value : null;
|
||||
@@ -102,7 +105,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getOAuthClientId(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['oauth_client_id'] ?? ''));
|
||||
|
||||
return $value !== '' ? $value : null;
|
||||
@@ -110,7 +113,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getOAuthClientSecret(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$encrypted = trim((string) ($row['oauth_client_secret_enc'] ?? ''));
|
||||
if ($encrypted === '') {
|
||||
return null;
|
||||
@@ -129,7 +132,7 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getOAuthTokenEndpoint(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['oauth_token_endpoint'] ?? ''));
|
||||
|
||||
return $value !== '' ? $value : null;
|
||||
@@ -137,20 +140,18 @@ class HelpdeskTenantSettingsGateway
|
||||
|
||||
public function getSystemRecommendationsConfig(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['system_recommendations_config'] ?? ''));
|
||||
|
||||
return ($row['system_recommendations_config'] ?? null) !== null
|
||||
? (string) $row['system_recommendations_config']
|
||||
: null;
|
||||
return $value !== '' ? $value : null;
|
||||
}
|
||||
|
||||
public function getControllingRiskConfig(int $tenantId): ?string
|
||||
{
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$row = $this->resolveRow($tenantId);
|
||||
$value = trim((string) ($row['controlling_risk_config'] ?? ''));
|
||||
|
||||
return ($row['controlling_risk_config'] ?? null) !== null
|
||||
? (string) $row['controlling_risk_config']
|
||||
: null;
|
||||
return $value !== '' ? $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,8 +175,6 @@ class HelpdeskTenantSettingsGateway
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$data['basic_password_enc'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,16 +187,42 @@ class HelpdeskTenantSettingsGateway
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$data['oauth_client_secret_enc'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate cache after write
|
||||
unset($this->rowCache[$tenantId]);
|
||||
|
||||
return $this->repository->upsert($tenantId, $data);
|
||||
}
|
||||
|
||||
public function deleteByTenantId(int $tenantId): bool
|
||||
{
|
||||
unset($this->rowCache[$tenantId]);
|
||||
|
||||
return $this->repository->deleteByTenantId($tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load row once per tenant per request. Returns null if no row exists.
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function resolveRow(int $tenantId): ?array
|
||||
{
|
||||
if ($tenantId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (array_key_exists($tenantId, $this->rowCache)) {
|
||||
$cached = $this->rowCache[$tenantId];
|
||||
|
||||
return $cached === false ? null : $cached;
|
||||
}
|
||||
|
||||
$row = $this->repository->findByTenantId($tenantId);
|
||||
$this->rowCache[$tenantId] = $row ?? false;
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user