218 lines
8.7 KiB
Python
218 lines
8.7 KiB
Python
from typing import cast
|
|
import strawberry
|
|
from strawberry.types import Info
|
|
from asgiref.sync import sync_to_async
|
|
from core.graphql.inputs.project_scope import (
|
|
ProjectScopeInput,
|
|
ProjectScopeUpdateInput,
|
|
ProjectScopeCategoryInput,
|
|
ProjectScopeCategoryUpdateInput,
|
|
ProjectScopeTaskInput,
|
|
ProjectScopeTaskUpdateInput,
|
|
CreateProjectScopeFromTemplateInput,
|
|
)
|
|
from core.graphql.types.project_scope import (
|
|
ProjectScopeType,
|
|
ProjectScopeCategoryType,
|
|
ProjectScopeTaskType,
|
|
)
|
|
from core.graphql.utils import create_object, update_object, delete_object
|
|
from core.models.account import Account, AccountAddress
|
|
from core.models.project import Project
|
|
from core.models.project_scope import ProjectScope, ProjectScopeCategory, ProjectScopeTask
|
|
from core.models.project_scope_template import ProjectScopeTemplate
|
|
from core.services.events import (
|
|
publish_project_scope_created, publish_project_scope_updated, publish_project_scope_deleted,
|
|
publish_project_scope_category_created, publish_project_scope_category_updated, publish_project_scope_category_deleted,
|
|
publish_project_scope_task_created, publish_project_scope_task_updated, publish_project_scope_task_deleted,
|
|
publish_project_scope_template_instantiated,
|
|
)
|
|
|
|
|
|
@strawberry.type
|
|
class Mutation:
|
|
# ProjectScope CRUD
|
|
@strawberry.mutation(description="Create a new ProjectScope")
|
|
async def create_project_scope(self, input: ProjectScopeInput, info: Info) -> ProjectScopeType:
|
|
instance = await create_object(input, ProjectScope)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_created(
|
|
scope_id=str(instance.id),
|
|
project_id=str(instance.project_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeType, instance)
|
|
|
|
@strawberry.mutation(description="Update an existing ProjectScope")
|
|
async def update_project_scope(self, input: ProjectScopeUpdateInput, info: Info) -> ProjectScopeType:
|
|
instance = await update_object(input, ProjectScope)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_updated(
|
|
scope_id=str(instance.id),
|
|
project_id=str(instance.project_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeType, instance)
|
|
|
|
@strawberry.mutation(description="Delete a ProjectScope")
|
|
async def delete_project_scope(self, id: strawberry.ID, info: Info) -> strawberry.ID:
|
|
instance = await delete_object(id, ProjectScope)
|
|
if not instance:
|
|
raise ValueError(f"ProjectScope with ID {id} does not exist")
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_deleted(
|
|
scope_id=str(id),
|
|
project_id=str(instance.project_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return id
|
|
|
|
@strawberry.mutation(description="Create a ProjectScopeCategory")
|
|
async def create_project_scope_category(self, input: ProjectScopeCategoryInput, info: Info) -> ProjectScopeCategoryType:
|
|
instance = await create_object(input, ProjectScopeCategory)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_category_created(
|
|
category_id=str(instance.id),
|
|
scope_id=str(instance.scope_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeCategoryType, instance)
|
|
|
|
@strawberry.mutation(description="Update a ProjectScopeCategory")
|
|
async def update_project_scope_category(self, input: ProjectScopeCategoryUpdateInput, info: Info) -> ProjectScopeCategoryType:
|
|
instance = await update_object(input, ProjectScopeCategory)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_category_updated(
|
|
category_id=str(instance.id),
|
|
scope_id=str(instance.scope_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeCategoryType, instance)
|
|
|
|
@strawberry.mutation(description="Delete a ProjectScopeCategory")
|
|
async def delete_project_scope_category(self, id: strawberry.ID, info: Info) -> strawberry.ID:
|
|
instance = await delete_object(id, ProjectScopeCategory)
|
|
if not instance:
|
|
raise ValueError(f"ProjectScopeCategory with ID {id} does not exist")
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_category_deleted(
|
|
category_id=str(id),
|
|
scope_id=str(instance.scope_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return id
|
|
|
|
@strawberry.mutation(description="Create a ProjectScopeTask")
|
|
async def create_project_scope_task(self, input: ProjectScopeTaskInput, info: Info) -> ProjectScopeTaskType:
|
|
instance = await create_object(input, ProjectScopeTask)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_task_created(
|
|
task_id=str(instance.id),
|
|
category_id=str(instance.category_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeTaskType, instance)
|
|
|
|
@strawberry.mutation(description="Update a ProjectScopeTask")
|
|
async def update_project_scope_task(self, input: ProjectScopeTaskUpdateInput, info: Info) -> ProjectScopeTaskType:
|
|
instance = await update_object(input, ProjectScopeTask)
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_task_updated(
|
|
task_id=str(instance.id),
|
|
category_id=str(instance.category_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeTaskType, instance)
|
|
|
|
@strawberry.mutation(description="Delete a ProjectScopeTask")
|
|
async def delete_project_scope_task(self, id: strawberry.ID, info: Info) -> strawberry.ID:
|
|
instance = await delete_object(id, ProjectScopeTask)
|
|
if not instance:
|
|
raise ValueError(f"ProjectScopeTask with ID {id} does not exist")
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_task_deleted(
|
|
task_id=str(id),
|
|
category_id=str(instance.category_id),
|
|
triggered_by=profile
|
|
)
|
|
|
|
return id
|
|
|
|
@strawberry.mutation(description="Instantiate a ProjectScope (with Categories and Tasks) from a ProjectScopeTemplate")
|
|
async def create_project_scope_from_template(self, input: CreateProjectScopeFromTemplateInput, info: Info) -> ProjectScopeType:
|
|
def _do_create_sync() -> tuple[ProjectScope, str, str]:
|
|
# Load required objects synchronously (ORM-safe in this thread)
|
|
project = (
|
|
Project.objects
|
|
.select_related("account_address__account")
|
|
.get(pk=input.project_id.node_id)
|
|
)
|
|
tpl = ProjectScopeTemplate.objects.get(pk=input.template_id.node_id)
|
|
|
|
# Defaults derived from project (if project has an account_address)
|
|
account = None
|
|
account_address = None
|
|
if project.account_address_id:
|
|
account_address = project.account_address
|
|
account = account_address.account
|
|
|
|
if input.account_address_id:
|
|
account_address = AccountAddress.objects.get(pk=input.account_address_id.node_id)
|
|
account = account_address.account
|
|
|
|
if input.account_id:
|
|
account = Account.objects.get(pk=input.account_id.node_id)
|
|
|
|
# Instantiate the ProjectScope object from the template
|
|
instance = tpl.instantiate(
|
|
project=project,
|
|
account=account,
|
|
account_address=account_address,
|
|
name=input.name,
|
|
description=input.description,
|
|
is_active=input.is_active if input.is_active is not None else True,
|
|
)
|
|
|
|
# Persist the relation on the project
|
|
project.scope = instance
|
|
project.save(update_fields=["scope"])
|
|
return instance, str(tpl.id), str(project.id)
|
|
|
|
instance, template_id, project_id = await sync_to_async(_do_create_sync, thread_sensitive=True)()
|
|
|
|
# Publish event
|
|
profile = getattr(info.context.request, 'profile', None)
|
|
await publish_project_scope_template_instantiated(
|
|
scope_id=str(instance.id),
|
|
template_id=template_id,
|
|
project_id=project_id,
|
|
triggered_by=profile
|
|
)
|
|
|
|
return cast(ProjectScopeType, instance) |