From f7159b7d577e5fd7360f9453d7623caf86ecc037 Mon Sep 17 00:00:00 2001 From: Sasha Rybkin Date: Wed, 20 Nov 2024 15:32:19 +0300 Subject: [PATCH] init --- GraphQL.php | 366 +++++++++++++++++++++++++++++++++ GraphQLPagination.php | 144 +++++++++++++ GraphQLResultSaveHalper.php | 24 +++ GraphQLSchemaPagination.php | 74 +++++++ Swagger.php | 274 ++++++++++++++++++++++++ composer.json | 21 ++ schema/ApiTypes.php | 19 ++ schema/ResultSaveErrorType.php | 30 +++ schema/ResultSaveType.php | 45 ++++ schema/Types.php | 8 + 10 files changed, 1005 insertions(+) create mode 100644 GraphQL.php create mode 100644 GraphQLPagination.php create mode 100644 GraphQLResultSaveHalper.php create mode 100644 GraphQLSchemaPagination.php create mode 100644 Swagger.php create mode 100644 composer.json create mode 100644 schema/ApiTypes.php create mode 100644 schema/ResultSaveErrorType.php create mode 100644 schema/ResultSaveType.php create mode 100644 schema/Types.php diff --git a/GraphQL.php b/GraphQL.php new file mode 100644 index 0000000..faf5f7f --- /dev/null +++ b/GraphQL.php @@ -0,0 +1,366 @@ +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; + } +} diff --git a/GraphQLPagination.php b/GraphQLPagination.php new file mode 100644 index 0000000..c1e4c65 --- /dev/null +++ b/GraphQLPagination.php @@ -0,0 +1,144 @@ + '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; + } + + +} diff --git a/GraphQLResultSaveHalper.php b/GraphQLResultSaveHalper.php new file mode 100644 index 0000000..293d1d2 --- /dev/null +++ b/GraphQLResultSaveHalper.php @@ -0,0 +1,24 @@ +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'; + } +} diff --git a/GraphQLSchemaPagination.php b/GraphQLSchemaPagination.php new file mode 100644 index 0000000..4e05761 --- /dev/null +++ b/GraphQLSchemaPagination.php @@ -0,0 +1,74 @@ + [ + '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; + + } + +} \ No newline at end of file diff --git a/Swagger.php b/Swagger.php new file mode 100644 index 0000000..37843bc --- /dev/null +++ b/Swagger.php @@ -0,0 +1,274 @@ +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; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..069c495 --- /dev/null +++ b/composer.json @@ -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\\": "" + } + } +} \ No newline at end of file diff --git a/schema/ApiTypes.php b/schema/ApiTypes.php new file mode 100644 index 0000000..e7c92f0 --- /dev/null +++ b/schema/ApiTypes.php @@ -0,0 +1,19 @@ + function() { + return [ + 'code' => [ + 'type' => Type::string(), + 'description' => 'поле', + ], + 'messages' => [ + 'type' => Type::listOf(Type::string()), + 'description' => 'текст ошибки', + ], + ]; + } + ]; + + parent::__construct($config); + } +} \ No newline at end of file diff --git a/schema/ResultSaveType.php b/schema/ResultSaveType.php new file mode 100644 index 0000000..f2cbf14 --- /dev/null +++ b/schema/ResultSaveType.php @@ -0,0 +1,45 @@ + 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); + } +} \ No newline at end of file diff --git a/schema/Types.php b/schema/Types.php new file mode 100644 index 0000000..7441772 --- /dev/null +++ b/schema/Types.php @@ -0,0 +1,8 @@ +