Remove REST API, keep GraphQL as sole API

- Add missing GraphQL mutations: retryJob, updateAsset, deleteAsset
- Add UpdateAssetRequest and DeleteResult to schema source of truth
- Move Lambda callback endpoint to main.py (only REST endpoint)
- Remove REST routes, pydantic schemas, and deps
- Remove pydantic target from modelgen.json
- Update architecture diagrams and documentation
This commit is contained in:
2026-02-12 20:07:51 -03:00
parent dbbaad5b94
commit 4e9d731cff
24 changed files with 393 additions and 1031 deletions

View File

@@ -1,7 +1,7 @@
"""
GraphQL API using graphene, mounted on FastAPI/Starlette.
Provides the same data as the REST API but via GraphQL queries and mutations.
Primary API for MPR — all client interactions go through GraphQL.
Uses Django ORM directly for data access.
Types are generated from schema/ via modelgen — see api/schema/graphql.py.
"""
@@ -12,11 +12,13 @@ import graphene
from api.schema.graphql import (
CreateJobInput,
DeleteResultType,
MediaAssetType,
ScanResultType,
SystemStatusType,
TranscodeJobType,
TranscodePresetType,
UpdateAssetInput,
)
from core.storage import BUCKET_IN, list_objects
@@ -238,10 +240,83 @@ class CancelJob(graphene.Mutation):
return job
class RetryJob(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
Output = TranscodeJobType
def mutate(self, info, id):
from mpr.media_assets.models import TranscodeJob
try:
job = TranscodeJob.objects.get(id=id)
except TranscodeJob.DoesNotExist:
raise Exception("Job not found")
if job.status != "failed":
raise Exception("Only failed jobs can be retried")
job.status = "pending"
job.progress = 0
job.error_message = None
job.save(update_fields=["status", "progress", "error_message"])
return job
class UpdateAsset(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
input = UpdateAssetInput(required=True)
Output = MediaAssetType
def mutate(self, info, id, input):
from mpr.media_assets.models import MediaAsset
try:
asset = MediaAsset.objects.get(id=id)
except MediaAsset.DoesNotExist:
raise Exception("Asset not found")
update_fields = []
if input.comments is not None:
asset.comments = input.comments
update_fields.append("comments")
if input.tags is not None:
asset.tags = input.tags
update_fields.append("tags")
if update_fields:
asset.save(update_fields=update_fields)
return asset
class DeleteAsset(graphene.Mutation):
class Arguments:
id = graphene.UUID(required=True)
Output = DeleteResultType
def mutate(self, info, id):
from mpr.media_assets.models import MediaAsset
try:
asset = MediaAsset.objects.get(id=id)
asset.delete()
return DeleteResultType(ok=True)
except MediaAsset.DoesNotExist:
raise Exception("Asset not found")
class Mutation(graphene.ObjectType):
scan_media_folder = ScanMediaFolder.Field()
create_job = CreateJob.Field()
cancel_job = CancelJob.Field()
retry_job = RetryJob.Field()
update_asset = UpdateAsset.Field()
delete_asset = DeleteAsset.Field()
# ---------------------------------------------------------------------------