keel_telegram_bot/api_client.py
import logging
from typing import List
import requests
from requests.auth import HTTPBasicAuth
from keel_telegram_bot.const import REQUESTS_TIMEOUT
LOGGER = logging.getLogger(__name__)
GET = "get"
POST = "post"
class KeelApiClient:
"""
Keel REST api client
"""
def __init__(self, host: str, port: int, ssl: bool, user: str, password: str):
self._host = host
self._port = port
self._ssl = ssl
self._auth = HTTPBasicAuth(user, password)
self._base_url = f"{'https' if ssl else 'http'}://{host}:{port}"
def get_approvals(self, rejected: bool = None, archived: bool = None) -> List[dict]:
"""
:param rejected: True for rejected, False for approved, None for all
:param archived: True for archived, False for not archived, None for all
:return: a list of all approvals matching criteria
"""
response = self._do_request(GET, self._base_url + "/v1/approvals")
if rejected is not None:
response = list(filter(lambda x: x["rejected"] == rejected, response))
if archived is not None:
response = list(filter(lambda x: x["archived"] == archived, response))
return response
def approve(self, id: str, identifier: str, voter: str) -> None:
"""
Approve a pending approval
:param id: item id
:param identifier: identifier for the approval request, something like "default/myimage:1.5.5"
:param voter: name of the voter
"""
self._do_request(POST, self._base_url + "/v1/approvals", json={
"id": id,
"identifier": identifier,
"action": "approve",
"voter": voter,
})
def reject(self, id: str, identifier: str, voter: str) -> None:
"""
Reject a pending approval
:param id: item id
:param identifier: identifier for the approval request, something like "default/myimage:1.5.5"
:param voter: name of the voter
"""
self._do_request(POST, self._base_url + "/v1/approvals", json={
"id": id,
"identifier": identifier,
"action": "reject",
"voter": voter,
})
def delete(self, id: str, identifier: str, voter: str) -> None:
"""
Delete a pending approval
:param id: item id
:param identifier: identifier for the approval request, something like "default/myimage:1.5.5"
:param voter: name of the voter
"""
self._do_request(POST, self._base_url + "/v1/approvals", json={
"id": id,
"identifier": identifier,
"action": "delete",
"voter": voter,
})
def _do_request(self, method: str = GET, url: str = "/", params: dict = None,
json: dict = None) -> list or dict or None:
"""
Executes a http request based on the given parameters
:param method: the method to use (GET, POST)
:param url: the url to use
:param params: query parameters that will be appended to the url
:param json: request body
:return: the response parsed as a json
"""
headers = []
url = self._create_request_url(url, params)
if method is GET:
method = requests.get
elif method is POST:
method = requests.post
else:
raise ValueError("Unsupported method: {}".format(method))
response = method(url, headers=headers, auth=self._auth, json=json, timeout=REQUESTS_TIMEOUT)
response.raise_for_status()
# some responses do not return data so we just ignore the body in that case
if len(response.content) > 0 and response.content != b"null":
return response.json()
@staticmethod
def _create_request_url(url: str, params: dict = None):
"""
Adds query params to the given url
:param url: the url to extend
:param params: query params as a keyed dictionary
:return: the url including the given query params
"""
if params:
first_param = True
for k, v in sorted(params.items(), key=lambda entry: entry[0]):
if not v:
# skip None values
continue
if first_param:
url += '?'
first_param = False
else:
url += '&'
url += "%s=%s" % (k, v)
return url