seeds klasör yapısı değişikliği

This commit is contained in:
Sedat ÖZTÜRK 2026-08-17 12:23:47 +03:00
parent 9a2d88b93b
commit f5ba73f875
31 changed files with 165 additions and 103 deletions

View file

@ -178,8 +178,10 @@ sozsoft-platform/
├── configs/
│ ├── docker/ # Yerel geliştirme için veri katmanı compose dosyaları ve scriptler
│ ├── deployment/ # Dev/Prod compose dosyaları, nginx/redis/turn konfigürasyonu, deploy scriptleri
│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler: SqlData, PostgresData, WizardData
│ │ # {SqlData|PostgresData}/{HostData|TenantData}/{ObjectData|ExecuteData}
│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler; CDN ile aynı kapsam düzeni
│ │ # host/{crud,custom,wizard,sql,postgres}
│ │ # tenants/{tenantId}/{crud,custom,wizard,sql,postgres}
│ │ # {sql|postgres}/{object,execute}
│ └── ai/ # AI entegrasyonu (n8n akış tanımları)
├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları), list.instructions.md
├── CLAUDE.md # Claude Code için operasyon kuralları
@ -238,14 +240,32 @@ host ayarları, ülke/şehir/ilçe verileri ile seed edilmiş ListForm ekranlar
(kodla versiyonlanan sabit seed'ler `api/src/Sozsoft.Platform.DbMigrator/Migrations/`,
runtime'da düzenlenebilen seed'ler `configs/seeds/` altındadır — bkz. `App:SeedsPath`).
`configs/seeds/{SqlData|PostgresData}` altındaki her `.sql` dosyası, önce veritabanı
kapsamına (`HostData` / `TenantData`), sonra amacına göre bir klasörde durur; klasör
köklerinde dosya bulunmaz:
`configs/seeds` altında en üst seviye kapsamdır (CDN klasörleriyle aynı mantık): host
veritabanı için `host/`, her tenant için `tenants/{tenantId}/`. Kapsamın altında içerik
türüne göre `crud/`, `custom/`, `wizard/`, `sql/`, `postgres/` klasörleri bulunur:
```
configs/seeds/
host/
crud/ custom/ wizard/
sql/ { object/ execute/ }
postgres/ { object/ execute/ }
tenants/
{tenantId}/
crud/ custom/ wizard/
sql/ { object/ execute/ }
postgres/ { object/ execute/ }
```
Aktif veritabanı sağlayıcısına göre `sql/` (SQL Server) ya da `postgres/` okunur. Her
`.sql` dosyası amacını belirten bir klasörün altındadır; klasör köklerinde dosya bulunmaz:
| Klasör | Ne zaman çalışır |
| --- | --- |
| `{HostData\|TenantData}/ObjectData` | Seed sırasında nesneyi oluşturur/günceller. SqlTableDesigner ve SqlQueryManager, deploy ettiği script'i çalıştığı kapsamın bu klasörüne yazar (host bağlamı`HostData`, tenant seçiliyken → `TenantData`). |
| `{HostData\|TenantData}/ExecuteData` | Nesneyi oluşturur; ayrıca tüm migration ve seed'ler bittikten sonra `AfterAllMigrationsSqlExecutor` dosya adından türettiği procedure'ü çalıştırır (dosya adı = procedure adı, `schema.Procedure.sql` de olur). |
| `{sql\|postgres}/object` | Seed sırasında nesneyi oluşturur/günceller. SqlTableDesigner ve SqlQueryManager, deploy ettiği script'i çalıştığı kapsamın bu klasörüne yazar (host bağlamı`host/`, tenant seçiliyken → `tenants/{tenantId}/`). |
| `{sql\|postgres}/execute` | Nesneyi oluşturur; ayrıca tüm migration ve seed'ler bittikten sonra `AfterAllMigrationsSqlExecutor` dosya adından türettiği procedure'ü çalıştırır (dosya adı = procedure adı, `schema.Procedure.sql` de olur). |
| `wizard` | ListForm Wizard'ın ürettiği `.json` konfigürasyonları; `WizardDataSeeder` kendi kapsamındaki dosyaları okur. |
| `crud` / `custom` | CrudEndpoint ve CustomEndpoint konfigürasyonları için ayrılmıştır. |
Klasör kökünde kalmış eski `.sql` dosyaları geriye dönük uyumluluk için hâlâ işlenir,
fakat `SqlDataSeeder` bunları uyarı ile loglar — ilgili klasöre taşınmaları beklenir.

