Skip to content
This repository has been archived by the owner on Nov 1, 2018. It is now read-only.

Commit

Permalink
Прототип MetaEdnpoint ApiClient и мелкие изменения (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
arturgspb committed Nov 1, 2018
1 parent 77b25cc commit e6900e6
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 48 deletions.
38 changes: 3 additions & 35 deletions metaappscriptsdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
import os
import sys
import time
from os.path import expanduser

import requests

from metaappscriptsdk.exceptions import UnexpectedError, DbQueryError, ServerError
from metaappscriptsdk.internal import read_developer_settings
from metaappscriptsdk.logger import create_logger, eprint
from metaappscriptsdk.logger.bulk_logger import BulkLogger
from metaappscriptsdk.logger.logger import Logger
from metaappscriptsdk.schedule.Schedule import Schedule
from metaappscriptsdk.services import get_api_call_headers, process_meta_api_error_code
from metaappscriptsdk.services.ApiProxyService import ApiProxyService
from metaappscriptsdk.services.DbQueryService import DbQueryService
Expand All @@ -36,7 +35,6 @@ class MetaApp(object):
meta_url = None
api_proxy_url = None
log = Logger()
schedule = Schedule()
worker = None

# Будет поставляться в конец UserAgent
Expand Down Expand Up @@ -148,23 +146,9 @@ def __read_developer_settings(self):
При этом переменная окружения приоритетнее
:return:
"""
dev_key_path = "/.rwmeta/developer_settings.json"
is_windows = os.name == "nt"
if is_windows:
dev_key_path = dev_key_path.replace("/", "\\")
dev_key_full_path = expanduser("~") + dev_key_path
if os.path.isfile(dev_key_full_path):
with open(dev_key_full_path, 'r') as myfile:
self.developer_settings = json.loads(myfile.read())

env_developer_settings = os.environ.get('META_SERVICE_ACCOUNT_SECRET', None)
if not env_developer_settings:
env_developer_settings = os.environ.get('X-META-Developer-Settings', None)
if env_developer_settings:
self.developer_settings = json.loads(env_developer_settings)

self.developer_settings = read_developer_settings()
if not self.developer_settings:
self.log.warning(u"НЕ УСТАНОВЛЕНЫ настройки разработчика, это может приводить к проблемам в дальнейшей работе!")
self.log.warning("НЕ УСТАНОВЛЕНЫ настройки разработчика, это может приводить к проблемам в дальнейшей работе!")

def api_call(self, service, method, data, options):
"""
Expand Down Expand Up @@ -192,10 +176,6 @@ def api_call(self, service, method, data, options):

for try_idx in range(20):
try:
# Пока непонятно почему частенько получаем ошибку:
# Failed to establish a new connection: [Errno 110] Connection timed out',))
# Пока пробуем делать доп. запросы
# Так же жестко указываем timeout-ы
resp = requests.post(**request)
if resp.status_code == 200:
decoded_resp = json.loads(resp.text)
Expand Down Expand Up @@ -251,21 +231,9 @@ def native_api_call(self, service, method, data, options, multipart_form=False,

for try_idx in range(20):
try:
# Пока непонятно почему частенько получаем ошибку:
# Failed to establish a new connection: [Errno 110] Connection timed out',))
# Пока пробуем делать доп. запросы
# Так же жестко указываем timeout-ы
# resp = requests.post(**request)
resp = requests.request(http_method, **request)
if resp.status_code == 200:
return resp
# if 'data' in decoded_resp:
# return decoded_resp['data'][method]
# if 'error' in decoded_resp:
# if 'details' in decoded_resp['error']:
# eprint(decoded_resp['error']['details'])
# raise DbQueryError(decoded_resp['error'])
# raise UnexpectedResponseError()
else:
process_meta_api_error_code(resp.status_code, request, resp.text)
except (requests.exceptions.ConnectionError, ConnectionError, TimeoutError) as e:
Expand Down
112 changes: 112 additions & 0 deletions metaappscriptsdk/apiclient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import requests

import json
import time
import base64

from metaappscriptsdk import read_developer_settings
from metaappscriptsdk.exceptions import SDKError
from metaappscriptsdk.internal import read_cfg, write_cfg

AUTH_CACHE_FILE = "/.rwmeta_auth_cache.json"
OAUTH_TOKEN_URL = "https://account.devision.io/oauth2/token"


class ApiClient:
"""
Упрощает взаимодействие с внутренними API Devision
"""

def __init__(self, host, api_version, access_token=None, refresh_token=None, client_id=None, client_secret=None):
"""
:type app: metaappscriptsdk.MetaApp
"""
self.host = host
self.api_version = api_version
self.access_token = access_token
self.refresh_token = refresh_token
self.client_id = client_id
self.client_secret = client_secret

def get(self, method_path, get_params=None):
return self.request("GET", method_path, get_params)

def post(self, method_path, get_params=None, post_data=None):
return self.request("POST", method_path, get_params, post_data)

def request(self, http_method, method_path, get_params=None, post_data=None):
auth_cache = read_cfg(AUTH_CACHE_FILE)
if auth_cache:
if self.refresh_token == auth_cache.get('refresh_token', ''):
self.access_token = self.__fetch_and_validate_access_token(auth_cache)

if not self.access_token:
self.refresh_access_token()

if not self.access_token:
raise ValueError("access_token должен был быть получен")

for try_idx in range(2):
url = self.host + '/' + self.api_version + '/' + method_path
headers = {"Authorization": "Bearer " + self.access_token}

req_param = {
"method": http_method,
"url": url,
"data": get_params,
"json": post_data,
"headers": headers
}
resp = requests.request(**req_param)
if resp.status_code == 401:
self.refresh_access_token()
continue

return resp.json()

def refresh_access_token(self):
resp = requests.post(OAUTH_TOKEN_URL, data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"refresh_token": self.refresh_token,
"grant_type": "refresh_token",
}, headers={
"Content-Type": "application/x-www-form-urlencoded"
})

json = resp.json()
if json.get('error'):
raise SDKError(json)
self.access_token = json.get('access_token')

write_cfg(AUTH_CACHE_FILE, {
"refresh_token": self.refresh_token,
"access_token": self.access_token
})

@staticmethod
def build_from_developer_settings(api_name: str, api_version: str):
"""
:param api_name: Example hello
:param api_version: Example v1, v2alpha
:return: ApiClient
"""
developer_settings = read_developer_settings()

api_host = "http://" + api_name + ".apis.devision.io"
return ApiClient(
host=api_host,
api_version=api_version,
access_token=None,
refresh_token=developer_settings['refreshToken'],
client_id=developer_settings['clientId'],
client_secret=developer_settings['clientSecret'],
)

@staticmethod
def __fetch_and_validate_access_token(auth_cache):
access_token = auth_cache.get('access_token', '')
t = json.loads(base64.b64decode(access_token.split(".")[1] + "=").decode(encoding="utf-8"))
if time.time() > t.get('exp'):
return None
return access_token
12 changes: 12 additions & 0 deletions metaappscriptsdk/examples/apiservice/hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from metaappscriptsdk.apiclient import ApiClient

api = ApiClient.build_from_developer_settings("hello", "v1")

# Установите другой URL api если надо
# api.host = "http://localhost:8084"

resp = api.request("hello/ping", post_data={
"ping": "Hi!!!"
})

print(u"resp = %s" % str(resp))
1 change: 1 addition & 0 deletions metaappscriptsdk/examples/metaql/assets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
2 changes: 1 addition & 1 deletion metaappscriptsdk/info.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '0.6.22'
__version__ = '0.7.0'
__package_name__ = 'metaappscriptsdk'

if __name__ == "__main__":
Expand Down
73 changes: 73 additions & 0 deletions metaappscriptsdk/internal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
ВНИМАНИЕ! Только для использования внутри кода SDK
Запрещено добавление общих утилит в этот файл
"""
import platform
from os.path import expanduser

import os

import json


def read_developer_settings():
"""
Читает конфигурации разработчика с локальной машины или из переменных окружения
При этом переменная окружения приоритетнее
:return: dict|None
"""
ret = read_cfg("/.rwmeta/developer_settings.json")

env_developer_settings = os.environ.get('META_SERVICE_ACCOUNT_SECRET', None)
if not env_developer_settings:
env_developer_settings = os.environ.get('X-META-Developer-Settings', None)
if env_developer_settings:
ret = json.loads(env_developer_settings)

return ret


def read_cfg(path) -> dict:
"""
:param path: example: "/.rwmeta/developer_settings.json"
:return: dict
"""
ret = None
full_path = __build_path(path)
if os.path.isfile(full_path):
with open(full_path, 'r') as myfile:
ret = json.loads(myfile.read())
return ret


def write_cfg(path, value) -> None:
"""
:param path: example: "/.rwmeta/developer_settings.json"
:param value: dict
"""
full_path = __build_path(path)
with open(full_path, 'w') as myfile:
myfile.write(json.dumps(value))


def __build_path(path):
if OS_NAME == "windows":
path = path.replace("/", "\\")
full_path = expanduser("~") + path
return full_path


def __get_os():
if os.name == "nt":
return "windows"

if platform.system() == "Darwin":
return "macos"

return "linux"


OS_NAME = __get_os()
13 changes: 5 additions & 8 deletions metaappscriptsdk/services/StarterService.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ def update_task_result_data(self, task):
"result_data": json.dumps(task['result_data'])
})

def await_task(self, task_id, service_id, callback_fn=None, sleep_sec=15):
def await_task(self, task_id, service_id, callback_fn, sleep_sec=15):
"""
Подождать выполнения задачи запускатора
:param task_id: ID задачи, за которой нужно следить
:param service_id: ID сервиса
:param callback_fn: Функция обратного вызова, в нее будет передаваться task_info и is_finish как признак, что обработка завершена
:param callback_fn: Функция обратного вызова, в нее будет передаваться task_info
:param sleep_sec: задержка между проверкой по БД. Не рекомендуется делать меньше 10, так как это может очень сильно ударить по производительности БД
:return: void
"""
Expand All @@ -64,13 +64,10 @@ def await_task(self, task_id, service_id, callback_fn=None, sleep_sec=15):
if task_info is None:
break

is_finish = task_info['status'] != 'NEW' and task_info['status'] != 'PROCESSING'
# Уведомляем вызывающего
callback_fn(task_info)

if callback_fn:
# Уведомляем вызывающего
callback_fn(task_info, is_finish)

if is_finish:
if task_info['status'] != 'NEW' and task_info['status'] != 'PROCESSING':
break

def submit(self, service_id: str, data: dict = None):
Expand Down
6 changes: 5 additions & 1 deletion metaappscriptsdk/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Супер мелкие функции, которые нужны в от 3 исптозований
"""
Супер мелкие функции, которые нужны от 3 использований
"""
import json
import jwt

Expand Down
8 changes: 5 additions & 3 deletions metaappscriptsdk/worker.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import json
import os

import time


class Worker:
"""
Класс для обработки фоновых задач на запускаторе
"""

def __init__(self, app, stdin):
"""
:type app: metaappscriptsdk.MetaApp
Expand Down Expand Up @@ -60,4 +62,4 @@ def __get_tasks(self):

@prop.getter
def current_task(self):
return self.__current_task
return self.__current_task

0 comments on commit e6900e6

Please sign in to comment.