mirror of
https://github.com/beestat/app.git
synced 2025-05-23 18:04:14 -04:00
270 lines
7.3 KiB
PHP
270 lines
7.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* All external APIs (ecobee, SmartyStreets, Patreon, MailChimp) extend this
|
|
* class. This provides a generic cURL function with a couple basic arguments,
|
|
* and also logging.
|
|
*
|
|
* @author Jon Ziebell
|
|
*/
|
|
class external_api extends cora\api {
|
|
|
|
/**
|
|
* Whether or not to log the API call to Influx. This will only log the
|
|
* event and basic timing information; no detail.
|
|
*/
|
|
protected static $log_influx = true;
|
|
|
|
/**
|
|
* Whether or not to log the API call to MySQL. This will log the entire
|
|
* request and response in full detail. Valid values are "error", "all", and
|
|
* false.
|
|
*/
|
|
protected static $log_mysql = 'error';
|
|
|
|
/**
|
|
* Whether or not to cache API calls. This will store a hash of the request
|
|
* and the response in the database and check there before performing the
|
|
* API call again.
|
|
*/
|
|
protected static $cache = false;
|
|
|
|
/**
|
|
* How long to cache the API call for. Set to null for infinite, otherwise
|
|
* define in seconds.
|
|
*/
|
|
protected static $cache_for = null;
|
|
|
|
/**
|
|
* Fire off a cURL request to an external API.
|
|
*
|
|
* @param array $arguments
|
|
* url (string) The URL to send the request to
|
|
* method (string) Set to POST to POST, else it will GET
|
|
* header (array) Any headers you want to set
|
|
* post_fields Fields to send
|
|
*
|
|
* @return string Result on success, false on failure.
|
|
*/
|
|
protected function curl($arguments) {
|
|
$this->request_timestamp = time();
|
|
$this->request_timestamp_microtime = $this->microtime();
|
|
|
|
$curl_handle = curl_init();
|
|
curl_setopt($curl_handle, CURLOPT_URL, $arguments['url']);
|
|
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 5);
|
|
curl_setopt($curl_handle, CURLOPT_TIMEOUT, 60);
|
|
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
|
|
// curl_setopt($curl_handle, CURLOPT_HEADER, true);
|
|
|
|
if(isset($arguments['method']) === true && $arguments['method'] === 'POST') {
|
|
curl_setopt($curl_handle, CURLOPT_POST, true);
|
|
}
|
|
|
|
if(isset($arguments['header']) === true) {
|
|
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $arguments['header']);
|
|
}
|
|
|
|
if(isset($arguments['post_fields']) === true) {
|
|
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $arguments['post_fields']);
|
|
}
|
|
|
|
if($this::$log_influx !== false || $this::$log_mysql !== false) {
|
|
curl_setopt($curl_handle, CURLINFO_HEADER_OUT, true);
|
|
}
|
|
|
|
$should_cache = (
|
|
$this::$cache === true &&
|
|
$this::should_cache($arguments) === true
|
|
);
|
|
|
|
// Check the cache
|
|
if ($should_cache === true) {
|
|
$cache_key = $this->generate_cache_key($arguments);
|
|
$cache_entry = $this->get_cache_entry($cache_key);
|
|
} else {
|
|
$cache_entry = null;
|
|
}
|
|
|
|
if($cache_entry === null) {
|
|
$curl_response = curl_exec($curl_handle);
|
|
$this->curl_info = curl_getinfo($curl_handle);
|
|
|
|
if($curl_response === false || curl_errno($curl_handle) !== 0) {
|
|
$this->cora->set_error_extra_info([
|
|
'curl_error' => curl_error($curl_handle)
|
|
]);
|
|
|
|
// Error logging
|
|
if($this::$log_influx === true) {
|
|
$this->log_influx(
|
|
$this->resource . '_api_log',
|
|
true
|
|
);
|
|
}
|
|
if($this::$log_mysql === 'all' || $this::$log_mysql === 'error') {
|
|
$this->log_mysql($curl_response);
|
|
}
|
|
|
|
throw new Exception('Could not connect to ' . $this->resource . '.');
|
|
}
|
|
|
|
// General (success) logging
|
|
if($this::$log_influx === true) {
|
|
$this->log_influx(
|
|
$this->resource . '_api_log',
|
|
false
|
|
);
|
|
}
|
|
if($this::$log_mysql === 'all') {
|
|
$this->log_mysql($curl_response);
|
|
}
|
|
|
|
if($should_cache === true) {
|
|
$this->create_update_cache_entry($cache_key, $curl_response);
|
|
}
|
|
|
|
curl_close($curl_handle);
|
|
}
|
|
else {
|
|
$curl_response = $cache_entry['response'];
|
|
}
|
|
|
|
return $curl_response;
|
|
}
|
|
|
|
/**
|
|
* Create an entry in the cache table. If one exists, update it.
|
|
*
|
|
* @param string $key
|
|
* @param string $response
|
|
*
|
|
* @return array The created or updated entry.
|
|
*/
|
|
private function create_update_cache_entry($key, $response) {
|
|
$cache_entry = $this->api(
|
|
($this->resource . '_api_cache'),
|
|
'get',
|
|
[
|
|
'attributes' => ['key' => $key]
|
|
]
|
|
);
|
|
|
|
if($cache_entry === null) {
|
|
return $this->api(
|
|
($this->resource . '_api_cache'),
|
|
'create',
|
|
[
|
|
'attributes' => [
|
|
'key' => $key,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'response' => $response
|
|
]
|
|
]
|
|
);
|
|
}
|
|
else {
|
|
$attributes = [
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'response' => $response
|
|
];
|
|
$attributes[$this->resource . '_api_cache_id'] = $cache_entry[$this->resource . '_api_cache_id'];
|
|
|
|
return $this->api(
|
|
($this->resource . '_api_cache'),
|
|
'update',
|
|
[
|
|
'attributes' => $attributes
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an entry in the cache table.
|
|
*
|
|
* @param string $key
|
|
*
|
|
* @return array The found cache entry, or null if none found.
|
|
*/
|
|
private function get_cache_entry($key) {
|
|
$attributes = [
|
|
'key' => $key
|
|
];
|
|
|
|
if($this::$cache_for !== null) {
|
|
$attributes['created_at'] = [
|
|
'operator' => '>',
|
|
'value' => date('Y-m-d H:i:s', strtotime('- ' . $this::$cache_for . ' seconds'))
|
|
];
|
|
}
|
|
|
|
return $this->api(
|
|
($this->resource . '_api_cache'),
|
|
'get',
|
|
[
|
|
'attributes' => $attributes
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Log to InfluxDB/Grafana.
|
|
*
|
|
* @param array $measurement Which measurement to log as.
|
|
* @param boolean $exception Whether or not this was an exception (failure
|
|
* to connect, etc).
|
|
*/
|
|
private function log_influx($measurement, $exception) {
|
|
$this->api(
|
|
'logger',
|
|
'log_influx',
|
|
[
|
|
'measurement' => $measurement,
|
|
'tags' => [
|
|
'user_id' => $this->session->get_user_id(),
|
|
'api_user_id' => $this->cora->get_api_user()['api_user_id'],
|
|
'exception' => $exception === true ? '1' : '0'
|
|
],
|
|
'fields' => [
|
|
'http_code' => (int) $this->curl_info['http_code'],
|
|
'connect_time' => round($this->curl_info['connect_time'], 4)
|
|
],
|
|
'timestamp' => $this->request_timestamp_microtime
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Log to MySQL with the complete details.
|
|
*
|
|
* @param array $curl_response The response of the cURL request.
|
|
*/
|
|
protected function log_mysql($curl_response) {
|
|
$this->api(
|
|
($this->resource . '_api_log'),
|
|
'create',
|
|
[
|
|
'attributes' => [
|
|
'api_user_id' => $this->cora->get_api_user()['api_user_id'],
|
|
'request_timestamp' => date('Y-m-d H:i:s', $this->request_timestamp),
|
|
'request' => $this->curl_info,
|
|
'response' => $curl_response,
|
|
]
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get microtime for influx.
|
|
*
|
|
* @link https://github.com/influxdata/influxdb-php
|
|
*
|
|
* @return string
|
|
*/
|
|
private function microtime() {
|
|
list($usec, $sec) = explode(' ', microtime());
|
|
return sprintf('%d%06d', $sec, $usec * 1000000);
|
|
}
|
|
}
|