CBL Import (#1834)

* Wrote my own step tracker and added a prev button. Works up to first conflict flow.

* Everything but final import is hooked up in the UI. Polish still needed, but getting there.

* Making more progress in the CBL import flow.

* Ready for the last step

* Cleaned up some logic to prepare for the last step and reset

* Users like order to be starting at 1

* Fixed a few bugs around cbl import

* CBL import is ready for some basic testing

* Added a reading list hook on side nav

* Fixed up unit tests

* Added icons and color to the import flow

* Tweaked some phrasing

* Hooked up a loading variable but disabled the component as it didn't look good.

* Styling it up

* changed an icon to better fit

---------

Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
Joe Milazzo 2023-03-03 16:51:11 -06:00 committed by GitHub
parent 57de661d71
commit d88a4d5d0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1125 additions and 466 deletions

View file

@ -961,322 +961,382 @@ public class ReadingListServiceTests
Assert.Single(userWithList.ReadingLists);
}
#endregion
//
// #region CreateReadingListFromCBL
//
// private static CblReadingList LoadCblFromPath(string path)
// {
// var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ReadingListService/");
//
// var reader = new System.Xml.Serialization.XmlSerializer(typeof(CblReadingList));
// using var file = new StreamReader(Path.Join(testDirectory, path));
// var cblReadingList = (CblReadingList) reader.Deserialize(file);
// file.Close();
// return cblReadingList;
// }
//
// [Fact]
// public async Task CreateReadingListFromCBL_ShouldCreateList()
// {
// await ResetDb();
// var cblReadingList = LoadCblFromPath("Fables.cbl");
//
// // Mock up our series
// var fablesSeries = DbFactory.Series("Fables");
// var fables2Series = DbFactory.Series("Fables: The Last Castle");
//
// fablesSeries.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2002",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
// fables2Series.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2003",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
//
// _context.AppUser.Add(new AppUser()
// {
// UserName = "majora2007",
// ReadingLists = new List<ReadingList>(),
// Libraries = new List<Library>()
// {
// new Library()
// {
// Name = "Test LIb",
// Type = LibraryType.Book,
// Series = new List<Series>()
// {
// fablesSeries,
// fables2Series
// },
// },
// },
// });
// await _unitOfWork.CommitAsync();
//
// var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
//
// Assert.Equal(CblImportResult.Partial, importSummary.Success);
// Assert.NotEmpty(importSummary.Results);
//
// var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
//
// Assert.NotNull(createdList);
// Assert.Equal("Fables", createdList.Title);
//
// Assert.Equal(4, createdList.Items.Count);
// Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
// Assert.Equal(2, createdList.Items.First(item => item.Order == 1).ChapterId);
// Assert.Equal(3, createdList.Items.First(item => item.Order == 2).ChapterId);
// Assert.Equal(4, createdList.Items.First(item => item.Order == 3).ChapterId);
// }
//
// [Fact]
// public async Task CreateReadingListFromCBL_ShouldCreateList_ButOnlyIncludeSeriesThatUserHasAccessTo()
// {
// await ResetDb();
// var cblReadingList = LoadCblFromPath("Fables.cbl");
//
// // Mock up our series
// var fablesSeries = DbFactory.Series("Fables");
// var fables2Series = DbFactory.Series("Fables: The Last Castle");
//
// fablesSeries.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2002",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
// fables2Series.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2003",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
//
// _context.AppUser.Add(new AppUser()
// {
// UserName = "majora2007",
// ReadingLists = new List<ReadingList>(),
// Libraries = new List<Library>()
// {
// new Library()
// {
// Name = "Test LIb",
// Type = LibraryType.Book,
// Series = new List<Series>()
// {
// fablesSeries,
// },
// },
// },
// });
//
// _context.Library.Add(new Library()
// {
// Name = "Test Lib 2",
// Type = LibraryType.Book,
// Series = new List<Series>()
// {
// fables2Series,
// },
// });
//
// await _unitOfWork.CommitAsync();
//
// var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
//
// Assert.Equal(CblImportResult.Partial, importSummary.Success);
// Assert.NotEmpty(importSummary.Results);
//
// var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
//
// Assert.NotNull(createdList);
// Assert.Equal("Fables", createdList.Title);
//
// Assert.Equal(3, createdList.Items.Count);
// Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
// Assert.Equal(2, createdList.Items.First(item => item.Order == 1).ChapterId);
// Assert.Equal(3, createdList.Items.First(item => item.Order == 2).ChapterId);
// Assert.NotNull(importSummary.Results.SingleOrDefault(r => r.Series == "Fables: The Last Castle"
// && r.Reason == CblImportReason.SeriesMissing));
// }
//
// [Fact]
// public async Task CreateReadingListFromCBL_ShouldFail_UserHasAccessToNoSeries()
// {
// await ResetDb();
// var cblReadingList = LoadCblFromPath("Fables.cbl");
//
// // Mock up our series
// var fablesSeries = DbFactory.Series("Fables");
// var fables2Series = DbFactory.Series("Fables: The Last Castle");
//
// fablesSeries.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2002",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
// fables2Series.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2003",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
//
// _context.AppUser.Add(new AppUser()
// {
// UserName = "majora2007",
// ReadingLists = new List<ReadingList>(),
// Libraries = new List<Library>(),
// });
//
// _context.Library.Add(new Library()
// {
// Name = "Test Lib 2",
// Type = LibraryType.Book,
// Series = new List<Series>()
// {
// fablesSeries,
// fables2Series,
// },
// });
//
// await _unitOfWork.CommitAsync();
//
// var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
//
// Assert.Equal(CblImportResult.Fail, importSummary.Success);
// Assert.NotEmpty(importSummary.Results);
//
// var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
//
// Assert.Null(createdList);
// }
//
//
// [Fact]
// public async Task CreateReadingListFromCBL_ShouldUpdateAnExistingList()
// {
// await ResetDb();
// var cblReadingList = LoadCblFromPath("Fables.cbl");
//
// // Mock up our series
// var fablesSeries = DbFactory.Series("Fables");
// var fables2Series = DbFactory.Series("Fables: The Last Castle");
//
// fablesSeries.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2002",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
// fables2Series.Volumes.Add(new Volume()
// {
// Number = 1,
// Name = "2003",
// Chapters = new List<Chapter>()
// {
// EntityFactory.CreateChapter("1", false),
// EntityFactory.CreateChapter("2", false),
// EntityFactory.CreateChapter("3", false),
//
// }
// });
//
// _context.AppUser.Add(new AppUser()
// {
// UserName = "majora2007",
// ReadingLists = new List<ReadingList>(),
// Libraries = new List<Library>()
// {
// new Library()
// {
// Name = "Test LIb",
// Type = LibraryType.Book,
// Series = new List<Series>()
// {
// fablesSeries,
// fables2Series
// },
// },
// },
// });
//
// await _unitOfWork.CommitAsync();
//
// // Create a reading list named Fables and add 2 chapters to it
// var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.ReadingLists);
// var readingList = await _readingListService.CreateReadingListForUser(user, "Fables");
// Assert.True(await _readingListService.AddChaptersToReadingList(1, new List<int>() {1, 3}, readingList));
// Assert.Equal(2, readingList.Items.Count);
//
// // Attempt to import a Cbl with same reading list name
// var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
//
// Assert.Equal(CblImportResult.Partial, importSummary.Success);
// Assert.NotEmpty(importSummary.Results);
//
// var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
//
// Assert.NotNull(createdList);
// Assert.Equal("Fables", createdList.Title);
//
// Assert.Equal(4, createdList.Items.Count);
// Assert.Equal(4, importSummary.SuccessfulInserts.Count);
//
// Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
// Assert.Equal(3, createdList.Items.First(item => item.Order == 1).ChapterId); // we inserted 3 first
// Assert.Equal(2, createdList.Items.First(item => item.Order == 2).ChapterId);
// Assert.Equal(4, createdList.Items.First(item => item.Order == 3).ChapterId);
// }
// #endregion
//
#region ValidateCBL
[Fact]
public async Task ValidateCblFile_ShouldFail_UserHasAccessToNoSeries()
{
await ResetDb();
var cblReadingList = LoadCblFromPath("Fables.cbl");
// Mock up our series
var fablesSeries = DbFactory.Series("Fables");
var fables2Series = DbFactory.Series("Fables: The Last Castle");
fablesSeries.Volumes.Add(new Volume()
{
Number = 1,
Name = "2002",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
fables2Series.Volumes.Add(new Volume()
{
Number = 1,
Name = "2003",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
ReadingLists = new List<ReadingList>(),
Libraries = new List<Library>(),
});
_context.Library.Add(new Library()
{
Name = "Test Lib 2",
Type = LibraryType.Book,
Series = new List<Series>()
{
fablesSeries,
fables2Series,
},
});
await _unitOfWork.CommitAsync();
var importSummary = await _readingListService.ValidateCblFile(1, cblReadingList);
Assert.Equal(CblImportResult.Fail, importSummary.Success);
Assert.NotEmpty(importSummary.Results);
}
[Fact]
public async Task ValidateCblFile_ShouldFail_ServerHasNoSeries()
{
await ResetDb();
var cblReadingList = LoadCblFromPath("Fables.cbl");
// Mock up our series
var fablesSeries = DbFactory.Series("Fablesa");
var fables2Series = DbFactory.Series("Fablesa: The Last Castle");
fablesSeries.Volumes.Add(new Volume()
{
Number = 1,
Name = "2002",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
fables2Series.Volumes.Add(new Volume()
{
Number = 1,
Name = "2003",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
ReadingLists = new List<ReadingList>(),
Libraries = new List<Library>(),
});
_context.Library.Add(new Library()
{
Name = "Test Lib 2",
Type = LibraryType.Book,
Series = new List<Series>()
{
fablesSeries,
fables2Series,
},
});
await _unitOfWork.CommitAsync();
var importSummary = await _readingListService.ValidateCblFile(1, cblReadingList);
Assert.Equal(CblImportResult.Fail, importSummary.Success);
Assert.NotEmpty(importSummary.Results);
}
#endregion
#region CreateReadingListFromCBL
private static CblReadingList LoadCblFromPath(string path)
{
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ReadingListService/");
var reader = new System.Xml.Serialization.XmlSerializer(typeof(CblReadingList));
using var file = new StreamReader(Path.Join(testDirectory, path));
var cblReadingList = (CblReadingList) reader.Deserialize(file);
file.Close();
return cblReadingList;
}
[Fact]
public async Task CreateReadingListFromCBL_ShouldCreateList()
{
await ResetDb();
var cblReadingList = LoadCblFromPath("Fables.cbl");
// Mock up our series
var fablesSeries = DbFactory.Series("Fables");
var fables2Series = DbFactory.Series("Fables: The Last Castle");
fablesSeries.Volumes.Add(new Volume()
{
Number = 1,
Name = "2002",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
fables2Series.Volumes.Add(new Volume()
{
Number = 1,
Name = "2003",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
ReadingLists = new List<ReadingList>(),
Libraries = new List<Library>()
{
new Library()
{
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
fablesSeries,
fables2Series
},
},
},
});
await _unitOfWork.CommitAsync();
var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
Assert.Equal(CblImportResult.Partial, importSummary.Success);
Assert.NotEmpty(importSummary.Results);
var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
Assert.NotNull(createdList);
Assert.Equal("Fables", createdList.Title);
Assert.Equal(4, createdList.Items.Count);
Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
Assert.Equal(2, createdList.Items.First(item => item.Order == 1).ChapterId);
Assert.Equal(3, createdList.Items.First(item => item.Order == 2).ChapterId);
Assert.Equal(4, createdList.Items.First(item => item.Order == 3).ChapterId);
}
[Fact]
public async Task CreateReadingListFromCBL_ShouldCreateList_ButOnlyIncludeSeriesThatUserHasAccessTo()
{
await ResetDb();
var cblReadingList = LoadCblFromPath("Fables.cbl");
// Mock up our series
var fablesSeries = DbFactory.Series("Fables");
var fables2Series = DbFactory.Series("Fables: The Last Castle");
fablesSeries.Volumes.Add(new Volume()
{
Number = 1,
Name = "2002",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
fables2Series.Volumes.Add(new Volume()
{
Number = 1,
Name = "2003",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
ReadingLists = new List<ReadingList>(),
Libraries = new List<Library>()
{
new Library()
{
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
fablesSeries,
},
},
},
});
_context.Library.Add(new Library()
{
Name = "Test Lib 2",
Type = LibraryType.Book,
Series = new List<Series>()
{
fables2Series,
},
});
await _unitOfWork.CommitAsync();
var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
Assert.Equal(CblImportResult.Partial, importSummary.Success);
Assert.NotEmpty(importSummary.Results);
var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
Assert.NotNull(createdList);
Assert.Equal("Fables", createdList.Title);
Assert.Equal(3, createdList.Items.Count);
Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
Assert.Equal(2, createdList.Items.First(item => item.Order == 1).ChapterId);
Assert.Equal(3, createdList.Items.First(item => item.Order == 2).ChapterId);
Assert.NotNull(importSummary.Results.SingleOrDefault(r => r.Series == "Fables: The Last Castle"
&& r.Reason == CblImportReason.SeriesMissing));
}
[Fact]
public async Task CreateReadingListFromCBL_ShouldUpdateAnExistingList()
{
await ResetDb();
var cblReadingList = LoadCblFromPath("Fables.cbl");
// Mock up our series
var fablesSeries = DbFactory.Series("Fables");
var fables2Series = DbFactory.Series("Fables: The Last Castle");
fablesSeries.Volumes.Add(new Volume()
{
Number = 1,
Name = "2002",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
fables2Series.Volumes.Add(new Volume()
{
Number = 1,
Name = "2003",
Chapters = new List<Chapter>()
{
EntityFactory.CreateChapter("1", false),
EntityFactory.CreateChapter("2", false),
EntityFactory.CreateChapter("3", false),
}
});
_context.AppUser.Add(new AppUser()
{
UserName = "majora2007",
ReadingLists = new List<ReadingList>(),
Libraries = new List<Library>()
{
new Library()
{
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
fablesSeries,
fables2Series
},
},
},
});
await _unitOfWork.CommitAsync();
// Create a reading list named Fables and add 2 chapters to it
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.ReadingLists);
var readingList = await _readingListService.CreateReadingListForUser(user, "Fables");
Assert.True(await _readingListService.AddChaptersToReadingList(1, new List<int>() {1, 3}, readingList));
Assert.Equal(2, readingList.Items.Count);
// Attempt to import a Cbl with same reading list name
var importSummary = await _readingListService.CreateReadingListFromCbl(1, cblReadingList);
Assert.Equal(CblImportResult.Partial, importSummary.Success);
Assert.NotEmpty(importSummary.Results);
var createdList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(1);
Assert.NotNull(createdList);
Assert.Equal("Fables", createdList.Title);
Assert.Equal(4, createdList.Items.Count);
Assert.Equal(4, importSummary.SuccessfulInserts.Count);
Assert.Equal(1, createdList.Items.First(item => item.Order == 0).ChapterId);
Assert.Equal(3, createdList.Items.First(item => item.Order == 1).ChapterId); // we inserted 3 first
Assert.Equal(2, createdList.Items.First(item => item.Order == 2).ChapterId);
Assert.Equal(4, createdList.Items.First(item => item.Order == 3).ChapterId);
}
#endregion
}