---
name: step-03b-provider
description: Generate IClientSeedDataProvider implementation for client projects
prev_step: steps/step-03-roles.md
next_step: steps/step-04-backend.md
---

# Step 3b: Client Seed Data Provider (Client Projects Only)

## CONDITION D'EXECUTION

> This step executes ONLY if `{seeding_strategy}` = "provider" (client project).
> If `{seeding_strategy}` = "hasdata", skip directly to step-04-backend.md.

## MANDATORY EXECUTION RULES

- ONLY execute for client projects (`{seeding_strategy}` = "provider")
- ALWAYS use factory methods (`NavigationModule.Create(...)`, etc.) - NEVER `new Entity()`
- ALWAYS implement idempotence (check existence before inserting)
- ALWAYS register the provider in DI
- ALWAYS consume the SeedData classes created in steps 01-03

## YOUR TASK

Generate the `IClientSeedDataProvider` implementation that wires the SeedData files
created in steps 01, 02, 03 into the SmartStack Core seeding pipeline.

---

## AVAILABLE STATE

From previous steps:

| Variable | Description |
|----------|-------------|
| `{level}` | context, application, module, or section |
| `{code}` | kebab-case identifier |
| `{full_path}` | Complete navigation path |
| `{labels}` | Object with fr, en, it, de |
| `{descriptions}` | Object with fr, en, it, de |
| `{icon}` | Lucide icon name |
| `{display_order}` | Numeric display order |
| `{navigation_guid}` | GUID of the navigation entity |
| `{permission_guids}` | GUIDs for generated permissions |
| `{project_type}` | "client" (guaranteed at this step) |
| `{seeding_strategy}` | "provider" (guaranteed at this step) |

---

## FILES TO GENERATE

### 1. The Provider Class

**Location:** `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`

Where `{AppPascalName}` is derived from the application code (e.g., `free-bike` -> `FreeBike`).

**Pattern:**

```csharp
using Microsoft.EntityFrameworkCore;
using SmartStack.Application.Common.Interfaces;
using SmartStack.Domain.Navigation;
using SmartStack.Domain.Platform.Administration.Roles;

namespace {BaseNamespace}.Infrastructure.Persistence.Seeding;

/// <summary>
/// Seeds {AppLabel} navigation, permissions, and role-permission data
/// into the SmartStack Core schema at application startup.
/// Implements <see cref="IClientSeedDataProvider"/> for runtime seeding
/// (no Core migrations required).
/// </summary>
public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
{
    public int Order => 100;

    public async Task SeedNavigationAsync(ICoreDbContext context, CancellationToken ct)
    {
        // Check idempotence
        var exists = await context.NavigationApplications
            .AnyAsync(a => a.Code == "{app_code}", ct);
        if (exists) return;

        // 1. Retrieve the parent context ("business", "platform", etc.)
        var parentContext = await context.NavigationContexts
            .FirstAsync(c => c.Code == "{context_code}", ct);

        // 2. Create the application
        var app = NavigationApplication.Create(
            parentContext.Id,
            "{app_code}",
            "{app_label_en}",
            "{app_desc_en}",
            "{app_icon}",
            IconType.Lucide,
            "/{context_code}/{app_code}",
            {display_order});
        context.NavigationApplications.Add(app);
        await ((DbContext)context).SaveChangesAsync(ct);

        // 3. Create modules
        // Use GUIDs and data from {Module}NavigationSeedData.cs
        foreach (var moduleData in GetModuleSeedEntries(app.Id))
        {
            var module = NavigationModule.Create(
                moduleData.ApplicationId,
                moduleData.Code,
                moduleData.Label,
                moduleData.Description,
                moduleData.Icon,
                IconType.Lucide,
                moduleData.Route,
                moduleData.DisplayOrder);
            context.NavigationModules.Add(module);
        }
        await ((DbContext)context).SaveChangesAsync(ct);

        // 4. Create translations (4 languages per entity)
        // Use data from {Module}NavigationTranslationSeedData.cs
        // ...
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    public async Task SeedPermissionsAsync(ICoreDbContext context, CancellationToken ct)
    {
        // Check idempotence
        var exists = await context.Permissions
            .AnyAsync(p => p.Path == "{full_path}.*", ct);
        if (exists) return;

        // Retrieve modules by Code for FK resolution
        // Use data from {Module}PermissionSeedData.cs
        // Create via Permission.CreateForModule(...) and Permission.CreateWildcard(...)
        // ...
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    public async Task SeedRolePermissionsAsync(ICoreDbContext context, CancellationToken ct)
    {
        // Check idempotence
        var exists = await context.RolePermissions
            .AnyAsync(rp => rp.Permission!.Path.StartsWith("{full_path}."), ct);
        if (exists) return;

        // Retrieve existing roles and created permissions
        // Use data from {Module}RolePermissionSeedData.cs
        // Create via RolePermission.Create(roleId, permissionId, "system")
        // ...
        await ((DbContext)context).SaveChangesAsync(ct);
    }

    // Private helper methods fed by SeedData classes
    // ...
}
```

