Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions polyapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ def {function_name}(
return {api_response_type}(resp.json()) # type: ignore


async def {function_name}_async(
{args}
) -> {api_response_type}:
\"""{function_description}

Function ID: {function_id}
\"""
if get_direct_execute_config():
resp = await direct_execute_async("{function_type}", "{function_id}", {data})
return {api_response_type}({{
"status": resp.status_code,
"headers": dict(resp.headers),
"data": resp.json()
}}) # type: ignore
else:
resp = await execute_async("{function_type}", "{function_id}", {data})
return {api_response_type}(resp.json()) # type: ignore


"""


Expand Down
33 changes: 33 additions & 0 deletions polyapi/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ def introspectToken(token: str) -> AuthFunctionResponse:
url = "/auth-providers/{function_id}/introspect"
resp = execute_post(url, {{"token": token}})
return resp.json()


async def introspectToken_async(token: str) -> AuthFunctionResponse:
\"""{description}

Function ID: {function_id}
\"""
url = "/auth-providers/{function_id}/introspect"
resp = await execute_post_async(url, {{"token": token}})
return resp.json()
"""

REFRESH_TOKEN_TEMPLATE = """
Expand All @@ -128,6 +138,16 @@ def refreshToken(token: str) -> AuthFunctionResponse:
url = "/auth-providers/{function_id}/refresh"
resp = execute_post(url, {{"token": token}})
return resp.json()


async def refreshToken_async(token: str) -> AuthFunctionResponse:
\"""{description}

Function ID: {function_id}
\"""
url = "/auth-providers/{function_id}/refresh"
resp = await execute_post_async(url, {{"token": token}})
return resp.json()
"""

REVOKE_TOKEN_TEMPLATE = """
Expand All @@ -142,6 +162,19 @@ def revokeToken(token: str) -> Optional[AuthFunctionResponse]:
return resp.json()
except:
return None


async def revokeToken_async(token: str) -> Optional[AuthFunctionResponse]:
\"""{description}

Function ID: {function_id}
\"""
url = "/auth-providers/{function_id}/revoke"
resp = await execute_post_async(url, {{"token": token}})
try:
return resp.json()
except:
return None
"""


Expand Down
221 changes: 168 additions & 53 deletions polyapi/execute.py
Original file line number Diff line number Diff line change
@@ -1,109 +1,224 @@
from typing import Dict, Optional
import requests
import httpx
import os
import logging
from requests import Response
from polyapi.config import get_api_key_and_url, get_mtls_config
from polyapi.exceptions import PolyApiException
from polyapi import http_client

logger = logging.getLogger("poly")

def direct_execute(function_type, function_id, data) -> Response:
""" execute a specific function id/type
"""
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"

endpoint_info = requests.post(url, json=data, headers=headers)
if endpoint_info.status_code < 200 or endpoint_info.status_code >= 300:
error_content = endpoint_info.content.decode("utf-8", errors="ignore")
def _check_response_error(resp, function_type, function_id, data):
if resp.status_code < 200 or resp.status_code >= 300:
error_content = resp.content.decode("utf-8", errors="ignore")
if function_type == 'api' and os.getenv("LOGS_ENABLED"):
raise PolyApiException(f"Error executing api function with id: {function_id}. Status code: {endpoint_info.status_code}. Request data: {data}, Response: {error_content}")
logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
elif function_type != 'api':
raise PolyApiException(f"{endpoint_info.status_code}: {error_content}")

endpoint_info_data = endpoint_info.json()
raise PolyApiException(f"{resp.status_code}: {error_content}")


def _check_endpoint_error(resp, function_type, function_id, data):
if resp.status_code < 200 or resp.status_code >= 300:
error_content = resp.content.decode("utf-8", errors="ignore")
if function_type == 'api' and os.getenv("LOGS_ENABLED"):
raise PolyApiException(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
elif function_type != 'api':
raise PolyApiException(f"{resp.status_code}: {error_content}")


def _build_direct_execute_params(endpoint_info_data):
request_params = endpoint_info_data.copy()
request_params.pop("url", None)

if "maxRedirects" in request_params:
request_params["allow_redirects"] = request_params.pop("maxRedirects") > 0

request_params["follow_redirects"] = request_params.pop("maxRedirects") > 0
return request_params


def _sync_direct_execute(function_type, function_id, data) -> httpx.Response:
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"

endpoint_info = http_client.post(url, json=data, headers=headers)
_check_endpoint_error(endpoint_info, function_type, function_id, data)

endpoint_info_data = endpoint_info.json()
request_params = _build_direct_execute_params(endpoint_info_data)

has_mtls, cert_path, key_path, ca_path = get_mtls_config()


# Direct-execute hits URL that may need custom TLS
# settings (mTLS certs or disabled verification). httpx Client.request()
# doesn't accept per-request transport kwargs, so use one-off calls.
if has_mtls:
resp = requests.request(
resp = httpx.request(
url=endpoint_info_data["url"],
cert=(cert_path, key_path),
verify=ca_path,
timeout=None,
**request_params
)
else:
resp = requests.request(
resp = httpx.request(
url=endpoint_info_data["url"],
verify=False,
timeout=None,
**request_params
)

if (resp.status_code < 200 or resp.status_code >= 300):
error_content = resp.content.decode("utf-8", errors="ignore")
if function_type == 'api' and os.getenv("LOGS_ENABLED"):
logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
elif function_type != 'api':
raise PolyApiException(f"{resp.status_code}: {error_content}")

_check_response_error(resp, function_type, function_id, data)
return resp


async def _async_direct_execute(function_type, function_id, data) -> httpx.Response:
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"

endpoint_info = await http_client.async_post(url, json=data, headers=headers)
_check_endpoint_error(endpoint_info, function_type, function_id, data)

endpoint_info_data = endpoint_info.json()
request_params = _build_direct_execute_params(endpoint_info_data)

has_mtls, cert_path, key_path, ca_path = get_mtls_config()

# One-off async client for custom TLS settings on external URLs.
if has_mtls:
async with httpx.AsyncClient(
cert=(cert_path, key_path), verify=ca_path, timeout=None
) as client:
resp = await client.request(
url=endpoint_info_data["url"], **request_params
)
else:
async with httpx.AsyncClient(verify=False, timeout=None) as client:
resp = await client.request(
url=endpoint_info_data["url"], **request_params
)

_check_response_error(resp, function_type, function_id, data)
return resp

def execute(function_type, function_id, data) -> Response:
""" execute a specific function id/type

def direct_execute(function_type, function_id, data) -> httpx.Response:
""" execute a specific function id/type (sync)
"""
return _sync_direct_execute(function_type, function_id, data)


async def direct_execute_async(function_type, function_id, data) -> httpx.Response:
""" execute a specific function id/type (async)
"""
return await _async_direct_execute(function_type, function_id, data)


def _sync_execute(function_type, function_id, data) -> httpx.Response:
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{api_url}/functions/{function_type}/{function_id}/execute"

resp = http_client.post(url, json=data, headers=headers)
_check_response_error(resp, function_type, function_id, data)
return resp


async def _async_execute(function_type, function_id, data) -> httpx.Response:
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{api_url}/functions/{function_type}/{function_id}/execute"

# Make the request
resp = requests.post(
url,
json=data,
headers=headers,
)

if (resp.status_code < 200 or resp.status_code >= 300) and os.getenv("LOGS_ENABLED"):
error_content = resp.content.decode("utf-8", errors="ignore")
if function_type == 'api' and os.getenv("LOGS_ENABLED"):
logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
elif function_type != 'api':
raise PolyApiException(f"{resp.status_code}: {error_content}")

resp = await http_client.async_post(url, json=data, headers=headers)
_check_response_error(resp, function_type, function_id, data)
return resp


def execute_post(path, data):
def execute(function_type, function_id, data) -> httpx.Response:
""" execute a specific function id/type (sync)
"""
return _sync_execute(function_type, function_id, data)


async def execute_async(function_type, function_id, data) -> httpx.Response:
""" execute a specific function id/type (async)
"""
return await _async_execute(function_type, function_id, data)


def _sync_execute_post(path, data):
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
return http_client.post(api_url + path, json=data, headers=headers)


async def _async_execute_post(path, data):
api_key, api_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
resp = requests.post(api_url + path, json=data, headers=headers)
return await http_client.async_post(api_url + path, json=data, headers=headers)


def execute_post(path, data):
return _sync_execute_post(path, data)


async def execute_post_async(path, data):
return await _async_execute_post(path, data)


def _sync_variable_get(variable_id: str) -> httpx.Response:
api_key, base_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{base_url}/variables/{variable_id}/value"
resp = http_client.get(url, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
error_content = resp.content.decode("utf-8", errors="ignore")
raise PolyApiException(f"{resp.status_code}: {error_content}")
return resp


def variable_get(variable_id: str) -> Response:
async def _async_variable_get(variable_id: str) -> httpx.Response:
api_key, base_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{base_url}/variables/{variable_id}/value"
resp = requests.get(url, headers=headers)
resp = await http_client.async_get(url, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
error_content = resp.content.decode("utf-8", errors="ignore")
raise PolyApiException(f"{resp.status_code}: {error_content}")
return resp


def variable_update(variable_id: str, value) -> Response:
def variable_get(variable_id: str) -> httpx.Response:
return _sync_variable_get(variable_id)


async def variable_get_async(variable_id: str) -> httpx.Response:
return await _async_variable_get(variable_id)


def _sync_variable_update(variable_id: str, value) -> httpx.Response:
api_key, base_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{base_url}/variables/{variable_id}"
resp = requests.patch(url, data={"value": value}, headers=headers)
resp = http_client.patch(url, data={"value": value}, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
error_content = resp.content.decode("utf-8", errors="ignore")
raise PolyApiException(f"{resp.status_code}: {error_content}")
return resp
return resp


async def _async_variable_update(variable_id: str, value) -> httpx.Response:
api_key, base_url = get_api_key_and_url()
headers = {"Authorization": f"Bearer {api_key}"}
url = f"{base_url}/variables/{variable_id}"
resp = await http_client.async_patch(url, data={"value": value}, headers=headers)
if resp.status_code != 200 and resp.status_code != 201:
error_content = resp.content.decode("utf-8", errors="ignore")
raise PolyApiException(f"{resp.status_code}: {error_content}")
return resp


def variable_update(variable_id: str, value) -> httpx.Response:
return _sync_variable_update(variable_id, value)


async def variable_update_async(variable_id: str, value) -> httpx.Response:
return await _async_variable_update(variable_id, value)
7 changes: 3 additions & 4 deletions polyapi/function_cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import sys
from typing import Any, List, Optional
import requests

from polyapi import http_client
from polyapi.config import get_api_key_and_url
from polyapi.utils import get_auth_headers, print_green, print_red, print_yellow
from polyapi.parser import parse_function_code, get_jsonschema_type
Expand Down Expand Up @@ -87,7 +86,7 @@ def function_add_or_update(
sys.exit(1)

headers = get_auth_headers(api_key)
resp = requests.post(url, headers=headers, json=data)
resp = http_client.post(url, headers=headers, json=data)
if resp.status_code in [200, 201]:
print_green("DEPLOYED")
function_id = resp.json()["id"]
Expand Down Expand Up @@ -126,5 +125,5 @@ def spec_delete(function_type: str, function_id: str):
print(f"Unknown function type: {function_type}")
sys.exit(1)
headers = get_auth_headers(api_key)
resp = requests.delete(url, headers=headers)
resp = http_client.delete(url, headers=headers)
return resp
Loading