Skip to content

Commit 8d21610

Browse files
authored
Merge pull request #114 from gottscj/fix-migration
fix migration 5 -> 6 not run
2 parents 4e779dc + 0bf17b4 commit 8d21610

File tree

8 files changed

+281
-68
lines changed

8 files changed

+281
-68
lines changed

.github/workflows/build.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Build
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
- name: Setup .NET
17+
uses: actions/setup-dotnet@v1
18+
with:
19+
dotnet-version: 5.0.x
20+
- name: Restore dependencies
21+
run: dotnet restore ./src/AspNetCore.Identity.Mongo/
22+
- name: Build
23+
run: dotnet build ./src/AspNetCore.Identity.Mongo/ -c Release --no-restore
24+
- name: Test
25+
run: dotnet test ./Tests -c Release

AspNetCore.Identity.Mongo.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{767BB7BE
2626
EndProject
2727
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7282BE10-249F-4308-BB55-EA2346584782}"
2828
EndProject
29+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{E3254B66-8E82-463F-9550-DFE8563341F8}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -40,13 +42,18 @@ Global
4042
{25627014-8963-42D8-B1EC-A56FBDE16937}.Debug|Any CPU.Build.0 = Debug|Any CPU
4143
{25627014-8963-42D8-B1EC-A56FBDE16937}.Release|Any CPU.ActiveCfg = Release|Any CPU
4244
{25627014-8963-42D8-B1EC-A56FBDE16937}.Release|Any CPU.Build.0 = Release|Any CPU
45+
{E3254B66-8E82-463F-9550-DFE8563341F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46+
{E3254B66-8E82-463F-9550-DFE8563341F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
47+
{E3254B66-8E82-463F-9550-DFE8563341F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
48+
{E3254B66-8E82-463F-9550-DFE8563341F8}.Release|Any CPU.Build.0 = Release|Any CPU
4349
EndGlobalSection
4450
GlobalSection(SolutionProperties) = preSolution
4551
HideSolutionNode = FALSE
4652
EndGlobalSection
4753
GlobalSection(NestedProjects) = preSolution
4854
{39E2704C-B0DE-4BD2-849F-5B51332EE03F} = {7282BE10-249F-4308-BB55-EA2346584782}
4955
{25627014-8963-42D8-B1EC-A56FBDE16937} = {238A25AE-691E-4A86-9B5E-727DFC186F33}
56+
{E3254B66-8E82-463F-9550-DFE8563341F8} = {767BB7BE-35C8-424C-B873-FEBD69EE6C1A}
5057
EndGlobalSection
5158
GlobalSection(ExtensibilityGlobals) = postSolution
5259
SolutionGuid = {3806160E-9B49-41B7-A532-95CD600CE2CF}

Tests/MigrationTests.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using AspNetCore.Identity.Mongo.Migrations;
6+
using AspNetCore.Identity.Mongo.Model;
7+
using MongoDB.Bson;
8+
using MongoDB.Driver;
9+
using NUnit.Framework;
10+
11+
namespace Tests
12+
{
13+
[TestFixture]
14+
public class MigrationTests
15+
{
16+
private IDisposable _runner;
17+
private IMongoClient _client;
18+
private IMongoDatabase _db;
19+
20+
[OneTimeSetUp]
21+
public void OneTimeSetup()
22+
{
23+
var runner = Mongo2Go.MongoDbRunner.Start();
24+
_client = new MongoClient(runner.ConnectionString);
25+
_db = _client.GetDatabase("migration-tests");
26+
_runner = runner;
27+
}
28+
29+
[OneTimeTearDown]
30+
public void OneTimeTearDown()
31+
{
32+
_runner.Dispose();
33+
}
34+
35+
[Test, Category("unit")]
36+
public void Apply_Schema4_AllMigrationsApplied()
37+
{
38+
// ARRANGE
39+
var history = _db.GetCollection<MigrationHistory>("migrations");
40+
var users = _db.GetCollection<MigrationMongoUser>("users");
41+
var roles = _db.GetCollection<MongoRole<ObjectId>>("roles");
42+
var initialVersion = 4;
43+
var existingHistory = new List<MigrationHistory>
44+
{
45+
new MigrationHistory
46+
{
47+
Id = ObjectId.GenerateNewId(),
48+
DatabaseVersion = 3,
49+
InstalledOn = DateTime.UtcNow.AddDays(-2)
50+
},
51+
new MigrationHistory
52+
{
53+
Id = ObjectId.GenerateNewId(),
54+
DatabaseVersion = initialVersion,
55+
InstalledOn = DateTime.UtcNow.AddDays(-1)
56+
}
57+
};
58+
history.InsertMany(existingHistory);
59+
60+
61+
// ACT
62+
Migrator.Apply<MigrationMongoUser, MongoRole<ObjectId>, ObjectId>(history, users, roles);
63+
64+
// ASSERT
65+
var historyAfter = history
66+
.Find("{}")
67+
.SortBy(h => h.DatabaseVersion)
68+
.ToList();
69+
70+
var expectedHistoryObjectsAfter = Migrator.CurrentVersion - initialVersion + existingHistory.Count;
71+
Assert.That(historyAfter.Count, Is.EqualTo(expectedHistoryObjectsAfter),
72+
() => "Expected all migrations to run");
73+
Assert.That(historyAfter.Last().DatabaseVersion, Is.EqualTo(Migrator.CurrentVersion));
74+
}
75+
}
76+
}

Tests/Tests.csproj

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
11+
<PackageReference Include="Mongo2Go" Version="3.1.3" />
12+
<PackageReference Include="NUnit" Version="3.13.1" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
14+
<PackageReference Include="coverlet.collector" Version="3.0.2" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\src\AspNetCore.Identity.Mongo\AspNetCore.Identity.Mongo.csproj" />
19+
</ItemGroup>
20+
21+
</Project>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using AspNetCore.Identity.Mongo.Model;
6+
using MongoDB.Bson;
7+
using MongoDB.Driver;
8+
9+
namespace AspNetCore.Identity.Mongo.Migrations
10+
{
11+
internal abstract class BaseMigration
12+
{
13+
private static List<BaseMigration> _migrations;
14+
public static List<BaseMigration> Migrations {
15+
get
16+
{
17+
if (_migrations == null)
18+
{
19+
_migrations = typeof(BaseMigration)
20+
.Assembly
21+
.GetTypes()
22+
.Where(t => typeof(BaseMigration).IsAssignableFrom(t))
23+
.Select(t => t.GetConstructor(Type.EmptyTypes)?.Invoke(Array.Empty<object>()))
24+
.Where(o => o != null)
25+
.Cast<BaseMigration>()
26+
.OrderBy(m => m.Version)
27+
.ToList();
28+
if (_migrations.Count != _migrations.Select(m => m.Version).Distinct().Count())
29+
{
30+
throw new InvalidOperationException("Migration versions must be unique, please check versions");
31+
}
32+
}
33+
34+
return _migrations;
35+
}
36+
}
37+
38+
39+
public abstract int Version { get; }
40+
41+
public MigrationHistory Apply<TUser, TRole, TKey>(IMongoCollection<TUser> usersCollection,
42+
IMongoCollection<TRole> rolesCollection)
43+
where TKey : IEquatable<TKey>
44+
where TUser : MigrationMongoUser<TKey>
45+
where TRole : MongoRole<TKey>
46+
{
47+
DoApply<TUser, TRole, TKey>(usersCollection, rolesCollection);
48+
return new MigrationHistory
49+
{
50+
Id = ObjectId.GenerateNewId(),
51+
InstalledOn = DateTime.UtcNow,
52+
DatabaseVersion = Version + 1
53+
};
54+
}
55+
56+
protected abstract void DoApply<TUser, TRole, TKey>(
57+
IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection)
58+
where TKey : IEquatable<TKey>
59+
where TUser : MigrationMongoUser<TKey>
60+
where TRole : MongoRole<TKey>;
61+
}
62+
}
Lines changed: 41 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,41 @@
1-
using System;
2-
using System.Linq;
3-
using System.Reflection;
4-
using System.Threading.Tasks;
5-
using AspNetCore.Identity.Mongo.Model;
6-
using AspNetCore.Identity.Mongo.Mongo;
7-
using MongoDB.Driver;
8-
9-
namespace AspNetCore.Identity.Mongo.Migrations
10-
{
11-
internal static class Migrator
12-
{
13-
//Starting from 4 in case we want to implement migrations for previous versions
14-
public static int CurrentVersion = 6;
15-
16-
public static void Apply<TUser, TRole, TKey>(IMongoCollection<MigrationHistory> migrationCollection, IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection)
17-
where TKey : IEquatable<TKey>
18-
where TUser : MigrationMongoUser<TKey>
19-
where TRole : MongoRole<TKey>
20-
{
21-
var history = migrationCollection.Find(_ => true).ToList();
22-
23-
if (history.Count > 0)
24-
{
25-
var lastHistory = history.OrderBy(x => x.DatabaseVersion).Last();
26-
27-
if (lastHistory.DatabaseVersion == CurrentVersion)
28-
{
29-
return;
30-
}
31-
32-
// 4 -> 5
33-
if (lastHistory.DatabaseVersion == 4)
34-
{
35-
var users = usersCollection.Find(x => !string.IsNullOrEmpty(x.AuthenticatorKey)).ToList();
36-
foreach (var user in users)
37-
{
38-
var tokens = user.Tokens;
39-
tokens.Add(new Microsoft.AspNetCore.Identity.IdentityUserToken<string>()
40-
{
41-
UserId = user.Id.ToString(),
42-
Value = user.AuthenticatorKey,
43-
LoginProvider = "[AspNetUserStore]",
44-
Name = "AuthenticatorKey"
45-
});
46-
usersCollection.UpdateOne(x => x.Id.Equals(user.Id),
47-
Builders<TUser>.Update.Set(x => x.Tokens, tokens)
48-
.Set(x => x.AuthenticatorKey, null));
49-
}
50-
}
51-
52-
// 5 -> 6
53-
if (lastHistory.DatabaseVersion == 5)
54-
{
55-
usersCollection.UpdateMany(x => true,
56-
Builders<TUser>.Update.Unset(x => x.AuthenticatorKey)
57-
.Unset(x => x.RecoveryCodes));
58-
}
59-
}
60-
61-
migrationCollection.InsertOne(new MigrationHistory
62-
{
63-
InstalledOn = DateTime.Now,
64-
DatabaseVersion = CurrentVersion
65-
});
66-
}
67-
}
68-
}
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Runtime.CompilerServices;
6+
using System.Threading.Tasks;
7+
using AspNetCore.Identity.Mongo.Model;
8+
using AspNetCore.Identity.Mongo.Mongo;
9+
using MongoDB.Driver;
10+
11+
[assembly: InternalsVisibleTo("Tests")]
12+
13+
namespace AspNetCore.Identity.Mongo.Migrations
14+
{
15+
internal static class Migrator
16+
{
17+
//Starting from 4 in case we want to implement migrations for previous versions
18+
public static int CurrentVersion = 6;
19+
20+
public static void Apply<TUser, TRole, TKey>(IMongoCollection<MigrationHistory> migrationCollection,
21+
IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection)
22+
where TKey : IEquatable<TKey>
23+
where TUser : MigrationMongoUser<TKey>
24+
where TRole : MongoRole<TKey>
25+
{
26+
var version = migrationCollection
27+
.Find(h => true)
28+
.SortByDescending(h => h.DatabaseVersion)
29+
.Project(h => h.DatabaseVersion)
30+
.FirstOrDefault();
31+
32+
var appliedMigrations = BaseMigration.Migrations
33+
.Where(m => m.Version >= version)
34+
.Select(migration => migration.Apply<TUser, TRole, TKey>(usersCollection, rolesCollection))
35+
.ToList();
36+
37+
migrationCollection.InsertMany(appliedMigrations);
38+
39+
}
40+
}
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using MongoDB.Driver;
2+
3+
namespace AspNetCore.Identity.Mongo.Migrations
4+
{
5+
internal class Schema4Migration: BaseMigration
6+
{
7+
public override int Version { get; } = 4;
8+
9+
protected override void DoApply<TUser, TRole, TKey>(
10+
IMongoCollection<TUser> usersCollection,
11+
IMongoCollection<TRole> rolesCollection)
12+
{
13+
var users = usersCollection.Find(x => !string.IsNullOrEmpty(x.AuthenticatorKey)).ToList();
14+
foreach (var user in users)
15+
{
16+
var tokens = user.Tokens;
17+
tokens.Add(new Microsoft.AspNetCore.Identity.IdentityUserToken<string>()
18+
{
19+
UserId = user.Id.ToString(),
20+
Value = user.AuthenticatorKey,
21+
LoginProvider = "[AspNetUserStore]",
22+
Name = "AuthenticatorKey"
23+
});
24+
usersCollection.UpdateOne(x => x.Id.Equals(user.Id),
25+
Builders<TUser>.Update.Set(x => x.Tokens, tokens)
26+
.Set(x => x.AuthenticatorKey, null));
27+
28+
}
29+
}
30+
}
31+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using MongoDB.Driver;
2+
3+
namespace AspNetCore.Identity.Mongo.Migrations
4+
{
5+
internal class Schema5Migration : BaseMigration
6+
{
7+
public override int Version { get; } = 5;
8+
9+
protected override void DoApply<TUser, TRole, TKey>(
10+
IMongoCollection<TUser> usersCollection,
11+
IMongoCollection<TRole> rolesCollection)
12+
{
13+
usersCollection.UpdateMany(x => true,
14+
Builders<TUser>.Update.Unset(x => x.AuthenticatorKey)
15+
.Unset(x => x.RecoveryCodes));
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)