### 2. DI Registration

Modify `Infrastructure/DependencyInjection.cs` of the client project:

```csharp
// Add using:
using SmartStack.Application.Common.Interfaces;

// In the registration method:
services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
```

---

## CRITICAL RULES

1. **Factory methods mandatory**: `NavigationModule.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` - NEVER `new Entity()`
2. **Idempotence**: Each Seed method checks existence before inserting
3. **SaveChangesAsync per group**: Navigation -> save -> Permissions -> save -> RolePermissions -> save
4. **Deterministic GUIDs**: Use IDs from SeedData classes (not Guid.NewGuid())
5. **Resolve FK by Code**: Parent modules are found by `Code`, not hardcoded GUID
6. **Order property**: Use `100` as default. If multiple providers exist, they run in Order sequence

---

## EXECUTION SEQUENCE

### 1. Read SeedData files from steps 01-03

Identify all SeedData files created in previous steps:

```
Glob: **/Persistence/Seeding/Data/{Domain}/*SeedData.cs
```

Expected files:
- `{Module}NavigationSeedData.cs` (from step 01)
- `{Module}NavigationTranslationSeedData.cs` (from step 01)
- `{Module}PermissionSeedData.cs` (from step 02)
- `{Module}RolePermissionSeedData.cs` (from step 03)

### 2. Generate Provider Class

Using the SeedData files as data sources, generate the `{AppPascalName}SeedDataProvider.cs` following the pattern above.

Adapt the template:
- Replace `{AppPascalName}` with the PascalCase application name
- Replace `{app_code}` with the kebab-case application code
- Replace `{context_code}` with the parent context code
- Fill in the actual navigation, permission, and role-permission creation logic
  using the helper methods from the SeedData classes

### 3. Register in DI

Find and update `Infrastructure/DependencyInjection.cs`:

```
Glob: **/Infrastructure/DependencyInjection.cs
```

Add the `IClientSeedDataProvider` registration.

### 4. Verify

Before proceeding to step-04, verify:
- [ ] Provider generated with 3 methods (SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync)
- [ ] Registered in DI (`services.AddScoped<IClientSeedDataProvider, ...>()`)
- [ ] Consumes SeedData classes from steps 01-03
- [ ] Idempotent (check existence before insert)
- [ ] Uses factory methods (no `new Entity()`)

---

## SUCCESS METRICS

- Provider class generated with all 3 seed methods
- DI registration added
- SeedData classes from steps 01-03 properly consumed
- All methods are idempotent
- Factory methods used throughout
- Proceeded to step-04-backend.md

## FAILURE MODES

- SeedData files not found from previous steps (return to step-01)
- DependencyInjection.cs not found (ask user for DI registration location)
- Missing IClientSeedDataProvider interface (verify SmartStack.Application reference)

---

## NEXT STEP

After generating the provider and registering it in DI,
proceed to `./step-04-backend.md`
