Further gql improvements & added test data #181
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 5s

This commit is contained in:
2025-09-27 21:57:33 +02:00
parent 2e98159d4e
commit af7945fe92
27 changed files with 305 additions and 88 deletions

View File

@@ -1,7 +1,7 @@
from starlette.responses import JSONResponse
from api.src.queries.cities import CityGraphType, CityFilter, CitySort
from api.src.queries.hello import UserGraphType
from api.src.queries.hello import UserGraphType, AuthUserFilter, AuthUserSort, AuthUserGraphType
from api.src.queries.user import UserFilter, UserSort
from cpl.api.api_module import ApiModule
from cpl.application.application_builder import ApplicationBuilder
@@ -14,9 +14,12 @@ from cpl.core.utils.cache import Cache
from cpl.database.mysql.mysql_module import MySQLModule
from cpl.graphql.application.graphql_app import GraphQLApp
from cpl.graphql.graphql_module import GraphQLModule
from model.post_dao import PostDao
from model.post_query import PostFilter, PostSort, PostGraphType
from queries.hello import HelloQuery
from scoped_service import ScopedService
from service import PingService
from test_data_seeder import TestDataSeeder
def main():
@@ -27,29 +30,38 @@ def main():
Configuration.add_json_file(f"appsettings.{Environment.get_host_name()}.json", optional=True)
# builder.services.add_logging()
builder.services.add_structured_logging()
builder.services.add_transient(PingService)
builder.services.add_module(MySQLModule)
builder.services.add_module(ApiModule)
builder.services.add_module(GraphQLModule)
(
builder.services.add_structured_logging()
.add_transient(PingService)
.add_module(MySQLModule)
.add_module(ApiModule)
.add_module(GraphQLModule)
.add_scoped(ScopedService)
.add_cache(AuthUser)
.add_cache(Role)
.add_transient(CityGraphType)
.add_transient(CityFilter)
.add_transient(CitySort)
.add_transient(UserGraphType)
.add_transient(UserFilter)
.add_transient(UserSort)
.add_transient(AuthUserGraphType)
.add_transient(AuthUserFilter)
.add_transient(AuthUserSort)
.add_transient(HelloQuery)
# posts
.add_transient(PostDao)
.add_transient(PostGraphType)
.add_transient(PostFilter)
.add_transient(PostSort)
builder.services.add_scoped(ScopedService)
builder.services.add_cache(AuthUser)
builder.services.add_cache(Role)
builder.services.add_transient(CityGraphType)
builder.services.add_transient(CityFilter)
builder.services.add_transient(CitySort)
builder.services.add_transient(UserGraphType)
builder.services.add_transient(UserFilter)
builder.services.add_transient(UserSort)
builder.services.add_transient(HelloQuery)
# test data
.add_singleton(TestDataSeeder)
)
app = builder.build()
app.with_logging()
app.with_migrations("./scripts")
app.with_authentication()
app.with_authorization()
@@ -66,6 +78,7 @@ def main():
schema = app.with_graphql()
schema.query.string_field("ping", resolver=lambda: "pong")
schema.query.with_query("hello", HelloQuery)
schema.query.dao_collection_field(PostGraphType, PostDao, "posts", PostFilter, PostSort)
app.with_playground()
app.with_graphiql()

View File

View File

@@ -0,0 +1,30 @@
from datetime import datetime
from typing import Self
from cpl.core.typing import SerialId
from cpl.database.abc import DbModelABC
class Post(DbModelABC[Self]):
def __init__(
self,
id: int,
title: str,
content: str,
deleted: bool = False,
editor_id: SerialId | None = None,
created: datetime | None = None,
updated: datetime | None = None,
):
DbModelABC.__init__(self, id, deleted, editor_id, created, updated)
self._title = title
self._content = content
@property
def title(self) -> str:
return self._title
@property
def content(self) -> str:
return self._content

View File

@@ -0,0 +1,11 @@
from cpl.database.abc import DbModelDaoABC
from model.post import Post
class PostDao(DbModelDaoABC):
def __init__(self):
DbModelDaoABC.__init__(self, Post, "posts")
self.attribute(Post.title, str)
self.attribute(Post.content, str)

View File

@@ -0,0 +1,38 @@
from cpl.graphql.schema.filter.filter import Filter
from cpl.graphql.schema.graph_type import GraphType
from cpl.graphql.schema.sort.sort import Sort
from cpl.graphql.schema.sort.sort_order import SortOrder
from model.post import Post
class PostFilter(Filter[Post]):
def __init__(self):
Filter.__init__(self)
self.field("id", int)
self.field("title", str)
self.field("content", str)
class PostSort(Sort[Post]):
def __init__(self):
Sort.__init__(self)
self.field("id", SortOrder)
self.field("title", SortOrder)
self.field("content", SortOrder)
class PostGraphType(GraphType[Post]):
def __init__(self):
GraphType.__init__(self)
self.int_field(
"id",
resolver=lambda root: root.id,
)
self.string_field(
"title",
resolver=lambda root: root.title,
)
self.string_field(
"content",
resolver=lambda root: root.content,
)

View File

@@ -1,11 +1,43 @@
from api.src.queries.cities import CityFilter, CitySort, CityGraphType, City
from api.src.queries.user import User, UserFilter, UserSort, UserGraphType
from cpl.api.middleware.request import get_request
from cpl.auth.schema import AuthUserDao, AuthUser
from cpl.graphql.schema.filter.filter import Filter
from cpl.graphql.schema.graph_type import GraphType
from cpl.graphql.schema.query import Query
from cpl.graphql.schema.sort.sort import Sort
from cpl.graphql.schema.sort.sort_order import SortOrder
users = [User(i, f"User {i}") for i in range(1, 101)]
cities = [City(i, f"City {i}") for i in range(1, 101)]
class AuthUserFilter(Filter[AuthUser]):
def __init__(self):
Filter.__init__(self)
self.field("id", int)
self.field("username", str)
class AuthUserSort(Sort[AuthUser]):
def __init__(self):
Sort.__init__(self)
self.field("id", SortOrder)
self.field("username", SortOrder)
class AuthUserGraphType(GraphType[AuthUser]):
def __init__(self):
GraphType.__init__(self)
self.int_field(
"id",
resolver=lambda root: root.id,
)
self.string_field(
"username",
resolver=lambda root: root.username,
)
class HelloQuery(Query):
def __init__(self):
Query.__init__(self)
@@ -28,3 +60,10 @@ class HelloQuery(Query):
CitySort,
resolver=lambda: cities,
)
self.dao_collection_field(
AuthUserGraphType,
AuthUserDao,
"authUsers",
AuthUserFilter,
AuthUserSort,
)

View File

@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS `posts` (
`id` INT(30) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(64) NOT NULL,
`content` VARCHAR(512) NOT NULL,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
editorId INT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
);

View File

@@ -0,0 +1,31 @@
from faker import Faker
from cpl.database.abc import DataSeederABC
from cpl.query import Enumerable
from model.post import Post
from model.post_dao import PostDao
fake = Faker()
class TestDataSeeder(DataSeederABC):
def __init__(self, posts: PostDao):
DataSeederABC.__init__(self)
self._posts = posts
async def seed(self):
if await self._posts.count() == 0:
await self._seed_posts()
async def _seed_posts(self):
posts = Enumerable.range(0, 100).select(
lambda x: Post(
id=0,
title=fake.sentence(nb_words=6),
content=fake.paragraph(nb_sentences=6),
)
).to_list()
await self._posts.create_many(posts, skip_editor=True)