<?php namespace App\Classes\External; use GuzzleHttp\Client; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; abstract class Supplier { private const LOGKEY = 'AS-'; protected $o = NULL; protected $_columns = []; public const traffic_connection_keys = ['user','pass','url']; public function __construct(Model $o) { $this->o = $o; $this->_columns = collect(); } /** * Connect and pull down traffic data * * @param array $connection * @param string $type * @return Collection * @throws \Exception */ public function fetch(array $connection,string $type): Collection { if (count(array_intersect(array_keys($connection),self::traffic_connection_keys)) !== 3) throw new \Exception('No or missing connection details for:'.$type); if ($x=$this->mustPause()) { Log::notice(sprintf('%s:API Throttle, waiting [%s]...',self::LOGKEY,$x),['m'=>__METHOD__]); sleep($x); } Log::debug(sprintf('%s:Supplier [%d], fetch data for [%s]...',self::LOGKEY,$this->o->id,Arr::get($connection,'last')),['m'=>__METHOD__]); $key = 'Supplier:'.$this->o->id.Arr::get($connection,'last'); $result = Cache::remember($key,86400,function() use ($connection) { $response = Http::get(Arr::get($connection,'url'),[ $this->login_user_field => Arr::get($connection,'user'), $this->login_pass_field => Arr::get($connection,'pass'), $this->date_field => Arr::get($connection,'last'), ]); // @todo These API rate limiting is untested. $api_remain = $response->header('X-RateLimit-Remaining'); $api_reset = $response->header('X-RateLimit-Reset'); if ($api_remain === 0 AND $api_reset) { Log::notice(sprintf('%s:API Throttle [%d].',self::LOGKEY,$api_reset),['m'=>__METHOD__]); Cache::put('api_throttle',$api_reset,now()->addSeconds($api_reset)); } // Assume the supplier provides an ASCII output for text/html if (preg_match('#^text/html;#',$x=$response->header('Content-Type'))) { return collect(explode("\n",$response->body()))->filter(); } else { Log::error(sprintf('%s:Havent handled header type [%s]',self::LOGKEY,$x),['m'=>__METHOD__]); throw new \Exception('Unhandled Content Type'); } }); Log::debug(sprintf('%s:Supplier [%d], records returned [%d]...',self::LOGKEY,$this->o->id,$result->count()),['m'=>__METHOD__]); return $result; } /** * Return the API HTTP client * @return Client */ protected function getClient(): Client { return new Client(['base_uri'=>$this->o->stats_url]); } /** * Return the expected columns from a supplier traffic import * * @param string $line * @param Collection $expect * @return Collection */ public function getColumns(string $line,Collection $expect): Collection { $fields = collect(explode(',',$line))->filter(); $this->_columns = $expect; if ($this->_columns->diff($fields)->count()) { abort('500','Missing columns in data: '.join('|',$this->_columns->diff($fields)->toArray()).' got: '.join('|',$fields)); } return $fields->intersect($this->_columns); } /** * Return the key ID for a column * * @param string $key * @return mixed */ public function getColumnKey(string $key) { return $this->_columns->search($key); } public function header(): array { return static::$header; } /** * If the supplier has API throttling... * * @return mixed */ protected function mustPause() { return Cache::get('api_throttle'); } }