mycroforge/MycroForge.CLI/CodeGen/CrudRouterGenerator.cs

152 lines
6.0 KiB
C#

using Humanizer;
using MycroForge.CLI.Extensions;
using MycroForge.Core;
namespace MycroForge.CLI.CodeGen;
public class CrudRouterGenerator
{
private static readonly string[] Template =
[
"from typing import Annotated",
"from fastapi import APIRouter, Depends",
"from fastapi.responses import JSONResponse, Response",
"from fastapi.encoders import jsonable_encoder",
"from %service_import_path% import %service_class_name%",
"from %create_entity_request_import_path% import %create_entity_request_class_name%",
"from %update_entity_request_import_path% import %update_entity_request_class_name%",
"",
"router = APIRouter()",
"",
"@router.get(\"/\")",
"async def list(",
"\tservice: Annotated[%service_class_name%, Depends()]",
"):",
"\ttry:",
"\t\tresult = await service.list()",
"\t\treturn JSONResponse(status_code=200, content=jsonable_encoder(result))",
"\texcept Exception as ex:",
"\t\tprint(str(ex))",
"\t\treturn JSONResponse(status_code=500, content=str(ex))",
"",
"@router.get(\"/{id}\")",
"async def get_by_id(",
"\tid: int,",
"\tservice: Annotated[%service_class_name%, Depends()]",
"):",
"\ttry:",
"\t\tresult = await service.get_by_id(id)",
"\t\treturn JSONResponse(status_code=200 if result is not None else 404, content=jsonable_encoder(result))",
"\texcept Exception as ex:",
"\t\tprint(str(ex))",
"\t\treturn JSONResponse(status_code=500, content=str(ex))",
"",
"@router.post(\"/\")",
"async def create(",
"\trequest: Create%entity_class_name%Request,",
"\tservice: Annotated[%service_class_name%, Depends()]",
"):",
"\ttry:",
"\t\tawait service.create(request.model_dump())",
"\t\treturn JSONResponse(status_code=201, content=None)",
"\texcept Exception as ex:",
"\t\tprint(str(ex))",
"\t\treturn JSONResponse(status_code=500, content=str(ex))",
"",
"@router.patch(\"/{id}\")",
"async def update(",
"\tid: int,",
"\trequest: Update%entity_class_name%Request,",
"\tservice: Annotated[%service_class_name%, Depends()]",
"):",
"\ttry:",
"\t\tupdated = await service.update(id, request.model_dump(exclude_unset=True))",
"\t\treturn Response(status_code=204 if updated else 404)",
"\texcept Exception as ex:",
"\t\tprint(str(ex))",
"\t\treturn JSONResponse(status_code=500, content=str(ex))",
"",
"@router.delete(\"/{id}\")",
"async def delete(",
"\tid: int,",
"\tservice: Annotated[%service_class_name%, Depends()]",
"):",
"\ttry:",
"\t\tdeleted = await service.delete(id)",
"\t\treturn JSONResponse(status_code=204 if deleted else 404, content=None)",
"\texcept Exception as ex:",
"\t\tprint(str(ex))",
"\t\treturn JSONResponse(status_code=500, content=str(ex))",
];
private readonly ProjectContext _context;
public CrudRouterGenerator(ProjectContext context)
{
_context = context;
}
public async Task Generate(string path, string entity)
{
var entitySnakeCaseName = entity.Underscore().ToLower();
var entityClassName = entity.Pascalize();
var serviceClassName = $"{entityClassName}Service";
var entityRoutePrefix = entity.Kebaberize().Pluralize().ToLower();
var servicesFolderPath = $"{Features.Api.FeatureName}/services/{path}";
var serviceFilePath = $"{servicesFolderPath}/{entitySnakeCaseName}_service.py";
var serviceImportPath = serviceFilePath
.Replace('/', '.')
.Replace('\\', '.')
.Replace(".py", string.Empty)
.DeduplicateDots()
.Trim();
var routersFolderPath = $"{Features.Api.FeatureName}/routers/{path}";
var routerFilePath = $"{routersFolderPath}/{entitySnakeCaseName}.py";
var routerImportPath = routersFolderPath
.Replace('/', '.')
.Replace('\\', '.')
.Replace(".py", "")
.DeduplicateDots()
.Trim();
var requestsFolderPath = $"{Features.Api.FeatureName}/requests/{path}";
var createRequestImportPath = $"{requestsFolderPath}/Create{entityClassName}Request"
.Replace('/', '.')
.Replace('\\', '.')
.DeduplicateDots()
.Underscore()
.ToLower();
var createRequestClassName = $"Create{entityClassName}Request";
var updateRequestImportPath = $"{requestsFolderPath}/Update{entityClassName}Request"
.Replace('/', '.')
.Replace('\\', '.')
.DeduplicateDots()
.Underscore()
.ToLower();
var updateRequestClassName = $"Update{entityClassName}Request";
var router = string.Join("\n", Template)
.Replace("%service_import_path%", serviceImportPath)
.Replace("%entity_class_name%", entityClassName)
.Replace("%service_class_name%", serviceClassName)
.Replace("%create_entity_request_import_path%", createRequestImportPath)
.Replace("%create_entity_request_class_name%", createRequestClassName)
.Replace("%update_entity_request_import_path%", updateRequestImportPath)
.Replace("%update_entity_request_class_name%", updateRequestClassName);
await _context.CreateFile(routerFilePath, router);
var main = await _context.ReadFile("main.py");
main = new MainModifier(main).Initialize()
.Import(from: routerImportPath, import: entitySnakeCaseName)
.IncludeRouter(prefix: entityRoutePrefix, router: entitySnakeCaseName)
.Rewrite();
await _context.WriteFile("main.py", main);
}
}