Added gql base #181
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 7s
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 7s
This commit is contained in:
@@ -3,7 +3,7 @@ from starlette.responses import JSONResponse
|
|||||||
from cpl.api.api_module import ApiModule
|
from cpl.api.api_module import ApiModule
|
||||||
from cpl.api.application.web_app import WebApp
|
from cpl.api.application.web_app import WebApp
|
||||||
from cpl.application.application_builder import ApplicationBuilder
|
from cpl.application.application_builder import ApplicationBuilder
|
||||||
from cpl.auth import AuthModule
|
from cpl.graphql.application.graphql_app import GraphQLApp
|
||||||
from cpl.auth.permission.permissions import Permissions
|
from cpl.auth.permission.permissions import Permissions
|
||||||
from cpl.auth.schema import AuthUser, Role
|
from cpl.auth.schema import AuthUser, Role
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
@@ -11,12 +11,13 @@ from cpl.core.console import Console
|
|||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.utils.cache import Cache
|
from cpl.core.utils.cache import Cache
|
||||||
from cpl.database.mysql.mysql_module import MySQLModule
|
from cpl.database.mysql.mysql_module import MySQLModule
|
||||||
|
from cpl.graphql.graphql_module import GraphQLModule
|
||||||
from scoped_service import ScopedService
|
from scoped_service import ScopedService
|
||||||
from service import PingService
|
from service import PingService
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
builder = ApplicationBuilder[WebApp](WebApp)
|
builder = ApplicationBuilder[GraphQLApp](GraphQLApp)
|
||||||
|
|
||||||
Configuration.add_json_file(f"appsettings.json")
|
Configuration.add_json_file(f"appsettings.json")
|
||||||
Configuration.add_json_file(f"appsettings.{Environment.get_environment()}.json")
|
Configuration.add_json_file(f"appsettings.{Environment.get_environment()}.json")
|
||||||
@@ -27,6 +28,7 @@ def main():
|
|||||||
builder.services.add_transient(PingService)
|
builder.services.add_transient(PingService)
|
||||||
builder.services.add_module(MySQLModule)
|
builder.services.add_module(MySQLModule)
|
||||||
builder.services.add_module(ApiModule)
|
builder.services.add_module(ApiModule)
|
||||||
|
builder.services.add_module(GraphQLModule)
|
||||||
|
|
||||||
builder.services.add_scoped(ScopedService)
|
builder.services.add_scoped(ScopedService)
|
||||||
|
|
||||||
@@ -47,6 +49,9 @@ def main():
|
|||||||
permissions=[Permissions.administrator],
|
permissions=[Permissions.administrator],
|
||||||
)
|
)
|
||||||
app.with_routes_directory("routes")
|
app.with_routes_directory("routes")
|
||||||
|
app.with_graphql()
|
||||||
|
app.with_playground()
|
||||||
|
app.with_graphiql()
|
||||||
|
|
||||||
provider = builder.service_provider
|
provider = builder.service_provider
|
||||||
user_cache = provider.get_service(Cache[AuthUser])
|
user_cache = provider.get_service(Cache[AuthUser])
|
||||||
|
|||||||
45
src/cpl-api/cpl/api/abc/web_app_abc.py
Normal file
45
src/cpl-api/cpl/api/abc/web_app_abc.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from abc import ABC
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
|
||||||
|
from cpl.api.model.api_route import ApiRoute
|
||||||
|
from cpl.api.model.validation_match import ValidationMatch
|
||||||
|
from cpl.api.typing import HTTPMethods, PartialMiddleware, TEndpoint, PolicyInput
|
||||||
|
from cpl.application.abc.application_abc import ApplicationABC
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
from cpl.dependency.typing import Modules
|
||||||
|
|
||||||
|
|
||||||
|
class WebAppABC(ApplicationABC, ABC):
|
||||||
|
|
||||||
|
def __init__(self, services: ServiceProvider, modules: Modules, required_modules: list[str | object] = None):
|
||||||
|
super().__init__(services, modules, required_modules)
|
||||||
|
|
||||||
|
def with_routes_directory(self, directory: str) -> Self: ...
|
||||||
|
def with_app(self, app: Starlette) -> Self: ...
|
||||||
|
def with_routes(
|
||||||
|
self,
|
||||||
|
routes: list[ApiRoute],
|
||||||
|
method: HTTPMethods,
|
||||||
|
authentication: bool = False,
|
||||||
|
roles: list[str | Enum] = None,
|
||||||
|
permissions: list[str | Enum] = None,
|
||||||
|
policies: list[str] = None,
|
||||||
|
match: ValidationMatch = None,
|
||||||
|
) -> Self: ...
|
||||||
|
def with_route(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
fn: TEndpoint,
|
||||||
|
method: HTTPMethods,
|
||||||
|
authentication: bool = False,
|
||||||
|
roles: list[str | Enum] = None,
|
||||||
|
permissions: list[str | Enum] = None,
|
||||||
|
policies: list[str] = None,
|
||||||
|
match: ValidationMatch = None,
|
||||||
|
) -> Self: ...
|
||||||
|
def with_middleware(self, middleware: PartialMiddleware) -> Self: ...
|
||||||
|
def with_authentication(self) -> Self: ...
|
||||||
|
def with_authorization(self, *policies: list[PolicyInput] | PolicyInput) -> Self: ...
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Mapping, Any, Callable, Self, Union
|
from typing import Mapping, Any, Self
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
@@ -10,6 +10,7 @@ from starlette.requests import Request
|
|||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
from starlette.types import ExceptionHandler
|
from starlette.types import ExceptionHandler
|
||||||
|
|
||||||
|
from cpl.api.abc.web_app_abc import WebAppABC
|
||||||
from cpl.api.api_module import ApiModule
|
from cpl.api.api_module import ApiModule
|
||||||
from cpl.api.error import APIError
|
from cpl.api.error import APIError
|
||||||
from cpl.api.logger import APILogger
|
from cpl.api.logger import APILogger
|
||||||
@@ -24,8 +25,7 @@ from cpl.api.registry.policy import PolicyRegistry
|
|||||||
from cpl.api.registry.route import RouteRegistry
|
from cpl.api.registry.route import RouteRegistry
|
||||||
from cpl.api.router import Router
|
from cpl.api.router import Router
|
||||||
from cpl.api.settings import ApiSettings
|
from cpl.api.settings import ApiSettings
|
||||||
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
|
from cpl.api.typing import HTTPMethods, PartialMiddleware, TEndpoint, PolicyInput
|
||||||
from cpl.application.abc.application_abc import ApplicationABC
|
|
||||||
from cpl.auth.auth_module import AuthModule
|
from cpl.auth.auth_module import AuthModule
|
||||||
from cpl.auth.permission.permission_module import PermissionsModule
|
from cpl.auth.permission.permission_module import PermissionsModule
|
||||||
from cpl.core.configuration.configuration import Configuration
|
from cpl.core.configuration.configuration import Configuration
|
||||||
@@ -33,12 +33,10 @@ from cpl.dependency.inject import inject
|
|||||||
from cpl.dependency.service_provider import ServiceProvider
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
from cpl.dependency.typing import Modules
|
from cpl.dependency.typing import Modules
|
||||||
|
|
||||||
PolicyInput = Union[dict[str, PolicyResolver], Policy]
|
|
||||||
|
|
||||||
|
class WebApp(WebAppABC):
|
||||||
class WebApp(ApplicationABC):
|
def __init__(self, services: ServiceProvider, modules: Modules, required_modules: list[str | object] = None):
|
||||||
def __init__(self, services: ServiceProvider, modules: Modules):
|
super().__init__(services, modules, [AuthModule, PermissionsModule, ApiModule] + (required_modules or []))
|
||||||
super().__init__(services, modules, [AuthModule, PermissionsModule, ApiModule])
|
|
||||||
self._app: Starlette | None = None
|
self._app: Starlette | None = None
|
||||||
|
|
||||||
self._logger = services.get_service(APILogger)
|
self._logger = services.get_service(APILogger)
|
||||||
@@ -78,16 +76,17 @@ class WebApp(ApplicationABC):
|
|||||||
self._logger.debug(f"Allowed origins: {origins}")
|
self._logger.debug(f"Allowed origins: {origins}")
|
||||||
return origins.split(",")
|
return origins.split(",")
|
||||||
|
|
||||||
def with_app(self, app: Starlette) -> Self:
|
|
||||||
assert app is not None, "app must not be None"
|
|
||||||
assert isinstance(app, Starlette), "app must be an instance of Starlette"
|
|
||||||
self._app = app
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _check_for_app(self):
|
def _check_for_app(self):
|
||||||
if self._app is not None:
|
if self._app is not None:
|
||||||
raise ValueError("App is already set, cannot add routes or middleware")
|
raise ValueError("App is already set, cannot add routes or middleware")
|
||||||
|
|
||||||
|
def _validate_policies(self):
|
||||||
|
for rule in Router.get_authorization_rules():
|
||||||
|
for policy_name in rule["policies"]:
|
||||||
|
policy = self._policies.get(policy_name)
|
||||||
|
if not policy:
|
||||||
|
self._logger.fatal(f"Authorization policy '{policy_name}' not found")
|
||||||
|
|
||||||
def with_routes_directory(self, directory: str) -> Self:
|
def with_routes_directory(self, directory: str) -> Self:
|
||||||
self._check_for_app()
|
self._check_for_app()
|
||||||
assert directory is not None, "directory must not be None"
|
assert directory is not None, "directory must not be None"
|
||||||
@@ -102,6 +101,12 @@ class WebApp(ApplicationABC):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def with_app(self, app: Starlette) -> Self:
|
||||||
|
assert app is not None, "app must not be None"
|
||||||
|
assert isinstance(app, Starlette), "app must be an instance of Starlette"
|
||||||
|
self._app = app
|
||||||
|
return self
|
||||||
|
|
||||||
def with_routes(
|
def with_routes(
|
||||||
self,
|
self,
|
||||||
routes: list[ApiRoute],
|
routes: list[ApiRoute],
|
||||||
@@ -131,7 +136,7 @@ class WebApp(ApplicationABC):
|
|||||||
def with_route(
|
def with_route(
|
||||||
self,
|
self,
|
||||||
path: str,
|
path: str,
|
||||||
fn: Callable[[Request], Any],
|
fn: TEndpoint,
|
||||||
method: HTTPMethods,
|
method: HTTPMethods,
|
||||||
authentication: bool = False,
|
authentication: bool = False,
|
||||||
roles: list[str | Enum] = None,
|
roles: list[str | Enum] = None,
|
||||||
@@ -179,6 +184,7 @@ class WebApp(ApplicationABC):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def with_authorization(self, *policies: list[PolicyInput] | PolicyInput) -> Self:
|
def with_authorization(self, *policies: list[PolicyInput] | PolicyInput) -> Self:
|
||||||
|
self._check_for_app()
|
||||||
if policies:
|
if policies:
|
||||||
_policies = []
|
_policies = []
|
||||||
|
|
||||||
@@ -206,13 +212,6 @@ class WebApp(ApplicationABC):
|
|||||||
self.with_middleware(AuthorizationMiddleware)
|
self.with_middleware(AuthorizationMiddleware)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _validate_policies(self):
|
|
||||||
for rule in Router.get_authorization_rules():
|
|
||||||
for policy_name in rule["policies"]:
|
|
||||||
policy = self._policies.get(policy_name)
|
|
||||||
if not policy:
|
|
||||||
self._logger.fatal(f"Authorization policy '{policy_name}' not found")
|
|
||||||
|
|
||||||
async def main(self):
|
async def main(self):
|
||||||
self._logger.debug(f"Preparing API")
|
self._logger.debug(f"Preparing API")
|
||||||
self._validate_policies()
|
self._validate_policies()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import Union, Literal, Callable, Type, Awaitable
|
|||||||
from urllib.request import Request
|
from urllib.request import Request
|
||||||
|
|
||||||
from starlette.middleware import Middleware
|
from starlette.middleware import Middleware
|
||||||
|
from starlette.responses import Response
|
||||||
from starlette.types import ASGIApp
|
from starlette.types import ASGIApp
|
||||||
from starlette.websockets import WebSocket
|
from starlette.websockets import WebSocket
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
|||||||
from cpl.auth.schema import AuthUser
|
from cpl.auth.schema import AuthUser
|
||||||
|
|
||||||
TRequest = Union[Request, WebSocket]
|
TRequest = Union[Request, WebSocket]
|
||||||
|
TEndpoint = Callable[[TRequest, ...], Awaitable[Response]] | Callable[[TRequest, ...], Response]
|
||||||
HTTPMethods = Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
|
HTTPMethods = Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
|
||||||
PartialMiddleware = Union[
|
PartialMiddleware = Union[
|
||||||
ASGIMiddleware,
|
ASGIMiddleware,
|
||||||
@@ -17,3 +19,4 @@ PartialMiddleware = Union[
|
|||||||
Callable[[ASGIApp], ASGIApp],
|
Callable[[ASGIApp], ASGIApp],
|
||||||
]
|
]
|
||||||
PolicyResolver = Callable[[AuthUser], bool | Awaitable[bool]]
|
PolicyResolver = Callable[[AuthUser], bool | Awaitable[bool]]
|
||||||
|
PolicyInput = Union[dict[str, PolicyResolver], "Policy"]
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ApplicationABC(ABC):
|
|||||||
|
|
||||||
module_dependency_error(
|
module_dependency_error(
|
||||||
type(self).__name__,
|
type(self).__name__,
|
||||||
module.__name__,
|
module.__name__ if not isinstance(module, str) else module,
|
||||||
ImportError(
|
ImportError(
|
||||||
f"Required module '{module}' for application '{self.__class__.__name__}' is not loaded. Load using 'add_module({module})' method."
|
f"Required module '{module}' for application '{self.__class__.__name__}' is not loaded. Load using 'add_module({module})' method."
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from cpl.auth.permission.permissions import Permissions
|
|||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbModelABC
|
from cpl.database.abc import DbModelABC
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.dependency import ServiceProvider
|
from cpl.dependency import get_provider
|
||||||
|
|
||||||
|
|
||||||
class AuthUser(DbModelABC):
|
class AuthUser(DbModelABC):
|
||||||
@@ -87,3 +87,4 @@ class AuthUser(DbModelABC):
|
|||||||
|
|
||||||
self._keycloak_id = str(uuid.UUID(int=0))
|
self._keycloak_id = str(uuid.UUID(int=0))
|
||||||
await auth_user_dao.update(self)
|
await auth_user_dao.update(self)
|
||||||
|
|
||||||
|
|||||||
0
src/cpl-graphql/cpl/graphql/__init__.py
Normal file
0
src/cpl-graphql/cpl/graphql/__init__.py
Normal file
0
src/cpl-graphql/cpl/graphql/_endpoints/__init__.py
Normal file
0
src/cpl-graphql/cpl/graphql/_endpoints/__init__.py
Normal file
37
src/cpl-graphql/cpl/graphql/_endpoints/graphiql.py
Normal file
37
src/cpl-graphql/cpl/graphql/_endpoints/graphiql.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
|
async def graphiql_endpoint(request):
|
||||||
|
return HTMLResponse("""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>GraphiQL</title>
|
||||||
|
<link href="https://unpkg.com/graphiql@2.4.0/graphiql.min.css" rel="stylesheet" />
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;overflow:hidden;">
|
||||||
|
<div id="graphiql" style="height:100vh;"></div>
|
||||||
|
|
||||||
|
<!-- React + ReactDOM -->
|
||||||
|
<script src="https://unpkg.com/react@18.2.0/umd/react.production.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
|
||||||
|
|
||||||
|
<!-- GraphiQL -->
|
||||||
|
<script src="https://unpkg.com/graphiql@2.4.0/graphiql.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const graphQLFetcher = graphQLParams =>
|
||||||
|
fetch('/api/graphql', {
|
||||||
|
method: 'post',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(graphQLParams),
|
||||||
|
}).then(response => response.json()).catch(() => response.text());
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
React.createElement(GraphiQL, { fetcher: graphQLFetcher }),
|
||||||
|
document.getElementById('graphiql'),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
19
src/cpl-graphql/cpl/graphql/_endpoints/graphql.py
Normal file
19
src/cpl-graphql/cpl/graphql/_endpoints/graphql.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response, JSONResponse
|
||||||
|
|
||||||
|
from cpl.graphql.service import GraphQLService
|
||||||
|
|
||||||
|
|
||||||
|
async def graphql_endpoint(request: Request, service: GraphQLService) -> Response:
|
||||||
|
body = await request.json()
|
||||||
|
query = body.get("query")
|
||||||
|
variables = body.get("variables")
|
||||||
|
|
||||||
|
result = service.execute(query, variables, {"request": request})
|
||||||
|
response_data = {}
|
||||||
|
if result.errors:
|
||||||
|
response_data["errors"] = [str(e) for e in result.errors]
|
||||||
|
if result.data:
|
||||||
|
response_data["data"] = result.data
|
||||||
|
|
||||||
|
return JSONResponse(response_data)
|
||||||
27
src/cpl-graphql/cpl/graphql/_endpoints/playground.py
Normal file
27
src/cpl-graphql/cpl/graphql/_endpoints/playground.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response, HTMLResponse
|
||||||
|
|
||||||
|
|
||||||
|
async def playground_endpoint(request: Request) -> Response:
|
||||||
|
return HTMLResponse("""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8/>
|
||||||
|
<title>GraphQL Playground</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/graphql-playground-react/build/static/css/index.css" />
|
||||||
|
<link rel="shortcut icon" href="https://raw.githubusercontent.com/graphql/graphql-playground/master/packages/graphql-playground-react/public/favicon.png" />
|
||||||
|
<script src="https://unpkg.com/graphql-playground-react/build/static/js/middleware.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"/>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
GraphQLPlayground.init(document.getElementById('root'), {
|
||||||
|
endpoint: '/api/graphql'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
1
src/cpl-graphql/cpl/graphql/application/__init__.py
Normal file
1
src/cpl-graphql/cpl/graphql/application/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .graphql_app import WebApp
|
||||||
72
src/cpl-graphql/cpl/graphql/application/graphql_app.py
Normal file
72
src/cpl-graphql/cpl/graphql/application/graphql_app.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from cpl.api.application import WebApp
|
||||||
|
from cpl.api.model.validation_match import ValidationMatch
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
from cpl.dependency.typing import Modules
|
||||||
|
from .._endpoints.graphiql import graphiql_endpoint
|
||||||
|
from .._endpoints.graphql import graphql_endpoint
|
||||||
|
from .._endpoints.playground import playground_endpoint
|
||||||
|
from ..graphql_module import GraphQLModule
|
||||||
|
|
||||||
|
|
||||||
|
class GraphQLApp(WebApp):
|
||||||
|
def __init__(self, services: ServiceProvider, modules: Modules):
|
||||||
|
super().__init__(services, modules, [GraphQLModule])
|
||||||
|
|
||||||
|
def with_graphql(
|
||||||
|
self,
|
||||||
|
authentication: bool = False,
|
||||||
|
roles: list[str | Enum] = None,
|
||||||
|
permissions: list[str | Enum] = None,
|
||||||
|
policies: list[str] = None,
|
||||||
|
match: ValidationMatch = None,
|
||||||
|
):
|
||||||
|
self.with_route(
|
||||||
|
path="/api/graphql",
|
||||||
|
fn=graphql_endpoint,
|
||||||
|
method="POST",
|
||||||
|
authentication=authentication,
|
||||||
|
roles=roles,
|
||||||
|
permissions=permissions,
|
||||||
|
policies=policies,
|
||||||
|
match=match,
|
||||||
|
)
|
||||||
|
|
||||||
|
def with_graphiql(
|
||||||
|
self,
|
||||||
|
authentication: bool = False,
|
||||||
|
roles: list[str | Enum] = None,
|
||||||
|
permissions: list[str | Enum] = None,
|
||||||
|
policies: list[str] = None,
|
||||||
|
match: ValidationMatch = None,
|
||||||
|
):
|
||||||
|
self.with_route(
|
||||||
|
path="/api/graphiql",
|
||||||
|
fn=graphiql_endpoint,
|
||||||
|
method="GET",
|
||||||
|
authentication=authentication,
|
||||||
|
roles=roles,
|
||||||
|
permissions=permissions,
|
||||||
|
policies=policies,
|
||||||
|
match=match,
|
||||||
|
)
|
||||||
|
|
||||||
|
def with_playground(
|
||||||
|
self,
|
||||||
|
authentication: bool = False,
|
||||||
|
roles: list[str | Enum] = None,
|
||||||
|
permissions: list[str | Enum] = None,
|
||||||
|
policies: list[str] = None,
|
||||||
|
match: ValidationMatch = None,
|
||||||
|
):
|
||||||
|
self.with_route(
|
||||||
|
path="/api/playground",
|
||||||
|
fn=playground_endpoint,
|
||||||
|
method="GET",
|
||||||
|
authentication=authentication,
|
||||||
|
roles=roles,
|
||||||
|
permissions=permissions,
|
||||||
|
policies=policies,
|
||||||
|
match=match,
|
||||||
|
)
|
||||||
13
src/cpl-graphql/cpl/graphql/graphql_module.py
Normal file
13
src/cpl-graphql/cpl/graphql/graphql_module.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from cpl.api import ApiModule
|
||||||
|
from cpl.dependency.module.module import Module
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
from cpl.graphql.service import GraphQLService
|
||||||
|
|
||||||
|
|
||||||
|
class GraphQLModule(Module):
|
||||||
|
dependencies = [ApiModule]
|
||||||
|
scoped = [GraphQLService]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def configure(services: ServiceProvider) -> None:
|
||||||
|
pass
|
||||||
16
src/cpl-graphql/cpl/graphql/service.py
Normal file
16
src/cpl-graphql/cpl/graphql/service.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from graphene import Schema
|
||||||
|
from graphql import graphql_sync
|
||||||
|
|
||||||
|
|
||||||
|
class GraphQLService:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def execute(self, query: str, variables: dict | None = None, context: dict | None = None):
|
||||||
|
result = graphql_sync(
|
||||||
|
self._schema.graphql_schema,
|
||||||
|
query,
|
||||||
|
variable_values=variables,
|
||||||
|
context_value=context or {},
|
||||||
|
)
|
||||||
|
return result
|
||||||
30
src/cpl-graphql/pyproject.toml
Normal file
30
src/cpl-graphql/pyproject.toml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "cpl-database"
|
||||||
|
version = "2024.7.0"
|
||||||
|
description = "CPL database"
|
||||||
|
readme ="CPL database package"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
license = { text = "MIT" }
|
||||||
|
authors = [
|
||||||
|
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
|
||||||
|
]
|
||||||
|
keywords = ["cpl", "database", "backend", "shared", "library"]
|
||||||
|
|
||||||
|
dynamic = ["dependencies", "optional-dependencies"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://www.sh-edraft.de"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
include = ["cpl*"]
|
||||||
|
|
||||||
|
[tool.setuptools.dynamic]
|
||||||
|
dependencies = { file = ["requirements.txt"] }
|
||||||
|
optional-dependencies.dev = { file = ["requirements.dev.txt"] }
|
||||||
|
|
||||||
|
|
||||||
1
src/cpl-graphql/requirements.dev.txt
Normal file
1
src/cpl-graphql/requirements.dev.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
black==25.1.0
|
||||||
2
src/cpl-graphql/requirements.txt
Normal file
2
src/cpl-graphql/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cpl-api
|
||||||
|
graphene==3.4.3
|
||||||
Reference in New Issue
Block a user