From 325d00d1dddc05652cc9ca6ffd5760e39bc926c5 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 23 Jun 2010 10:56:14 -0700 Subject: [PATCH] Implemented GetFeaturesThatNeedUpdate() method Returns all features that could be updated --HG-- branch : dev --- .../DataMigration/DataMigrationTests.cs | 123 +++++++++++++++--- .../DataMigration/DataMigrationManager.cs | 81 ++++++++++-- 2 files changed, 171 insertions(+), 33 deletions(-) diff --git a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs index d44348414..26c19c624 100644 --- a/src/Orchard.Tests/DataMigration/DataMigrationTests.cs +++ b/src/Orchard.Tests/DataMigration/DataMigrationTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Autofac; -using Moq; using NHibernate; using NUnit.Framework; using Orchard.ContentManagement.Records; @@ -10,7 +9,6 @@ using Orchard.Data; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Folders; -using Orchard.Environment.Extensions.Loaders; using Orchard.Environment.Extensions.Models; using Orchard.Tests.ContentManagement; using Orchard.DataMigration; @@ -19,7 +17,6 @@ namespace Orchard.Tests.DataMigration { [TestFixture] public class DataMigrationTests { private IContainer _container; - private IExtensionManager _manager; private StubFolders _folders; private IDataMigrationManager _dataMigrationManager; private IRepository _repository; @@ -27,13 +24,8 @@ namespace Orchard.Tests.DataMigration { private ISessionFactory _sessionFactory; private ISession _session; - [SetUp] - public void Init() { - Init(Enumerable.Empty()); - } - - public void Init(IEnumerable dataMigrations) { - + [TestFixtureSetUp] + public void CreateDb() { var databaseFileName = System.IO.Path.GetTempFileName(); _sessionFactory = DataUtility.CreateSessionFactory( databaseFileName, @@ -41,7 +33,17 @@ namespace Orchard.Tests.DataMigration { typeof(ContentItemVersionRecord), typeof(ContentItemRecord), typeof(ContentTypeRecord)); + } + public void InitDb() { + foreach ( var record in _repository.Fetch(m => true) ) { + _repository.Delete(record); + } + _repository.Flush(); + } + + public void Init(IEnumerable dataMigrations) { + var builder = new ContainerBuilder(); _folders = new StubFolders(); @@ -58,10 +60,11 @@ namespace Orchard.Tests.DataMigration { builder.RegisterType(type).As(); } _container = builder.Build(); - _manager = _container.Resolve(); + _container.Resolve(); _dataMigrationManager = _container.Resolve(); _repository = _container.Resolve>(); + InitDb(); } public class StubFolders : IExtensionFolders { @@ -144,6 +147,7 @@ namespace Orchard.Tests.DataMigration { return 999; } } + public class DataMigrationDependenciesModule2 : IDataMigration { public string Feature { get { return "Feature2"; } @@ -165,9 +169,41 @@ namespace Orchard.Tests.DataMigration { return 1; } } + + public class DataMigrationFeatureNeedUpdate1 : IDataMigration { + public string Feature { + get { return "Feature1"; } + } + } + + + public class DataMigrationFeatureNeedUpdate2 : IDataMigration { + public string Feature { + get { return "Feature2"; } + } + + public int Create() { + return 999; + } + } + + public class DataMigrationFeatureNeedUpdate3 : IDataMigration { + public string Feature { + get { return "Feature3"; } + } + + public int Create() { + return 999; + } + + public int UpdateFrom42() { + return 999; + } + } + [Test] public void DataMigrationShouldDoNothingIfNoDataMigrationIsProvidedForFeature() { - Init(new Type[] {typeof (DataMigrationEmpty)}); + Init(new[] {typeof (DataMigrationEmpty)}); _folders.Manifests.Add("Module2", @" name: Module2 @@ -184,7 +220,7 @@ features: [Test] public void DataMigrationShouldDoNothingIfNoUpgradeOrCreateMethodWasFound() { - Init(new Type[] { typeof(DataMigration11) }); + Init(new[] { typeof(DataMigration11) }); _folders.Manifests.Add("Module1", @" name: Module1 @@ -201,7 +237,7 @@ features: [Test] public void CreateShouldReturnVersionNumber() { - Init(new Type[] { typeof(DataMigration11Create) }); + Init(new[] { typeof(DataMigration11Create) }); _folders.Manifests.Add("Module1", @" name: Module1 @@ -220,7 +256,7 @@ features: [Test] public void CreateCanBeFollowedByUpdates() { - Init(new Type[] {typeof (DataMigrationCreateCanBeFollowedByUpdates)}); + Init(new[] {typeof (DataMigrationCreateCanBeFollowedByUpdates)}); _folders.Manifests.Add("Module1", @" name: Module1 @@ -238,7 +274,7 @@ features: [Test] public void SameMigrationClassCanEvolve() { - Init(new Type[] { typeof(DataMigrationSameMigrationClassCanEvolve) }); + Init(new[] { typeof(DataMigrationSameMigrationClassCanEvolve) }); _folders.Manifests.Add("Module1", @" name: Module1 @@ -248,7 +284,7 @@ features: Feature1: Description: Feature "); - _repository.Create(new DataMigrationRecord() { + _repository.Create(new DataMigrationRecord { Current = 42, DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationSameMigrationClassCanEvolve" }); @@ -261,7 +297,7 @@ features: [Test] public void DependenciesShouldBeUpgradedFirst() { - Init(new Type[] { typeof(DataMigrationDependenciesModule1), typeof(DataMigrationDependenciesModule2) }); + Init(new[] { typeof(DataMigrationDependenciesModule1), typeof(DataMigrationDependenciesModule2) }); _folders.Manifests.Add("Module1", @" name: Module1 @@ -290,7 +326,7 @@ features: [Test] public void DataMigrationImplShouldGetASchemaBuilder() { - Init(new Type[] { typeof(DataMigrationWithSchemaBuilder) }); + Init(new[] { typeof(DataMigrationWithSchemaBuilder) }); _folders.Manifests.Add("Module1", @" name: Module1 @@ -304,5 +340,54 @@ features: _dataMigrationManager.Update("Feature1"); Assert.That(_repository.Table.Count(), Is.EqualTo(1)); } + + [Test] + public void ShouldDetectFeaturesThatNeedUpdates() { + + Init(new[] { typeof(DataMigrationFeatureNeedUpdate1), typeof(DataMigrationFeatureNeedUpdate2), typeof(DataMigrationFeatureNeedUpdate3) }); + + _folders.Manifests.Add("Module1", @" +name: Module1 +version: 0.1 +orchardversion: 1 +features: + Feature1: + Description: Feature + Feature2: + Description: Feature + Feature3: + Description: Feature + Feature4: + Description: Feature +"); + + // even if there is a data migration class, as it is empty there should me no migration to do + Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature1"), Is.False); + + // there is no available class for this feature + Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature4"), Is.False); + + // there is a create method and no record in db, so let's create it + Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature2"), Is.True); + + // there is an UpdateFrom42 method, so it should be fired if Current == 42 + + _repository.Create(new DataMigrationRecord { + Current = 42, + DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationFeatureNeedUpdate3" + }); + + Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature3"), Is.True); + + _repository.Delete(_repository.Fetch(m => m.Current == 42).First()); + _repository.Flush(); + + _repository.Create(new DataMigrationRecord { + Current = 43, + DataMigrationClass = "Orchard.Tests.DataMigration.DataMigrationTests+DataMigrationFeatureNeedUpdate3" + }); + + Assert.That(_dataMigrationManager.GetFeaturesThatNeedUpdate().Contains("Feature3"), Is.False); + } } } \ No newline at end of file diff --git a/src/Orchard/DataMigration/DataMigrationManager.cs b/src/Orchard/DataMigration/DataMigrationManager.cs index 05c877a30..8d1a66e0c 100644 --- a/src/Orchard/DataMigration/DataMigrationManager.cs +++ b/src/Orchard/DataMigration/DataMigrationManager.cs @@ -38,7 +38,38 @@ namespace Orchard.DataMigration { public ILogger Logger { get; set; } public IEnumerable GetFeaturesThatNeedUpdate() { - throw new NotImplementedException(); + + var features = new List(); + + // compare current version and available migration methods for each migration class + foreach ( var dataMigration in _dataMigrations ) { + + // get current version for this migration + var dataMigrationRecord = GetDataMigrationRecord(dataMigration); + + var current = 0; + if (dataMigrationRecord != null) { + current = dataMigrationRecord.Current; + } + + // do we need to call Create() ? + if (current == 0) { + + // try to resolve a Create method + if ( GetCreateMethod(dataMigration) != null ) { + features.Add(dataMigration.Feature); + continue; + } + } + + var lookupTable = CreateUpgradeLookupTable(dataMigration); + + if(lookupTable.ContainsKey(current)) { + features.Add(dataMigration.Feature); + } + } + + return features; } public void Update(IEnumerable features) { @@ -71,7 +102,6 @@ namespace Orchard.DataMigration { // get current version for this migration var dataMigrationRecord = GetDataMigrationRecord(tempMigration); - var updateMethodNameRegex = new Regex(@"^UpdateFrom(?\d+)$", RegexOptions.Compiled); var current = 0; if(dataMigrationRecord != null) { current = dataMigrationRecord.Current; @@ -81,8 +111,8 @@ namespace Orchard.DataMigration { if(current == 0) { // try to resolve a Create method - var createMethod = migration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance); - if(createMethod != null && createMethod.ReturnType == typeof(int)) { + var createMethod = GetCreateMethod(migration); + if(createMethod != null) { current = (int)createMethod.Invoke(migration, new object[0]); } else { @@ -91,16 +121,7 @@ namespace Orchard.DataMigration { } } - // update methods might also be called after Create() - var lookupTable = new Dictionary(); - - // construct a lookup table with all managed initial versions - foreach(var methodInfo in migration.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)) { - var match = updateMethodNameRegex.Match(methodInfo.Name); - if(match.Success) { - lookupTable.Add(int.Parse(match.Groups["version"].Value), methodInfo); - } - } + var lookupTable = CreateUpgradeLookupTable(migration); while(lookupTable.ContainsKey(current)) { try { @@ -154,5 +175,37 @@ namespace Orchard.DataMigration { public bool IsFeatureAlreadyInstalled(string feature) { return GetDataMigrations(feature).Any(dataMigration => GetDataMigrationRecord(dataMigration) != null); } + + /// + /// Create a list of all available Update methods from a data migration class, indexed by the version number + /// + private static Dictionary CreateUpgradeLookupTable(IDataMigration dataMigration) { + var updateMethodNameRegex = new Regex(@"^UpdateFrom(?\d+)$", RegexOptions.Compiled); + + // update methods might also be called after Create() + var lookupTable = new Dictionary(); + + // construct a lookup table with all managed initial versions + foreach ( var methodInfo in dataMigration.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance) ) { + var match = updateMethodNameRegex.Match(methodInfo.Name); + if ( match.Success ) { + lookupTable.Add(int.Parse(match.Groups["version"].Value), methodInfo); + } + } + + return lookupTable; + } + + /// + /// Returns the Create metho from a data migration class if it's found + /// + private static MethodInfo GetCreateMethod(IDataMigration dataMigration) { + var methodInfo = dataMigration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance); + if(methodInfo != null && methodInfo.ReturnType == typeof(int)) { + return methodInfo; + } + + return null; + } } }