* @link https://www.tassos.gr * @copyright Copyright © 2024 Tassos All Rights Reserved * @license GNU GPLv3 or later */ namespace NRFramework\Integrations; // No direct access defined('_JEXEC') or die; use Joomla\Registry\Registry; use Joomla\CMS\Http\HttpFactory; class Integration { protected $key; protected $endpoint; protected $request_successful = false; protected $last_error = ''; protected $last_response = []; protected $last_request = []; protected $timeout = 60; protected $options; protected $encode = true; protected $response_type = 'json'; public function __construct() { $this->options = new Registry; $this->options->set('timeout', $this->timeout); $this->options->set('headers.Accept', 'application/json'); $this->options->set('headers.Content-Type', 'application/json'); } /** * Setter method for the API Key or Access Token * * @param string $apiKey */ public function setKey($apiKey) { $apiKey = is_array($apiKey) && isset($apiKey['api']) ? $apiKey['api'] : $apiKey; if (!is_string($apiKey) || empty($apiKey) || is_null($apiKey)) { throw new \Exception('Invalid API Key supplied.'); } $this->key = trim($apiKey); } /** * Setter method for the endpoint * @param string $url The URL which is set in the account's developer settings * @throws \Exception */ public function setEndpoint($url) { if (!empty($url)) { $this->endpoint = $url; } else { throw new \Exception("Invalid Endpoint URL `{$url}` supplied."); } } /** * Was the last request successful? * @return bool True for success, false for failure */ public function success() { return $this->request_successful; } /** * Get the last error returned by either the network transport, or by the API. * If something didn't work, this should contain the string describing the problem. * @return array|false describing the error */ public function getLastError() { return $this->last_error ?: false; } /** * Get an array containing the HTTP headers and the body of the API response. * @return array Assoc array with keys 'headers' and 'body' */ public function getLastResponse() { return $this->last_response; } /** * Get an array containing the HTTP headers and the body of the API request. * @return array Assoc array */ public function getLastRequest() { return $this->last_request; } /** * Make an HTTP DELETE request - for deleting data * @param string $method URL of the API request method * @param array $args Assoc array of arguments (if any) * @return array|false Assoc array of API response, decoded from JSON */ public function delete($method, $args = []) { return $this->makeRequest('delete', $method, $args); } /** * Make an HTTP GET request - for retrieving data * @param string $method URL of the API request method * @param array $args Assoc array of arguments (usually your data) * @return array|false Assoc array of API response, decoded from JSON */ public function get($method, $args = []) { return $this->makeRequest('get', $method, $args); } /** * Make an HTTP PATCH request - for performing partial updates * @param string $method URL of the API request method * @param array $args Assoc array of arguments (usually your data) * @return array|false Assoc array of API response, decoded from JSON */ public function patch($method, $args = []) { return $this->makeRequest('patch', $method, $args); } /** * Make an HTTP POST request - for creating and updating items * @param string $method URL of the API request method * @param array $args Assoc array of arguments (usually your data) * @return array|false Assoc array of API response, decoded from JSON */ public function post($method, $args = []) { return $this->makeRequest('post', $method, $args); } /** * Make an HTTP PUT request - for creating new items * @param string $method URL of the API request method * @param array $args Assoc array of arguments (usually your data) * @return array|false Assoc array of API response, decoded from JSON */ public function put($method, $args = []) { return $this->makeRequest('put', $method, $args); } /** * Performs the underlying HTTP request. Not very exciting. * @param string $http_verb The HTTP verb to use: get, post, put, patch, delete * @param string $method The API method to be called * @param array $args Assoc array of parameters to be passed * @return array|false Assoc array of decoded result * @throws \Exception */ protected function makeRequest($http_verb, $method, $args = []) { $url = $this->endpoint; if (!empty($method) && !is_null($method) && strpos($url, '?') === false) { $url .= '/' . $method; } $this->last_error = ''; $this->request_successful = false; $this->last_response = []; $this->last_request = [ 'method' => $http_verb, 'path' => $method, 'url' => $url, 'body' => '', 'timeout' => $this->timeout, ]; $http = HttpFactory::getHttp($this->options); switch ($http_verb) { case 'post': $this->attachRequestPayload($args); $response = $http->post($url, $this->last_request['body']); break; case 'get': $query = http_build_query($args, '', '&'); $this->last_request['body'] = $query; $response = (strpos($url,'?') !== false) ? $http->get($url . '&' . $query) : $http->get($url . '?' . $query); break; case 'delete': $response = $http->delete($url); break; case 'patch': $this->attachRequestPayload($args); $response = $http->patch($url, $this->last_request['body']); break; case 'put': $this->attachRequestPayload($args); $response = $http->put($url, $this->last_request['body']); break; } // Do not touch directly the $response object to prevent the PHP 8.2 "Creation of dynamic property" deprecation notice. $this->last_response = (object) [ 'body' => $this->convertResponse($response->body), 'headers' => $response->headers, 'code' => $response->code ]; $this->determineSuccess(); return $this->last_response->body; } /** * Encode the data and attach it to the request * @param array $data Assoc array of data to attach */ protected function attachRequestPayload($data) { if (!$this->encode) { $this->last_request['body'] = http_build_query($data); return; } $this->last_request['body'] = json_encode($data); } /** * Check if the response was successful or a failure. If it failed, store the error. * * @return bool If the request was successful */ protected function determineSuccess() { $status = $this->last_response->code; $success = ($status >= 200 && $status <= 299) ? true : false; return ($this->request_successful = $success); } /** * Converts the HTTP Call response to a traversable type * * @param json|xml $response * * @return array|object */ protected function convertResponse($response) { switch ($this->response_type) { case 'json': return json_decode($response, true); case 'xml': return new \SimpleXMLElement($response); case 'text': return $response; } } /** * Search Custom Fields declared by the user for a specific custom field. If exists return its value. * * @param array $needles The custom field names * @param array $haystack The custom fields array * * @return string The value of the custom field or an empty string if not found */ protected function getCustomFieldValue($needles, $haystack) { $needles = is_array($needles) ? $needles : (array) $needles; $haystack = array_change_key_case($haystack); $found = ''; foreach ($needles as $needle) { $needle = strtolower($needle); if (array_key_exists($needle, $haystack)) { $found = is_string($haystack[$needle]) ? trim($haystack[$needle]) : $haystack[$needle]; break; } } return $found; } /** * Set encode * * @param boolean $encode * * @return void */ public function setEncode($encode) { $this->encode = $encode; } }