add route to search instances by field value

This commit is contained in:
2026-07-21 20:43:36 +02:00
parent dc5a8897e0
commit 9c5e089573
8 changed files with 632 additions and 12 deletions
@@ -0,0 +1,124 @@
<?php
namespace Api\Controllers\V1;
use Api\Core\Config;
use Api\Core\Request;
use Api\Core\Response;
use ElixForms\Auth\ElixFormsApiClient;
use ElixForms\Auth\ElixFormsAuthenticationClient;
use ElixForms\Exceptions\ElixFormsException;
class ElixFormsController
{
private ElixFormsAuthenticationClient $authenticationClient;
private ElixFormsApiClient $apiClient;
private Config $config;
public function __construct(
ElixFormsAuthenticationClient $authenticationClient,
ElixFormsApiClient $apiClient,
Config $config
) {
$this->authenticationClient = $authenticationClient;
$this->apiClient = $apiClient;
$this->config = $config;
}
public function searchInstances(Request $req, Response $res)
{
$query = $req->query();
$moduleTag = $this->requiredString($query, 'moduleTag');
$fieldName = $this->requiredString($query, 'fieldName');
$fieldValue = $this->requiredString($query, 'fieldValue');
$exportGroup = $this->optionalNonEmptyString($query, 'exportGroup', 'API');
if ($moduleTag === null || $fieldName === null || $fieldValue === null || $exportGroup === null) {
return $res->json([
'error' => 'moduleTag, fieldName e fieldValue sono obbligatori; exportGroup, se specificato, deve essere una stringa non vuota.'
], 400);
}
$username = $this->config->secret('elixforms_api_username');
$password = $this->config->secret('elixforms_api_password');
if (!is_string($username) || trim($username) === '' || !is_string($password) || $password === '') {
throw new ElixFormsException('Credenziali elixForms non configurate.');
}
$token = $this->authenticationClient->login($username, $password);
$instances = $this->apiClient->lookupByStatus($moduleTag, $token, $username);
$matchingInstances = [];
foreach ($instances as $instance) {
if (!is_array($instance)) {
continue;
}
$requestId = $this->requestId($instance);
if ($requestId === null) {
continue;
}
$exportTags = $this->apiClient->getExportTags(
$requestId,
$moduleTag,
$token,
$username,
$exportGroup
);
foreach ($exportTags as $exportTag) {
if (!is_array($exportTag) || !isset($exportTag['name'])) {
continue;
}
$name = (string) $exportTag['name'];
$value = isset($exportTag['value']) ? (string) $exportTag['value'] : '';
if (strcasecmp($name, $fieldName) === 0 && stripos($value, $fieldValue) !== false) {
$matchingInstances[] = $instance;
break;
}
}
}
return $res->json($matchingInstances);
}
private function requiredString(array $values, string $key): ?string
{
if (!isset($values[$key]) || !is_string($values[$key])) {
return null;
}
$value = trim($values[$key]);
return $value === '' ? null : $value;
}
private function optionalNonEmptyString(array $values, string $key, string $default): ?string
{
if (!array_key_exists($key, $values)) {
return $default;
}
if (!is_string($values[$key])) {
return null;
}
$value = trim($values[$key]);
return $value === '' ? null : $value;
}
private function requestId(array $instance)
{
foreach (['idDomanda', 'requestId', 'idRequest'] as $key) {
if (isset($instance[$key]) && (is_int($instance[$key]) || is_string($instance[$key]))) {
return $instance[$key];
}
}
return null;
}
}
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace ElixForms\Auth;
use Api\Core\HttpClient;
use ElixForms\Exceptions\ElixFormsException;
class ElixFormsApiClient
{
private string $baseUrl;
private HttpClient $httpClient;
public function __construct(string $baseUrl, HttpClient $httpClient)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->httpClient = $httpClient;
}
/**
* Restituisce le istanze del modulo, opzionalmente filtrate per stato.
* I flag di ElixFormsRequestStatus possono essere combinati con l'operatore |.
*
* @return array<int,array<string,mixed>>
*/
public function lookupByStatus(
string $moduleTag,
string $authToken,
string $username,
?int $status = null
): array
{
$queryParts = [];
if ($status !== null) {
foreach (ElixFormsRequestStatus::toApiValues($status) as $statusValue) {
$queryParts[] = 'requestStatus=' . rawurlencode($statusValue);
}
}
$queryParts[] = 'moduleTag=' . rawurlencode($moduleTag);
$url = $this->baseUrl . '/eF/api/request/lookup/by-status?' . implode('&', $queryParts);
$payload = $this->getJson($url, $authToken, $username, 'LookupByStatus');
$requests = $payload['value']['requests'] ?? [];
if (!is_array($requests)) {
throw new ElixFormsException('Risposta LookupByStatus non valida: requests deve essere un array.');
}
return array_values($requests);
}
/**
* @param int|string $requestId
* @return array<int,array{name?:mixed,value?:mixed}>
*/
public function getExportTags(
$requestId,
string $moduleTag,
string $authToken,
string $username,
string $exportGroup = 'API'
): array
{
$exportGroup = trim($exportGroup);
if ($exportGroup === '') {
throw new \InvalidArgumentException('exportGroup deve essere una stringa non vuota.');
}
$url = sprintf(
'%s/eF/services/api/request/%s/view/_DEFAULT/exportTags/get/v1?moduleTag=%s&exportGroup=%s',
$this->baseUrl,
rawurlencode((string) $requestId),
rawurlencode($moduleTag),
rawurlencode($exportGroup)
);
$payload = $this->getJson($url, $authToken, $username, 'GetExportTags');
$exportTags = $payload['value']['exportTags'] ?? [];
if (!is_array($exportTags)) {
throw new ElixFormsException('Risposta GetExportTags non valida: exportTags deve essere un array.');
}
return array_values($exportTags);
}
private function getJson(
string $url,
string $authToken,
string $username,
string $operation
): array {
$response = $this->httpClient->get($url, [
'Authorization' => 'Bearer ' . $authToken,
'Accept' => 'application/json',
'x-requested-with' => 'XMLHttpRequest',
'x-api-username' => $username,
]);
$status = $response['status'] ?? 500;
if ($status !== 200) {
throw new ElixFormsException(sprintf(
'Errore durante %s. HTTP Status: %d',
$operation,
$status
));
}
$payload = $response['json'] ?? null;
$jsonError = JSON_ERROR_NONE;
if (!is_array($payload) && isset($response['body']) && is_string($response['body'])) {
$payload = json_decode($response['body'], true);
$jsonError = json_last_error();
}
if (!is_array($payload) || $jsonError !== JSON_ERROR_NONE) {
throw new ElixFormsException(sprintf(
'Risposta non valida da %s: atteso JSON.',
$operation
));
}
$globalStatus = $payload['value']['globalStatus'] ?? null;
if ($globalStatus === 'ERROR') {
$description = $payload['value']['description'] ?? 'errore non specificato';
throw new ElixFormsException(sprintf(
'%s ha restituito un errore: %s',
$operation,
$description
));
}
return $payload;
}
}
@@ -9,7 +9,7 @@ class ElixFormsAuthenticationClient {
private $baseUrl;
private $httpClient;
public function __construct(string $baseUrl, Client $httpClient = null) {
public function __construct(string $baseUrl, ?Client $httpClient = null) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->httpClient = $httpClient ?? new Client(['timeout' => 10.0, 'http_errors' => false]);
}
@@ -0,0 +1,46 @@
<?php
namespace ElixForms\Auth;
use InvalidArgumentException;
/**
* Enum-like di flag combinabili per gli stati delle istanze elixForms.
*
* Esempio: ElixFormsRequestStatus::IN_PROGRESS | ElixFormsRequestStatus::PROCESSED
*/
final class ElixFormsRequestStatus
{
public const IN_PROGRESS = 1;
public const SUBMITTED = 2;
public const PROCESSED = 4;
public const ALL = self::IN_PROGRESS | self::SUBMITTED | self::PROCESSED;
private const API_VALUES = [
self::IN_PROGRESS => 'IN_PROGRESS',
self::SUBMITTED => 'SUBMITTED',
self::PROCESSED => 'PROCESSED',
];
private function __construct()
{
}
/**
* @return array<int,string>
*/
public static function toApiValues(int $status): array
{
if ($status <= 0 || ($status & ~self::ALL) !== 0) {
throw new InvalidArgumentException('La combinazione di stati elixForms non è valida.');
}
$values = [];
foreach (self::API_VALUES as $flag => $apiValue) {
if (($status & $flag) === $flag) {
$values[] = $apiValue;
}
}
return $values;
}
}