View file

@ -30,14 +30,14 @@ public interface ISqlObjectManagerAppService : IApplicationService
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
/// <summary>
/// Saves the T-SQL script to {SeedsRoot}/{SqlData|PostgresData}/{HostData|TenantData}/ObjectData/{fileName}.sql
/// (repo: configs/seeds). The scope folder follows the current tenant: host context writes to HostData.
/// Saves the T-SQL script to {SeedsRoot}/{host|tenants/{tenantId}}/{sql|postgres}/object/{fileName}.sql
/// (repo: configs/seeds). The scope folder follows the current tenant: host context writes to host/.
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
/// </summary>
Task SaveTableScriptAsync(SaveTableScriptDto input);
/// <summary>
/// Deletes matching SQL seed files from the current scope's ObjectData folder when objects are
/// Deletes matching SQL seed files from the current scope's object folder when objects are
/// dropped from the UI.
/// Non-existing files are ignored.
/// </summary>

View file

@ -20,7 +20,8 @@ public class SqlQueryExecutionResultDto
}
/// <summary>
/// Input for saving a T-SQL script file to the seeds folder ({App:SeedsPath}/SqlData).
/// Input for saving a T-SQL script file to the seeds folder
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
/// </summary>
public class SaveTableScriptDto
{
@ -36,7 +37,8 @@ public class SaveTableScriptDto
}
/// <summary>
/// Input for deleting seed files from the seeds folder ({App:SeedsPath}/SqlData).
/// Input for deleting seed files from the seeds folder
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
/// </summary>
public class DeleteSqlDataFilesDto
{

View file

@ -962,26 +962,21 @@ FROM (
/// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz.
/// <para>
/// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda
/// <c>{SqlData|PostgresData}/HostData/ObjectData</c>, bir tenant secilmisken
/// <c>{SqlData|PostgresData}/TenantData/ObjectData</c>. Boylece nesne, seed sirasinda
/// <c>host/{sql|postgres}/object</c>, bir tenant secilmisken
/// <c>tenants/{tenantId}/{sql|postgres}/object</c>. Boylece nesne, seed sirasinda
/// olusturuldugu veritabaninda yeniden olusturulur.
/// </para>
/// </summary>
private string ResolveSqlDataOutputPath()
{
var dataDirectoryName = SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql
? SeedPathResolver.PostgresDataFolder
: SeedPathResolver.SqlDataFolder;
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql);
var scopeDirectoryName = _currentTenant.Id == null
? SeedPathResolver.HostDataFolder
: SeedPathResolver.TenantDataFolder;
return SeedPathResolver.GetPath(
return SeedPathResolver.GetScopePath(
_configuration,
dataDirectoryName,
scopeDirectoryName,
SeedPathResolver.ObjectDataFolder);
_currentTenant.Id,
providerDirectoryName,
SeedPathResolver.ObjectFolder);
}
}

View file

@ -4,7 +4,7 @@ namespace Sozsoft.Platform.ListForms;
/// <summary>
/// Wizard seed dosyası formatı.
/// WizardAppService tarafından Seeds/WizardData/{wizardName}.json olarak kaydedilir.
/// WizardAppService tarafından Seeds/{host|tenants/{tenantId}}/wizard/{wizardName}.json olarak kaydedilir.
/// WizardDataSeeder (DbMigrator) bu dosyaları okuyarak veritabanını yeniden oluşturur.
/// </summary>
public class WizardSeedFileDto

View file

