Changed orm to db and added path resolution for entity generation
This commit is contained in:
parent
15bf94e195
commit
d59dedc106
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
namespace MycroForge.CLI.CodeGen;
|
namespace MycroForge.CLI.CodeGen;
|
||||||
|
|
||||||
public class OrmEnvInitializer : PythonSourceModifier
|
public class DbEnvInitializer : PythonSourceModifier
|
||||||
{
|
{
|
||||||
private PythonParser.Import_fromContext? _alembicImport;
|
private PythonParser.Import_fromContext? _alembicImport;
|
||||||
private PythonParser.AssignmentContext? _targetMetaDataAssignment;
|
private PythonParser.AssignmentContext? _targetMetaDataAssignment;
|
||||||
private PythonParser.AssignmentContext? _urlAssignmentContext;
|
private PythonParser.AssignmentContext? _urlAssignmentContext;
|
||||||
private PythonParser.AssignmentContext? _connectableAssignmentContext;
|
private PythonParser.AssignmentContext? _connectableAssignmentContext;
|
||||||
|
|
||||||
public OrmEnvInitializer(string source) : base(source)
|
public DbEnvInitializer(string source) : base(source)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,17 +32,17 @@ public class OrmEnvInitializer : PythonSourceModifier
|
|||||||
|
|
||||||
Rewrite(_alembicImport, [
|
Rewrite(_alembicImport, [
|
||||||
GetOriginalText(_alembicImport),
|
GetOriginalText(_alembicImport),
|
||||||
"from orm.settings import OrmSettings",
|
"from db.settings import DbSettings",
|
||||||
"from orm.entities.entity_base import EntityBase"
|
"from db.entities.entity_base import EntityBase"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Rewrite(_targetMetaDataAssignment, "target_metadata = EntityBase.metadata");
|
Rewrite(_targetMetaDataAssignment, "target_metadata = EntityBase.metadata");
|
||||||
|
|
||||||
Rewrite(_urlAssignmentContext, "url = OrmSettings.get_connectionstring()");
|
Rewrite(_urlAssignmentContext, "url = DbSettings.get_connectionstring()");
|
||||||
|
|
||||||
const string indent = " ";
|
const string indent = " ";
|
||||||
Rewrite(_connectableAssignmentContext, [
|
Rewrite(_connectableAssignmentContext, [
|
||||||
"url = OrmSettings.get_connectionstring()",
|
"url = DbSettings.get_connectionstring()",
|
||||||
$"{indent}context.config.set_main_option('sqlalchemy.url', url)",
|
$"{indent}context.config.set_main_option('sqlalchemy.url', url)",
|
||||||
$"{indent}{GetOriginalText(_connectableAssignmentContext)}"
|
$"{indent}{GetOriginalText(_connectableAssignmentContext)}"
|
||||||
]);
|
]);
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
namespace MycroForge.CLI.CodeGen;
|
namespace MycroForge.CLI.CodeGen;
|
||||||
|
|
||||||
public class OrmEnvUpdater : PythonSourceModifier
|
public class DbEnvUpdater : PythonSourceModifier
|
||||||
{
|
{
|
||||||
private readonly string _moduleName;
|
private readonly string _importPath;
|
||||||
private readonly string _className;
|
private readonly string _className;
|
||||||
private PythonParser.Import_fromContext? _lastImport;
|
private PythonParser.Import_fromContext? _lastImport;
|
||||||
|
|
||||||
public OrmEnvUpdater(string source, string moduleName, string className) : base(source)
|
public DbEnvUpdater(string source, string importPath, string className) : base(source)
|
||||||
{
|
{
|
||||||
_moduleName = moduleName;
|
_importPath = importPath;
|
||||||
_className = className;
|
_className = className;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,11 +22,11 @@ public class OrmEnvUpdater : PythonSourceModifier
|
|||||||
if (_lastImport is null)
|
if (_lastImport is null)
|
||||||
throw new Exception("Could not find import insertion point.");
|
throw new Exception("Could not find import insertion point.");
|
||||||
|
|
||||||
var text = GetOriginalText(_lastImport);
|
var lastImportText = GetOriginalText(_lastImport);
|
||||||
|
|
||||||
Rewrite(_lastImport, [
|
Rewrite(_lastImport, [
|
||||||
text,
|
lastImportText,
|
||||||
$"from orm.entities.{_moduleName} import {_className}"
|
$"from db.entities.{_importPath} import {_className}"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return Rewriter.GetText();
|
return Rewriter.GetText();
|
||||||
@ -36,7 +36,7 @@ public class OrmEnvUpdater : PythonSourceModifier
|
|||||||
{
|
{
|
||||||
var text = GetOriginalText(context);
|
var text = GetOriginalText(context);
|
||||||
|
|
||||||
if (text.StartsWith("from orm.entities"))
|
if (text.StartsWith("from db.entities"))
|
||||||
_lastImport = context;
|
_lastImport = context;
|
||||||
|
|
||||||
return base.VisitImport_from(context);
|
return base.VisitImport_from(context);
|
@ -77,9 +77,9 @@ public partial class EntityLinker
|
|||||||
// var right = await LoadEntity(_right);
|
// var right = await LoadEntity(_right);
|
||||||
//
|
//
|
||||||
// left.AppendImportIfNotExists(
|
// left.AppendImportIfNotExists(
|
||||||
// $"orm.entities.{right.FieldName}",
|
// $"db.entities.{right.FieldName}",
|
||||||
// $"{right.ClassName}",
|
// $"{right.ClassName}",
|
||||||
// $"from orm.entities.{right.FieldName} import {right.ClassName}"
|
// $"from db.entities.{right.FieldName} import {right.ClassName}"
|
||||||
// );
|
// );
|
||||||
// left.AppendImportIfNotExists(
|
// left.AppendImportIfNotExists(
|
||||||
// "typing",
|
// "typing",
|
||||||
@ -92,9 +92,9 @@ public partial class EntityLinker
|
|||||||
// ]);
|
// ]);
|
||||||
//
|
//
|
||||||
// right.AppendImportIfNotExists(
|
// right.AppendImportIfNotExists(
|
||||||
// $"orm.entities.{left.FieldName}",
|
// $"db.entities.{left.FieldName}",
|
||||||
// $"{left.ClassName}",
|
// $"{left.ClassName}",
|
||||||
// $"from orm.entities.{left.FieldName} import {left.ClassName}"
|
// $"from db.entities.{left.FieldName} import {left.ClassName}"
|
||||||
// );
|
// );
|
||||||
// left.AppendImportIfNotExists(
|
// left.AppendImportIfNotExists(
|
||||||
// "typing",
|
// "typing",
|
||||||
@ -118,5 +118,5 @@ public partial class EntityLinker
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetEntityPath(string name) => $"orm/entities/{name.Underscore().ToLower()}.py";
|
private string GetEntityPath(string name) => $"{Features.Db.FeatureName}/entities/{name.Underscore().ToLower()}.py";
|
||||||
}
|
}
|
@ -40,14 +40,14 @@ public partial class MycroForge
|
|||||||
|
|
||||||
private async Task ExecuteAsync(string name)
|
private async Task ExecuteAsync(string name)
|
||||||
{
|
{
|
||||||
_context.AssertDirectoryExists("api/routers");
|
_context.AssertDirectoryExists($"{Features.Api.FeatureName}/routers");
|
||||||
|
|
||||||
var moduleName = name.Underscore();
|
var moduleName = name.Underscore();
|
||||||
await _context.CreateFile($"api/routers/{moduleName}.py", Template);
|
await _context.CreateFile($"{Features.Api.FeatureName}/routers/{moduleName}.py", Template);
|
||||||
|
|
||||||
var main = await _context.ReadFile("main.py");
|
var main = await _context.ReadFile("main.py");
|
||||||
main += string.Join('\n',
|
main += string.Join('\n',
|
||||||
$"\n\nfrom api.routers import {moduleName}",
|
$"\n\nfrom {Features.Api.FeatureName}.routers import {moduleName}",
|
||||||
$"app.include_router(prefix=\"/{name.Kebaberize()}\", router={moduleName}.router)"
|
$"app.include_router(prefix=\"/{name.Kebaberize()}\", router={moduleName}.router)"
|
||||||
);
|
);
|
||||||
await _context.WriteFile("main.py", main);
|
await _context.WriteFile("main.py", main);
|
||||||
|
131
MycroForge.CLI/Commands/MycroForge.Db.Generate.Entity.cs
Normal file
131
MycroForge.CLI/Commands/MycroForge.Db.Generate.Entity.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System.CommandLine;
|
||||||
|
using Humanizer;
|
||||||
|
using MycroForge.CLI.CodeGen;
|
||||||
|
using MycroForge.CLI.Commands.Interfaces;
|
||||||
|
|
||||||
|
namespace MycroForge.CLI.Commands;
|
||||||
|
|
||||||
|
public partial class MycroForge
|
||||||
|
{
|
||||||
|
public partial class Db
|
||||||
|
{
|
||||||
|
public partial class Generate
|
||||||
|
{
|
||||||
|
public class Entity : Command, ISubCommandOf<Generate>
|
||||||
|
{
|
||||||
|
private record ColumnDefinition(string Name, string NativeType, string OrmType);
|
||||||
|
|
||||||
|
private static readonly string[] Template =
|
||||||
|
[
|
||||||
|
"from sqlalchemy import %type_imports%",
|
||||||
|
"from sqlalchemy.orm import Mapped, mapped_column",
|
||||||
|
"from db.entities.entity_base import EntityBase",
|
||||||
|
"",
|
||||||
|
"class %class_name%(EntityBase):",
|
||||||
|
"\t__tablename__ = \"%table_name%\"",
|
||||||
|
"\tid: Mapped[int] = mapped_column(primary_key=True)",
|
||||||
|
"\t%column_definitions%",
|
||||||
|
"",
|
||||||
|
"\tdef __repr__(self) -> str:",
|
||||||
|
"\t\treturn f\"%class_name%(id={self.id!r})\""
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly Argument<string> NameArgument =
|
||||||
|
new(name: "name", description: string.Join('\n', [
|
||||||
|
"The name of the database entity",
|
||||||
|
"",
|
||||||
|
"Supported formats:",
|
||||||
|
"\tEntity",
|
||||||
|
"\tpath/relative/to/entities:Entity",
|
||||||
|
]));
|
||||||
|
|
||||||
|
private static readonly Option<IEnumerable<string>> ColumnsOption =
|
||||||
|
new(aliases: ["--column", "-c"], description: string.Join('\n', [
|
||||||
|
"Specify the fields to add.",
|
||||||
|
"",
|
||||||
|
"Format:",
|
||||||
|
"\t<name>:<native_type>:<orm_type>",
|
||||||
|
"\t",
|
||||||
|
"\t<name> = Name of the column",
|
||||||
|
"\t<native_type> = The native Python type",
|
||||||
|
"\t<orm_type> = The SQLAlchemy type",
|
||||||
|
"",
|
||||||
|
"Example:",
|
||||||
|
"\tfirst_name:str:String(255)",
|
||||||
|
])) { AllowMultipleArgumentsPerToken = true };
|
||||||
|
|
||||||
|
private readonly ProjectContext _context;
|
||||||
|
|
||||||
|
public Entity(ProjectContext context) : base("entity", "Generate and database entity")
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
AddAlias("e");
|
||||||
|
AddArgument(NameArgument);
|
||||||
|
AddOption(ColumnsOption);
|
||||||
|
this.SetHandler(ExecuteAsync, NameArgument, ColumnsOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteAsync(string name, IEnumerable<string> columns)
|
||||||
|
{
|
||||||
|
_context.AssertDirectoryExists(Features.Db.FeatureName);
|
||||||
|
|
||||||
|
var path = string.Empty;
|
||||||
|
if (name.Split(':').Select(s => s.Trim()).ToArray() is { Length: 2 } fullName)
|
||||||
|
{
|
||||||
|
path = fullName[0];
|
||||||
|
name = fullName[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var _columns = GetColumnDefinitions(columns.ToArray());
|
||||||
|
var className = name.Underscore().Pascalize();
|
||||||
|
var typeImports = string.Join(", ", _columns.Select(c => c.OrmType.Split('(').First()).Distinct());
|
||||||
|
var columnDefinitions = string.Join("\n\t", _columns.Select(ColumnToString));
|
||||||
|
|
||||||
|
var code = string.Join('\n', Template);
|
||||||
|
code = code.Replace("%type_imports%", typeImports);
|
||||||
|
code = code.Replace("%class_name%", className);
|
||||||
|
code = code.Replace("%table_name%", name.Underscore().ToLower().Pluralize());
|
||||||
|
code = code.Replace("%column_definitions%", columnDefinitions);
|
||||||
|
|
||||||
|
var folderPath = Path.Join($"{Features.Db.FeatureName}/entities", path);
|
||||||
|
var fileName = $"{name.ToLower()}.py";
|
||||||
|
var filePath = Path.Join(folderPath, fileName);
|
||||||
|
await _context.CreateFile(filePath, code);
|
||||||
|
|
||||||
|
var importPathParts = new[] { path, fileName.Replace(".py", "") }
|
||||||
|
.Where(s => !string.IsNullOrEmpty(s));
|
||||||
|
|
||||||
|
var importPath = string.Join('.', importPathParts)
|
||||||
|
.Replace('/', '.')
|
||||||
|
.Replace('\\', '.')
|
||||||
|
.Underscore()
|
||||||
|
.ToLower();
|
||||||
|
|
||||||
|
var env = await _context.ReadFile($"{Features.Db.FeatureName}/env.py");
|
||||||
|
env = new DbEnvUpdater(env, importPath, className).Rewrite();
|
||||||
|
await _context.WriteFile($"{Features.Db.FeatureName}/env.py", env);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ColumnDefinition> GetColumnDefinitions(string[] fields)
|
||||||
|
{
|
||||||
|
var definitions = new List<ColumnDefinition>();
|
||||||
|
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.Split(':') is not { Length: 3 } definition)
|
||||||
|
throw new Exception($"Field definition {field} is invalid.");
|
||||||
|
|
||||||
|
definitions.Add(new ColumnDefinition(definition[0], definition[1], definition[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ColumnToString(ColumnDefinition definition)
|
||||||
|
{
|
||||||
|
return $"{definition.Name}: Mapped[{definition.NativeType}] = mapped_column({definition.OrmType})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public partial class Generate
|
public partial class Generate
|
||||||
{
|
{
|
||||||
@ -26,7 +26,7 @@ public partial class MycroForge
|
|||||||
|
|
||||||
private async Task ExecuteAsync(string name)
|
private async Task ExecuteAsync(string name)
|
||||||
{
|
{
|
||||||
_context.AssertDirectoryExists("orm/versions");
|
_context.AssertDirectoryExists($"{Features.Db.FeatureName}/versions");
|
||||||
|
|
||||||
await _context.Bash(
|
await _context.Bash(
|
||||||
"source .venv/bin/activate",
|
"source .venv/bin/activate",
|
@ -5,12 +5,12 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public partial class Generate : Command, ISubCommandOf<Orm>
|
public partial class Generate : Command, ISubCommandOf<Db>
|
||||||
{
|
{
|
||||||
public Generate(IEnumerable<ISubCommandOf<Generate>> subCommands) :
|
public Generate(IEnumerable<ISubCommandOf<Generate>> subCommands) :
|
||||||
base("generate", "Generate an ORM item")
|
base("generate", "Generate a database item")
|
||||||
{
|
{
|
||||||
AddAlias("g");
|
AddAlias("g");
|
||||||
foreach (var subCommandOf in subCommands.Cast<Command>())
|
foreach (var subCommandOf in subCommands.Cast<Command>())
|
@ -6,7 +6,7 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public partial class Link
|
public partial class Link
|
||||||
{
|
{
|
||||||
@ -39,20 +39,20 @@ public partial class MycroForge
|
|||||||
throw new Exception("Cannot set both --to-one and --to-many option.");
|
throw new Exception("Cannot set both --to-one and --to-many option.");
|
||||||
|
|
||||||
if (toOneOption is not null)
|
if (toOneOption is not null)
|
||||||
await ManyToOne(left, toOneOption);
|
await ToOne(left, toOneOption);
|
||||||
|
|
||||||
else if (toManyOption is not null)
|
else if (toManyOption is not null)
|
||||||
await ManyToMany(left, toManyOption);
|
await ToMany(left, toManyOption);
|
||||||
|
|
||||||
else throw new Exception("Set --to-one or --to-many option.");
|
else throw new Exception("Set --to-one or --to-many option.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ManyToOne(string left, string toOneOption)
|
private async Task ToOne(string left, string toOneOption)
|
||||||
{
|
{
|
||||||
await new EntityLinker(_context, left, toOneOption).ManyToOne();
|
await new EntityLinker(_context, left, toOneOption).ManyToOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ManyToMany(string left, string toManyOption)
|
private async Task ToMany(string left, string toManyOption)
|
||||||
{
|
{
|
||||||
await new EntityLinker(_context, left, toManyOption).ManyToMany();
|
await new EntityLinker(_context, left, toManyOption).ManyToMany();
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public partial class Link
|
public partial class Link
|
||||||
{
|
{
|
||||||
@ -39,20 +39,20 @@ public partial class MycroForge
|
|||||||
throw new Exception("Cannot set both --to-one and --to-many option.");
|
throw new Exception("Cannot set both --to-one and --to-many option.");
|
||||||
|
|
||||||
if (toOneOption is not null)
|
if (toOneOption is not null)
|
||||||
await OneToOne(left, toOneOption);
|
await ToOne(left, toOneOption);
|
||||||
|
|
||||||
else if (toManyOption is not null)
|
else if (toManyOption is not null)
|
||||||
await OneToMany(left, toManyOption);
|
await ToMany(left, toManyOption);
|
||||||
|
|
||||||
else throw new Exception("Set --to-one or --to-many option.");
|
else throw new Exception("Set --to-one or --to-many option.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OneToOne(string left, string toOneOption)
|
private async Task ToOne(string left, string toOneOption)
|
||||||
{
|
{
|
||||||
await new EntityLinker(_context, left, toOneOption).OneToOne();
|
await new EntityLinker(_context, left, toOneOption).OneToOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OneToMany(string left, string toManyOption)
|
private async Task ToMany(string left, string toManyOption)
|
||||||
{
|
{
|
||||||
await new EntityLinker(_context, left, toManyOption).OneToMany();
|
await new EntityLinker(_context, left, toManyOption).OneToMany();
|
||||||
}
|
}
|
@ -5,9 +5,9 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public partial class Link : Command, ISubCommandOf<Orm>
|
public partial class Link : Command, ISubCommandOf<Db>
|
||||||
{
|
{
|
||||||
public Link(IEnumerable<ISubCommandOf<Link>> commands) :
|
public Link(IEnumerable<ISubCommandOf<Link>> commands) :
|
||||||
base("link", "Define relationships between entities")
|
base("link", "Define relationships between entities")
|
@ -5,9 +5,9 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public class Migrate : Command, ISubCommandOf<Orm>
|
public class Migrate : Command, ISubCommandOf<Db>
|
||||||
{
|
{
|
||||||
private readonly ProjectContext _context;
|
private readonly ProjectContext _context;
|
||||||
|
|
@ -5,9 +5,9 @@ namespace MycroForge.CLI.Commands;
|
|||||||
|
|
||||||
public partial class MycroForge
|
public partial class MycroForge
|
||||||
{
|
{
|
||||||
public partial class Orm
|
public partial class Db
|
||||||
{
|
{
|
||||||
public class Rollback : Command, ISubCommandOf<Orm>
|
public class Rollback : Command, ISubCommandOf<Db>
|
||||||
{
|
{
|
||||||
private readonly ProjectContext _context;
|
private readonly ProjectContext _context;
|
||||||
|
|
17
MycroForge.CLI/Commands/MycroForge.Db.cs
Normal file
17
MycroForge.CLI/Commands/MycroForge.Db.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System.CommandLine;
|
||||||
|
using MycroForge.CLI.Commands.Interfaces;
|
||||||
|
|
||||||
|
namespace MycroForge.CLI.Commands;
|
||||||
|
|
||||||
|
public partial class MycroForge
|
||||||
|
{
|
||||||
|
public partial class Db : Command, ISubCommandOf<MycroForge>
|
||||||
|
{
|
||||||
|
public Db(IEnumerable<ISubCommandOf<Db>> commands)
|
||||||
|
: base("db", "Database related commands")
|
||||||
|
{
|
||||||
|
foreach (var command in commands.Cast<Command>())
|
||||||
|
AddCommand(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,9 +30,9 @@ public partial class MycroForge
|
|||||||
|
|
||||||
private static readonly string[] WithSessionTemplate =
|
private static readonly string[] WithSessionTemplate =
|
||||||
[
|
[
|
||||||
"from orm.engine.async_session import async_session",
|
"from db.engine.async_session import async_session",
|
||||||
"from sqlalchemy import select",
|
"from sqlalchemy import select",
|
||||||
"# from orm.entities.some_entity import SomeEntity",
|
"# from db.entities.some_entity import SomeEntity",
|
||||||
"",
|
"",
|
||||||
"class %class_name%Service:",
|
"class %class_name%Service:",
|
||||||
"",
|
"",
|
||||||
|
@ -10,7 +10,7 @@ public partial class MycroForge
|
|||||||
private readonly ProjectContext _context;
|
private readonly ProjectContext _context;
|
||||||
|
|
||||||
public Hydrate(ProjectContext context)
|
public Hydrate(ProjectContext context)
|
||||||
: base("hydrate", "Create a new venv and install dependencies listed in requirements.txt")
|
: base("hydrate", "Initialize venv and install dependencies from requirements.txt")
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
this.SetHandler(ExecuteAsync);
|
this.SetHandler(ExecuteAsync);
|
||||||
|
@ -59,7 +59,7 @@ public partial class MycroForge
|
|||||||
[
|
[
|
||||||
Features.Git.FeatureName,
|
Features.Git.FeatureName,
|
||||||
Features.Api.FeatureName,
|
Features.Api.FeatureName,
|
||||||
Features.Orm.FeatureName
|
Features.Db.FeatureName
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly Argument<string> NameArgument =
|
private static readonly Argument<string> NameArgument =
|
||||||
@ -111,8 +111,17 @@ public partial class MycroForge
|
|||||||
|
|
||||||
// Initialize default features
|
// Initialize default features
|
||||||
foreach (var feature in _features.Where(f => DefaultFeatures.Contains(f.Name)))
|
foreach (var feature in _features.Where(f => DefaultFeatures.Contains(f.Name)))
|
||||||
|
{
|
||||||
if (!withoutList.Contains(feature.Name))
|
if (!withoutList.Contains(feature.Name))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Initializing feature {feature.Name}");
|
||||||
await feature.ExecuteAsync(_context);
|
await feature.ExecuteAsync(_context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Skipping feature {feature.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine($"Directory {projectRoot} was successfully initialized");
|
Console.WriteLine($"Directory {projectRoot} was successfully initialized");
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
using System.CommandLine;
|
|
||||||
using Humanizer;
|
|
||||||
using MycroForge.CLI.CodeGen;
|
|
||||||
using MycroForge.CLI.Commands.Interfaces;
|
|
||||||
|
|
||||||
namespace MycroForge.CLI.Commands;
|
|
||||||
|
|
||||||
public partial class MycroForge
|
|
||||||
{
|
|
||||||
public partial class Orm
|
|
||||||
{
|
|
||||||
public partial class Generate
|
|
||||||
{
|
|
||||||
public class Entity : Command, ISubCommandOf<Generate>
|
|
||||||
{
|
|
||||||
private static readonly string[] Template =
|
|
||||||
[
|
|
||||||
"from sqlalchemy import String",
|
|
||||||
"from sqlalchemy.orm import Mapped, mapped_column",
|
|
||||||
"from orm.entities.entity_base import EntityBase",
|
|
||||||
"",
|
|
||||||
"class %class_name%(EntityBase):",
|
|
||||||
"\t__tablename__ = \"%table_name%\"",
|
|
||||||
"\tid: Mapped[int] = mapped_column(primary_key=True)",
|
|
||||||
"\tvalue: Mapped[str] = mapped_column(String(255))",
|
|
||||||
"",
|
|
||||||
"\tdef __repr__(self) -> str:",
|
|
||||||
"\t\treturn f\"%class_name%(id={self.id!r}, value={self.value!r})\""
|
|
||||||
];
|
|
||||||
|
|
||||||
private static readonly Argument<string> NameArgument =
|
|
||||||
new(name: "name", description: "The name of the orm entity");
|
|
||||||
|
|
||||||
private readonly ProjectContext _context;
|
|
||||||
|
|
||||||
public Entity(ProjectContext context) : base("entity", "Generate and orm entity")
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
AddAlias("e");
|
|
||||||
AddArgument(NameArgument);
|
|
||||||
this.SetHandler(ExecuteAsync, NameArgument);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ExecuteAsync(string name)
|
|
||||||
{
|
|
||||||
_context.AssertDirectoryExists("orm");
|
|
||||||
|
|
||||||
var className = name.Underscore().Pascalize();
|
|
||||||
var moduleName = name.Underscore();
|
|
||||||
var code = string.Join('\n', Template);
|
|
||||||
|
|
||||||
code = code.Replace("%class_name%", className);
|
|
||||||
code = code.Replace("%table_name%", name.Underscore().ToLower().Pluralize());
|
|
||||||
await _context.CreateFile($"orm/entities/{moduleName}.py", code);
|
|
||||||
|
|
||||||
var env = await _context.ReadFile("orm/env.py");
|
|
||||||
env = new OrmEnvUpdater(env, moduleName, className).Rewrite();
|
|
||||||
await _context.WriteFile("orm/env.py", env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using System.CommandLine;
|
|
||||||
using MycroForge.CLI.Commands.Interfaces;
|
|
||||||
|
|
||||||
namespace MycroForge.CLI.Commands;
|
|
||||||
|
|
||||||
public partial class MycroForge
|
|
||||||
{
|
|
||||||
public partial class Orm : Command, ISubCommandOf<MycroForge>
|
|
||||||
{
|
|
||||||
public Orm(IEnumerable<ISubCommandOf<Orm>> subCommands)
|
|
||||||
: base("orm", "ORM related commands")
|
|
||||||
{
|
|
||||||
foreach (var subCommandOf in subCommands.Cast<Command>())
|
|
||||||
AddCommand(subCommandOf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<ProjectContext>();
|
services.AddScoped<ProjectContext>();
|
||||||
services.AddScoped<IFeature, Git>();
|
services.AddScoped<IFeature, Git>();
|
||||||
services.AddScoped<IFeature, Api>();
|
services.AddScoped<IFeature, Api>();
|
||||||
services.AddScoped<IFeature, Orm>();
|
services.AddScoped<IFeature, Db>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -37,15 +37,15 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Router>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Api.Generate>, Commands.MycroForge.Api.Generate.Router>();
|
||||||
|
|
||||||
// Register "m4g orm"
|
// Register "m4g orm"
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Orm>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Db>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm>, Commands.MycroForge.Orm.Migrate>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Migrate>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm>, Commands.MycroForge.Orm.Rollback>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Rollback>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm>, Commands.MycroForge.Orm.Generate>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Generate>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm.Generate>, Commands.MycroForge.Orm.Generate.Entity>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db.Generate>, Commands.MycroForge.Db.Generate.Entity>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm.Generate>, Commands.MycroForge.Orm.Generate.Migration>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db.Generate>, Commands.MycroForge.Db.Generate.Migration>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm>, Commands.MycroForge.Orm.Link>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db>, Commands.MycroForge.Db.Link>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm.Link>, Commands.MycroForge.Orm.Link.One>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db.Link>, Commands.MycroForge.Db.Link.One>();
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge.Orm.Link>, Commands.MycroForge.Orm.Link.Many>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge.Db.Link>, Commands.MycroForge.Db.Link.Many>();
|
||||||
|
|
||||||
// Register "m4g script"
|
// Register "m4g script"
|
||||||
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Script>();
|
services.AddScoped<ISubCommandOf<Commands.MycroForge>, Commands.MycroForge.Script>();
|
||||||
|
@ -22,7 +22,7 @@ public sealed class Api : IFeature
|
|||||||
"from fastapi import FastAPI",
|
"from fastapi import FastAPI",
|
||||||
"app = FastAPI()",
|
"app = FastAPI()",
|
||||||
"",
|
"",
|
||||||
"from api.routers import hello",
|
$"from {FeatureName}.routers import hello",
|
||||||
"app.include_router(prefix=\"/hello\", router=hello.router)"
|
"app.include_router(prefix=\"/hello\", router=hello.router)"
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ public sealed class Api : IFeature
|
|||||||
"python3 -m pip freeze > requirements.txt"
|
"python3 -m pip freeze > requirements.txt"
|
||||||
);
|
);
|
||||||
|
|
||||||
await context.CreateFile("api/routers/hello.py", HelloRouter);
|
await context.CreateFile($"{FeatureName}/routers/hello.py", HelloRouter);
|
||||||
|
|
||||||
var main = await context.ReadFile("main.py");
|
var main = await context.ReadFile("main.py");
|
||||||
main = string.Join('\n', Main) + main;
|
main = string.Join('\n', Main) + main;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace MycroForge.CLI.Features;
|
namespace MycroForge.CLI.Features;
|
||||||
|
|
||||||
public sealed class Orm : IFeature
|
public sealed class Db : IFeature
|
||||||
{
|
{
|
||||||
#region Defaults
|
#region Defaults
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ public sealed class Orm : IFeature
|
|||||||
[
|
[
|
||||||
"connectionstring = \"mysql+asyncmy://root:root@localhost:3306/example\"",
|
"connectionstring = \"mysql+asyncmy://root:root@localhost:3306/example\"",
|
||||||
"",
|
"",
|
||||||
"class OrmSettings:",
|
"class DbSettings:",
|
||||||
"\tdef get_connectionstring() -> str:",
|
"\tdef get_connectionstring() -> str:",
|
||||||
"\t\treturn connectionstring"
|
"\t\treturn connectionstring"
|
||||||
];
|
];
|
||||||
@ -18,9 +18,9 @@ public sealed class Orm : IFeature
|
|||||||
private static readonly string[] AsyncSession =
|
private static readonly string[] AsyncSession =
|
||||||
[
|
[
|
||||||
"from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine, AsyncSession",
|
"from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine, AsyncSession",
|
||||||
"from orm.settings import OrmSettings",
|
"from db.settings import DbSettings",
|
||||||
"",
|
"",
|
||||||
"async_engine: AsyncEngine = create_async_engine(OrmSettings.get_connectionstring())",
|
"async_engine: AsyncEngine = create_async_engine(DbSettings.get_connectionstring())",
|
||||||
"",
|
"",
|
||||||
"def async_session() -> AsyncSession:",
|
"def async_session() -> AsyncSession:",
|
||||||
"\treturn AsyncSession(async_engine, expire_on_commit=False)"
|
"\treturn AsyncSession(async_engine, expire_on_commit=False)"
|
||||||
@ -38,7 +38,7 @@ public sealed class Orm : IFeature
|
|||||||
[
|
[
|
||||||
"from sqlalchemy import String",
|
"from sqlalchemy import String",
|
||||||
"from sqlalchemy.orm import Mapped, mapped_column",
|
"from sqlalchemy.orm import Mapped, mapped_column",
|
||||||
"from orm.entities.entity_base import EntityBase",
|
"from db.entities.entity_base import EntityBase",
|
||||||
"",
|
"",
|
||||||
"class User(EntityBase):",
|
"class User(EntityBase):",
|
||||||
"\t__tablename__ = \"users\"",
|
"\t__tablename__ = \"users\"",
|
||||||
@ -52,7 +52,7 @@ public sealed class Orm : IFeature
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public const string FeatureName = "orm";
|
public const string FeatureName = "db";
|
||||||
|
|
||||||
public string Name => FeatureName;
|
public string Name => FeatureName;
|
||||||
|
|
||||||
@ -64,21 +64,21 @@ public sealed class Orm : IFeature
|
|||||||
"source .venv/bin/activate",
|
"source .venv/bin/activate",
|
||||||
"python3 -m pip install asyncmy sqlalchemy alembic",
|
"python3 -m pip install asyncmy sqlalchemy alembic",
|
||||||
"python3 -m pip freeze > requirements.txt",
|
"python3 -m pip freeze > requirements.txt",
|
||||||
"alembic init -t async orm"
|
$"alembic init -t async {FeatureName}"
|
||||||
);
|
);
|
||||||
|
|
||||||
var env = await context.ReadFile("orm/env.py");
|
var env = await context.ReadFile($"{FeatureName}/env.py");
|
||||||
env = new OrmEnvInitializer(env).Rewrite();
|
env = new DbEnvInitializer(env).Rewrite();
|
||||||
env = new OrmEnvUpdater(env, "user", "User").Rewrite();
|
env = new DbEnvUpdater(env, "user", "User").Rewrite();
|
||||||
await context.WriteFile("orm/env.py", env);
|
await context.WriteFile($"{FeatureName}/env.py", env);
|
||||||
|
|
||||||
await context.CreateFile("orm/settings.py", Settings);
|
await context.CreateFile($"{FeatureName}/settings.py", Settings);
|
||||||
|
|
||||||
await context.CreateFile("orm/engine/async_session.py", AsyncSession);
|
await context.CreateFile($"{FeatureName}/engine/async_session.py", AsyncSession);
|
||||||
|
|
||||||
await context.CreateFile("orm/entities/entity_base.py", EntityBase);
|
await context.CreateFile($"{FeatureName}/entities/entity_base.py", EntityBase);
|
||||||
|
|
||||||
await context.CreateFile("orm/entities/user.py", User);
|
await context.CreateFile($"{FeatureName}/entities/user.py", User);
|
||||||
|
|
||||||
await context.SaveConfig(config);
|
await context.SaveConfig(config);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user