forked from matteofabbri/AspNetCore.Identity.Mongo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes if DatabaseVersion == 4, migration 5 -> 6 will not run resulting in invalid schema. adds build.yml github action which builds and runs test on PR's and push'es to master branch. also addresses matteofabbri#81
- Loading branch information
Showing
8 changed files
with
281 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v1 | ||
with: | ||
dotnet-version: 5.0.x | ||
- name: Restore dependencies | ||
run: dotnet restore ./src/AspNetCore.Identity.Mongo/ | ||
- name: Build | ||
run: dotnet build ./src/AspNetCore.Identity.Mongo/ -c Release --no-restore | ||
- name: Test | ||
run: dotnet test ./Tests -c Release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using AspNetCore.Identity.Mongo.Migrations; | ||
using AspNetCore.Identity.Mongo.Model; | ||
using MongoDB.Bson; | ||
using MongoDB.Driver; | ||
using NUnit.Framework; | ||
|
||
namespace Tests | ||
{ | ||
[TestFixture] | ||
public class MigrationTests | ||
{ | ||
private IDisposable _runner; | ||
private IMongoClient _client; | ||
private IMongoDatabase _db; | ||
|
||
[OneTimeSetUp] | ||
public void OneTimeSetup() | ||
{ | ||
var runner = Mongo2Go.MongoDbRunner.Start(); | ||
_client = new MongoClient(runner.ConnectionString); | ||
_db = _client.GetDatabase("migration-tests"); | ||
_runner = runner; | ||
} | ||
|
||
[OneTimeTearDown] | ||
public void OneTimeTearDown() | ||
{ | ||
_runner.Dispose(); | ||
} | ||
|
||
[Test, Category("unit")] | ||
public void Apply_Schema4_AllMigrationsApplied() | ||
{ | ||
// ARRANGE | ||
var history = _db.GetCollection<MigrationHistory>("migrations"); | ||
var users = _db.GetCollection<MigrationMongoUser>("users"); | ||
var roles = _db.GetCollection<MongoRole<ObjectId>>("roles"); | ||
var initialVersion = 4; | ||
var existingHistory = new List<MigrationHistory> | ||
{ | ||
new MigrationHistory | ||
{ | ||
Id = ObjectId.GenerateNewId(), | ||
DatabaseVersion = 3, | ||
InstalledOn = DateTime.UtcNow.AddDays(-2) | ||
}, | ||
new MigrationHistory | ||
{ | ||
Id = ObjectId.GenerateNewId(), | ||
DatabaseVersion = initialVersion, | ||
InstalledOn = DateTime.UtcNow.AddDays(-1) | ||
} | ||
}; | ||
history.InsertMany(existingHistory); | ||
|
||
|
||
// ACT | ||
Migrator.Apply<MigrationMongoUser, MongoRole<ObjectId>, ObjectId>(history, users, roles); | ||
|
||
// ASSERT | ||
var historyAfter = history | ||
.Find("{}") | ||
.SortBy(h => h.DatabaseVersion) | ||
.ToList(); | ||
|
||
var expectedHistoryObjectsAfter = Migrator.CurrentVersion - initialVersion + existingHistory.Count; | ||
Assert.That(historyAfter.Count, Is.EqualTo(expectedHistoryObjectsAfter), | ||
() => "Expected all migrations to run"); | ||
Assert.That(historyAfter.Last().DatabaseVersion, Is.EqualTo(Migrator.CurrentVersion)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net5.0</TargetFramework> | ||
|
||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> | ||
<PackageReference Include="Mongo2Go" Version="3.1.3" /> | ||
<PackageReference Include="NUnit" Version="3.13.1" /> | ||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> | ||
<PackageReference Include="coverlet.collector" Version="3.0.2" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\src\AspNetCore.Identity.Mongo\AspNetCore.Identity.Mongo.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using AspNetCore.Identity.Mongo.Model; | ||
using MongoDB.Bson; | ||
using MongoDB.Driver; | ||
|
||
namespace AspNetCore.Identity.Mongo.Migrations | ||
{ | ||
internal abstract class BaseMigration | ||
{ | ||
private static List<BaseMigration> _migrations; | ||
public static List<BaseMigration> Migrations { | ||
get | ||
{ | ||
if (_migrations == null) | ||
{ | ||
_migrations = typeof(BaseMigration) | ||
.Assembly | ||
.GetTypes() | ||
.Where(t => typeof(BaseMigration).IsAssignableFrom(t)) | ||
.Select(t => t.GetConstructor(Type.EmptyTypes)?.Invoke(Array.Empty<object>())) | ||
.Where(o => o != null) | ||
.Cast<BaseMigration>() | ||
.OrderBy(m => m.Version) | ||
.ToList(); | ||
if (_migrations.Count != _migrations.Select(m => m.Version).Distinct().Count()) | ||
{ | ||
throw new InvalidOperationException("Migration versions must be unique, please check versions"); | ||
} | ||
} | ||
|
||
return _migrations; | ||
} | ||
} | ||
|
||
|
||
public abstract int Version { get; } | ||
|
||
public MigrationHistory Apply<TUser, TRole, TKey>(IMongoCollection<TUser> usersCollection, | ||
IMongoCollection<TRole> rolesCollection) | ||
where TKey : IEquatable<TKey> | ||
where TUser : MigrationMongoUser<TKey> | ||
where TRole : MongoRole<TKey> | ||
{ | ||
DoApply<TUser, TRole, TKey>(usersCollection, rolesCollection); | ||
return new MigrationHistory | ||
{ | ||
Id = ObjectId.GenerateNewId(), | ||
InstalledOn = DateTime.UtcNow, | ||
DatabaseVersion = Version + 1 | ||
}; | ||
} | ||
|
||
protected abstract void DoApply<TUser, TRole, TKey>( | ||
IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection) | ||
where TKey : IEquatable<TKey> | ||
where TUser : MigrationMongoUser<TKey> | ||
where TRole : MongoRole<TKey>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,41 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading.Tasks; | ||
using AspNetCore.Identity.Mongo.Model; | ||
using AspNetCore.Identity.Mongo.Mongo; | ||
using MongoDB.Driver; | ||
|
||
namespace AspNetCore.Identity.Mongo.Migrations | ||
{ | ||
internal static class Migrator | ||
{ | ||
//Starting from 4 in case we want to implement migrations for previous versions | ||
public static int CurrentVersion = 6; | ||
|
||
public static void Apply<TUser, TRole, TKey>(IMongoCollection<MigrationHistory> migrationCollection, IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection) | ||
where TKey : IEquatable<TKey> | ||
where TUser : MigrationMongoUser<TKey> | ||
where TRole : MongoRole<TKey> | ||
{ | ||
var history = migrationCollection.Find(_ => true).ToList(); | ||
|
||
if (history.Count > 0) | ||
{ | ||
var lastHistory = history.OrderBy(x => x.DatabaseVersion).Last(); | ||
|
||
if (lastHistory.DatabaseVersion == CurrentVersion) | ||
{ | ||
return; | ||
} | ||
|
||
// 4 -> 5 | ||
if (lastHistory.DatabaseVersion == 4) | ||
{ | ||
var users = usersCollection.Find(x => !string.IsNullOrEmpty(x.AuthenticatorKey)).ToList(); | ||
foreach (var user in users) | ||
{ | ||
var tokens = user.Tokens; | ||
tokens.Add(new Microsoft.AspNetCore.Identity.IdentityUserToken<string>() | ||
{ | ||
UserId = user.Id.ToString(), | ||
Value = user.AuthenticatorKey, | ||
LoginProvider = "[AspNetUserStore]", | ||
Name = "AuthenticatorKey" | ||
}); | ||
usersCollection.UpdateOne(x => x.Id.Equals(user.Id), | ||
Builders<TUser>.Update.Set(x => x.Tokens, tokens) | ||
.Set(x => x.AuthenticatorKey, null)); | ||
} | ||
} | ||
|
||
// 5 -> 6 | ||
if (lastHistory.DatabaseVersion == 5) | ||
{ | ||
usersCollection.UpdateMany(x => true, | ||
Builders<TUser>.Update.Unset(x => x.AuthenticatorKey) | ||
.Unset(x => x.RecoveryCodes)); | ||
} | ||
} | ||
|
||
migrationCollection.InsertOne(new MigrationHistory | ||
{ | ||
InstalledOn = DateTime.Now, | ||
DatabaseVersion = CurrentVersion | ||
}); | ||
} | ||
} | ||
} | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading.Tasks; | ||
using AspNetCore.Identity.Mongo.Model; | ||
using AspNetCore.Identity.Mongo.Mongo; | ||
using MongoDB.Driver; | ||
|
||
[assembly: InternalsVisibleTo("Tests")] | ||
|
||
namespace AspNetCore.Identity.Mongo.Migrations | ||
{ | ||
internal static class Migrator | ||
{ | ||
//Starting from 4 in case we want to implement migrations for previous versions | ||
public static int CurrentVersion = 6; | ||
|
||
public static void Apply<TUser, TRole, TKey>(IMongoCollection<MigrationHistory> migrationCollection, | ||
IMongoCollection<TUser> usersCollection, IMongoCollection<TRole> rolesCollection) | ||
where TKey : IEquatable<TKey> | ||
where TUser : MigrationMongoUser<TKey> | ||
where TRole : MongoRole<TKey> | ||
{ | ||
var version = migrationCollection | ||
.Find(h => true) | ||
.SortByDescending(h => h.DatabaseVersion) | ||
.Project(h => h.DatabaseVersion) | ||
.FirstOrDefault(); | ||
|
||
var appliedMigrations = BaseMigration.Migrations | ||
.Where(m => m.Version >= version) | ||
.Select(migration => migration.Apply<TUser, TRole, TKey>(usersCollection, rolesCollection)) | ||
.ToList(); | ||
|
||
migrationCollection.InsertMany(appliedMigrations); | ||
|
||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/AspNetCore.Identity.Mongo/Migrations/Schema4Migration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace AspNetCore.Identity.Mongo.Migrations | ||
{ | ||
internal class Schema4Migration: BaseMigration | ||
{ | ||
public override int Version { get; } = 4; | ||
|
||
protected override void DoApply<TUser, TRole, TKey>( | ||
IMongoCollection<TUser> usersCollection, | ||
IMongoCollection<TRole> rolesCollection) | ||
{ | ||
var users = usersCollection.Find(x => !string.IsNullOrEmpty(x.AuthenticatorKey)).ToList(); | ||
foreach (var user in users) | ||
{ | ||
var tokens = user.Tokens; | ||
tokens.Add(new Microsoft.AspNetCore.Identity.IdentityUserToken<string>() | ||
{ | ||
UserId = user.Id.ToString(), | ||
Value = user.AuthenticatorKey, | ||
LoginProvider = "[AspNetUserStore]", | ||
Name = "AuthenticatorKey" | ||
}); | ||
usersCollection.UpdateOne(x => x.Id.Equals(user.Id), | ||
Builders<TUser>.Update.Set(x => x.Tokens, tokens) | ||
.Set(x => x.AuthenticatorKey, null)); | ||
|
||
} | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/AspNetCore.Identity.Mongo/Migrations/Schema5Migration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using MongoDB.Driver; | ||
|
||
namespace AspNetCore.Identity.Mongo.Migrations | ||
{ | ||
internal class Schema5Migration : BaseMigration | ||
{ | ||
public override int Version { get; } = 5; | ||
|
||
protected override void DoApply<TUser, TRole, TKey>( | ||
IMongoCollection<TUser> usersCollection, | ||
IMongoCollection<TRole> rolesCollection) | ||
{ | ||
usersCollection.UpdateMany(x => true, | ||
Builders<TUser>.Update.Unset(x => x.AuthenticatorKey) | ||
.Unset(x => x.RecoveryCodes)); | ||
} | ||
} | ||
} |