@ -535,7 +535,7 @@ public class ListFormWizardAppService(
/// <summary>
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
/// Dosya, seeder'lar ve File Manager ile ortak olan seed klasörünün WizardData dizinine yazılır.
/// Dosya, seeder'lar ve File Manager ile ortak olan seed klasörünün wizard dizinine yazılır.
/// Veritabanı silinip yeniden oluşturulduğunda WizardDataSeeder bu dosyaları okuyarak konfigürasyonu geri yükler.
/// </summary>
private async Task SaveWizardSeedFileAsync(ListFormWizardDto input, bool isDeletedField, bool isCreatedField, WizardInsertedRecordsDto inserted)
@ -758,12 +758,13 @@ public class ListFormWizardAppService(
}
/// <summary>
/// Wizard seed dosyalarinin yazilacagi WizardData dizinini dondurur.
/// Wizard seed dosyalarinin yazilacagi wizard dizinini dondurur; kapsam aktif tenant'i izler
/// (host baglaminda <c>host/wizard</c>, tenant baglaminda <c>tenants/{tenantId}/wizard</c>).
/// Yol cozumu <see cref="SeedPathResolver"/> uzerinden seeder'lar ve File Manager ile ortaktir.
/// </summary>
private string ResolveWizardSeedOutputPath()
{
return SeedPathResolver.GetPath(_configuration, "WizardData");
return SeedPathResolver.GetScopePath(_configuration, CurrentTenant.Id, SeedPathResolver.WizardFolder);
}
private async Task<HashSet<string>> GetTableColumnNamesAsync(string dataSourceCode, SelectCommandTypeEnum commandType, string selectCommand)

View file

@ -16,24 +16,18 @@ using static Sozsoft.Settings.SettingsConsts;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// Seeds/{SqlData|PostgresData}/{HostData|TenantData}/ExecuteData klasöründeki .sql dosyaları
/// Seeds/{host|tenants/{tenantId}}/{sql|postgres}/execute klasöründeki .sql dosyaları
/// iki aşamada işlenir:
/// 1) Migration/seed sırasında <see cref="SqlDataSeeder"/> tarafından, üst klasörle (HostData/TenantData)
/// aynı kapsamda çalıştırılır (nesne oluşturulur/güncellenir).
/// 1) Migration/seed sırasında <see cref="SqlDataSeeder"/> tarafından, kapsam klasörüyle
/// (host/tenants/{tenantId}) aynı bağlamda çalıştırılır (nesne oluşturulur/güncellenir).
/// 2) Tüm migration ve seed işlemleri bittikten sonra bu sınıf, dosya adından türetilen stored
/// procedure'ü ilgili bağlamda (host ya da tenant) varlığı doğrulanarak çalıştırır.
/// Dosya adı = procedure adı. İsteğe bağlı olarak "schema.Procedure.sql" formatı da desteklenir.
/// </summary>
public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITransientDependency
{
/// <summary>HostData/TenantData altındaki, migration sonrası da çalıştırılacak script klasörünün adı.</summary>
public const string DirectoryName = SeedPathResolver.ExecuteDataFolder;
/// <summary>Host kapsamındaki script klasörü.</summary>
public const string HostDataDirectoryName = SeedPathResolver.HostDataFolder;
/// <summary>Tenant kapsamındaki script klasörü (henüz kullanılmıyorsa sessizce atlanır).</summary>
public const string TenantDataDirectoryName = SeedPathResolver.TenantDataFolder;
/// <summary>{sql|postgres} altındaki, migration sonrası da çalıştırılacak script klasörünün adı.</summary>
public const string DirectoryName = SeedPathResolver.ExecuteFolder;
private static readonly Regex SafeIdentifierRegex =
new(@"^[A-Za-z_][A-Za-z0-9_]{0,126}$", RegexOptions.Compiled);
@ -57,8 +51,7 @@ public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITr
public async Task ExecuteAsync(Guid? tenantId = null)
{
var scopeDirectoryName = tenantId == null ? HostDataDirectoryName : TenantDataDirectoryName;
var directoryPath = GetScriptDirectoryPath(scopeDirectoryName);
var directoryPath = GetScriptDirectoryPath(tenantId);
if (!Directory.Exists(directoryPath))
{
@ -135,13 +128,12 @@ public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITr
}
}
private string GetScriptDirectoryPath(string scopeDirectoryName)
private string GetScriptDirectoryPath(Guid? tenantId)
{
var dataDirectoryName = DefaultDatabaseProvider == DatabaseProvider.PostgreSql
? SeedPathResolver.PostgresDataFolder
: SeedPathResolver.SqlDataFolder;
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
DefaultDatabaseProvider == DatabaseProvider.PostgreSql);
return SeedPathResolver.GetPath(_configuration, dataDirectoryName, scopeDirectoryName, DirectoryName);
return SeedPathResolver.GetScopePath(_configuration, tenantId, providerDirectoryName, DirectoryName);
}
/// <summary>

View file

@ -17,21 +17,23 @@ namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// SqlTableDesigner ve SqlQueryManager üzerinden deploy edilen nesneleri
/// <c>{SqlData|PostgresData}</c> klasöründeki .sql dosyalarından okuyarak veritabanına uygular.
/// <c>{host|tenants/{tenantId}}/{sql|postgres}</c> klasöründeki .sql dosyalarından okuyarak
/// veritabanına uygular.
/// Her dosya, tek bir nesneye (ve ilgili ALTER/INDEX script'lerine) ait varlık kontrolü içeren
/// batch'lerden oluşmalıdır. Veritabanı silinip yeniden oluşturulduğunda tüm scriptler yeniden çalışır.
/// <para>
/// Klasör düzeni — her .sql dosyası amacını belirten bir klasörün altındadır, kökte dosya durmaz:
/// <code>
/// {SqlData|PostgresData}/
/// HostData/ → host (varsayılan) veritabanı kapsamı
/// ObjectData/ → nesne oluşturulur/güncellenir. SqlTableDesigner ve
/// host/ → host (varsayılan) veritabanı kapsamı
/// {sql|postgres}/
/// object/ → nesne oluşturulur/güncellenir. SqlTableDesigner ve
/// SqlQueryManager, host bağlamındayken buraya yazar.
/// ExecuteData/ → nesne oluşturulur, ayrıca tüm migration'lar bittikten sonra
/// execute/ → nesne oluşturulur, ayrıca tüm migration'lar bittikten sonra
/// <see cref="AfterAllMigrationsSqlExecutor"/> tarafından çalıştırılır.
/// TenantData/ → tenant veritabanı kapsamı (bir tenant seçiliyken üretilenler)
/// ObjectData/
/// ExecuteData/
/// tenants/{tenantId}/ → ilgili tenant veritabanı kapsamı
/// {sql|postgres}/
/// object/
/// execute/
/// </code>
/// Geriye dönük uyumluluk: klasör köklerinde kalmış eski .sql dosyaları da işlenir, ancak
/// uyarı ile loglanır — taşınmaları beklenir.
@ -40,7 +42,7 @@ namespace Sozsoft.Platform.Data.Seeds;
public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
{
/// <summary>Yalnızca oluşturulan/güncellenen nesne scriptlerinin klasörü.</summary>
public const string ObjectDataDirectoryName = SeedPathResolver.ObjectDataFolder;
public const string ObjectDirectoryName = SeedPathResolver.ObjectFolder;
private readonly IDbContextProvider<PlatformDbContext> _dbContextProvider;
private readonly IConfiguration _configuration;
@ -58,16 +60,15 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
public async Task SeedAsync(DataSeedContext context)
{
var dataDirectoryName = GetDataDirectoryName();
var sqlDataPath = SeedPathResolver.GetPath(_configuration, dataDirectoryName);
var dataDirectoryName = $"{SeedPathResolver.GetScopeFolderName(context.TenantId)}/{GetProviderDirectoryName()}";
var sqlDataPath = SeedPathResolver.GetScopePath(_configuration, context.TenantId, GetProviderDirectoryName());
if (!Directory.Exists(sqlDataPath))
{
_logger.LogInformation("Seeds/{DirectoryName} directory not found, skipping SqlDataSeeder.", dataDirectoryName);
return;
}
var isHostSeed = context.TenantId == null;
var sqlFiles = GetSqlFiles(sqlDataPath, isHostSeed);
var sqlFiles = GetSqlFiles(sqlDataPath);
if (sqlFiles.Length == 0)
{
@ -79,7 +80,7 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
"SqlDataSeeder started for provider '{Provider}' from Seeds/{DirectoryName}. Seed target: {SeedTarget}. {Count} file(s) to be processed.",
DefaultDatabaseProvider,
dataDirectoryName,
isHostSeed ? "Host" : "Tenant",
context.TenantId == null ? "Host" : $"Tenant {context.TenantId}",
sqlFiles.Length);
var dbContext = await _dbContextProvider.GetDbContextAsync();
@ -129,35 +130,24 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
_logger.LogInformation("SqlDataSeeder completed. {Count} file(s) processed.", sqlFiles.Length);
}
private static string GetDataDirectoryName()
private static string GetProviderDirectoryName()
{
return DefaultDatabaseProvider == DatabaseProvider.PostgreSql
? SeedPathResolver.PostgresDataFolder
: SeedPathResolver.SqlDataFolder;
return SeedPathResolver.GetProviderFolderName(DefaultDatabaseProvider == DatabaseProvider.PostgreSql);
}
private string[] GetSqlFiles(string dataDirectoryPath, bool isHostSeed)
private string[] GetSqlFiles(string providerDirectoryPath)
{
var sqlFiles = new List<string>();
// Klasor kokunde dosya beklenmez; eskiden kalanlar uyari ile islenir.
sqlFiles.AddRange(GetLegacyRootSqlFiles(dataDirectoryPath));
sqlFiles.AddRange(GetLegacyRootSqlFiles(providerDirectoryPath));
var scopeDirectoryName = isHostSeed
? AfterAllMigrationsSqlExecutor.HostDataDirectoryName
: AfterAllMigrationsSqlExecutor.TenantDataDirectoryName;
sqlFiles.AddRange(GetOrderedSqlFiles(Path.Combine(providerDirectoryPath, ObjectDirectoryName)));
var scopePath = Path.Combine(dataDirectoryPath, scopeDirectoryName);
if (Directory.Exists(scopePath))
{
sqlFiles.AddRange(GetOrderedSqlFiles(Path.Combine(scopePath, ObjectDataDirectoryName)));
sqlFiles.AddRange(GetLegacyRootSqlFiles(scopePath));
// {HostData|TenantData}/ExecuteData: nesneler burada da oluşturulur, ayrıca tüm
// migration bittikten sonra AfterAllMigrationsSqlExecutor dosya adı üzerinden çalıştırır.
sqlFiles.AddRange(GetOrderedSqlFiles(
Path.Combine(scopePath, AfterAllMigrationsSqlExecutor.DirectoryName)));
}
// {sql|postgres}/execute: nesneler burada da oluşturulur, ayrıca tüm migration
// bittikten sonra AfterAllMigrationsSqlExecutor dosya adı üzerinden çalıştırır.
sqlFiles.AddRange(GetOrderedSqlFiles(
Path.Combine(providerDirectoryPath, AfterAllMigrationsSqlExecutor.DirectoryName)));
return sqlFiles.ToArray();
}
@ -179,10 +169,10 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
if (files.Length > 0)
{
_logger.LogWarning(
"{Count} .sql file(s) found directly under '{Directory}'. Move them into the '{ObjectData}' folder; root-level files are deprecated.",
"{Count} .sql file(s) found directly under '{Directory}'. Move them into the '{ObjectFolder}' folder; root-level files are deprecated.",
files.Length,
directoryPath,
ObjectDataDirectoryName);
ObjectDirectoryName);
}
return files;

View file

@ -22,7 +22,8 @@ using static Sozsoft.Platform.PlatformConsts;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// Wizard ile oluşturulan konfigürasyonları Seeds/WizardData/*.json dosyalarından okuyarak veritabanına aktarır.
/// Wizard ile oluşturulan konfigürasyonları Seeds/{host|tenants/{tenantId}}/wizard/*.json
/// dosyalarından okuyarak veritabanına aktarır (kapsam, seed edilen veritabanını izler).
/// Wizard çalıştıktan sonra oluşturulan JSON dosyaları projeye commit edildikten sonra,
/// veritabanı silinip yeniden oluşturulduğunda bu seeder tüm wizard konfigürasyonlarını geri yükler.
/// </summary>
@ -71,17 +72,18 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
public async Task SeedAsync(DataSeedContext context)
{
var wizardDataPath = SeedPathResolver.GetPath(_configuration, "WizardData");
var scopeFolderName = SeedPathResolver.GetScopeFolderName(context.TenantId);
var wizardDataPath = SeedPathResolver.GetScopePath(_configuration, context.TenantId, SeedPathResolver.WizardFolder);
if (!Directory.Exists(wizardDataPath))
{
_logger.LogInformation("WizardData directory not found, skipping.");
_logger.LogInformation("Seeds/{Scope}/wizard directory not found, skipping.", scopeFolderName);
return;
}
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
if (jsonFiles.Length == 0)
{
_logger.LogInformation("No JSON files found in WizardData directory, skipping.");
_logger.LogInformation("No JSON files found in Seeds/{Scope}/wizard directory, skipping.", scopeFolderName);
return;
}

View file

@ -110,15 +110,16 @@ public class PlatformDbMigrationService : ITransientDependency
}
/// <summary>
/// Tüm host ve tenant migration/seed adımları bittikten sonra, {HostData|TenantData}/ExecuteData
/// klasöründeki script'lerin dosya adından türeyen procedure'lerini ilgili bağlamda çalıştırır.
/// TenantData klasörü yoksa tenant adımı sessizce atlanır.
/// Tüm host ve tenant migration/seed adımları bittikten sonra,
/// {host|tenants/{tenantId}}/{sql|postgres}/execute klasöründeki script'lerin dosya adından
/// türeyen procedure'lerini ilgili bağlamda çalıştırır. İlgili tenant klasörü yoksa
/// o tenant adımı sessizce atlanır.
/// </summary>
private async Task ExecuteAfterAllMigrationsSqlAsync(List<Tenant> tenants)
{
if (!_afterAllMigrationsSqlExecutors.Any())
{
Logger.LogInformation("No SQL executor registered, skipping ExecuteData scripts.");
Logger.LogInformation("No SQL executor registered, skipping execute scripts.");
return;
}

View file

@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Configuration;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
/// Runtime'da duzenlenebilen seed dosyalarinin (SqlData / PostgresData / WizardData) klasorunu cozer.
/// Runtime'da duzenlenebilen seed dosyalarinin klasorunu cozer.
/// <para>
/// Kod ile birlikte versiyonlanan sabit seed'ler (ornegin TenantDataSeeder'in okudugu
/// TenantData.json) buraya dahil degildir; onlar kendi projelerinin ciktisinda kalir.
@ -17,6 +19,25 @@ namespace Sozsoft.Platform.Data.Seeds;
/// uzerinden de duzenlenebildigi icin yapilan degisiklik dogrudan host diskinde kalici olur.
/// </para>
/// <para>
/// Klasor duzeni CDN ile ayni kapsam mantigini izler — once kapsam, sonra icerik turu:
/// <code>
/// seeds/
/// host/ → host (varsayilan) veritabani kapsami
/// crud/
/// custom/
/// wizard/
/// sql/ → SQL Server saglayicisi
/// object/ → nesne olusturulur/guncellenir
/// execute/ → nesne olusturulur, ayrica tum migration'lar bitince calistirilir
/// postgres/ → PostgreSQL saglayicisi
/// object/
/// execute/
/// tenants/
/// {tenantId}/ → her tenant kendi klasorunde
/// crud/ custom/ wizard/ sql/{object,execute}/ postgres/{object,execute}/
/// </code>
/// </para>
/// <para>
/// Cozum sirasi:
/// 1) <c>App:SeedsPath</c> ayari (konteynerde mount noktasi),
/// 2) calisma dizininden yukari dogru <c>configs/seeds</c> aramasi (local gelistirme),
@ -27,26 +48,32 @@ public static class SeedPathResolver
{
public const string ConfigurationKey = "App:SeedsPath";
/// <summary>Host veritabani kapsamindaki seed'lerin kok klasoru.</summary>
public const string HostFolder = "host";
/// <summary>Tenant kapsamindaki seed'lerin kok klasoru; altinda tenant Id'si ile klasorler bulunur.</summary>
public const string TenantsFolder = "tenants";
/// <summary>SQL Server saglayicisinin script klasoru.</summary>
public const string SqlDataFolder = "SqlData";
public const string SqlFolder = "sql";
/// <summary>PostgreSQL saglayicisinin script klasoru.</summary>
public const string PostgresDataFolder = "PostgresData";
/// <summary>Host veritabani kapsamindaki scriptler.</summary>
public const string HostDataFolder = "HostData";
/// <summary>Tenant veritabani kapsamindaki scriptler.</summary>
public const string TenantDataFolder = "TenantData";
public const string PostgresFolder = "postgres";
/// <summary>Yalnizca olusturulan/guncellenen nesne scriptleri.</summary>
public const string ObjectDataFolder = "ObjectData";
public const string ObjectFolder = "object";
/// <summary>Olusturulan, ayrica tum migration'lar bittikten sonra calistirilan scriptler.</summary>
public const string ExecuteDataFolder = "ExecuteData";
public const string ExecuteFolder = "execute";
/// <summary>Wizard'in urettigi konfigurasyon dosyalari.</summary>
public const string WizardDataFolder = "WizardData";
public const string WizardFolder = "wizard";
/// <summary>Crud endpoint konfigurasyon dosyalari.</summary>
public const string CrudFolder = "crud";
/// <summary>Custom endpoint konfigurasyon dosyalari.</summary>
public const string CustomFolder = "custom";
private const string RepositorySeedsFolder = "seeds";
private const string RepositoryConfigsFolder = "configs";
@ -83,6 +110,38 @@ public static class SeedPathResolver
return path;
}
/// <summary>
/// Kapsam klasorunun (host ya da ilgili tenant) altindaki yolu dondurur.
/// <paramref name="tenantId"/> null ise <c>host/</c>, aksi halde <c>tenants/{tenantId}/</c> kullanilir.
/// </summary>
public static string GetScopePath(IConfiguration? configuration, Guid? tenantId, params string[] subPaths)
{
return GetPath(configuration, BuildScopeSubPaths(tenantId, subPaths));
}
/// <summary>Kapsam klasorunun seed koküne gore goreli adi (<c>host</c> ya da <c>tenants/{tenantId}</c>).</summary>
public static string GetScopeFolderName(Guid? tenantId)
{
return tenantId == null
? HostFolder
: $"{TenantsFolder}/{tenantId.Value}";
}
/// <summary>Aktif veritabani saglayicisina karsilik gelen script klasorunun adi.</summary>
public static string GetProviderFolderName(bool isPostgreSql)
{
return isPostgreSql ? PostgresFolder : SqlFolder;
}
private static string[] BuildScopeSubPaths(Guid? tenantId, string[] subPaths)
{
var scopeSegments = tenantId == null
? new[] { HostFolder }
: new[] { TenantsFolder, tenantId.Value.ToString() };
return scopeSegments.Concat(subPaths ?? Array.Empty<string>()).ToArray();
}
/// <summary>Calisma dizininden yukari dogru cikarak depodaki configs/seeds klasorunu arar.</summary>
private static string? FindRepositorySeedsPath()
{

View file

View file

View file

View file

View file

View file