@atlashub/smartstack-cli 3.22.0 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-entry.mjs +87 -159
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +21 -0
- package/templates/skills/apex/references/smartstack-api.md +481 -0
- package/templates/skills/apex/references/smartstack-layers.md +85 -15
- package/templates/skills/apex/steps/step-00-init.md +27 -14
- package/templates/skills/apex/steps/step-01-analyze.md +18 -0
- package/templates/skills/apex/steps/step-03-execute.md +8 -6
- package/templates/skills/apex/steps/step-04-validate.md +92 -0
- package/templates/skills/apex/steps/step-07-tests.md +29 -5
package/dist/mcp-entry.mjs
CHANGED
|
@@ -26901,12 +26901,16 @@ async function validateNamespaces(structure, config2, result) {
|
|
|
26901
26901
|
if (namespaceMatch) {
|
|
26902
26902
|
const namespace = namespaceMatch[1];
|
|
26903
26903
|
if (!namespace.startsWith(layer.expected)) {
|
|
26904
|
+
const lastDot = namespace.lastIndexOf(".");
|
|
26905
|
+
const namespaceSuffix = lastDot > 0 ? namespace.substring(lastDot + 1) : "";
|
|
26906
|
+
const isValidLayerPattern = ["Domain", "Application", "Infrastructure", "Api"].some((l) => namespace.includes(`.${l}`) || namespace.endsWith(`.${l}`));
|
|
26907
|
+
const severity = isValidLayerPattern ? "warning" : "error";
|
|
26904
26908
|
result.errors.push({
|
|
26905
|
-
type:
|
|
26909
|
+
type: severity,
|
|
26906
26910
|
category: "namespaces",
|
|
26907
|
-
message: `${layer.name} file has
|
|
26911
|
+
message: `${layer.name} file has namespace "${namespace}" (expected prefix: "${layer.expected}")`,
|
|
26908
26912
|
file: path8.relative(structure.root, file),
|
|
26909
|
-
suggestion: `Should start with "${layer.expected}"`
|
|
26913
|
+
suggestion: severity === "warning" ? `Client extension project detected. Namespace "${namespace}" follows Clean Architecture pattern but differs from config.` : `Should start with "${layer.expected}"`
|
|
26910
26914
|
});
|
|
26911
26915
|
}
|
|
26912
26916
|
}
|
|
@@ -34497,18 +34501,28 @@ public interface I{{name}}Service
|
|
|
34497
34501
|
using System.Threading.Tasks;
|
|
34498
34502
|
using System.Collections.Generic;
|
|
34499
34503
|
using Microsoft.Extensions.Logging;
|
|
34504
|
+
using SmartStack.Application.Common.Interfaces.Identity;
|
|
34505
|
+
using SmartStack.Application.Common.Interfaces.Persistence;
|
|
34500
34506
|
|
|
34501
34507
|
namespace {{implNamespace}};
|
|
34502
34508
|
|
|
34503
34509
|
/// <summary>
|
|
34504
|
-
/// Service implementation for {{name}} operations
|
|
34510
|
+
/// Service implementation for {{name}} operations.
|
|
34511
|
+
/// IMPORTANT: All queries MUST filter by _currentUser.TenantId for multi-tenant isolation.
|
|
34505
34512
|
/// </summary>
|
|
34506
34513
|
public class {{name}}Service : I{{name}}Service
|
|
34507
34514
|
{
|
|
34515
|
+
private readonly IExtensionsDbContext _db;
|
|
34516
|
+
private readonly ICurrentUser _currentUser;
|
|
34508
34517
|
private readonly ILogger<{{name}}Service> _logger;
|
|
34509
34518
|
|
|
34510
|
-
public {{name}}Service(
|
|
34519
|
+
public {{name}}Service(
|
|
34520
|
+
IExtensionsDbContext db,
|
|
34521
|
+
ICurrentUser currentUser,
|
|
34522
|
+
ILogger<{{name}}Service> logger)
|
|
34511
34523
|
{
|
|
34524
|
+
_db = db;
|
|
34525
|
+
_currentUser = currentUser;
|
|
34512
34526
|
_logger = logger;
|
|
34513
34527
|
}
|
|
34514
34528
|
|
|
@@ -34516,8 +34530,8 @@ public class {{name}}Service : I{{name}}Service
|
|
|
34516
34530
|
/// <inheritdoc />
|
|
34517
34531
|
public async Task<object> {{this}}(CancellationToken cancellationToken = default)
|
|
34518
34532
|
{
|
|
34519
|
-
_logger.LogInformation("Executing {{this}}");
|
|
34520
|
-
// TODO: Implement {{this}}
|
|
34533
|
+
_logger.LogInformation("Executing {{this}} for tenant {TenantId}", _currentUser.TenantId);
|
|
34534
|
+
// TODO: Implement {{this}} \u2014 ALL queries must filter by _currentUser.TenantId
|
|
34521
34535
|
await Task.CompletedTask;
|
|
34522
34536
|
throw new NotImplementedException();
|
|
34523
34537
|
}
|
|
@@ -34561,7 +34575,6 @@ async function scaffoldEntity(name, options, structure, config2, result, dryRun
|
|
|
34561
34575
|
const schema = options?.schema || config2.conventions.schemas.platform;
|
|
34562
34576
|
const entityTemplate = `using System;
|
|
34563
34577
|
using SmartStack.Domain.Common;
|
|
34564
|
-
using SmartStack.Domain.Common.Interfaces;
|
|
34565
34578
|
|
|
34566
34579
|
namespace {{namespace}};
|
|
34567
34580
|
|
|
@@ -34570,14 +34583,10 @@ namespace {{namespace}};
|
|
|
34570
34583
|
{{#unless isSystemEntity}}
|
|
34571
34584
|
/// Tenant-scoped: data is isolated per tenant
|
|
34572
34585
|
{{else}}
|
|
34573
|
-
///
|
|
34586
|
+
/// Platform-level entity: no tenant isolation
|
|
34574
34587
|
{{/unless}}
|
|
34575
34588
|
/// </summary>
|
|
34576
|
-
{{#
|
|
34577
|
-
public class {{name}} : SystemEntity
|
|
34578
|
-
{{else}}
|
|
34579
|
-
public class {{name}} : BaseEntity, ITenantEntity
|
|
34580
|
-
{{/if}}
|
|
34589
|
+
public class {{name}} : BaseEntity{{#unless isSystemEntity}}, ITenantEntity, IAuditableEntity{{else}}, IAuditableEntity{{/unless}}
|
|
34581
34590
|
{
|
|
34582
34591
|
{{#unless isSystemEntity}}
|
|
34583
34592
|
// === MULTI-TENANT ===
|
|
@@ -34588,6 +34597,14 @@ public class {{name}} : BaseEntity, ITenantEntity
|
|
|
34588
34597
|
public Guid TenantId { get; private set; }
|
|
34589
34598
|
|
|
34590
34599
|
{{/unless}}
|
|
34600
|
+
// === AUDIT ===
|
|
34601
|
+
|
|
34602
|
+
/// <summary>User who created this entity</summary>
|
|
34603
|
+
public string? CreatedBy { get; set; }
|
|
34604
|
+
|
|
34605
|
+
/// <summary>User who last updated this entity</summary>
|
|
34606
|
+
public string? UpdatedBy { get; set; }
|
|
34607
|
+
|
|
34591
34608
|
{{#if baseEntity}}
|
|
34592
34609
|
// === RELATIONSHIPS ===
|
|
34593
34610
|
|
|
@@ -34599,7 +34616,7 @@ public class {{name}} : BaseEntity, ITenantEntity
|
|
|
34599
34616
|
/// <summary>
|
|
34600
34617
|
/// Navigation property to {{baseEntity}}
|
|
34601
34618
|
/// </summary>
|
|
34602
|
-
public
|
|
34619
|
+
public {{baseEntity}}? {{baseEntity}} { get; private set; }
|
|
34603
34620
|
|
|
34604
34621
|
{{/if}}
|
|
34605
34622
|
// === BUSINESS PROPERTIES ===
|
|
@@ -34613,71 +34630,38 @@ public class {{name}} : BaseEntity, ITenantEntity
|
|
|
34613
34630
|
/// <summary>
|
|
34614
34631
|
/// Factory method to create a new {{name}}
|
|
34615
34632
|
/// </summary>
|
|
34616
|
-
{{#
|
|
34633
|
+
{{#unless isSystemEntity}}
|
|
34634
|
+
/// <param name="tenantId">Required tenant identifier</param>
|
|
34617
34635
|
public static {{name}} Create(
|
|
34618
|
-
|
|
34619
|
-
string? createdBy = null)
|
|
34636
|
+
Guid tenantId)
|
|
34620
34637
|
{
|
|
34638
|
+
if (tenantId == Guid.Empty)
|
|
34639
|
+
throw new ArgumentException("TenantId is required", nameof(tenantId));
|
|
34640
|
+
|
|
34621
34641
|
return new {{name}}
|
|
34622
34642
|
{
|
|
34623
34643
|
Id = Guid.NewGuid(),
|
|
34624
|
-
|
|
34625
|
-
CreatedAt = DateTime.UtcNow
|
|
34626
|
-
CreatedBy = createdBy
|
|
34644
|
+
TenantId = tenantId,
|
|
34645
|
+
CreatedAt = DateTime.UtcNow
|
|
34627
34646
|
};
|
|
34628
34647
|
}
|
|
34629
34648
|
{{else}}
|
|
34630
|
-
|
|
34631
|
-
/// <param name="code">Unique code within tenant (will be lowercased)</param>
|
|
34632
|
-
/// <param name="createdBy">User who created this entity</param>
|
|
34633
|
-
public static {{name}} Create(
|
|
34634
|
-
Guid tenantId,
|
|
34635
|
-
string code,
|
|
34636
|
-
string? createdBy = null)
|
|
34649
|
+
public static {{name}} Create()
|
|
34637
34650
|
{
|
|
34638
|
-
if (tenantId == Guid.Empty)
|
|
34639
|
-
throw new ArgumentException("TenantId is required", nameof(tenantId));
|
|
34640
|
-
|
|
34641
34651
|
return new {{name}}
|
|
34642
34652
|
{
|
|
34643
34653
|
Id = Guid.NewGuid(),
|
|
34644
|
-
|
|
34645
|
-
Code = code.ToLowerInvariant(),
|
|
34646
|
-
CreatedAt = DateTime.UtcNow,
|
|
34647
|
-
CreatedBy = createdBy
|
|
34654
|
+
CreatedAt = DateTime.UtcNow
|
|
34648
34655
|
};
|
|
34649
34656
|
}
|
|
34650
|
-
{{/
|
|
34657
|
+
{{/unless}}
|
|
34651
34658
|
|
|
34652
34659
|
/// <summary>
|
|
34653
34660
|
/// Update the entity
|
|
34654
34661
|
/// </summary>
|
|
34655
|
-
public void Update(
|
|
34662
|
+
public void Update()
|
|
34656
34663
|
{
|
|
34657
34664
|
UpdatedAt = DateTime.UtcNow;
|
|
34658
|
-
UpdatedBy = updatedBy;
|
|
34659
|
-
}
|
|
34660
|
-
|
|
34661
|
-
/// <summary>
|
|
34662
|
-
/// Soft delete the entity
|
|
34663
|
-
/// </summary>
|
|
34664
|
-
public void SoftDelete(string? deletedBy = null)
|
|
34665
|
-
{
|
|
34666
|
-
IsDeleted = true;
|
|
34667
|
-
DeletedAt = DateTime.UtcNow;
|
|
34668
|
-
DeletedBy = deletedBy;
|
|
34669
|
-
}
|
|
34670
|
-
|
|
34671
|
-
/// <summary>
|
|
34672
|
-
/// Restore a soft-deleted entity
|
|
34673
|
-
/// </summary>
|
|
34674
|
-
public void Restore(string? restoredBy = null)
|
|
34675
|
-
{
|
|
34676
|
-
IsDeleted = false;
|
|
34677
|
-
DeletedAt = null;
|
|
34678
|
-
DeletedBy = null;
|
|
34679
|
-
UpdatedAt = DateTime.UtcNow;
|
|
34680
|
-
UpdatedBy = restoredBy;
|
|
34681
34665
|
}
|
|
34682
34666
|
}
|
|
34683
34667
|
`;
|
|
@@ -34689,85 +34673,38 @@ namespace {{infrastructureNamespace}}.Persistence.Configurations{{#if configName
|
|
|
34689
34673
|
|
|
34690
34674
|
/// <summary>
|
|
34691
34675
|
/// EF Core configuration for {{name}}
|
|
34692
|
-
{{#unless isSystemEntity}}
|
|
34693
|
-
/// Tenant-aware: includes tenant isolation query filter
|
|
34694
|
-
{{else}}
|
|
34695
|
-
/// System entity: no tenant isolation
|
|
34696
|
-
{{/unless}}
|
|
34697
34676
|
/// </summary>
|
|
34698
34677
|
public class {{name}}Configuration : IEntityTypeConfiguration<{{name}}>
|
|
34699
34678
|
{
|
|
34700
34679
|
public void Configure(EntityTypeBuilder<{{name}}> builder)
|
|
34701
34680
|
{
|
|
34702
|
-
// Table name with schema and domain prefix
|
|
34703
34681
|
builder.ToTable("{{tablePrefix}}{{name}}s", "{{schema}}");
|
|
34704
34682
|
|
|
34705
|
-
// Primary key
|
|
34706
34683
|
builder.HasKey(e => e.Id);
|
|
34707
34684
|
|
|
34708
34685
|
{{#unless isSystemEntity}}
|
|
34709
|
-
//
|
|
34710
|
-
// MULTI-TENANT CONFIGURATION
|
|
34711
|
-
// ============================================
|
|
34712
|
-
|
|
34713
|
-
// TenantId is required for tenant isolation
|
|
34686
|
+
// Multi-tenant
|
|
34714
34687
|
builder.Property(e => e.TenantId).IsRequired();
|
|
34715
34688
|
builder.HasIndex(e => e.TenantId)
|
|
34716
34689
|
.HasDatabaseName("IX_{{tablePrefix}}{{name}}s_TenantId");
|
|
34717
34690
|
|
|
34718
|
-
// Tenant relationship (configured in Tenant configuration)
|
|
34719
|
-
// builder.HasOne<Tenant>().WithMany().HasForeignKey(e => e.TenantId);
|
|
34720
|
-
|
|
34721
|
-
// Code: lowercase, unique per tenant (filtered for soft delete)
|
|
34722
|
-
builder.Property(e => e.Code).HasMaxLength(100).IsRequired();
|
|
34723
|
-
builder.HasIndex(e => new { e.TenantId, e.Code })
|
|
34724
|
-
.IsUnique()
|
|
34725
|
-
.HasFilter("[IsDeleted] = 0")
|
|
34726
|
-
.HasDatabaseName("IX_{{tablePrefix}}{{name}}s_Tenant_Code_Unique");
|
|
34727
|
-
{{else}}
|
|
34728
|
-
// Code: lowercase, unique globally (filtered for soft delete)
|
|
34729
|
-
builder.Property(e => e.Code).HasMaxLength(100).IsRequired();
|
|
34730
|
-
builder.HasIndex(e => e.Code)
|
|
34731
|
-
.IsUnique()
|
|
34732
|
-
.HasFilter("[IsDeleted] = 0")
|
|
34733
|
-
.HasDatabaseName("IX_{{tablePrefix}}{{name}}s_Code_Unique");
|
|
34734
34691
|
{{/unless}}
|
|
34735
|
-
|
|
34736
|
-
// Concurrency token
|
|
34737
|
-
builder.Property(e => e.RowVersion).IsRowVersion();
|
|
34738
|
-
|
|
34739
|
-
// Audit fields
|
|
34692
|
+
// Audit fields (from IAuditableEntity)
|
|
34740
34693
|
builder.Property(e => e.CreatedBy).HasMaxLength(256);
|
|
34741
34694
|
builder.Property(e => e.UpdatedBy).HasMaxLength(256);
|
|
34742
|
-
builder.Property(e => e.DeletedBy).HasMaxLength(256);
|
|
34743
34695
|
|
|
34744
34696
|
{{#if baseEntity}}
|
|
34745
|
-
//
|
|
34746
|
-
// RELATIONSHIPS
|
|
34747
|
-
// ============================================
|
|
34748
|
-
|
|
34749
|
-
// Relationship to {{baseEntity}} (1:1)
|
|
34697
|
+
// Relationship to {{baseEntity}}
|
|
34750
34698
|
builder.HasOne(e => e.{{baseEntity}})
|
|
34751
|
-
.
|
|
34752
|
-
.HasForeignKey
|
|
34753
|
-
.OnDelete(DeleteBehavior.
|
|
34699
|
+
.WithMany()
|
|
34700
|
+
.HasForeignKey(e => e.{{baseEntity}}Id)
|
|
34701
|
+
.OnDelete(DeleteBehavior.Restrict);
|
|
34754
34702
|
|
|
34755
|
-
// Index on foreign key
|
|
34756
34703
|
builder.HasIndex(e => e.{{baseEntity}}Id)
|
|
34757
|
-
.
|
|
34704
|
+
.HasDatabaseName("IX_{{tablePrefix}}{{name}}s_{{baseEntity}}Id");
|
|
34758
34705
|
{{/if}}
|
|
34759
34706
|
|
|
34760
|
-
//
|
|
34761
|
-
// QUERY FILTERS
|
|
34762
|
-
// ============================================
|
|
34763
|
-
// Note: Global query filters are applied in DbContext.OnModelCreating
|
|
34764
|
-
// - Soft delete: .HasQueryFilter(e => !e.IsDeleted)
|
|
34765
|
-
{{#unless isSystemEntity}}
|
|
34766
|
-
// - Tenant isolation: .HasQueryFilter(e => e.TenantId == _tenantId)
|
|
34767
|
-
// Combined filter applied via ITenantEntity interface check
|
|
34768
|
-
{{/unless}}
|
|
34769
|
-
|
|
34770
|
-
// TODO: Add additional business-specific configuration
|
|
34707
|
+
// TODO: Add business property configurations (HasMaxLength, IsRequired, indexes)
|
|
34771
34708
|
}
|
|
34772
34709
|
}
|
|
34773
34710
|
`;
|
|
@@ -34813,11 +34750,11 @@ public class {{name}}Configuration : IEntityTypeConfiguration<{{name}}>
|
|
|
34813
34750
|
result.instructions.push("Create migration:");
|
|
34814
34751
|
result.instructions.push(`dotnet ef migrations add ${migrationPrefix}_vX.X.X_XXX_Add${name} --context ${dbContextName}`);
|
|
34815
34752
|
result.instructions.push("");
|
|
34816
|
-
result.instructions.push("
|
|
34817
|
-
result.instructions.push(`- Id (Guid),
|
|
34818
|
-
result.instructions.push("-
|
|
34819
|
-
result.instructions.push("-
|
|
34820
|
-
result.instructions.push("-
|
|
34753
|
+
result.instructions.push("BaseEntity fields (inherited):");
|
|
34754
|
+
result.instructions.push(`- Id (Guid), CreatedAt (DateTime), UpdatedAt (DateTime?)`);
|
|
34755
|
+
result.instructions.push(`${isSystemEntity ? "" : "- TenantId (Guid) \u2014 from ITenantEntity"}`);
|
|
34756
|
+
result.instructions.push("- CreatedBy, UpdatedBy (string?) \u2014 from IAuditableEntity");
|
|
34757
|
+
result.instructions.push("- Add your own business properties (Code, Name, etc.) as needed");
|
|
34821
34758
|
if (options?.withSeedData) {
|
|
34822
34759
|
result.instructions.push("");
|
|
34823
34760
|
result.instructions.push("### Seed Data");
|
|
@@ -34889,9 +34826,7 @@ public static class {{name}}SeedData
|
|
|
34889
34826
|
{{#unless isSystemEntity}}
|
|
34890
34827
|
TenantId = (Guid?)null, // Seed data systeme sans tenant
|
|
34891
34828
|
{{/unless}}
|
|
34892
|
-
Code = ExampleCode,
|
|
34893
34829
|
// TODO: Ajouter les proprietes specifiques
|
|
34894
|
-
IsDeleted = false,
|
|
34895
34830
|
CreatedAt = seedDate
|
|
34896
34831
|
}
|
|
34897
34832
|
};
|
|
@@ -35116,11 +35051,8 @@ async function scaffoldController(name, options, structure, config2, result, dry
|
|
|
35116
35051
|
const namespace = options?.namespace || (hierarchy.controllerArea ? `${config2.conventions.namespaces.api}.Controllers.${hierarchy.controllerArea}` : `${config2.conventions.namespaces.api}.Controllers`);
|
|
35117
35052
|
const navRoute = options?.navRoute;
|
|
35118
35053
|
const navRouteSuffix = options?.navRouteSuffix;
|
|
35119
|
-
const
|
|
35120
|
-
const
|
|
35121
|
-
[NavRoute("${navRoute}", Suffix = "${navRouteSuffix}")]` : `[Route("${apiRoute}")]
|
|
35122
|
-
[NavRoute("${navRoute}")]` : `[Route("api/[controller]")]`;
|
|
35123
|
-
const navRouteUsing = navRoute ? "using SmartStack.Api.Core.Routing;\n" : "";
|
|
35054
|
+
const routeAttribute = navRoute ? navRouteSuffix ? `[NavRoute("${navRoute}", Suffix = "${navRouteSuffix}")]` : `[NavRoute("${navRoute}")]` : `[Route("api/[controller]")]`;
|
|
35055
|
+
const navRouteUsing = navRoute ? "using SmartStack.Api.Routing;\n" : "";
|
|
35124
35056
|
const controllerTemplate = `using Microsoft.AspNetCore.Authorization;
|
|
35125
35057
|
using Microsoft.AspNetCore.Mvc;
|
|
35126
35058
|
using Microsoft.Extensions.Logging;
|
|
@@ -35128,17 +35060,20 @@ ${navRouteUsing}
|
|
|
35128
35060
|
namespace {{namespace}};
|
|
35129
35061
|
|
|
35130
35062
|
/// <summary>
|
|
35131
|
-
/// API controller for {{name}} operations
|
|
35063
|
+
/// API controller for {{name}} operations.
|
|
35064
|
+
/// IMPORTANT: Use [RequirePermission] on each endpoint for RBAC enforcement.
|
|
35132
35065
|
/// </summary>
|
|
35133
35066
|
[ApiController]
|
|
35134
35067
|
{{routeAttribute}}
|
|
35135
35068
|
[Authorize]
|
|
35136
35069
|
public class {{name}}Controller : ControllerBase
|
|
35137
35070
|
{
|
|
35071
|
+
private readonly I{{name}}Service _service;
|
|
35138
35072
|
private readonly ILogger<{{name}}Controller> _logger;
|
|
35139
35073
|
|
|
35140
|
-
public {{name}}Controller(ILogger<{{name}}Controller> logger)
|
|
35074
|
+
public {{name}}Controller(I{{name}}Service service, ILogger<{{name}}Controller> logger)
|
|
35141
35075
|
{
|
|
35076
|
+
_service = service;
|
|
35142
35077
|
_logger = logger;
|
|
35143
35078
|
}
|
|
35144
35079
|
|
|
@@ -35146,21 +35081,21 @@ public class {{name}}Controller : ControllerBase
|
|
|
35146
35081
|
/// Get all {{nameLower}}s
|
|
35147
35082
|
/// </summary>
|
|
35148
35083
|
[HttpGet]
|
|
35149
|
-
|
|
35084
|
+
// TODO: Add [RequirePermission(Permissions.{Module}.Read)] for RBAC
|
|
35085
|
+
public async Task<ActionResult<IEnumerable<object>>> GetAll(CancellationToken ct)
|
|
35150
35086
|
{
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
return Ok(Array.Empty<{{name}}Dto>());
|
|
35087
|
+
// TODO: Call _service.GetAllAsync(ct)
|
|
35088
|
+
return Ok(Array.Empty<object>());
|
|
35154
35089
|
}
|
|
35155
35090
|
|
|
35156
35091
|
/// <summary>
|
|
35157
35092
|
/// Get {{nameLower}} by ID
|
|
35158
35093
|
/// </summary>
|
|
35159
35094
|
[HttpGet("{id:guid}")]
|
|
35160
|
-
|
|
35095
|
+
// TODO: Add [RequirePermission(Permissions.{Module}.Read)] for RBAC
|
|
35096
|
+
public async Task<ActionResult<object>> GetById(Guid id, CancellationToken ct)
|
|
35161
35097
|
{
|
|
35162
|
-
|
|
35163
|
-
// TODO: Implement
|
|
35098
|
+
// TODO: Call _service.GetByIdAsync(id, ct)
|
|
35164
35099
|
return NotFound();
|
|
35165
35100
|
}
|
|
35166
35101
|
|
|
@@ -35168,10 +35103,10 @@ public class {{name}}Controller : ControllerBase
|
|
|
35168
35103
|
/// Create new {{nameLower}}
|
|
35169
35104
|
/// </summary>
|
|
35170
35105
|
[HttpPost]
|
|
35171
|
-
|
|
35106
|
+
// TODO: Add [RequirePermission(Permissions.{Module}.Create)] for RBAC
|
|
35107
|
+
public async Task<ActionResult<object>> Create([FromBody] object request, CancellationToken ct)
|
|
35172
35108
|
{
|
|
35173
|
-
|
|
35174
|
-
// TODO: Implement
|
|
35109
|
+
// TODO: Call _service.CreateAsync(dto, ct)
|
|
35175
35110
|
return CreatedAtAction(nameof(GetById), new { id = Guid.NewGuid() }, null);
|
|
35176
35111
|
}
|
|
35177
35112
|
|
|
@@ -35179,10 +35114,10 @@ public class {{name}}Controller : ControllerBase
|
|
|
35179
35114
|
/// Update {{nameLower}}
|
|
35180
35115
|
/// </summary>
|
|
35181
35116
|
[HttpPut("{id:guid}")]
|
|
35182
|
-
|
|
35117
|
+
// TODO: Add [RequirePermission(Permissions.{Module}.Update)] for RBAC
|
|
35118
|
+
public async Task<ActionResult> Update(Guid id, [FromBody] object request, CancellationToken ct)
|
|
35183
35119
|
{
|
|
35184
|
-
|
|
35185
|
-
// TODO: Implement
|
|
35120
|
+
// TODO: Call _service.UpdateAsync(id, dto, ct)
|
|
35186
35121
|
return NoContent();
|
|
35187
35122
|
}
|
|
35188
35123
|
|
|
@@ -35190,18 +35125,13 @@ public class {{name}}Controller : ControllerBase
|
|
|
35190
35125
|
/// Delete {{nameLower}}
|
|
35191
35126
|
/// </summary>
|
|
35192
35127
|
[HttpDelete("{id:guid}")]
|
|
35193
|
-
|
|
35128
|
+
// TODO: Add [RequirePermission(Permissions.{Module}.Delete)] for RBAC
|
|
35129
|
+
public async Task<ActionResult> Delete(Guid id, CancellationToken ct)
|
|
35194
35130
|
{
|
|
35195
|
-
|
|
35196
|
-
// TODO: Implement
|
|
35131
|
+
// TODO: Call _service.DeleteAsync(id, ct)
|
|
35197
35132
|
return NoContent();
|
|
35198
35133
|
}
|
|
35199
35134
|
}
|
|
35200
|
-
|
|
35201
|
-
// DTOs
|
|
35202
|
-
public record {{name}}Dto(Guid Id, DateTime CreatedAt);
|
|
35203
|
-
public record Create{{name}}Request();
|
|
35204
|
-
public record Update{{name}}Request();
|
|
35205
35135
|
`;
|
|
35206
35136
|
const context = {
|
|
35207
35137
|
namespace,
|
|
@@ -35221,14 +35151,12 @@ public record Update{{name}}Request();
|
|
|
35221
35151
|
}
|
|
35222
35152
|
result.files.push({ path: controllerFilePath, content: controllerContent, type: "created" });
|
|
35223
35153
|
if (navRoute) {
|
|
35224
|
-
result.instructions.push("Controller created with NavRoute
|
|
35154
|
+
result.instructions.push("Controller created with NavRoute (route resolved from DB at startup).");
|
|
35225
35155
|
result.instructions.push(`NavRoute: ${navRoute}${navRouteSuffix ? ` (Suffix: ${navRouteSuffix})` : ""}`);
|
|
35226
|
-
result.instructions.push(`API Route: ${apiRoute}`);
|
|
35227
35156
|
result.instructions.push("");
|
|
35228
|
-
result.instructions.push("
|
|
35229
|
-
result.instructions.push("
|
|
35230
|
-
result.instructions.push(
|
|
35231
|
-
result.instructions.push(` Context > Application > Module > Section matching "${navRoute}"`);
|
|
35157
|
+
result.instructions.push("NavRoute resolves API routes from Navigation entities in the database.");
|
|
35158
|
+
result.instructions.push("Ensure the navigation path exists (seed data required):");
|
|
35159
|
+
result.instructions.push(` Context > Application > Module matching "${navRoute}"`);
|
|
35232
35160
|
} else {
|
|
35233
35161
|
result.instructions.push("Controller created with traditional routing.");
|
|
35234
35162
|
result.instructions.push("");
|
|
@@ -35810,7 +35738,7 @@ public interface I{{name}}Repository
|
|
|
35810
35738
|
/// <summary>Update entity</summary>
|
|
35811
35739
|
Task UpdateAsync({{name}} entity, CancellationToken ct = default);
|
|
35812
35740
|
|
|
35813
|
-
/// <summary>
|
|
35741
|
+
/// <summary>Delete entity</summary>
|
|
35814
35742
|
Task DeleteAsync({{name}} entity, CancellationToken ct = default);
|
|
35815
35743
|
}
|
|
35816
35744
|
`;
|
|
@@ -35885,8 +35813,8 @@ public class {{name}}Repository : I{{name}}Repository
|
|
|
35885
35813
|
/// <inheritdoc />
|
|
35886
35814
|
public async Task DeleteAsync({{name}} entity, CancellationToken ct = default)
|
|
35887
35815
|
{
|
|
35888
|
-
|
|
35889
|
-
await
|
|
35816
|
+
_context.{{name}}s.Remove(entity);
|
|
35817
|
+
await _context.SaveChangesAsync(ct);
|
|
35890
35818
|
}
|
|
35891
35819
|
}
|
|
35892
35820
|
`;
|