Skip to content
Snippets Groups Projects
Commit a769f9a7 authored by Mohammad Torkashvand's avatar Mohammad Torkashvand
Browse files

add graphql

parent 11234ba7
Branches
Tags
1 merge request!205add graphql
Pipeline #86673 passed
......@@ -5,19 +5,22 @@ from gso import monkeypatches # noqa: F401, isort:skip
import typer
from orchestrator import OrchestratorCore, app_settings
from orchestrator.cli.main import app as cli_app
from orchestrator.graphql import SCALAR_OVERRIDES
# noinspection PyUnresolvedReferences
import gso.products
import gso.workflows # noqa: F401
from gso.api import router as api_router
from gso.middlewares import ModifyProcessEndpointResponse
from gso.graphql.types import GSO_SCALAR_OVERRIDES
SCALAR_OVERRIDES.update(GSO_SCALAR_OVERRIDES)
def init_gso_app() -> OrchestratorCore:
"""Initialise the :term:`GSO` app."""
app = OrchestratorCore(base_settings=app_settings)
app.register_graphql()
app.include_router(api_router, prefix="/api")
app.add_middleware(ModifyProcessEndpointResponse)
return app
......
"""graphql module."""
"""Map some Orchestrator types to scalars."""
from ipaddress import IPv4Network, IPv6Network
from typing import Any, NewType
import strawberry
from orchestrator.graphql.types import serialize_to_string
from strawberry.custom_scalar import ScalarDefinition, ScalarWrapper
IPv4NetworkType = strawberry.scalar(
NewType("IPv4NetworkType", str),
description="Represent the Orchestrator IPv4Network data type",
serialize=serialize_to_string,
parse_value=lambda v: v,
)
IPv6NetworkType = strawberry.scalar(
NewType("IPv6NetworkType", str),
description="Represent the Orchestrator IPv6Network data type",
serialize=serialize_to_string,
parse_value=lambda v: v,
)
GSO_SCALAR_OVERRIDES: dict[object, Any | ScalarWrapper | ScalarDefinition] = {
IPv4Network: IPv4NetworkType,
IPv6Network: IPv6NetworkType,
}
"""Custom middlewares for the GSO API."""
import json
import re
from collections.abc import Callable
from typing import Any
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
from starlette.status import HTTP_200_OK
class ModifyProcessEndpointResponse(BaseHTTPMiddleware):
"""Middleware to modify the response for Process details endpoint."""
async def dispatch(self, request: Request, call_next: Callable) -> Response:
"""Middleware to modify the response for Process details endpoint.
:param request: The incoming HTTP request.
:type request: Request
:param call_next: The next middleware or endpoint in the stack.
:type call_next: Callable
:return: The modified HTTP response.
:rtype: Response
"""
response = await call_next(request)
path_pattern = re.compile(
r"/api/processes/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})"
)
match = path_pattern.match(request.url.path)
if match and response.status_code == HTTP_200_OK:
# Modify the response body as needed
response_body = b""
async for chunk in response.body_iterator:
response_body += chunk
try:
json_content = json.loads(response_body)
await self._modify_response_body(json_content, request)
modified_response_body = json.dumps(json_content).encode()
headers = dict(response.headers)
headers["content-length"] = str(len(modified_response_body))
return Response(
content=modified_response_body,
status_code=response.status_code,
headers=headers,
media_type=response.media_type,
)
except json.JSONDecodeError:
pass
return response
@staticmethod
async def _get_token(request: Request) -> str:
"""Get the token from the request headers.
:param request: The incoming HTTP request.
:type request: Request
:return: The token from the request headers in specific format.
:rtype: str
"""
bearer_prefix = "Bearer "
authorization_header = request.headers.get("Authorization")
if authorization_header:
# Remove the "Bearer " prefix from the token
token = authorization_header.replace(bearer_prefix, "")
return f"?token={token}"
return ""
async def _modify_response_body(self, response_body: dict[str, Any], request: Request) -> None:
"""Modify the response body as needed.
:param response_body: The response body in dictionary format.
:type response_body: dict[str, Any]
:param request: The incoming HTTP request.
:type request: Request
:return: None
"""
max_output_length = 500
token = await self._get_token(request)
try:
for step in response_body["steps"]:
if step["state"].get("callback_result", None):
callback_result = step["state"]["callback_result"]
if callback_result and isinstance(callback_result, str):
callback_result = json.loads(callback_result)
if callback_result.get("output") and len(callback_result["output"]) > max_output_length:
callback_result["output"] = (
f'{request.base_url}api/v1/processes/steps/{step["step_id"]}/callback-results{token}'
)
step["state"]["callback_result"] = callback_result
except (AttributeError, KeyError, TypeError):
pass
......@@ -7,11 +7,12 @@ oauth2_lib package to meet specific requirements of the gso application.
import oauth2_lib.fastapi
import oauth2_lib.settings
from gso.auth.oidc_policy_helper import HTTPX_SSL_CONTEXT, OIDCUser, OIDCUserModel, opa_decision
from gso.auth.oidc_policy_helper import HTTPX_SSL_CONTEXT, OIDCUser, OIDCUserModel, _get_decision, opa_decision
from gso.auth.settings import oauth2lib_settings
oauth2_lib.fastapi.OIDCUser = OIDCUser # type: ignore[assignment, misc]
oauth2_lib.fastapi.OIDCUserModel = OIDCUserModel # type: ignore[assignment, misc]
oauth2_lib.fastapi.opa_decision = opa_decision # type: ignore[assignment]
oauth2_lib.fastapi._get_decision = _get_decision # type: ignore[assignment] # noqa: SLF001
oauth2_lib.fastapi.HTTPX_SSL_CONTEXT = HTTPX_SSL_CONTEXT
oauth2_lib.settings.oauth2lib_settings = oauth2lib_settings # type: ignore[assignment]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment