seeds klasör yapısı değişikliği
This commit is contained in:
parent
9a2d88b93b
commit
f5ba73f875
31 changed files with 165 additions and 103 deletions
34
README.md
34
README.md
|
|
@ -178,8 +178,10 @@ sozsoft-platform/
|
||||||
├── configs/
|
├── configs/
|
||||||
│ ├── docker/ # Yerel geliştirme için veri katmanı compose dosyaları ve scriptler
|
│ ├── docker/ # Yerel geliştirme için veri katmanı compose dosyaları ve scriptler
|
||||||
│ ├── deployment/ # Dev/Prod compose dosyaları, nginx/redis/turn konfigürasyonu, deploy scriptleri
|
│ ├── deployment/ # Dev/Prod compose dosyaları, nginx/redis/turn konfigürasyonu, deploy scriptleri
|
||||||
│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler: SqlData, PostgresData, WizardData
|
│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler; CDN ile aynı kapsam düzeni
|
||||||
│ │ # {SqlData|PostgresData}/{HostData|TenantData}/{ObjectData|ExecuteData}
|
│ │ # host/{crud,custom,wizard,sql,postgres}
|
||||||
|
│ │ # tenants/{tenantId}/{crud,custom,wizard,sql,postgres}
|
||||||
|
│ │ # {sql|postgres}/{object,execute}
|
||||||
│ └── ai/ # AI entegrasyonu (n8n akış tanımları)
|
│ └── ai/ # AI entegrasyonu (n8n akış tanımları)
|
||||||
├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları), list.instructions.md
|
├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları), list.instructions.md
|
||||||
├── CLAUDE.md # Claude Code için operasyon kuralları
|
├── 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/`,
|
(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`).
|
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ı
|
`configs/seeds` altında en üst seviye kapsamdır (CDN klasörleriyle aynı mantık): host
|
||||||
kapsamına (`HostData` / `TenantData`), sonra amacına göre bir klasörde durur; klasör
|
veritabanı için `host/`, her tenant için `tenants/{tenantId}/`. Kapsamın altında içerik
|
||||||
köklerinde dosya bulunmaz:
|
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 |
|
| 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`). |
|
| `{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}/`). |
|
||||||
| `{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}/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,
|
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.
|
fakat `SqlDataSeeder` bunları uyarı ile loglar — ilgili klasöre taşınmaları beklenir.
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ public interface ISqlObjectManagerAppService : IApplicationService
|
||||||
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the T-SQL script to {SeedsRoot}/{SqlData|PostgresData}/{HostData|TenantData}/ObjectData/{fileName}.sql
|
/// 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 HostData.
|
/// (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.
|
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// dropped from the UI.
|
||||||
/// Non-existing files are ignored.
|
/// Non-existing files are ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ public class SqlQueryExecutionResultDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public class SaveTableScriptDto
|
public class SaveTableScriptDto
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +37,8 @@ public class SaveTableScriptDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public class DeleteSqlDataFilesDto
|
public class DeleteSqlDataFilesDto
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -962,26 +962,21 @@ FROM (
|
||||||
/// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz.
|
/// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda
|
/// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda
|
||||||
/// <c>{SqlData|PostgresData}/HostData/ObjectData</c>, bir tenant secilmisken
|
/// <c>host/{sql|postgres}/object</c>, bir tenant secilmisken
|
||||||
/// <c>{SqlData|PostgresData}/TenantData/ObjectData</c>. Boylece nesne, seed sirasinda
|
/// <c>tenants/{tenantId}/{sql|postgres}/object</c>. Boylece nesne, seed sirasinda
|
||||||
/// olusturuldugu veritabaninda yeniden olusturulur.
|
/// olusturuldugu veritabaninda yeniden olusturulur.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ResolveSqlDataOutputPath()
|
private string ResolveSqlDataOutputPath()
|
||||||
{
|
{
|
||||||
var dataDirectoryName = SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql
|
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
|
||||||
? SeedPathResolver.PostgresDataFolder
|
SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql);
|
||||||
: SeedPathResolver.SqlDataFolder;
|
|
||||||
|
|
||||||
var scopeDirectoryName = _currentTenant.Id == null
|
return SeedPathResolver.GetScopePath(
|
||||||
? SeedPathResolver.HostDataFolder
|
|
||||||
: SeedPathResolver.TenantDataFolder;
|
|
||||||
|
|
||||||
return SeedPathResolver.GetPath(
|
|
||||||
_configuration,
|
_configuration,
|
||||||
dataDirectoryName,
|
_currentTenant.Id,
|
||||||
scopeDirectoryName,
|
providerDirectoryName,
|
||||||
SeedPathResolver.ObjectDataFolder);
|
SeedPathResolver.ObjectFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wizard seed dosyası formatı.
|
/// 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.
|
/// WizardDataSeeder (DbMigrator) bu dosyaları okuyarak veritabanını yeniden oluşturur.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WizardSeedFileDto
|
public class WizardSeedFileDto
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,7 @@ public class ListFormWizardAppService(
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
|
/// 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.
|
/// Veritabanı silinip yeniden oluşturulduğunda WizardDataSeeder bu dosyaları okuyarak konfigürasyonu geri yükler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task SaveWizardSeedFileAsync(ListFormWizardDto input, bool isDeletedField, bool isCreatedField, WizardInsertedRecordsDto inserted)
|
private async Task SaveWizardSeedFileAsync(ListFormWizardDto input, bool isDeletedField, bool isCreatedField, WizardInsertedRecordsDto inserted)
|
||||||
|
|
@ -758,12 +758,13 @@ public class ListFormWizardAppService(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// Yol cozumu <see cref="SeedPathResolver"/> uzerinden seeder'lar ve File Manager ile ortaktir.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string ResolveWizardSeedOutputPath()
|
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)
|
private async Task<HashSet<string>> GetTableColumnNamesAsync(string dataSourceCode, SelectCommandTypeEnum commandType, string selectCommand)
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,18 @@ using static Sozsoft.Settings.SettingsConsts;
|
||||||
namespace Sozsoft.Platform.Data.Seeds;
|
namespace Sozsoft.Platform.Data.Seeds;
|
||||||
|
|
||||||
/// <summary>
|
/// <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:
|
/// iki aşamada işlenir:
|
||||||
/// 1) Migration/seed sırasında <see cref="SqlDataSeeder"/> tarafından, üst klasörle (HostData/TenantData)
|
/// 1) Migration/seed sırasında <see cref="SqlDataSeeder"/> tarafından, kapsam klasörüyle
|
||||||
/// aynı kapsamda çalıştırılır (nesne oluşturulur/güncellenir).
|
/// (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
|
/// 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.
|
/// 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.
|
/// Dosya adı = procedure adı. İsteğe bağlı olarak "schema.Procedure.sql" formatı da desteklenir.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITransientDependency
|
public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITransientDependency
|
||||||
{
|
{
|
||||||
/// <summary>HostData/TenantData altındaki, migration sonrası da çalıştırılacak script klasörünün adı.</summary>
|
/// <summary>{sql|postgres} altındaki, migration sonrası da çalıştırılacak script klasörünün adı.</summary>
|
||||||
public const string DirectoryName = SeedPathResolver.ExecuteDataFolder;
|
public const string DirectoryName = SeedPathResolver.ExecuteFolder;
|
||||||
|
|
||||||
/// <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;
|
|
||||||
|
|
||||||
private static readonly Regex SafeIdentifierRegex =
|
private static readonly Regex SafeIdentifierRegex =
|
||||||
new(@"^[A-Za-z_][A-Za-z0-9_]{0,126}$", RegexOptions.Compiled);
|
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)
|
public async Task ExecuteAsync(Guid? tenantId = null)
|
||||||
{
|
{
|
||||||
var scopeDirectoryName = tenantId == null ? HostDataDirectoryName : TenantDataDirectoryName;
|
var directoryPath = GetScriptDirectoryPath(tenantId);
|
||||||
var directoryPath = GetScriptDirectoryPath(scopeDirectoryName);
|
|
||||||
|
|
||||||
if (!Directory.Exists(directoryPath))
|
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
|
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
|
||||||
? SeedPathResolver.PostgresDataFolder
|
DefaultDatabaseProvider == DatabaseProvider.PostgreSql);
|
||||||
: SeedPathResolver.SqlDataFolder;
|
|
||||||
|
|
||||||
return SeedPathResolver.GetPath(_configuration, dataDirectoryName, scopeDirectoryName, DirectoryName);
|
return SeedPathResolver.GetScopePath(_configuration, tenantId, providerDirectoryName, DirectoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -17,21 +17,23 @@ namespace Sozsoft.Platform.Data.Seeds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SqlTableDesigner ve SqlQueryManager üzerinden deploy edilen nesneleri
|
/// 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
|
/// 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.
|
/// batch'lerden oluşmalıdır. Veritabanı silinip yeniden oluşturulduğunda tüm scriptler yeniden çalışır.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Klasör düzeni — her .sql dosyası amacını belirten bir klasörün altındadır, kökte dosya durmaz:
|
/// Klasör düzeni — her .sql dosyası amacını belirten bir klasörün altındadır, kökte dosya durmaz:
|
||||||
/// <code>
|
/// <code>
|
||||||
/// {SqlData|PostgresData}/
|
/// host/ → host (varsayılan) veritabanı kapsamı
|
||||||
/// HostData/ → host (varsayılan) veritabanı kapsamı
|
/// {sql|postgres}/
|
||||||
/// ObjectData/ → nesne oluşturulur/güncellenir. SqlTableDesigner ve
|
/// object/ → nesne oluşturulur/güncellenir. SqlTableDesigner ve
|
||||||
/// SqlQueryManager, host bağlamındayken buraya yazar.
|
/// 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.
|
/// <see cref="AfterAllMigrationsSqlExecutor"/> tarafından çalıştırılır.
|
||||||
/// TenantData/ → tenant veritabanı kapsamı (bir tenant seçiliyken üretilenler)
|
/// tenants/{tenantId}/ → ilgili tenant veritabanı kapsamı
|
||||||
/// ObjectData/
|
/// {sql|postgres}/
|
||||||
/// ExecuteData/
|
/// object/
|
||||||
|
/// execute/
|
||||||
/// </code>
|
/// </code>
|
||||||
/// Geriye dönük uyumluluk: klasör köklerinde kalmış eski .sql dosyaları da işlenir, ancak
|
/// Geriye dönük uyumluluk: klasör köklerinde kalmış eski .sql dosyaları da işlenir, ancak
|
||||||
/// uyarı ile loglanır — taşınmaları beklenir.
|
/// uyarı ile loglanır — taşınmaları beklenir.
|
||||||
|
|
@ -40,7 +42,7 @@ namespace Sozsoft.Platform.Data.Seeds;
|
||||||
public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
|
public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
{
|
{
|
||||||
/// <summary>Yalnızca oluşturulan/güncellenen nesne scriptlerinin klasörü.</summary>
|
/// <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 IDbContextProvider<PlatformDbContext> _dbContextProvider;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
|
@ -58,16 +60,15 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
|
|
||||||
public async Task SeedAsync(DataSeedContext context)
|
public async Task SeedAsync(DataSeedContext context)
|
||||||
{
|
{
|
||||||
var dataDirectoryName = GetDataDirectoryName();
|
var dataDirectoryName = $"{SeedPathResolver.GetScopeFolderName(context.TenantId)}/{GetProviderDirectoryName()}";
|
||||||
var sqlDataPath = SeedPathResolver.GetPath(_configuration, dataDirectoryName);
|
var sqlDataPath = SeedPathResolver.GetScopePath(_configuration, context.TenantId, GetProviderDirectoryName());
|
||||||
if (!Directory.Exists(sqlDataPath))
|
if (!Directory.Exists(sqlDataPath))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Seeds/{DirectoryName} directory not found, skipping SqlDataSeeder.", dataDirectoryName);
|
_logger.LogInformation("Seeds/{DirectoryName} directory not found, skipping SqlDataSeeder.", dataDirectoryName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isHostSeed = context.TenantId == null;
|
var sqlFiles = GetSqlFiles(sqlDataPath);
|
||||||
var sqlFiles = GetSqlFiles(sqlDataPath, isHostSeed);
|
|
||||||
|
|
||||||
if (sqlFiles.Length == 0)
|
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.",
|
"SqlDataSeeder started for provider '{Provider}' from Seeds/{DirectoryName}. Seed target: {SeedTarget}. {Count} file(s) to be processed.",
|
||||||
DefaultDatabaseProvider,
|
DefaultDatabaseProvider,
|
||||||
dataDirectoryName,
|
dataDirectoryName,
|
||||||
isHostSeed ? "Host" : "Tenant",
|
context.TenantId == null ? "Host" : $"Tenant {context.TenantId}",
|
||||||
sqlFiles.Length);
|
sqlFiles.Length);
|
||||||
|
|
||||||
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
var dbContext = await _dbContextProvider.GetDbContextAsync();
|
||||||
|
|
@ -129,35 +130,24 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
_logger.LogInformation("SqlDataSeeder completed. {Count} file(s) processed.", sqlFiles.Length);
|
_logger.LogInformation("SqlDataSeeder completed. {Count} file(s) processed.", sqlFiles.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDataDirectoryName()
|
private static string GetProviderDirectoryName()
|
||||||
{
|
{
|
||||||
return DefaultDatabaseProvider == DatabaseProvider.PostgreSql
|
return SeedPathResolver.GetProviderFolderName(DefaultDatabaseProvider == DatabaseProvider.PostgreSql);
|
||||||
? SeedPathResolver.PostgresDataFolder
|
|
||||||
: SeedPathResolver.SqlDataFolder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string[] GetSqlFiles(string dataDirectoryPath, bool isHostSeed)
|
private string[] GetSqlFiles(string providerDirectoryPath)
|
||||||
{
|
{
|
||||||
var sqlFiles = new List<string>();
|
var sqlFiles = new List<string>();
|
||||||
|
|
||||||
// Klasor kokunde dosya beklenmez; eskiden kalanlar uyari ile islenir.
|
// Klasor kokunde dosya beklenmez; eskiden kalanlar uyari ile islenir.
|
||||||
sqlFiles.AddRange(GetLegacyRootSqlFiles(dataDirectoryPath));
|
sqlFiles.AddRange(GetLegacyRootSqlFiles(providerDirectoryPath));
|
||||||
|
|
||||||
var scopeDirectoryName = isHostSeed
|
sqlFiles.AddRange(GetOrderedSqlFiles(Path.Combine(providerDirectoryPath, ObjectDirectoryName)));
|
||||||
? AfterAllMigrationsSqlExecutor.HostDataDirectoryName
|
|
||||||
: AfterAllMigrationsSqlExecutor.TenantDataDirectoryName;
|
|
||||||
|
|
||||||
var scopePath = Path.Combine(dataDirectoryPath, scopeDirectoryName);
|
// {sql|postgres}/execute: nesneler burada da oluşturulur, ayrıca tüm migration
|
||||||
if (Directory.Exists(scopePath))
|
// bittikten sonra AfterAllMigrationsSqlExecutor dosya adı üzerinden çalıştırır.
|
||||||
{
|
|
||||||
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(
|
sqlFiles.AddRange(GetOrderedSqlFiles(
|
||||||
Path.Combine(scopePath, AfterAllMigrationsSqlExecutor.DirectoryName)));
|
Path.Combine(providerDirectoryPath, AfterAllMigrationsSqlExecutor.DirectoryName)));
|
||||||
}
|
|
||||||
|
|
||||||
return sqlFiles.ToArray();
|
return sqlFiles.ToArray();
|
||||||
}
|
}
|
||||||
|
|
@ -179,10 +169,10 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
if (files.Length > 0)
|
if (files.Length > 0)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_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,
|
files.Length,
|
||||||
directoryPath,
|
directoryPath,
|
||||||
ObjectDataDirectoryName);
|
ObjectDirectoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ using static Sozsoft.Platform.PlatformConsts;
|
||||||
namespace Sozsoft.Platform.Data.Seeds;
|
namespace Sozsoft.Platform.Data.Seeds;
|
||||||
|
|
||||||
/// <summary>
|
/// <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,
|
/// 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.
|
/// veritabanı silinip yeniden oluşturulduğunda bu seeder tüm wizard konfigürasyonlarını geri yükler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -71,17 +72,18 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
|
|
||||||
public async Task SeedAsync(DataSeedContext context)
|
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))
|
if (!Directory.Exists(wizardDataPath))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("WizardData directory not found, skipping.");
|
_logger.LogInformation("Seeds/{Scope}/wizard directory not found, skipping.", scopeFolderName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
||||||
if (jsonFiles.Length == 0)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,16 @@ public class PlatformDbMigrationService : ITransientDependency
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tüm host ve tenant migration/seed adımları bittikten sonra, {HostData|TenantData}/ExecuteData
|
/// Tüm host ve tenant migration/seed adımları bittikten sonra,
|
||||||
/// klasöründeki script'lerin dosya adından türeyen procedure'lerini ilgili bağlamda çalıştırır.
|
/// {host|tenants/{tenantId}}/{sql|postgres}/execute klasöründeki script'lerin dosya adından
|
||||||
/// TenantData klasörü yoksa tenant adımı sessizce atlanır.
|
/// türeyen procedure'lerini ilgili bağlamda çalıştırır. İlgili tenant klasörü yoksa
|
||||||
|
/// o tenant adımı sessizce atlanır.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task ExecuteAfterAllMigrationsSqlAsync(List<Tenant> tenants)
|
private async Task ExecuteAfterAllMigrationsSqlAsync(List<Tenant> tenants)
|
||||||
{
|
{
|
||||||
if (!_afterAllMigrationsSqlExecutors.Any())
|
if (!_afterAllMigrationsSqlExecutors.Any())
|
||||||
{
|
{
|
||||||
Logger.LogInformation("No SQL executor registered, skipping ExecuteData scripts.");
|
Logger.LogInformation("No SQL executor registered, skipping execute scripts.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Data.Seeds;
|
namespace Sozsoft.Platform.Data.Seeds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runtime'da duzenlenebilen seed dosyalarinin (SqlData / PostgresData / WizardData) klasorunu cozer.
|
/// Runtime'da duzenlenebilen seed dosyalarinin klasorunu cozer.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Kod ile birlikte versiyonlanan sabit seed'ler (ornegin TenantDataSeeder'in okudugu
|
/// Kod ile birlikte versiyonlanan sabit seed'ler (ornegin TenantDataSeeder'in okudugu
|
||||||
/// TenantData.json) buraya dahil degildir; onlar kendi projelerinin ciktisinda kalir.
|
/// 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.
|
/// uzerinden de duzenlenebildigi icin yapilan degisiklik dogrudan host diskinde kalici olur.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <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:
|
/// Cozum sirasi:
|
||||||
/// 1) <c>App:SeedsPath</c> ayari (konteynerde mount noktasi),
|
/// 1) <c>App:SeedsPath</c> ayari (konteynerde mount noktasi),
|
||||||
/// 2) calisma dizininden yukari dogru <c>configs/seeds</c> aramasi (local gelistirme),
|
/// 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";
|
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>
|
/// <summary>SQL Server saglayicisinin script klasoru.</summary>
|
||||||
public const string SqlDataFolder = "SqlData";
|
public const string SqlFolder = "sql";
|
||||||
|
|
||||||
/// <summary>PostgreSQL saglayicisinin script klasoru.</summary>
|
/// <summary>PostgreSQL saglayicisinin script klasoru.</summary>
|
||||||
public const string PostgresDataFolder = "PostgresData";
|
public const string PostgresFolder = "postgres";
|
||||||
|
|
||||||
/// <summary>Host veritabani kapsamindaki scriptler.</summary>
|
|
||||||
public const string HostDataFolder = "HostData";
|
|
||||||
|
|
||||||
/// <summary>Tenant veritabani kapsamindaki scriptler.</summary>
|
|
||||||
public const string TenantDataFolder = "TenantData";
|
|
||||||
|
|
||||||
/// <summary>Yalnizca olusturulan/guncellenen nesne scriptleri.</summary>
|
/// <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>
|
/// <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>
|
/// <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 RepositorySeedsFolder = "seeds";
|
||||||
private const string RepositoryConfigsFolder = "configs";
|
private const string RepositoryConfigsFolder = "configs";
|
||||||
|
|
@ -83,6 +110,38 @@ public static class SeedPathResolver
|
||||||
return path;
|
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>
|
/// <summary>Calisma dizininden yukari dogru cikarak depodaki configs/seeds klasorunu arar.</summary>
|
||||||
private static string? FindRepositorySeedsPath()
|
private static string? FindRepositorySeedsPath()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
0
configs/seeds/host/crud/.gitkeep
Normal file
0
configs/seeds/host/crud/.gitkeep
Normal file
0
configs/seeds/host/custom/.gitkeep
Normal file
0
configs/seeds/host/custom/.gitkeep
Normal file
0
configs/seeds/host/postgres/execute/.gitkeep
Normal file
0
configs/seeds/host/postgres/execute/.gitkeep
Normal file
0
configs/seeds/host/postgres/object/.gitkeep
Normal file
0
configs/seeds/host/postgres/object/.gitkeep
Normal file
0
configs/seeds/host/sql/execute/.gitkeep
Normal file
0
configs/seeds/host/sql/execute/.gitkeep
Normal file
0
configs/seeds/host/sql/object/.gitkeep
Normal file
0
configs/seeds/host/sql/object/.gitkeep
Normal file
0
configs/seeds/host/wizard/.gitkeep
Normal file
0
configs/seeds/host/wizard/.gitkeep
Normal file
0
configs/seeds/tenants/.gitkeep
Normal file
0
configs/seeds/tenants/.gitkeep
Normal file
Loading…
Reference in a new issue