diff --git a/src/Business/Grand.Business.Common/Services/Pdf/WkPdfService.cs b/src/Business/Grand.Business.Common/Services/Pdf/WkPdfService.cs index 8759f1c06..c581d24b6 100644 --- a/src/Business/Grand.Business.Common/Services/Pdf/WkPdfService.cs +++ b/src/Business/Grand.Business.Common/Services/Pdf/WkPdfService.cs @@ -26,16 +26,16 @@ public class WkPdfService : IPdfService private readonly IViewRenderService _viewRenderService; private readonly ILanguageService _languageService; private readonly IRepository _downloadRepository; - private readonly IDatabaseContext _dbContext; + private readonly IStoreFilesContext _storeFilesContext; public WkPdfService(IGeneratePdf generatePdf, IViewRenderService viewRenderService, IRepository downloadRepository, - ILanguageService languageService, IDatabaseContext dbContext) + ILanguageService languageService, IStoreFilesContext storeFilesContext) { _generatePdf = generatePdf; _viewRenderService = viewRenderService; _languageService = languageService; _downloadRepository = downloadRepository; - _dbContext = dbContext; + _storeFilesContext = storeFilesContext; } public async Task PrintOrdersToPdf(Stream stream, IList orders, string languageId = "", string vendorId = "") @@ -122,7 +122,7 @@ public async Task SaveOrderToBinary(Order order, string languageId, stri ContentType = "application/pdf", }; - download.DownloadObjectId = await _dbContext.GridFSBucketUploadFromBytesAsync(download.Filename, ms.ToArray()); + download.DownloadObjectId = await _storeFilesContext.BucketUploadFromBytesAsync(download.Filename, ms.ToArray()); await _downloadRepository.InsertAsync(download); //TODO diff --git a/src/Business/Grand.Business.Storage/Services/DownloadService.cs b/src/Business/Grand.Business.Storage/Services/DownloadService.cs index a1cd4953d..23dea4c0f 100644 --- a/src/Business/Grand.Business.Storage/Services/DownloadService.cs +++ b/src/Business/Grand.Business.Storage/Services/DownloadService.cs @@ -17,7 +17,7 @@ public partial class DownloadService : IDownloadService #region Fields private readonly IRepository _downloadRepository; - private readonly IDatabaseContext _dbContext; + private readonly IStoreFilesContext _storeFilesContext; private readonly IMediator _mediator; #endregion @@ -28,14 +28,14 @@ public partial class DownloadService : IDownloadService /// Ctor /// /// Download repository - /// DbContext + /// Store Files Context /// Mediator public DownloadService(IRepository downloadRepository, - IDatabaseContext dbContext, + IStoreFilesContext storeFilesContext, IMediator mediator) { _downloadRepository = downloadRepository; - _dbContext = dbContext; + _storeFilesContext = storeFilesContext; _mediator = mediator; } @@ -62,7 +62,7 @@ public virtual async Task GetDownloadById(string downloadId) protected virtual async Task DownloadAsBytes(string objectId) { - var binary = await _dbContext.GridFSBucketDownload(objectId); + var binary = await _storeFilesContext.BucketDownload(objectId); return binary; } /// @@ -96,7 +96,7 @@ public virtual async Task InsertDownload(Download download) throw new ArgumentNullException(nameof(download)); if (!download.UseDownloadUrl) { - download.DownloadObjectId = await _dbContext.GridFSBucketUploadFromBytesAsync(download.Filename, download.DownloadBinary); + download.DownloadObjectId = await _storeFilesContext.BucketUploadFromBytesAsync(download.Filename, download.DownloadBinary); } download.DownloadBinary = null; diff --git a/src/Business/Grand.Business.System/Services/Migrations/1.1/MigrationUpgradeDbVersion_11.cs b/src/Business/Grand.Business.System/Services/Migrations/1.1/MigrationUpgradeDbVersion_11.cs new file mode 100644 index 000000000..a644f085a --- /dev/null +++ b/src/Business/Grand.Business.System/Services/Migrations/1.1/MigrationUpgradeDbVersion_11.cs @@ -0,0 +1,39 @@ +using Grand.Domain.Common; +using Grand.Domain.Data; +using Grand.Infrastructure; +using Grand.Infrastructure.Migrations; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Linq; + +namespace Grand.Business.System.Services.Migrations._1._1 +{ + public class MigrationUpgradeDbVersion_11 : IMigration + { + + public int Priority => 0; + + public DbVersion Version => new(1, 1); + + public Guid Identity => new("6BDB7093-4C31-4D78-9604-58188DF728D3"); + + public string Name => "Upgrade version of the database to 1.1"; + + /// + /// Upgrade process + /// + /// + /// + /// + public bool UpgradeProcess(IDatabaseContext database, IServiceProvider serviceProvider) + { + var repository = serviceProvider.GetRequiredService>(); + + var dbversion = repository.Table.ToList().FirstOrDefault(); + dbversion.DataBaseVersion = $"{GrandVersion.SupportedDBVersion}"; + repository.Update(dbversion); + + return true; + } + } +} diff --git a/src/Business/Grand.Business.System/Services/Migrations/MigrationProcess.cs b/src/Business/Grand.Business.System/Services/Migrations/MigrationProcess.cs new file mode 100644 index 000000000..2a777bd11 --- /dev/null +++ b/src/Business/Grand.Business.System/Services/Migrations/MigrationProcess.cs @@ -0,0 +1,85 @@ +using Grand.Business.Common.Interfaces.Logging; +using Grand.Domain.Data; +using Grand.Infrastructure.Migrations; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Grand.Business.System.Services.Migrations +{ + public class MigrationProcess : IMigrationProcess + { + private readonly IDatabaseContext _databaseContext; + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + + private readonly IRepository _repositoryMigration; + + public MigrationProcess( + IDatabaseContext databaseContext, + IServiceProvider serviceProvider, + ILogger logger, + IRepository repositoryMigration) + { + _databaseContext = databaseContext; + _serviceProvider = serviceProvider; + _logger = logger; + _repositoryMigration = repositoryMigration; + } + + public virtual MigrationResult RunProcess(IMigration migration) + { + var result = RunProcessInternal(migration); + try + { + if (result.Success) + SaveMigration(result); + else + _logger.InsertLog(Domain.Logging.LogLevel.Error, $"Something went wrong during migration process {migration.Name}"); + return result; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Exception run migration {migration.Name}", ex); + } + } + + private MigrationResult RunProcessInternal(IMigration migration) + { + var model = new MigrationResult { + Success = migration.UpgradeProcess(_databaseContext, _serviceProvider), + Migration = migration, + }; + return model; + } + + private void SaveMigration(MigrationResult migrationResult) + { + _repositoryMigration.Insert(new MigrationDb() { + Identity = migrationResult.Migration.Identity, + Name = migrationResult.Migration.Name, + Version = migrationResult.Migration.Version.ToString(), + CreatedOnUtc = DateTime.UtcNow, + }); + } + + private IList GetMigrationDb() + { + return _repositoryMigration.Table.ToList(); + } + + public virtual void RunMigrationProcess() + { + var migrationsDb = GetMigrationDb(); + var migrationManager = new MigrationManager(); + foreach (var item in migrationManager.GetCurrentMigrations()) + { + if (migrationsDb.FirstOrDefault(x => x.Identity == item.Identity) == null) + { + RunProcess(item); + } + } + } + + } +} diff --git a/src/Business/Grand.Business.System/Startup/StartupApplication.cs b/src/Business/Grand.Business.System/Startup/StartupApplication.cs index d45f406c6..48dbf6ad3 100644 --- a/src/Business/Grand.Business.System/Startup/StartupApplication.cs +++ b/src/Business/Grand.Business.System/Startup/StartupApplication.cs @@ -9,10 +9,12 @@ using Grand.Business.System.Services.ExportImport; using Grand.Business.System.Services.Installation; using Grand.Business.System.Services.MachineNameProvider; +using Grand.Business.System.Services.Migrations; using Grand.Business.System.Services.Reports; using Grand.Domain.Data; using Grand.Infrastructure; using Grand.Infrastructure.Configuration; +using Grand.Infrastructure.Migrations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -91,6 +93,8 @@ private void RegisterInstallService(IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddScoped(); } + + serviceCollection.AddScoped(); } private void RegisterExportImportService(IServiceCollection serviceCollection) diff --git a/src/Core/Grand.Domain/Data/IDatabaseContext.cs b/src/Core/Grand.Domain/Data/IDatabaseContext.cs index 68026aefb..294757aac 100644 --- a/src/Core/Grand.Domain/Data/IDatabaseContext.cs +++ b/src/Core/Grand.Domain/Data/IDatabaseContext.cs @@ -5,11 +5,12 @@ namespace Grand.Domain.Data { public interface IDatabaseContext { + string ConnectionString { get; } IQueryable Table(string collectionName); - Task GridFSBucketDownload(string id); - Task GridFSBucketUploadFromBytesAsync(string filename, byte[] source); Task DatabaseExist(string connectionString); Task CreateTable(string name, string collation); + Task DeleteTable(string name); Task CreateIndex(IRepository repository, OrderBuilder orderBuilder, string indexName, bool unique = false) where T : BaseEntity; + Task DeleteIndex(IRepository repository, string indexName) where T : BaseEntity; } } diff --git a/src/Core/Grand.Domain/Data/IStoreFilesContext.cs b/src/Core/Grand.Domain/Data/IStoreFilesContext.cs new file mode 100644 index 000000000..cccd178d8 --- /dev/null +++ b/src/Core/Grand.Domain/Data/IStoreFilesContext.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Grand.Domain.Data +{ + public interface IStoreFilesContext + { + Task BucketDownload(string id); + Task BucketUploadFromBytesAsync(string filename, byte[] source); + } +} diff --git a/src/Core/Grand.Domain/Data/MigrationDb.cs b/src/Core/Grand.Domain/Data/MigrationDb.cs new file mode 100644 index 000000000..daa16b46f --- /dev/null +++ b/src/Core/Grand.Domain/Data/MigrationDb.cs @@ -0,0 +1,27 @@ +using System; + +namespace Grand.Domain.Data +{ + public class MigrationDb : BaseEntity + { + /// + /// The unique identity of migration. + /// + public Guid Identity { get; set; } + + /// + /// Name of migration. + /// + public string Name { get; set; } + + /// + /// Db Version + /// + public string Version { get; set; } + + /// + /// Gets or sets the date and time of migration creation + /// + public DateTime CreatedOnUtc { get; set; } + } +} diff --git a/src/Core/Grand.Domain/Data/Mongo/MongoDBContext.cs b/src/Core/Grand.Domain/Data/Mongo/MongoDBContext.cs index 398dd35f5..4419cb426 100644 --- a/src/Core/Grand.Domain/Data/Mongo/MongoDBContext.cs +++ b/src/Core/Grand.Domain/Data/Mongo/MongoDBContext.cs @@ -1,5 +1,6 @@ using MongoDB.Bson; using MongoDB.Driver; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -8,6 +9,7 @@ namespace Grand.Domain.Data.Mongo { public class MongoDBContext : IDatabaseContext { + private string _connectionString; protected IMongoDatabase _database; public MongoDBContext() @@ -16,11 +18,22 @@ public MongoDBContext() } public MongoDBContext(string connectionString) { - var mongourl = new MongoUrl(connectionString); + _connectionString = connectionString; + + var mongourl = new MongoUrl(_connectionString); var databaseName = mongourl.DatabaseName; _database = new MongoClient(connectionString).GetDatabase(databaseName); } + public string ConnectionString { + get { + if (string.IsNullOrEmpty(_connectionString)) + TryReadMongoDatabase(); + + return _connectionString; + } + } + public MongoDBContext(IMongoDatabase mongodatabase) { _database = mongodatabase; @@ -33,36 +46,27 @@ public IMongoDatabase Database() public IQueryable Table(string collectionName) { + if (string.IsNullOrEmpty(collectionName)) + throw new ArgumentNullException(nameof(collectionName)); + return _database.GetCollection(collectionName).AsQueryable(); } protected IMongoDatabase TryReadMongoDatabase() { - var connectionString = DataSettingsManager.LoadSettings().ConnectionString; + _connectionString = DataSettingsManager.LoadSettings().ConnectionString; - var mongourl = new MongoUrl(connectionString); + var mongourl = new MongoUrl(_connectionString); var databaseName = mongourl.DatabaseName; - var mongodb = new MongoClient(connectionString).GetDatabase(databaseName); + var mongodb = new MongoClient(_connectionString).GetDatabase(databaseName); return mongodb; } - public async Task GridFSBucketDownload(string id) - { - var bucket = new MongoDB.Driver.GridFS.GridFSBucket(_database); - var binary = await bucket.DownloadAsBytesAsync(new ObjectId(id), new MongoDB.Driver.GridFS.GridFSDownloadOptions() { CheckMD5 = true}); - return binary; - - } - public async Task GridFSBucketUploadFromBytesAsync(string filename, byte[] source) - { - var database = _database ?? TryReadMongoDatabase(); - var bucket = new MongoDB.Driver.GridFS.GridFSBucket(database); - var id = await bucket.UploadFromBytesAsync(filename, source); - return id.ToString(); - } - public async Task DatabaseExist(string connectionString) { + if (string.IsNullOrEmpty(connectionString)) + throw new ArgumentNullException(nameof(connectionString)); + var client = new MongoClient(connectionString); var databaseName = new MongoUrl(connectionString).DatabaseName; var database = client.GetDatabase(databaseName); @@ -78,6 +82,9 @@ public async Task DatabaseExist(string connectionString) public async Task CreateTable(string name, string collation) { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException(nameof(name)); + var database = _database ?? TryReadMongoDatabase(); if (!string.IsNullOrEmpty(collation)) @@ -90,9 +97,21 @@ public async Task CreateTable(string name, string collation) await database.CreateCollectionAsync(name); } + public async Task DeleteTable(string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException(nameof(name)); + + var database = _database ?? TryReadMongoDatabase(); + + await database.DropCollectionAsync(name); + } public async Task CreateIndex(IRepository repository, OrderBuilder orderBuilder, string indexName, bool unique = false) where T : BaseEntity { + if (string.IsNullOrEmpty(indexName)) + throw new ArgumentNullException(nameof(indexName)); + IList> keys = new List>(); foreach (var item in orderBuilder.Fields) { @@ -127,5 +146,16 @@ public async Task CreateIndex(IRepository repository, OrderBuilder orde } catch { } } + + public async Task DeleteIndex(IRepository repository, string indexName) where T : BaseEntity + { + if (string.IsNullOrEmpty(indexName)) + throw new ArgumentNullException(nameof(indexName)); + try + { + await ((MongoRepository)repository).Collection.Indexes.DropOneAsync(indexName); + } + catch { } + } } } diff --git a/src/Core/Grand.Domain/Data/Mongo/MongoStoreFilesContext.cs b/src/Core/Grand.Domain/Data/Mongo/MongoStoreFilesContext.cs new file mode 100644 index 000000000..9312d43e9 --- /dev/null +++ b/src/Core/Grand.Domain/Data/Mongo/MongoStoreFilesContext.cs @@ -0,0 +1,39 @@ +using MongoDB.Bson; +using MongoDB.Driver; +using System.Threading.Tasks; + +namespace Grand.Domain.Data.Mongo +{ + public class MongoStoreFilesContext : IStoreFilesContext + { + protected IMongoDatabase _database; + + public MongoStoreFilesContext() + { + var connectionString = DataSettingsManager.LoadSettings().ConnectionString; + + var mongourl = new MongoUrl(connectionString); + var databaseName = mongourl.DatabaseName; + _database = new MongoClient(connectionString).GetDatabase(databaseName); + } + + public MongoStoreFilesContext(IMongoDatabase database) + { + _database = database; + } + + public async Task BucketDownload(string id) + { + var bucket = new MongoDB.Driver.GridFS.GridFSBucket(_database); + var binary = await bucket.DownloadAsBytesAsync(new ObjectId(id), new MongoDB.Driver.GridFS.GridFSDownloadOptions() { CheckMD5 = true }); + return binary; + + } + public async Task BucketUploadFromBytesAsync(string filename, byte[] source) + { + var bucket = new MongoDB.Driver.GridFS.GridFSBucket(_database); + var id = await bucket.UploadFromBytesAsync(filename, source); + return id.ToString(); + } + } +} diff --git a/src/Core/Grand.Infrastructure/Configuration/AppConfig.cs b/src/Core/Grand.Infrastructure/Configuration/AppConfig.cs index 886ce214d..4a57fda69 100644 --- a/src/Core/Grand.Infrastructure/Configuration/AppConfig.cs +++ b/src/Core/Grand.Infrastructure/Configuration/AppConfig.cs @@ -32,6 +32,12 @@ public AppConfig() /// public bool IgnoreStoreLimitations { get; set; } + /// + /// A value indicating whether to ignore the migration process + /// + public bool SkipMigrationProcess { get; set; } + + /// /// Gets or sets a value indicating whether to clear /Plugins/bin directory on application startup /// diff --git a/src/Core/Grand.Infrastructure/Migrations/DbVersion.cs b/src/Core/Grand.Infrastructure/Migrations/DbVersion.cs new file mode 100644 index 000000000..e9b939f3d --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/DbVersion.cs @@ -0,0 +1,55 @@ +using System; + +namespace Grand.Infrastructure.Migrations +{ + public class DbVersion: IComparable + { + + public DbVersion(int major, int minor) + { + Major = major; + Minor = minor; + } + + /// + /// Gets the major version + /// + public readonly int Major; + + /// + /// Gets the minor version + /// + public readonly int Minor; + + /// + /// Compares the current Version object to a specified Version object and returns an indication of their relative values. + /// + /// + /// + public int CompareTo(DbVersion otherVersion) + { + if (Major != otherVersion.Major) + if (Major > otherVersion.Major) + return 1; + else + return -1; + + if (Minor != otherVersion.Minor) + if (Minor > otherVersion.Minor) + return 1; + else + return -1; + + return 0; + + } + + public override string ToString() + { + return $"{Major}.{Minor}"; + } + + + + } +} diff --git a/src/Core/Grand.Infrastructure/Migrations/IBaseMigration.cs b/src/Core/Grand.Infrastructure/Migrations/IBaseMigration.cs new file mode 100644 index 000000000..9516b11e7 --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/IBaseMigration.cs @@ -0,0 +1,22 @@ +using System; + +namespace Grand.Infrastructure.Migrations +{ + public interface IBaseMigration + { + /// + /// Field to which version should be upgraded db + /// + DbVersion Version { get; } + + /// + /// The unique identity of migration. + /// + Guid Identity { get; } + + /// + /// Name of migration. + /// + string Name { get; } + } +} diff --git a/src/Core/Grand.Infrastructure/Migrations/IMigration.cs b/src/Core/Grand.Infrastructure/Migrations/IMigration.cs new file mode 100644 index 000000000..0576c12f1 --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/IMigration.cs @@ -0,0 +1,21 @@ +using Grand.Domain.Data; +using System; + +namespace Grand.Infrastructure.Migrations +{ + public interface IMigration : IBaseMigration + { + /// + /// Upgrade process + /// + /// + /// + /// + bool UpgradeProcess(IDatabaseContext database, IServiceProvider serviceProvider); + + /// + /// Gets order of this startup migration implementation + /// + int Priority { get; } + } +} diff --git a/src/Core/Grand.Infrastructure/Migrations/IMigrationProcess.cs b/src/Core/Grand.Infrastructure/Migrations/IMigrationProcess.cs new file mode 100644 index 000000000..e9657b337 --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/IMigrationProcess.cs @@ -0,0 +1,8 @@ +namespace Grand.Infrastructure.Migrations +{ + public interface IMigrationProcess + { + void RunMigrationProcess(); + MigrationResult RunProcess(IMigration migration); + } +} diff --git a/src/Core/Grand.Infrastructure/Migrations/MigrationManager.cs b/src/Core/Grand.Infrastructure/Migrations/MigrationManager.cs new file mode 100644 index 000000000..49db305ce --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/MigrationManager.cs @@ -0,0 +1,47 @@ +using Grand.Infrastructure.Plugins; +using Grand.Infrastructure.TypeSearchers; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Grand.Infrastructure.Migrations +{ + public class MigrationManager + { + private readonly IEnumerable _migrationConfigurations; + + public MigrationManager() + { + var typeSearcher = new AppTypeSearcher(); + _migrationConfigurations = typeSearcher.ClassesOfType(); + } + + /// + /// Get all migrations + /// + /// + public IEnumerable GetAllMigrations() + { + return _migrationConfigurations + .Where(mg => PluginExtensions.OnlyInstalledPlugins(mg)) + .Select(mg => (IMigration)Activator.CreateInstance(mg)) + .OrderBy(mg => mg.Priority); + } + + /// + /// Get current migrations + /// + /// + /// + public IEnumerable GetCurrentMigrations() + { + var currentDbVersion = new DbVersion(int.Parse(GrandVersion.MajorVersion), int.Parse(GrandVersion.MinorVersion)); + + return GetAllMigrations() + .Where(x => currentDbVersion.CompareTo(x.Version) >= 0) + .OrderBy(mg => mg.Version.ToString()) + .OrderBy(mg => mg.Priority) + .ToList(); + } + } +} diff --git a/src/Core/Grand.Infrastructure/Migrations/MigrationResult.cs b/src/Core/Grand.Infrastructure/Migrations/MigrationResult.cs new file mode 100644 index 000000000..0a3d5ccf5 --- /dev/null +++ b/src/Core/Grand.Infrastructure/Migrations/MigrationResult.cs @@ -0,0 +1,8 @@ +namespace Grand.Infrastructure.Migrations +{ + public class MigrationResult + { + public bool Success; + public IBaseMigration Migration; + } +} diff --git a/src/Core/Grand.Infrastructure/Startup/StartupApplication.cs b/src/Core/Grand.Infrastructure/Startup/StartupApplication.cs index fc79cf702..abbfc2b71 100644 --- a/src/Core/Grand.Infrastructure/Startup/StartupApplication.cs +++ b/src/Core/Grand.Infrastructure/Startup/StartupApplication.cs @@ -41,9 +41,12 @@ private void RegisterDataLayer(IServiceCollection serviceCollection) } + //database context serviceCollection.AddScoped(); + //store files context - gridfs + serviceCollection.AddScoped(); - //MongoDbRepository + //Mongo Repository serviceCollection.AddScoped(typeof(IRepository<>), typeof(MongoRepository<>)); } diff --git a/src/Tests/Grand.Business.Storage.Tests/Services/DownloadServiceTests.cs b/src/Tests/Grand.Business.Storage.Tests/Services/DownloadServiceTests.cs index 516c8e6e5..baa7079a6 100644 --- a/src/Tests/Grand.Business.Storage.Tests/Services/DownloadServiceTests.cs +++ b/src/Tests/Grand.Business.Storage.Tests/Services/DownloadServiceTests.cs @@ -15,7 +15,7 @@ public class DownloadServiceTests { private Mock _mediatorMock; private Mock> _repositoryMock; - private Mock _dbContext; + private Mock _storeFilesContext; private DownloadService _service; [TestInitialize] @@ -23,8 +23,8 @@ public void Init() { _mediatorMock = new Mock(); _repositoryMock = new Mock>(); - _dbContext = new Mock(); - _service = new DownloadService(_repositoryMock.Object, _dbContext.Object, _mediatorMock.Object); + _storeFilesContext = new Mock(); + _service = new DownloadService(_repositoryMock.Object, _storeFilesContext.Object, _mediatorMock.Object); } [TestMethod] diff --git a/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationManagerTests.cs b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationManagerTests.cs new file mode 100644 index 000000000..69c220d70 --- /dev/null +++ b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationManagerTests.cs @@ -0,0 +1,26 @@ +using Grand.Infrastructure.Migrations; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +namespace Grand.Business.System.Tests.Services.Migrations +{ + [TestClass] + public class MigrationManagerTests + { + private MigrationManager _migrationManager; + + [TestInitialize] + public void Init() + { + _migrationManager = new MigrationManager(); + } + + [TestMethod] + public void GetCurrentMigrations_Exists() + { + var migrations = _migrationManager.GetCurrentMigrations(); + Assert.IsTrue(migrations.Count() > 0); + } + + } +} diff --git a/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationProcessTest.cs b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationProcessTest.cs new file mode 100644 index 000000000..89f9f9dd1 --- /dev/null +++ b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationProcessTest.cs @@ -0,0 +1,39 @@ +using Grand.Business.Common.Interfaces.Logging; +using Grand.Business.System.Services.Migrations; +using Grand.Domain.Data; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; + +namespace Grand.Business.System.Tests.Services.Migrations +{ + [TestClass()] + public class MigrationProcessTest + { + private Mock> _repository; + private MigrationProcess _service; + private Mock _dbContext; + private Mock _loggerMock; + private IServiceProvider _serviceProvider; + + [TestInitialize] + public void Init() + { + var serviceProvider = new Mock(); + _serviceProvider = serviceProvider.Object; + + _repository = new Mock>(); + _dbContext = new Mock(); + _loggerMock = new Mock(); + _service = new MigrationProcess(_dbContext.Object, _serviceProvider, _loggerMock.Object, _repository.Object); + } + + [TestMethod] + public void RunMigrationProcess_CheckInsertMigrationDb() + { + _service.RunMigrationProcess(); + _repository.Verify(c => c.Insert(It.IsAny()), Times.AtLeastOnce); + } + + } +} diff --git a/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationUpgrade_Test.cs b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationUpgrade_Test.cs new file mode 100644 index 000000000..9558984bd --- /dev/null +++ b/src/Tests/Grand.Business.System.Tests/Services/Migrations/MigrationUpgrade_Test.cs @@ -0,0 +1,33 @@ +using Grand.Domain.Data; +using Grand.Infrastructure.Migrations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grand.Business.System.Tests.Services.Migrations +{ + public class MigrationUpgrade_Test : IMigration + { + + public int Priority => 0; + + public DbVersion Version => new(int.Parse(Grand.Infrastructure.GrandVersion.MajorVersion), int.Parse(Grand.Infrastructure.GrandVersion.MinorVersion)); + + public Guid Identity => new("6BDB7093-4C31-4D78-9604-58188DF728D3"); + + public string Name => "Upgrade database"; + + /// + /// Upgrade process + /// + /// + /// + /// + public bool UpgradeProcess(IDatabaseContext database, IServiceProvider serviceProvider) + { + return true; + } + } +} diff --git a/src/Web/Grand.Web.Common/Startup/MigrationApplicationStartup.cs b/src/Web/Grand.Web.Common/Startup/MigrationApplicationStartup.cs new file mode 100644 index 000000000..413c105f3 --- /dev/null +++ b/src/Web/Grand.Web.Common/Startup/MigrationApplicationStartup.cs @@ -0,0 +1,53 @@ +using Grand.Domain.Data; +using Grand.Infrastructure; +using Grand.Infrastructure.Configuration; +using Grand.Infrastructure.Migrations; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Grand.Web.Common.Startup +{ + /// + /// Represents object for the configuring/load migration process on application startup + /// + public class MigrationApplicationStartup : IStartupApplication + { + /// + /// Add and configure any of the middleware + /// + /// Collection of service descriptors + /// Configuration root of the application + public void ConfigureServices(IServiceCollection services, IConfiguration configuration) + { + } + + /// + /// Configure the using + /// + /// Builder for configuring an application's request pipeline + /// WebHostEnvironment + public void Configure(IApplicationBuilder application, IWebHostEnvironment webHostEnvironment) + { + if (!DataSettingsManager.DatabaseIsInstalled()) + return; + + var serviceProvider = application.ApplicationServices; + var appConfig = serviceProvider.GetRequiredService(); + if (!appConfig.SkipMigrationProcess) + { + var migrationProcess = serviceProvider.GetRequiredService(); + migrationProcess.RunMigrationProcess(); + } + } + + /// + /// Gets order of this startup configuration implementation + /// + public int Priority => 1000; + + public bool BeforeConfigure => false; + + } +} diff --git a/src/Web/Grand.Web/App_Data/appsettings.json b/src/Web/Grand.Web/App_Data/appsettings.json index da320fa92..3058e09d2 100644 --- a/src/Web/Grand.Web/App_Data/appsettings.json +++ b/src/Web/Grand.Web/App_Data/appsettings.json @@ -36,6 +36,9 @@ //A list of plugins to be ignored during start application - pattern "PluginSkipLoadingPattern": "", + //Ignore the migration process + "SkipMigrationProcess": false, + //Load url rewrite rules from external file AppData/UrlRewrite.xml "UseUrlRewrite": false, "UrlRewriteHttpsOptions": false,