init
This commit is contained in:
commit
f7159b7d57
|
@ -0,0 +1,366 @@
|
||||||
|
<?php
|
||||||
|
namespace dominion\api;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\console\Exception;
|
||||||
|
use linslin\yii2\curl;
|
||||||
|
|
||||||
|
class GraphQL
|
||||||
|
{
|
||||||
|
private $url;
|
||||||
|
private $authUrl;
|
||||||
|
private $refreshUrl;
|
||||||
|
private $login;
|
||||||
|
private $pass;
|
||||||
|
private $query = '';
|
||||||
|
private $variables = [];
|
||||||
|
private static $jwt;
|
||||||
|
|
||||||
|
private static $jwtKeyCache = 'jwt';
|
||||||
|
private static $jwtRefreshKeyCache = 'refresh-token';
|
||||||
|
|
||||||
|
function __construct($key = 'apiGraphQL')
|
||||||
|
{
|
||||||
|
$this->getConfig($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConfig($key = 'apiGraphQL')
|
||||||
|
{
|
||||||
|
if(empty(Yii::$app->params[$key])){
|
||||||
|
throw new Exception('Отсутствует конфигурация для rest-сервиса');
|
||||||
|
}
|
||||||
|
if(!isset(Yii::$app->params[$key]['url'])){
|
||||||
|
throw Exception('Отсутствует конфигурация для "url"');
|
||||||
|
}
|
||||||
|
if(!isset(Yii::$app->params[$key]['authUrl'])){
|
||||||
|
throw new Exception('Отсутствует конфигурация для "authUrl"');
|
||||||
|
}
|
||||||
|
if(!isset(Yii::$app->params[$key]['refreshUrl'])){
|
||||||
|
throw new Exception('Отсутствует конфигурация для "refreshUrl"');
|
||||||
|
}
|
||||||
|
if(!isset(Yii::$app->params[$key]['login'])){
|
||||||
|
throw new Exception('Отсутствует конфигурация для "login"');
|
||||||
|
}
|
||||||
|
if(!isset(Yii::$app->params[$key]['pass'])){
|
||||||
|
throw new Exception('Отсутствует конфигурация для "pass"');
|
||||||
|
}
|
||||||
|
$this->url = Yii::$app->params[$key]['url'];
|
||||||
|
$this->authUrl = Yii::$app->params[$key]['authUrl'];
|
||||||
|
$this->refreshUrl = Yii::$app->params[$key]['refreshUrl'];
|
||||||
|
$this->login = Yii::$app->params[$key]['login'];
|
||||||
|
$this->pass = Yii::$app->params[$key]['pass'];
|
||||||
|
|
||||||
|
self::$jwtKeyCache = 'jwt'.$key;
|
||||||
|
self::$jwtRefreshKeyCache = 'refresh-token'.$key;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuery($str, $settings = '')
|
||||||
|
{
|
||||||
|
$this->query = "query {$settings} {" . $str . "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMutation($str, $settings = '')
|
||||||
|
{
|
||||||
|
$this->query = "mutation {$settings} {" . $str . "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setVariables($array)
|
||||||
|
{
|
||||||
|
$this->variables = $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMutationVariables($str, $array = [])
|
||||||
|
{
|
||||||
|
$arSettings = [];
|
||||||
|
$variables = [];
|
||||||
|
foreach ($array as $key =>$value)
|
||||||
|
{
|
||||||
|
$arSettings[] = "\${$key}: {$value['type']}";
|
||||||
|
$type = str_replace('!', '', trim($value['type']));
|
||||||
|
$variables[$key] = null;
|
||||||
|
if(mb_substr(trim($value['type']), -1) == '!' || $value['value'] !== null)
|
||||||
|
{
|
||||||
|
switch (mb_strtolower($type)){
|
||||||
|
case 'int':
|
||||||
|
$variables[$key] = (int)$value['value'];
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
$variables[$key] = (bool)$value['value'];
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
$variables[$key] = (string)$value['value'];
|
||||||
|
break;
|
||||||
|
case '[int]':
|
||||||
|
$variables[$key] = self::intValue($value['value']);
|
||||||
|
break;
|
||||||
|
case '[string]':
|
||||||
|
$variables[$key] = self::stringValue($value['value']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$settingsStr = 'settings(' . implode(', ', $arSettings) .')';
|
||||||
|
$this->setMutation($str, $settingsStr);
|
||||||
|
$this->setVariables($variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue($attribute = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$value = $this->sendRequest();
|
||||||
|
}
|
||||||
|
catch (\Exception $ex)
|
||||||
|
{
|
||||||
|
$value = $this->sendRequest();
|
||||||
|
}
|
||||||
|
$output = isset($value['data']) ? $value['data'] : [];
|
||||||
|
if(!empty($attribute))
|
||||||
|
{
|
||||||
|
$output = isset($output[$attribute]) ? $output[$attribute] : [];
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshToken()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(($refreshToken = Yii::$app->cache->get(self::$jwtRefreshKeyCache)) !== false)
|
||||||
|
{
|
||||||
|
$curl = new curl\Curl();
|
||||||
|
$curl->setHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
$curl->setHeader('cookie', $refreshToken);
|
||||||
|
$result = $curl->post($this->refreshUrl,false);
|
||||||
|
if(isset($result["token"]))
|
||||||
|
{
|
||||||
|
self::$jwt = $result["token"];
|
||||||
|
Yii::$app->cache->set(self::$jwtKeyCache, self::$jwt, self::getJwtExtDelta());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
throw new Exception($e->getMessage() . "\n". "url:\n " . $this->refreshUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login($update = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if($update || (self::$jwt = Yii::$app->cache->get(self::$jwtKeyCache)) == false)
|
||||||
|
{
|
||||||
|
$curl = new curl\Curl();
|
||||||
|
$curl->setHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
$curl->setRequestBody(json_encode(['login' => $this->login, 'pass' => $this->pass]));
|
||||||
|
|
||||||
|
$result = $curl->post($this->authUrl,false);
|
||||||
|
if(isset($result["token"]))
|
||||||
|
{
|
||||||
|
self::$jwt = $result["token"];
|
||||||
|
Yii::$app->cache->set(self::$jwtKeyCache, self::$jwt, self::getJwtExtDelta());
|
||||||
|
preg_match_all('/^\s*([^;]*)/mi', $curl->responseHeaders["Set-Cookie"], $matches);
|
||||||
|
$cookies = array();
|
||||||
|
foreach($matches[1] as $item) {
|
||||||
|
parse_str($item, $cookie);
|
||||||
|
$cookies = array_merge($cookies, $cookie);
|
||||||
|
}
|
||||||
|
if(isset($cookies["refresh-token"]))
|
||||||
|
{
|
||||||
|
Yii::$app->cache->set(self::$jwtRefreshKeyCache, 'refresh-token='.$cookies["refresh-token"], 3600*24*100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception('Нет токена');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
throw new Exception($e->getMessage() . "\n". "url:\n " . $this->authUrl);
|
||||||
|
}
|
||||||
|
return self::$jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getJwtParam($key, $default = false)
|
||||||
|
{
|
||||||
|
$output = $default;
|
||||||
|
self::$jwt = self::$jwt ? self::$jwt: Yii::$app->cache->get(self::$jwtKeyCache);
|
||||||
|
if(self::$jwt)
|
||||||
|
{
|
||||||
|
$params = explode('.', self::$jwt);
|
||||||
|
if(isset($params[1]))
|
||||||
|
{
|
||||||
|
$value = json_decode(base64_decode($params[1]), true);
|
||||||
|
$output = isset($value[$key]) ? $value[$key] : $output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getJwtExtDelta()
|
||||||
|
{
|
||||||
|
$jwtExp = self::getJwtParam('exp');
|
||||||
|
return $jwtExp - time() - 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJwt()
|
||||||
|
{
|
||||||
|
self::$jwt = self::$jwt ? self::$jwt: Yii::$app->cache->get(self::$jwtKeyCache);
|
||||||
|
if(self::$jwt)
|
||||||
|
{
|
||||||
|
if(self::getJwtExtDelta() <= 0)
|
||||||
|
{
|
||||||
|
self::$jwt = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!self::$jwt)
|
||||||
|
{
|
||||||
|
$this->refreshToken();
|
||||||
|
}
|
||||||
|
if(self::$jwt)
|
||||||
|
{
|
||||||
|
$role = self::getJwtParam('role', 'anon');
|
||||||
|
if($role == 'anon')
|
||||||
|
{
|
||||||
|
$this->login(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!self::$jwt)
|
||||||
|
{
|
||||||
|
$this->login();
|
||||||
|
}
|
||||||
|
return self::$jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function sendRequest()
|
||||||
|
{
|
||||||
|
$curl = new curl\Curl();
|
||||||
|
$curl->setHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
$curl->setHeader('Authorization', 'Bearer ' . $this->getJwt());
|
||||||
|
$body = json_encode([
|
||||||
|
'query' => $this->query,
|
||||||
|
'variables' => $this->variables,
|
||||||
|
]);
|
||||||
|
if(isset(\Yii::$app->params['CURL_TIMEOUT']) && \Yii::$app->params['CURL_TIMEOUT'] > 0)
|
||||||
|
{
|
||||||
|
$curl->setOption(CURLOPT_TIMEOUT, \Yii::$app->params['CURL_TIMEOUT']);
|
||||||
|
}
|
||||||
|
$curl->setRequestBody($body);
|
||||||
|
$response = $curl->post($this->url,false);
|
||||||
|
if(isset($response['errors']))
|
||||||
|
{
|
||||||
|
throw new Exception('graphql error ' . json_encode($response) . "Тело:\n " . $body);
|
||||||
|
}
|
||||||
|
$errorMessage = ($curl->responseCode != 200) ? ("\n". print_r($response, true) . "Тело:\n " . $body) : '';
|
||||||
|
switch ($curl->responseCode) {
|
||||||
|
case 'timeout':
|
||||||
|
throw new Exception('Network timeout'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
return $response;
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
throw new Exception('404. Page not found'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
throw new Exception('403. Access deny'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
throw new Exception('401. Autorization required'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
throw new Exception('400. Bad request'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
throw new Exception('500. Internal server error'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 503:
|
||||||
|
throw new Exception('503. Service unavailable'.$errorMessage);
|
||||||
|
break;
|
||||||
|
case 502:
|
||||||
|
throw new Exception('502. Bad gateway'.$errorMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQueryPaginction($object, $select, $filter = [], $page = null, $perPage = null, $wrapper = 'data')
|
||||||
|
{
|
||||||
|
$page = $page === null ? (isset($_GET['page']) ? $_GET['page'] : 1) : $page;
|
||||||
|
$perPage = $perPage === null ? \Yii::$app->user->identity->countPage : $perPage;
|
||||||
|
$filterStr = empty($filter) ? '' : ('(' . implode(', ', $filter) . ')');
|
||||||
|
$this->setQuery(" {$object}(page:{$page}, perPage:{$perPage}) { {$wrapper} {$filterStr} { {$select} }, totalCount }");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSortFilters($filter, $params, $default = 'id_desc', $field = 'sort')
|
||||||
|
{
|
||||||
|
$sort = $default;
|
||||||
|
if (isset($params[$field]))
|
||||||
|
{
|
||||||
|
$sort = (mb_substr($params[$field], 0, 1) == '-') ? (mb_substr($params[$field], 1).'_desc') : ( $params[$field].'_asc');
|
||||||
|
}
|
||||||
|
$filter[] = "sort: \"{$sort}\"";
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilterModel($type, $filter, $model, $fields)
|
||||||
|
{
|
||||||
|
foreach ($fields as $key)
|
||||||
|
{
|
||||||
|
if(!empty($model->{$key}) || $model->{$key} === true || $model->{$key} === false || $model->{$key} === "0" || $model->{$key} === 0 )
|
||||||
|
{
|
||||||
|
switch ($type){
|
||||||
|
case "string":
|
||||||
|
$filter[] = "{$key}: \"{$model->$key}\"";
|
||||||
|
break;
|
||||||
|
case "number":
|
||||||
|
$value = (float)$model->$key;
|
||||||
|
$filter[] = "{$key}: {$value}";
|
||||||
|
break;
|
||||||
|
case "bool":
|
||||||
|
$value = $model->$key ? 'true' : 'false';
|
||||||
|
$filter[] = "{$key}: {$value}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilterString($filter, $model, $fields)
|
||||||
|
{
|
||||||
|
return $this->setFilterModel('string', $filter, $model, $fields);
|
||||||
|
}
|
||||||
|
public function setFilterNumber($filter, $model, $fields)
|
||||||
|
{
|
||||||
|
return $this->setFilterModel('number', $filter, $model, $fields);
|
||||||
|
}
|
||||||
|
public function setFilterBool($filter, $model, $fields)
|
||||||
|
{
|
||||||
|
return $this->setFilterModel('bool', $filter, $model, $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function intValue($array)
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
foreach($array as $id)
|
||||||
|
{
|
||||||
|
$output[] = (int)$id;
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
public static function stringValue($array)
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
foreach($array as $id)
|
||||||
|
{
|
||||||
|
$output[] = (string)$id;
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace dominion\api;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
|
||||||
|
class GraphQLPagination
|
||||||
|
{
|
||||||
|
public $parentRoot;
|
||||||
|
public $page = 1;
|
||||||
|
public $perPage = 25;
|
||||||
|
public $offset = 0;
|
||||||
|
public $totalCount = 0;
|
||||||
|
public $pageCount = 0;
|
||||||
|
public $currentPage = 0;
|
||||||
|
public $sort = '';
|
||||||
|
public $data = [];
|
||||||
|
public $addArgs = [];
|
||||||
|
|
||||||
|
|
||||||
|
public static $arPrefix = [
|
||||||
|
'<' => 'lt',
|
||||||
|
'>' => 'gt',
|
||||||
|
'<=' => 'lte',
|
||||||
|
'>=' => 'gte',
|
||||||
|
'!=' => 'neq',
|
||||||
|
'IN' => 'in',
|
||||||
|
'NOT IN' => 'nin',
|
||||||
|
// 'LIKE' => 'like',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function setMeta($totalCount, $perPage)
|
||||||
|
{
|
||||||
|
$this->totalCount = $totalCount - $this->offset;
|
||||||
|
$this->perPage = $perPage;
|
||||||
|
$this->currentPage = $this->page;
|
||||||
|
$this->pageCount = ceil($this->totalCount/ $this->perPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMetaByQuery($query)
|
||||||
|
{
|
||||||
|
$this->setMeta($query->count(), $this->perPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function andWhere($query, $params, $fields, $prefix = '')
|
||||||
|
{
|
||||||
|
$prefix = empty($prefix) ? '' : ($prefix . '.');
|
||||||
|
foreach($fields as $item)
|
||||||
|
{
|
||||||
|
if(isset($params[$item]))
|
||||||
|
{
|
||||||
|
$query->andWhere([$prefix . $item => is_bool($params[$item]) ? (int)$params[$item] : $params[$item]]);
|
||||||
|
}
|
||||||
|
foreach (self::$arPrefix as $key => $pref)
|
||||||
|
{
|
||||||
|
$newKey = "{$pref}_{$item}";
|
||||||
|
if(isset($params[$newKey]))
|
||||||
|
{
|
||||||
|
$query->andWhere([$key, $prefix . $item, $params[$newKey]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function andWhereLike($query, $params, $fields, $prefix = '')
|
||||||
|
{
|
||||||
|
$prefix = empty($prefix) ? '' : ($prefix . '.');
|
||||||
|
foreach($fields as $item)
|
||||||
|
{
|
||||||
|
if(isset($params[$item]))
|
||||||
|
{
|
||||||
|
$query->andWhere(['like', $prefix . $item, $params[$item]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function orderBy($query, $params, $fields, $sortParam = 'sort', $prefix = '')
|
||||||
|
{
|
||||||
|
$prefix = empty($prefix) ? '' : ($prefix . '.');
|
||||||
|
$sort = isset($params[$sortParam]) ? $params[$sortParam] : (isset($params['in_'.$sortParam]) ? $params['in_'.$sortParam] : []);
|
||||||
|
if(!empty($sort))
|
||||||
|
{
|
||||||
|
$sort = is_array($sort) ? $sort : (array)$sort;
|
||||||
|
$orderBy = [];
|
||||||
|
foreach($sort as $item)
|
||||||
|
{
|
||||||
|
$value = explode('_', $item);
|
||||||
|
if(count($value) == 2 && in_array($value[0], $fields) && in_array($value[1], ['asc', 'desc']))
|
||||||
|
{
|
||||||
|
$orderBy[$prefix . $value[0]] = $value[1] == 'asc' ? SORT_ASC : SORT_DESC;
|
||||||
|
}
|
||||||
|
elseif($item == 'rand')
|
||||||
|
{
|
||||||
|
$orderBy['RAND()'] = SORT_ASC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!empty($orderBy))
|
||||||
|
{
|
||||||
|
$query->orderBy($orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getItemsByPage($query)
|
||||||
|
{
|
||||||
|
$offset = $this->perPage * ($this->page -1);
|
||||||
|
$offset += $this->offset;
|
||||||
|
return $query->limit($this->perPage)->offset($offset)->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function argumentModify($args = [])
|
||||||
|
{
|
||||||
|
foreach ($args as $key=> $value)
|
||||||
|
{
|
||||||
|
if(isset($value['type']) && /*in_array($value['type'], [Type::int()]) &&*/ isset($value['modify']) && $value['modify'])
|
||||||
|
{
|
||||||
|
$arPrefix = isset($value['modifyOption']) ? (array)$value['modifyOption'] : self::$arPrefix;
|
||||||
|
if(str_replace($arPrefix, '', $key) == $key)
|
||||||
|
{
|
||||||
|
foreach ($arPrefix as $prefix)
|
||||||
|
{
|
||||||
|
$newKey = "{$prefix}_{$key}";
|
||||||
|
if (!isset($args[$newKey]))
|
||||||
|
{
|
||||||
|
$args[$newKey] = $value;
|
||||||
|
if(in_array($prefix, ['in', 'nin']))
|
||||||
|
{
|
||||||
|
$args[$newKey]['type'] = Type::listOf($args[$newKey]['type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
namespace dominion\api;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
|
||||||
|
class GraphQLResultSaveHalper
|
||||||
|
{
|
||||||
|
public static function setResultValue($model, $value)
|
||||||
|
{
|
||||||
|
$model->id = isset($value["id"]) ? $value["id"] : 0;
|
||||||
|
if(isset($value["errors"]))
|
||||||
|
{
|
||||||
|
foreach ($value["errors"] as $errors)
|
||||||
|
{
|
||||||
|
foreach ($errors['messages'] as $message)
|
||||||
|
{
|
||||||
|
$model->addError($errors['code'], $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isset($value['status']) && $value['status'] == 'success';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace dominion\api;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use dominion\api\GraphQLPagination;
|
||||||
|
|
||||||
|
class GraphQLSchemaPagination extends ObjectType
|
||||||
|
{
|
||||||
|
public $isPagination = false;
|
||||||
|
public function getCustomFields()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'totalCount' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Количество элементов',
|
||||||
|
],
|
||||||
|
'pageCount' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Количество страниц',
|
||||||
|
],
|
||||||
|
'currentPage' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Текущая страница',
|
||||||
|
],
|
||||||
|
'perPage' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Элементов на странице',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomResolve($root, $args)
|
||||||
|
{
|
||||||
|
$model = new GraphQLPagination;
|
||||||
|
$model->parentRoot = $root;
|
||||||
|
$model->page = isset($args['page']) && $args['page'] > 1 ? $args['page'] : 1;
|
||||||
|
$model->perPage = isset($args['perPage']) && $args['perPage'] > 0 ? $args['perPage'] : 50;
|
||||||
|
$model->offset = isset($args['offset']) && $args['offset'] > 0 ? $args['offset'] : 0;
|
||||||
|
$model->addArgs = isset($args['addArgs']) ? (array)$args['addArgs'] : [];
|
||||||
|
|
||||||
|
return $model;//['totalCount' =>0, 'pageCount' => 0, 'currentPage' => 0, 'perPage'=> 0, 'data' => []];
|
||||||
|
}
|
||||||
|
public function getCustomArgs()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Номер страницы (по умолчанию 1)',
|
||||||
|
],
|
||||||
|
'perPage' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Количество элементов на странице (по умолчанию 50)',
|
||||||
|
],
|
||||||
|
'offset' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Смещение от начала выборки',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resultArguments($args)
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
foreach ($args as $key => $value)
|
||||||
|
{
|
||||||
|
$output[$key] = json_decode($value, true);
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
<?php
|
||||||
|
namespace dominion\api;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\console\Exception;
|
||||||
|
use linslin\yii2\curl;
|
||||||
|
|
||||||
|
class Swagger
|
||||||
|
{
|
||||||
|
private $url;
|
||||||
|
private $method;
|
||||||
|
private $perPage;
|
||||||
|
private $perPageParam;
|
||||||
|
private $pageParam;
|
||||||
|
private $params;
|
||||||
|
private $content;
|
||||||
|
private $currentPage = 1;
|
||||||
|
private $lastPage = false;
|
||||||
|
public $isRaw = false;
|
||||||
|
|
||||||
|
function __construct($apiName, $function, $params = [], $urlParams = [], $version = false, $raw = false)
|
||||||
|
{
|
||||||
|
$arConfig = $this->getConfig($apiName, $function, $urlParams, $version);
|
||||||
|
$this->url = $arConfig['url'];
|
||||||
|
$this->method = $arConfig['method'];
|
||||||
|
$this->perPage = $arConfig['perPage'];
|
||||||
|
$this->perPageParam = $arConfig['perPageParam'];
|
||||||
|
$this->pageParam = $arConfig['pageParam'];
|
||||||
|
$this->content = $arConfig['content'];
|
||||||
|
$this->params = $params;
|
||||||
|
$this->isRaw = $raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConfig($apiName, $function, $urlParams = [], $version = false)
|
||||||
|
{
|
||||||
|
$configParams = isset(Yii::$app->params[$apiName]) ? Yii::$app->params[$apiName] : [];
|
||||||
|
if(empty($configParams)) {
|
||||||
|
throw new Exception('Отсутствует конфигурация для rest-сервиса');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['defaultVersion']) or (empty($configParams['defaultVersion']))) {
|
||||||
|
throw new Exception('Отсутствует дефолтная версия для rest-сервиса');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['defaultPerPage']) or (empty($configParams['defaultPerPage']))) {
|
||||||
|
throw new Exception('Отсутствует дефолтная конфигурация постраничной навигации rest-сервиса');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['protocol']) or empty($configParams['protocol'])) {
|
||||||
|
throw new Exception('Протокол rest-сервиса не указан.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($configParams['protocol'] !== 'http://') and ($configParams['protocol'] !== 'https://')) {
|
||||||
|
throw new Exception('Протокол rest-сервиса указан неверно.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['perPageParam']) or empty($configParams['perPageParam'])) {
|
||||||
|
throw new Exception('Отсутствует параметр постраничной навигации perPageParam.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['pageParam']) or empty($configParams['pageParam'])) {
|
||||||
|
throw new Exception('Отсутствует парамер постраничной навигации pageParam.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['baseUrl']) or empty($configParams['baseUrl'])) {
|
||||||
|
throw new Exception('Отсутствует доменное имя для rest-сервиса.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($version)) {
|
||||||
|
$version = $configParams['defaultVersion'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($configParams['version'][$version])) {
|
||||||
|
throw new Exception('Выбранная версия rest-сервиса отсутствует');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['version'][$version]['functions'][$function])) {
|
||||||
|
throw new Exception('Функция '.$function.' отсутствует в api версии '.$version);
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = '';
|
||||||
|
if(is_string($configParams['version'][$version]['functions'][$function]))
|
||||||
|
{
|
||||||
|
$url = $configParams['version'][$version]['functions'][$function];
|
||||||
|
}
|
||||||
|
elseif(isset($configParams['version'][$version]['functions'][$function]['url']))
|
||||||
|
{
|
||||||
|
$url = $configParams['version'][$version]['functions'][$function]['url'];
|
||||||
|
}
|
||||||
|
if(!isset($url) or empty($url)) {
|
||||||
|
throw new Exception('Для функции '.$function.' отсутствует url');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($configParams['version'][$version]['functions'][$function]['content']))
|
||||||
|
{
|
||||||
|
$content = $configParams['version'][$version]['functions'][$function]['content'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$content = $configParams['version'][$version]['content'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($content) or empty($content)) {
|
||||||
|
throw new Exception('Для функции '.$function.' отсутствует ключ массива в котором содержится ответ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['version'][$version]['functions'][$function]['perPage']) or empty($configParams['version'][$version]['functions'][$function]['perPage'])) {
|
||||||
|
$perPage = $configParams['defaultPerPage'];
|
||||||
|
} else {
|
||||||
|
$perPage = $configParams['version'][$version]['functions'][$function]['perPage'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($configParams['version'][$version]['functions'][$function]['method']) or
|
||||||
|
empty($configParams['version'][$version]['functions'][$function]['method']))
|
||||||
|
{
|
||||||
|
$method = isset($configParams['version'][$version]['method']) ? $configParams['version'][$version]['method'] : 'GET';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$method = $configParams['version'][$version]['functions'][$function]['method'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($urlParams)) {
|
||||||
|
$url = vsprintf($url, $urlParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'url' => $configParams['protocol']
|
||||||
|
.$configParams['baseUrl']
|
||||||
|
.$configParams['version'][$version]['url']
|
||||||
|
.$url,
|
||||||
|
'method' => $method,
|
||||||
|
'perPage' => $perPage,
|
||||||
|
'perPageParam' => $configParams['perPageParam'],
|
||||||
|
'pageParam' => $configParams['pageParam'],
|
||||||
|
'content' => $content,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetCounter()
|
||||||
|
{
|
||||||
|
$this->currentPage = 1;
|
||||||
|
$this->lastPage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNextPage()
|
||||||
|
{
|
||||||
|
if($this->lastPage) return false;
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$response = $this->sendRequest($this->currentPage);
|
||||||
|
|
||||||
|
if(isset($response[$this->content]))
|
||||||
|
{
|
||||||
|
foreach ($response[$this->content] as $value)
|
||||||
|
{
|
||||||
|
$result[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(
|
||||||
|
!isset($response['_meta']['pageCount']) or
|
||||||
|
empty($response['_meta']['pageCount']) or
|
||||||
|
$response['_meta']['pageCount'] === $this->currentPage
|
||||||
|
) {
|
||||||
|
$this->lastPage = true;
|
||||||
|
} else {
|
||||||
|
$this->currentPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOne()
|
||||||
|
{
|
||||||
|
$result = $this->getNextPage();
|
||||||
|
return isset($result[0]) ? $result[0] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doBatchRequest()
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$page = 1;
|
||||||
|
while(true) {
|
||||||
|
$response = $this->sendRequest($page);
|
||||||
|
|
||||||
|
if (isset($response[$this->content]) and is_array($response[$this->content]) and !empty($response[$this->content]) ) {
|
||||||
|
foreach ($response[$this->content] as $value) {
|
||||||
|
$result[] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
!isset($response['_meta']['pageCount']) or
|
||||||
|
empty($response['_meta']['pageCount']) or
|
||||||
|
$response['_meta']['pageCount'] === $page
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($response);
|
||||||
|
$page++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBody()
|
||||||
|
{
|
||||||
|
return $this->isRaw ? json_encode($this->params) : json_encode(['filter' => $this->params]) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendRequest($page = 1)
|
||||||
|
{
|
||||||
|
$curl = new curl\Curl();
|
||||||
|
$curl->setHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
$params = [$this->perPageParam => $this->perPage, $this->pageParam => $page];
|
||||||
|
if (!empty($this->sort))
|
||||||
|
{
|
||||||
|
$params['sort'] = $this->sort;
|
||||||
|
}
|
||||||
|
if($this->method === 'POST')
|
||||||
|
{
|
||||||
|
$curl->setGetParams($params);
|
||||||
|
if(!empty($this->params))
|
||||||
|
{
|
||||||
|
$curl->setRequestBody($this->getBody());
|
||||||
|
}
|
||||||
|
$response = $curl->post($this->url,false);
|
||||||
|
}
|
||||||
|
elseif($this->method === 'GET') {
|
||||||
|
$curl->setGetParams(array_merge($params, ['filter' => $this->params]));
|
||||||
|
$response = $curl->get($this->url,false);
|
||||||
|
} else {
|
||||||
|
throw new Exception('Метод '.$this->method.' не поддерживается');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($curl->responseCode) {
|
||||||
|
case 'timeout':
|
||||||
|
throw new Exception('Network timeout');
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
return $response;
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
throw new Exception('404. Page not found '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
throw new Exception('403. Access deny '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
throw new Exception('401. Autorization required '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
throw new Exception('400. Bad request '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
throw new Exception('500. Internal server error '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 503:
|
||||||
|
throw new Exception('503. Service unavailable '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
case 502:
|
||||||
|
throw new Exception('502. Bad gateway '. $this->url . ' ' . $this->getBody());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function setSort($str)
|
||||||
|
{
|
||||||
|
$this->sort = $str;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "dominion/api",
|
||||||
|
"description": "Функционал для работы с api",
|
||||||
|
"type": "yii2-extension",
|
||||||
|
"keywords": ["yii2","extension"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Rybkin Sasha",
|
||||||
|
"email": "ribkin@dominion.ru"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"yiisoft/yii2": "~2.0.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"dominion\\api\\": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace dominion\api\schema;
|
||||||
|
|
||||||
|
trait ApiTypes
|
||||||
|
{
|
||||||
|
private static $resultSave;
|
||||||
|
private static $resultSaveError;
|
||||||
|
|
||||||
|
|
||||||
|
public static function resultSave()
|
||||||
|
{
|
||||||
|
return self::$resultSave ?: (self::$resultSave = new ResultSaveType());
|
||||||
|
}
|
||||||
|
public static function resultSaveError()
|
||||||
|
{
|
||||||
|
return self::$resultSaveError ?: (self::$resultSaveError = new ResultSaveErrorType());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace dominion\api\schema;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
|
||||||
|
class ResultSaveErrorType extends ObjectType
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$config = [
|
||||||
|
'fields' => function() {
|
||||||
|
return [
|
||||||
|
'code' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'description' => 'поле',
|
||||||
|
],
|
||||||
|
'messages' => [
|
||||||
|
'type' => Type::listOf(Type::string()),
|
||||||
|
'description' => 'текст ошибки',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
parent::__construct($config);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace dominion\api\schema;
|
||||||
|
|
||||||
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
|
||||||
|
class ResultSaveType extends ObjectType
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$config = [
|
||||||
|
'fields' => function() {
|
||||||
|
return [
|
||||||
|
'id' => [
|
||||||
|
'type' => Type::int(),
|
||||||
|
'description' => 'Id',
|
||||||
|
],
|
||||||
|
'status' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'description' => 'status',
|
||||||
|
'resolve' => function($root, $args) {
|
||||||
|
return empty($root->errors) ? 'success' : 'error';
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'errors' => [
|
||||||
|
'type' => Type::listOf(Types::resultSaveError()),
|
||||||
|
'description' => 'ошибки',
|
||||||
|
'resolve' => function($root, $args) {
|
||||||
|
$output = [];
|
||||||
|
foreach ($root->errors as $key => $value)
|
||||||
|
{
|
||||||
|
$output[] = ['code' => $key, 'messages'=> $value];
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
parent::__construct($config);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace dominion\api\schema;
|
||||||
|
|
||||||
|
class Types
|
||||||
|
{
|
||||||
|
use ApiTypes;
|
||||||
|
}
|
Loading…
Reference in New Issue