- Added tests to ensure Redis namespace safety for `db_object_t` and `users_o`. - Implemented `MotorApiCachedResultTest` to validate metadata caching behavior. - Introduced configuration classes for `xlvask_automatic_order_attachment_enabled` and `xlvask_automatic_order_creation_enabled`. - Developed `xlvask_automation_service` with supporting features for usage log evaluation, suggestion building, and order automation.
396 lines
13 KiB
PHP
396 lines
13 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
require_once WD . '/modules/motorapi/motorapi_c.php';
|
|
require_once WD . '/modules/motorapi/helpers/motorapi_vehicle_types.php';
|
|
/** Actions */
|
|
require_once WD . '/modules/motorapi/actions/license_plate_lookup_a.php';
|
|
|
|
use Exception;
|
|
use interfaces\motorapi_i;
|
|
use motorapi\actions\license_plate_lookup_a;
|
|
use motorapi\helpers\motorapi_vehicle_types;
|
|
use motorapi\motorapi_c;
|
|
use objects\motorapi_lookups_o;
|
|
|
|
class motorapi implements motorapi_i
|
|
{
|
|
/**
|
|
* Configuration of the motorapi module
|
|
* @var motorapi_c
|
|
*/
|
|
public motorapi_c $config;
|
|
/**
|
|
* Vehicle types
|
|
* @var motorapi_vehicle_types
|
|
*/
|
|
public motorapi_vehicle_types $vehicle_types;
|
|
|
|
/**
|
|
* API URL
|
|
* @var string
|
|
*/
|
|
private string $api_url = 'https://v1.motorapi.dk/';
|
|
|
|
/**
|
|
* ACTION: LICENSE_PLATE_LOOKUP
|
|
* @see license_plate_lookup_a
|
|
* @notation This action is when a license plate lookup request is made, and logs the request in the database
|
|
* @var license_plate_lookup_a
|
|
*/
|
|
private license_plate_lookup_a $license_plate_lookup;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->config = new motorapi_c();
|
|
$this->vehicle_types = new motorapi_vehicle_types();
|
|
/** Actions */
|
|
$this->license_plate_lookup = new license_plate_lookup_a();
|
|
}
|
|
|
|
private static function cleanJSON(mixed $value): string
|
|
{
|
|
if (is_string($value)) {
|
|
// Remove backslashes
|
|
$value = stripslashes($value);
|
|
$value = stripslashes($value);
|
|
// Ensure the string is valid UTF-8
|
|
$value = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
|
|
// Optionally, you can use regex to remove any non-printable characters
|
|
$value = preg_replace('/[^\x20-\x7E]/', '', $value);
|
|
} else {
|
|
echo 'Unable to perform cleaning on value of type ' . gettype($value) . ': ' . print_r($value, true) . PHP_EOL;
|
|
}
|
|
// Remove any invalid UTF-8 characters
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Get the recommended products for a license plate
|
|
* @param string $plate The license plate
|
|
* @return array The recommended products
|
|
* @throws Exception If the module is not enabled
|
|
* @throws Exception If the license plate is invalid
|
|
* @throws Exception If the daily limit is exceeded
|
|
* @throws Exception If the secret key is invalid
|
|
*/
|
|
public function getRecommendedProducts(string $plate): array
|
|
{
|
|
self::requireModuleEnabled();
|
|
self::requireValidLicensePlate($plate);
|
|
$data = self::getLicensePlateInformation($plate, false);
|
|
// Get the recommended products for the license plate
|
|
return match ($data->type) {
|
|
// TODO: Add recommended products for each vehicle type, and convert this into an object type.
|
|
$this->vehicle_types->PLATE_TYPE_CAR => [
|
|
1,
|
|
2,
|
|
],
|
|
$this->vehicle_types->PLATE_TYPE_TRUCK => [
|
|
3,
|
|
4,
|
|
],
|
|
default => [],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function requireModuleEnabled(): void
|
|
{
|
|
// Check if the module is enabled
|
|
if (!$this->config->enabled->isTrue()) {
|
|
throw new Exception('The motorapi module is not enabled');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function requireValidLicensePlate(string $licensePlate): void
|
|
{
|
|
// Check if the license plate is valid
|
|
if (!preg_match('/^[A-Z0-9]{1,10}$/', $licensePlate)) {
|
|
throw new Exception('Invalid license plate');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
|
|
*/
|
|
function getLicensePlateInformation(string $licensePlate, bool $ignoreCache = true): object
|
|
{
|
|
// Check if the license plate information is cached
|
|
if (!$ignoreCache && self::isCached($licensePlate)) {
|
|
// Get the cached result
|
|
$tmp_res = self::getCachedResult($licensePlate);
|
|
if ($tmp_res !== null) {
|
|
return $tmp_res;
|
|
}
|
|
}
|
|
// Get the license plate information from the motorapi
|
|
return $this->sendRequest($licensePlate, 'vehicles', [], 'GET');
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function isCached(string $licensePlate): bool
|
|
{
|
|
// Check if the license plate response is stored in the log/local database/cache
|
|
$motorapi_lookups = new motorapi_lookups_o();
|
|
return $motorapi_lookups->isCached($licensePlate);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function getCachedResult(string $licensePlate): ?object
|
|
{
|
|
global /** @var response $response */
|
|
$response;
|
|
// Get the cached result from the log/local database/cache
|
|
$motorapi_lookups = new motorapi_lookups_o();
|
|
// Add the cached value to the meta
|
|
self::addCachedMetaIfPossible($response);
|
|
$cleaned_result = self::cleanJSON($motorapi_lookups->getCachedResult($licensePlate)->result->value());
|
|
$object = json_decode($cleaned_result);
|
|
if ($object === null) {
|
|
print_r($cleaned_result);
|
|
throw new Exception('Cached result is not valid JSON');
|
|
}
|
|
return json_decode($cleaned_result);
|
|
}
|
|
|
|
public static function addCachedMetaIfPossible(mixed $response): void
|
|
{
|
|
if (is_object($response) && method_exists($response, 'add_meta')) {
|
|
$response->add_meta('cached', true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
|
|
*/
|
|
function sendRequest(string $licensePlate, string $endpoint, array $data = [], string $method = 'GET'): object
|
|
{
|
|
// Validate the module is enabled
|
|
self::requireModuleEnabled();
|
|
// Validate the license plate
|
|
self::requireValidLicensePlate($licensePlate);
|
|
// Validate the daily limit
|
|
self::requireDailyLimitNotExceeded();
|
|
// Validate the secret key
|
|
self::requireValidSecretKey();
|
|
// Send the request
|
|
$response = match ($method) {
|
|
'GET' => self::sendGetRequest($licensePlate, $endpoint, $data),
|
|
'POST' => self::sendPostRequest($licensePlate, $endpoint, $data),
|
|
'PUT' => self::sendPutRequest($licensePlate, $endpoint, $data),
|
|
'DELETE' => self::sendDeleteRequest($licensePlate, $endpoint, $data),
|
|
default => self::exception(
|
|
$licensePlate, [
|
|
'method' => $method,
|
|
'endpoint' => $endpoint,
|
|
'data' => $data,
|
|
'license_plate' => $licensePlate,
|
|
'response' => null,
|
|
'status_code' => 400,
|
|
'error' => 'Invalid request method',
|
|
],
|
|
500
|
|
),
|
|
};
|
|
// Add the request to the log
|
|
self::addRequestToLog($licensePlate, $endpoint, $response);
|
|
// Return the response
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function requireDailyLimitNotExceeded(): void
|
|
{
|
|
// Check if the daily limit is exceeded
|
|
if ($this->getDailyRequestCounter() >= $this->config->daily_limit->getVariableValue()) {
|
|
throw new Exception('Daily limit exceeded');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function getDailyRequestCounter(): int
|
|
{
|
|
// Count the rows from the motorapi request log that was made today
|
|
$motorapi_lookups = new motorapi_lookups_o();
|
|
$motorapi_lookups->getTodayCount();
|
|
return $motorapi_lookups->getTodayCount();
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function requireValidSecretKey(): void
|
|
{
|
|
// Check if the secret key is valid
|
|
if ($this->config->secret_key->getVariableValue() === null) {
|
|
self::exception(
|
|
'',
|
|
[
|
|
'status_code' => 500,
|
|
'error' => 'Invalid secret key defined in the config (motorapi_secret_key_c)',
|
|
],
|
|
500
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws Exception
|
|
*/
|
|
function exception(string $licensePlate, array $data = [], int $status_code = 500): exception
|
|
{
|
|
// Add the request to the log
|
|
$this->license_plate_lookup->license_plate_lookup($licensePlate, $data, $status_code);
|
|
return throw new Exception($data['error'] ?? 'An error occurred while processing the request, in ' . $this->config->getModuleName() . ' module');
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception
|
|
*/
|
|
function sendGetRequest(string $licensePlate, string $endpoint, array $data): object
|
|
{
|
|
// Build the query string if any data is provided
|
|
$query_string = '';
|
|
if (!empty($data)) {
|
|
$query_string = '?' . http_build_query($data);
|
|
}
|
|
|
|
// Initialize cURL session
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->api_url . $endpoint . '/' . $licensePlate . $query_string);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
// Set HTTP headers including the X-AUTH-TOKEN
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'X-AUTH-TOKEN: ' . $this->config->secret_key->getVariableValue(),
|
|
]);
|
|
|
|
// Execute the request and handle response
|
|
$output = curl_exec($ch);
|
|
|
|
// Check for errors
|
|
if (curl_errno($ch)) {
|
|
self::exception(
|
|
$licensePlate, [
|
|
'method' => 'GET',
|
|
'endpoint' => $endpoint,
|
|
'data' => $data,
|
|
'license_plate' => $licensePlate,
|
|
'response' => null,
|
|
'status_code' => 500,
|
|
'error' => curl_error($ch),
|
|
],
|
|
500
|
|
);
|
|
}
|
|
|
|
// Get the status code
|
|
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
curl_close($ch);
|
|
// Check if the response is valid JSON
|
|
if (!json_decode($output)) {
|
|
$exception_message = match ($status_code) {
|
|
401 => 'Unauthorized',
|
|
404 => 'Not found',
|
|
429 => 'Too many requests',
|
|
default => 'Invalid response',
|
|
};
|
|
self::exception(
|
|
$licensePlate, [
|
|
'method' => 'GET',
|
|
'endpoint' => $endpoint,
|
|
'data' => $data,
|
|
'license_plate' => $licensePlate,
|
|
'response' => null,
|
|
'status_code' => $status_code,
|
|
'error' => $exception_message,
|
|
],
|
|
$status_code
|
|
);
|
|
}
|
|
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), $status_code);
|
|
return json_decode($output);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function sendPostRequest(string $licensePlate, string $endpoint, array $data): object
|
|
{
|
|
// Send a POST request to the motorapi
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->api_url . $endpoint);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
$output = curl_exec($ch);
|
|
curl_close($ch);
|
|
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
|
|
return json_decode($output);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function sendPutRequest(string $licensePlate, string $endpoint, array $data): object
|
|
{
|
|
// Send a PUT request to the motorapi
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->api_url . $endpoint);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
$output = curl_exec($ch);
|
|
curl_close($ch);
|
|
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
|
|
return json_decode($output);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function sendDeleteRequest(string $licensePlate, string $endpoint, array $data): object
|
|
{
|
|
// Send a DELETE request to the motorapi
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->api_url . $endpoint);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
$output = curl_exec($ch);
|
|
curl_close($ch);
|
|
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
|
|
return json_decode($output);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception
|
|
*/
|
|
function addRequestToLog(string $licensePlate, string $endpoint, object $response): void
|
|
{
|
|
// Add the request to the motorapi request log
|
|
$motorapi_lookups = new motorapi_lookups_o();
|
|
$motorapi_lookups->add($licensePlate, json_encode($response), $endpoint);
|
|
}
|
|
}
|