Merge pull request #4312 from Ombi-app/feature/request-limits

Feature/request limits
pull/4317/head v4.0.1499
Jamie 3 years ago committed by GitHub
commit e302cf685f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,527 @@
using MockQueryable.Moq;
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Engine
{
[TestFixture]
public class MovieRequestLimitsTests
{
private AutoMocker _mocker;
private RequestLimitService _subject;
[SetUp]
public void SetUp()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
_mocker = new AutoMocker();
var principleMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
_mocker.Use(principleMock.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}
[Test]
public async Task User_No_MovieLimit_Set()
{
var user = new OmbiUser();
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task No_UserPassedIn_UsernotExist_No_MovieLimit_Set()
{
var user = new OmbiUser();
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(null);
Assert.That(result, Is.Null);
}
[Test]
public async Task No_UserPassedIn_No_MovieLimit_Set()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST"
};
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(null);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_No_Requests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 1
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(new List<RequestLog>().AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = yesterday, // Yesterday
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(7))
);
}
[Test]
[Ignore("Failing on CI")]
public async Task UserPassedIn_MovieLimit_Set_Limit_MultipleRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = yesterday,
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = yesterday.AddDays(-2),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate =yesterday.AddDays(-3), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate =yesterday.AddDays(-4), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate =yesterday.AddDays(-5), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate =yesterday.AddDays(-6), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate =yesterday.AddDays(-7), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = yesterday.AddDays(-8), // Yesterday
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(1))
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Daily_NoRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = yesterday,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Daily_OneRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today.AddHours(-1),
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).Date)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Daily_AllRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today.AddHours(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today.AddHours(-2),
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).Date)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Weekly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var lastWeek = DateTime.Now.FirstDateInWeek().AddDays(-1); // Day before reset
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Weekly_OneRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.UtcNow;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.FirstDateInWeek().AddDays(7).Date)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Weekly_AllRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.FirstDateInWeek().AddDays(7).Date)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Monthly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var lastWeek = DateTime.Now.AddMonths(-1).AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Monthly_OneRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(firstDayOfMonth.AddMonths(1).Date)
);
}
[Test]
public async Task UserPassedIn_MovieLimit_Set_Limit_Monthly_AllRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MovieRequestLimit = 2,
MovieRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Movie,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMovieRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(firstDayOfMonth.AddMonths(1).Date)
);
}
}
}

@ -0,0 +1,527 @@
using MockQueryable.Moq;
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Helpers;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Engine
{
[TestFixture]
public class MusicRequestLimitTests
{
private AutoMocker _mocker;
private RequestLimitService _subject;
[SetUp]
public void SetUp()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
_mocker = new AutoMocker();
var principleMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
_mocker.Use(principleMock.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}
[Test]
public async Task User_No_MusicLimit_Set()
{
var user = new OmbiUser();
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task No_UserPassedIn_UsernotExist_No_MusicLimit_Set()
{
var user = new OmbiUser();
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(null);
Assert.That(result, Is.Null);
}
[Test]
public async Task No_UserPassedIn_No_MusicLimit_Set()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST"
};
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(null);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_No_Requests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 1
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(new List<RequestLog>().AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = yesterday, // Yesterday
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(7))
);
}
[Test]
[Ignore("Failing on CI")]
public async Task UserPassedIn_MusicLimit_Set_Limit_MultipleRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = yesterday,
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = yesterday.AddDays(-2),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate =yesterday.AddDays(-3), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate =yesterday.AddDays(-4), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate =yesterday.AddDays(-5), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate =yesterday.AddDays(-6), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate =yesterday.AddDays(-7), // Yesterday
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = yesterday.AddDays(-8), // Yesterday
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(1))
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Daily_NoRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = yesterday,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Daily_OneRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today.AddHours(-1),
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).Date)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Daily_AllRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today.AddHours(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today.AddHours(-2),
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).Date)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Weekly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var lastWeek = DateTime.Now.FirstDateInWeek().AddDays(-1); // Day before reset
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Weekly_OneRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.UtcNow;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.FirstDateInWeek().AddDays(7).Date)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Weekly_AllRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.FirstDateInWeek().AddDays(7).Date)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Monthly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var lastWeek = DateTime.Now.AddMonths(-1).AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Monthly_OneRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(firstDayOfMonth.AddMonths(1).Date)
);
}
[Test]
public async Task UserPassedIn_MusicLimit_Set_Limit_Monthly_AllRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
MusicRequestLimit = 2,
MusicRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var firstDayOfMonth = new DateTime(today.Year, today.Month, 1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.Album,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingMusicRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(firstDayOfMonth.AddMonths(1).Date)
);
}
}
}

@ -0,0 +1,665 @@
using MockQueryable.Moq;
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Engine;
using Ombi.Core.Models;
using Ombi.Core.Services;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Engine
{
[TestFixture]
public class TvRequestLimitsTests
{
private AutoMocker _mocker;
private RequestLimitService _subject;
[SetUp]
public void SetUp()
{
_mocker = new AutoMocker();
var principleMock = new Mock<IPrincipal>();
var identityMock = new Mock<IIdentity>();
identityMock.SetupGet(x => x.Name).Returns("Test");
principleMock.SetupGet(x => x.Identity).Returns(identityMock.Object);
_mocker.Use(principleMock.Object);
_subject = _mocker.CreateInstance<RequestLimitService>();
}
[Test]
public async Task User_No_TvLimit_Set()
{
var user = new OmbiUser();
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task No_UserPassedIn_UsernotExist_No_TvLimit_Set()
{
var user = new OmbiUser();
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(null);
Assert.That(result, Is.Null);
}
[Test]
public async Task No_UserPassedIn_No_TvLimit_Set()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST"
};
var um = _mocker.GetMock<OmbiUserManager>();
um.SetupGet(x => x.Users).Returns(new List<OmbiUser> { user }.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(null);
Assert.That(result.HasLimit, Is.False);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_No_Requests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 1
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(new List<RequestLog>().AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate = yesterday, // Yesterday
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(7).Date)
);
}
[Test]
[Ignore("Failing on CI")]
public async Task UserPassedIn_TvLimit_Set_Limit_MultipleRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = yesterday,
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = yesterday.AddDays(-2),
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate =yesterday.AddDays(-3),
},
new RequestLog
{
EpisodeCount = 1,
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate =yesterday.AddDays(-4),
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate =yesterday.AddDays(-5),
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate =yesterday.AddDays(-6),
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate =yesterday.AddDays(-7),
},
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = yesterday.AddDays(-8),
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(yesterday.AddDays(1).Date)
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Daily_NoRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var yesterday = DateTime.Now.AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = yesterday,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Daily_OneRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = today.AddHours(-1),
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).AddHours(-1))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Daily_AllRequestsToday()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate = today.AddHours(-1),
EpisodeCount = 1,
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = today.AddHours(-2),
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).AddHours(-2))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Daily_MultipleEpisodeRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 10,
EpisodeRequestLimitType = RequestLimitType.Day,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate = today.AddHours(-1),
EpisodeCount = 5,
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 4,
RequestDate = today.AddHours(-2),
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(10)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(1).AddHours(-2))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Weekly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var lastWeek = DateTime.Now.AddDays(-8);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Weekly_OneRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(7))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Weekly_AllRequestsWeek()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate = today,
EpisodeCount = 1,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(6))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Weekly_MultipleEpisodeRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 10,
EpisodeRequestLimitType = RequestLimitType.Week,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 5,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
RequestDate = today,
EpisodeCount = 4,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(10)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddDays(6))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Monthly_NoRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var lastWeek = DateTime.Now.AddMonths(-1).AddDays(-1);
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = lastWeek,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(2)
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Monthly_OneRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
EpisodeCount = 1,
RequestType = RequestType.TvShow,
RequestDate = today,
}
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddMonths(1))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Monthly_AllRequests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 2,
EpisodeRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 1,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(2)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(0)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddMonths(1).AddDays(-1))
);
}
[Test]
public async Task UserPassedIn_TvLimit_Set_Limit_Monthly_MultipleEpisodeReuests()
{
var user = new OmbiUser
{
NormalizedUserName = "TEST",
EpisodeRequestLimit = 10,
EpisodeRequestLimitType = RequestLimitType.Month,
Id = "id1"
};
var today = DateTime.Now;
var log = new List<RequestLog>
{
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount =5,
RequestDate = today.AddDays(-1),
},
new RequestLog
{
UserId = "id1",
RequestType = RequestType.TvShow,
EpisodeCount = 4,
RequestDate = today,
},
};
var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
repoMock.Setup(x => x.GetAll()).Returns(log.AsQueryable().BuildMock().Object);
var result = await _subject.GetRemainingTvRequests(user);
Assert.That(result, Is.InstanceOf<RequestQuotaCountModel>()
.With.Property(nameof(RequestQuotaCountModel.HasLimit)).EqualTo(true)
.And.Property(nameof(RequestQuotaCountModel.Limit)).EqualTo(10)
.And.Property(nameof(RequestQuotaCountModel.Remaining)).EqualTo(1)
.And.Property(nameof(RequestQuotaCountModel.NextRequest)).EqualTo(today.AddMonths(1).AddDays(-1))
);
}
}
}

@ -9,7 +9,8 @@
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
<PackageReference Include="Moq" Version="4.14.1" />
<PackageReference Include="Moq" Version="4.15.1" />
<PackageReference Include="Moq.AutoMock" Version="3.0.0" />
<PackageReference Include="Nunit" Version="3.12.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.11.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">

@ -0,0 +1,258 @@
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Rule;
using Ombi.Core.Rule.Rules.Request;
using Ombi.Core.Services;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository.Requests;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Rule.Request
{
[TestFixture]
public class RequestLimitRuleTests
{
private AutoMocker _mocker;
private RequestLimitRule _subject;
[SetUp]
public void SetUp()
{
_mocker = new AutoMocker();
_subject = _mocker.CreateInstance<RequestLimitRule>();
}
[Test]
public async Task MovieRule_No_Limit()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMovieRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = false
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Movie
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task MovieRule_Limit_NotReached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMovieRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 2,
Remaining = 1
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Movie
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task MovieRule_Limit_Reached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMovieRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 1,
Remaining = 0
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Movie
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(false));
}
[Test]
public async Task MusicRule_No_Limit()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMusicRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = false
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Album
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task MusicRule_Limit_NotReached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMusicRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 2,
Remaining = 1
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Album
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task MusicRule_Limit_Reached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingMusicRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 1,
Remaining = 0
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.Album
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(false));
}
[Test]
public async Task TvRule_No_Limit()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingTvRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = false
});
var result = await _subject.Execute(new Store.Entities.Requests.BaseRequest
{
RequestType = RequestType.TvShow
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task TvRule_Limit_NotReached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingTvRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 2,
Remaining = 1
});
var result = await _subject.Execute(new ChildRequests
{
RequestType = RequestType.TvShow,
SeasonRequests = new List<SeasonRequests>
{
new SeasonRequests
{
Episodes = new List<EpisodeRequests>
{
new EpisodeRequests()
}
}
}
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(true));
}
[Test]
public async Task TvRule_Limit_Reached()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingTvRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 1,
Remaining = 0
});
var result = await _subject.Execute(new ChildRequests
{
RequestType = RequestType.TvShow,
SeasonRequests = new List<SeasonRequests>
{
new SeasonRequests
{
Episodes = new List<EpisodeRequests>
{
new EpisodeRequests()
}
}
}
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(false));
}
[Test]
public async Task TvRule_Limit_Reached_ManyEpisodes()
{
var limitService = _mocker.GetMock<IRequestLimitService>();
limitService.Setup(x => x.GetRemainingTvRequests(It.IsAny<OmbiUser>())).ReturnsAsync(new Models.RequestQuotaCountModel
{
HasLimit = true,
Limit = 1,
Remaining = 5
});
var result = await _subject.Execute(new ChildRequests
{
RequestType = RequestType.TvShow,
SeasonRequests = new List<SeasonRequests>
{
new SeasonRequests
{
Episodes = new List<EpisodeRequests>
{
new EpisodeRequests(),
new EpisodeRequests(),
new EpisodeRequests(),
}
},
new SeasonRequests
{
Episodes = new List<EpisodeRequests>
{
new EpisodeRequests(),
new EpisodeRequests(),
new EpisodeRequests(),
}
}
}
});
Assert.That(result, Is.InstanceOf<RuleResult>().With.Property(nameof(RuleResult.Success)).EqualTo(false));
}
}
}

@ -22,7 +22,6 @@ namespace Ombi.Core.Engine
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
Task<bool> UserHasRequest(string userId);
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
Task<RequestsViewModel<AlbumRequest>> GetRequestsByStatus(int count, int position, string sort, string sortOrder, RequestStatus available);
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, string sort, string sortOrder);
}

@ -35,7 +35,7 @@ namespace Ombi.Core.Engine.Interfaces
return null;
}
var username = Username.ToUpper();
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username));
return _user ??= await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
protected async Task<string> UserAlias()

@ -24,7 +24,6 @@ namespace Ombi.Core.Engine.Interfaces
Task<int> GetTotal();
Task UnSubscribeRequest(int requestId, RequestType type);
Task SubscribeToRequest(int requestId, RequestType type);
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken);
}
}

@ -566,7 +566,7 @@ namespace Ombi.Core.Engine
{
var langCode = await DefaultLanguageCode(null);
var collections = await Cache.GetOrAddAsync($"GetCollection{collectionId}{langCode}",
() => MovieApi.GetCollection(langCode, collectionId, cancellationToken), DateTimeOffset.Now.AddDays(1));
() => MovieApi.GetCollection(langCode, collectionId, cancellationToken), DateTimeOffset.Now.AddDays(1));
var results = new List<RequestEngineResult>();
foreach (var collection in collections.parts)
@ -583,7 +583,7 @@ namespace Ombi.Core.Engine
new RequestEngineResult { Result = false, ErrorMessage = $"The whole collection {collections.name} Is already monitored or requested!" };
}
return new RequestEngineResult { Result = true, Message = $"The collection {collections.name} has been successfully added!", RequestId = results.FirstOrDefault().RequestId};
return new RequestEngineResult { Result = true, Message = $"The collection {collections.name} has been successfully added!", RequestId = results.FirstOrDefault().RequestId };
}
private async Task<RequestEngineResult> ProcessSendingMovie(MovieRequests request)
@ -753,49 +753,5 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!", RequestId = model.Id };
}
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.MovieRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Movie);
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
};
}
}
}

@ -435,49 +435,6 @@ namespace Ombi.Core.Engine
Result = true
};
}
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.MusicRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Album);
int count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
DateTime oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
};
}
public async Task<RequestEngineResult> MarkAvailable(int modelId)
{

@ -955,56 +955,7 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, RequestId = model.Id };
}
public async Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.EpisodeRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll()
.Where(x => x.UserId == user.Id
&& x.RequestType == RequestType.TvShow
&& x.RequestDate >= DateTime.UtcNow.AddDays(-7));
// Needed, due to a bug which would cause all episode counts to be 0
int zeroEpisodeCount = await log.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
int episodeCount = await log.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
int count = limit - (zeroEpisodeCount + episodeCount);
DateTime oldestRequestedAt = await log.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
};
}
public async Task<RequestEngineResult> UpdateAdvancedOptions(MediaAdvancedOptions options)
{

@ -24,6 +24,9 @@ namespace Ombi.Core.Models.UI
public RequestQuotaCountModel MusicRequestQuota { get; set; }
public int MusicRequestLimit { get; set; }
public UserQualityProfiles UserQualityProfiles { get; set; }
public RequestLimitType MovieRequestLimitType { get; set; }
public RequestLimitType MusicRequestLimitType { get; set; }
public RequestLimitType EpisodeRequestLimitType { get; set; }
}
public class ClaimCheckboxes

@ -31,6 +31,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Services;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
@ -39,45 +40,37 @@ namespace Ombi.Core.Rule.Rules.Request
{
public class RequestLimitRule : BaseRequestRule, IRules<BaseRequest>
{
public RequestLimitRule(IRepository<RequestLog> rl, OmbiUserManager um)
public RequestLimitRule(IRequestLimitService requestLimitService)
{
_requestLog = rl;
_userManager = um;
_requestLimitService = requestLimitService;
}
private readonly IRepository<RequestLog> _requestLog;
private readonly OmbiUserManager _userManager;
private readonly IRequestLimitService _requestLimitService;
public async Task<RuleResult> Execute(BaseRequest obj)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == obj.RequestedUserId);
var movieLimit = user.MovieRequestLimit;
var episodeLimit = user.EpisodeRequestLimit;
var musicLimit = user.MusicRequestLimit;
var requestLog = _requestLog.GetAll().Where(x => x.UserId == obj.RequestedUserId);
if (obj.RequestType == RequestType.Movie)
{
if (movieLimit <= 0)
var remainingLimitsModel = await _requestLimitService.GetRemainingMovieRequests();
if (!remainingLimitsModel.HasLimit)
{
return Success();
var movieLogs = requestLog.Where(x => x.RequestType == RequestType.Movie);
// Count how many requests in the past 7 days
var count = await movieLogs.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
count += 1; // Since we are including this request
if (count > movieLimit)
}
if (remainingLimitsModel.Remaining < 1)
{
return Fail("You have exceeded your Movie request quota!");
}
}
else if (obj.RequestType == RequestType.TvShow)
if (obj.RequestType == RequestType.TvShow)
{
if (episodeLimit <= 0)
var remainingLimitsModel = await _requestLimitService.GetRemainingTvRequests();
if (!remainingLimitsModel.HasLimit)
{
return Success();
var child = (ChildRequests) obj;
}
var child = (ChildRequests)obj;
var requestCount = 0;
// Get the count of requests to be made
foreach (var s in child.SeasonRequests)
@ -85,37 +78,25 @@ namespace Ombi.Core.Rule.Rules.Request
requestCount += s.Episodes.Count;
}
var tvLogs = requestLog.Where(x => x.RequestType == RequestType.TvShow);
// Count how many requests in the past 7 days
var tv = tvLogs.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
// Needed, due to a bug which would cause all episode counts to be 0
var zeroEpisodeCount = await tv.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
var episodeCount = await tv.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
var count = requestCount + episodeCount + zeroEpisodeCount; // Add the amount of requests in
if (count > episodeLimit)
if ((remainingLimitsModel.Remaining - requestCount) < 0)
{
return Fail("You have exceeded your Episode request quota!");
}
} else if (obj.RequestType == RequestType.Album)
}
if (obj.RequestType == RequestType.Album)
{
if (musicLimit <= 0)
var remainingLimitsModel = await _requestLimitService.GetRemainingMusicRequests();
if (!remainingLimitsModel.HasLimit)
{
return Success();
}
var albumLogs = requestLog.Where(x => x.RequestType == RequestType.Album);
// Count how many requests in the past 7 days
var count = await albumLogs.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
count += 1; // Since we are including this request
if (count > musicLimit)
if (remainingLimitsModel.Remaining < 1)
{
return Fail("You have exceeded your Album request quota!");
}
}
return Success();
return Success();
}
}
}

@ -0,0 +1,303 @@
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models;
using Ombi.Core.Rule.Interfaces;
using Ombi.Helpers;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using System;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Ombi.Core.Services
{
public interface IRequestLimitService
{
Task<RequestQuotaCountModel> GetRemainingMovieRequests(OmbiUser user = default);
Task<RequestQuotaCountModel> GetRemainingTvRequests(OmbiUser user = default);
Task<RequestQuotaCountModel> GetRemainingMusicRequests(OmbiUser user = default);
}
public class RequestLimitService : IRequestLimitService
{
private readonly IPrincipal _user;
private readonly OmbiUserManager _userManager;
private readonly IRepository<RequestLog> _requestLog;
public RequestLimitService(IPrincipal user, OmbiUserManager userManager, IRepository<RequestLog> rl)
{
_user = user;
_userManager = userManager;
_requestLog = rl;
}
public async Task<RequestQuotaCountModel> GetRemainingMovieRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.MovieRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Movie);
if (!user.MovieRequestLimitType.HasValue)
{
var count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
var oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count < 0 ? 0 : count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc),
};
}
return await CalculateBasicRemaingRequests(user, limit, user.MovieRequestLimitType ?? RequestLimitType.Day, log);
}
public async Task<RequestQuotaCountModel> GetRemainingMusicRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.MusicRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.Album);
// Hisoric Limits
if (!user.MusicRequestLimitType.HasValue)
{
var oldcount = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
var oldestRequestedAtOld = await log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = oldcount < 0 ? 0 : oldcount,
NextRequest = DateTime.SpecifyKind(oldestRequestedAtOld.AddDays(7), DateTimeKind.Utc),
};
}
return await CalculateBasicRemaingRequests(user, limit, user.MusicRequestLimitType ?? RequestLimitType.Day, log);
}
private async Task<OmbiUser> GetUser()
{
var username = _user.Identity.Name.ToUpper();
return await _userManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
}
private static async Task<RequestQuotaCountModel> CalculateBasicRemaingRequests(OmbiUser user, int limit, RequestLimitType type, IQueryable<RequestLog> log)
{
int count = 0;
DateTime oldestRequestedAt = DateTime.Now;
DateTime nextRequest = DateTime.Now;
switch (type)
{
case RequestLimitType.Day:
count = limit - await log.CountAsync(x => x.RequestDate >= DateTime.UtcNow.Date);
oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.Date)
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = oldestRequestedAt.AddDays(1).Date;
break;
case RequestLimitType.Week:
var fdow = DateTime.UtcNow.FirstDateInWeek();
count = limit - await log.CountAsync(x => x.RequestDate >= fdow);
oldestRequestedAt = await log.Where(x => x.RequestDate >= fdow)
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = fdow.AddDays(7).Date;
break;
case RequestLimitType.Month:
var now = DateTime.UtcNow;
var firstDayOfMonth = new DateTime(now.Year, now.Month, 1);
count = limit - await log.CountAsync(x => x.RequestDate >= firstDayOfMonth);
oldestRequestedAt = await log.Where(x => x.RequestDate >= firstDayOfMonth)
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = firstDayOfMonth.AddMonths(1).Date;
break;
}
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count < 0 ? 0 : count,
NextRequest = DateTime.SpecifyKind(nextRequest, DateTimeKind.Utc),
};
}
public async Task<RequestQuotaCountModel> GetRemainingTvRequests(OmbiUser user)
{
if (user == null)
{
user = await GetUser();
// If user is still null after attempting to get the logged in user, return null.
if (user == null)
{
return null;
}
}
int limit = user.EpisodeRequestLimit ?? 0;
if (limit <= 0)
{
return new RequestQuotaCountModel()
{
HasLimit = false,
Limit = 0,
Remaining = 0,
NextRequest = DateTime.Now,
};
}
IQueryable<RequestLog> log = _requestLog.GetAll().Where(x => x.UserId == user.Id && x.RequestType == RequestType.TvShow);
int count = 0;
DateTime oldestRequestedAt = DateTime.Now;
DateTime nextRequest = DateTime.Now;
IQueryable<RequestLog> filteredLog;
int zeroEpisodeCount;
int episodeCount;
if (!user.EpisodeRequestLimitType.HasValue)
{
filteredLog = log.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7));
// Needed, due to a bug which would cause all episode counts to be 0
zeroEpisodeCount = await filteredLog.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
episodeCount = await filteredLog.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
count = limit - (zeroEpisodeCount + episodeCount);
oldestRequestedAt = await log
.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count < 0 ? 0 : count,
NextRequest = DateTime.SpecifyKind(oldestRequestedAt.AddDays(7), DateTimeKind.Utc).Date,
};
}
switch (user.EpisodeRequestLimitType)
{
case RequestLimitType.Day:
filteredLog = log.Where(x => x.RequestDate >= DateTime.UtcNow.Date);
// Needed, due to a bug which would cause all episode counts to be 0
zeroEpisodeCount = await filteredLog.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
episodeCount = await filteredLog.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
count = limit - (zeroEpisodeCount + episodeCount);
oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.Date)
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = oldestRequestedAt.AddDays(1);
break;
case RequestLimitType.Week:
filteredLog = log.Where(x => x.RequestDate >= DateTime.UtcNow.Date.AddDays(-7));
// Needed, due to a bug which would cause all episode counts to be 0
zeroEpisodeCount = await filteredLog.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
episodeCount = await filteredLog.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
count = limit - (zeroEpisodeCount + episodeCount);
oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.Date.AddDays(-7))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = oldestRequestedAt.AddDays(7);
break;
case RequestLimitType.Month:
filteredLog = log.Where(x => x.RequestDate >= DateTime.UtcNow.Date.AddMonths(-1));
// Needed, due to a bug which would cause all episode counts to be 0
zeroEpisodeCount = await filteredLog.Where(x => x.EpisodeCount == 0).Select(x => x.EpisodeCount).CountAsync();
episodeCount = await filteredLog.Where(x => x.EpisodeCount != 0).Select(x => x.EpisodeCount).SumAsync();
count = limit - (zeroEpisodeCount + episodeCount);
oldestRequestedAt = await log.Where(x => x.RequestDate >= DateTime.UtcNow.Date.AddMonths(-1))
.OrderBy(x => x.RequestDate)
.Select(x => x.RequestDate)
.FirstOrDefaultAsync();
nextRequest = oldestRequestedAt.AddMonths(1);
break;
}
return new RequestQuotaCountModel()
{
HasLimit = true,
Limit = limit,
Remaining = count < 0 ? 0 : count,
NextRequest = DateTime.SpecifyKind(nextRequest, DateTimeKind.Utc),
};
}
}
}

@ -69,6 +69,7 @@ using Ombi.Api.CloudService;
using Ombi.Api.RottenTomatoes;
using System.Net.Http;
using Microsoft.Extensions.Logging;
using Ombi.Core.Services;
namespace Ombi.DependencyInjection
{
@ -80,10 +81,10 @@ namespace Ombi.DependencyInjection
services.RegisterEngines();
services.RegisterEnginesV2();
services.RegisterApi();
services.RegisterHttp();
services.RegisterServices();
services.RegisterStore();
services.RegisterJobs();
services.RegisterHttp();
}
public static void RegisterEngines(this IServiceCollection services)
@ -124,7 +125,7 @@ namespace Ombi.DependencyInjection
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddHttpClient("OmbiClient", client =>
{
{
client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{runtimeVersion} (https://ombi.io/)");
}).ConfigurePrimaryHttpMessageHandler(() =>
{
@ -174,11 +175,12 @@ namespace Ombi.DependencyInjection
services.AddTransient<IRottenTomatoesApi, RottenTomatoesApi>();
}
public static void RegisterStore(this IServiceCollection services) {
public static void RegisterStore(this IServiceCollection services)
{
//services.AddDbContext<OmbiContext>();
//services.AddDbContext<SettingsContext>();
//services.AddDbContext<ExternalContext>();
//services.AddScoped<OmbiContext, OmbiContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
//services.AddScoped<ISettingsContext, SettingsContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
//services.AddScoped<ExternalContext, ExternalContext>(); // https://docs.microsoft.com/en-us/aspnet/core/data/entity-framework-6
@ -188,7 +190,7 @@ namespace Ombi.DependencyInjection
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
services.AddScoped<ITvRequestRepository, TvRequestRepository>();
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
services.AddScoped<IMusicRequestRepository, MusicRequestRepository>();
@ -208,6 +210,7 @@ namespace Ombi.DependencyInjection
services.AddSingleton<ICacheService, CacheService>();
services.AddSingleton<IMediaCacheService, MediaCacheService>();
services.AddScoped<IImageService, ImageService>();
services.AddScoped<IRequestLimitService, RequestLimitService>();
services.AddTransient<IDiscordNotification, DiscordNotification>();
services.AddTransient<IEmailNotification, EmailNotification>();

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;
using NUnit.Framework.Internal;
namespace Ombi.Helpers.Tests
{
[TestFixture]
public class DateTimeExtensionsTests
{
[TestCaseSource(nameof(DayOfWeekData))]
public DateTime FirstDateInWeekTests(DateTime input, string culture)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
return input.FirstDateInWeek();
}
public static IEnumerable<TestCaseData> DayOfWeekData
{
get
{
yield return new TestCaseData(new DateTime(2021, 09, 20), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Monday, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 21), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Tuesday, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 22), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Wednesday, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 23), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Thursday, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 24), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Friday, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 25), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Sat, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 26), "en-GB").Returns(new DateTime(2021, 09, 20)).SetName("en-GB Sun, FDOW is Monday");
yield return new TestCaseData(new DateTime(2021, 09, 20), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Monday, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 21), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Tuesday, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 22), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Wednesday, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 23), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Thursday, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 24), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Friday, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 25), "en-US").Returns(new DateTime(2021, 09, 19)).SetName("en-US Sat, FDOW is Sunday");
yield return new TestCaseData(new DateTime(2021, 09, 26), "en-US").Returns(new DateTime(2021, 09, 26)).SetName("en-US Sun, FDOW is Sunday");
}
}
}
}

@ -0,0 +1,18 @@
using System;
using System.Threading;
namespace Ombi.Helpers
{
public static class DateTimeExtensions
{
public static DateTime FirstDateInWeek(this DateTime dt)
{
while (dt.DayOfWeek != Thread.CurrentThread.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
{
dt = dt.AddDays(-1);
}
return dt;
}
}
}

@ -32,7 +32,7 @@ namespace Ombi.Helpers
}
// Not in the cache, so add this Key into our MediaServiceCache
await UpdateLocalCache(cacheKey);
UpdateLocalCache(cacheKey);
return await _memoryCache.GetOrCreateAsync<T>(cacheKey, entry =>
{
@ -41,7 +41,7 @@ namespace Ombi.Helpers
});
}
private async Task UpdateLocalCache(string cacheKey)
private void UpdateLocalCache(string cacheKey)
{
var mediaServiceCache = _memoryCache.Get<List<string>>(CacheKey);
if (mediaServiceCache == null)

@ -31,6 +31,10 @@ namespace Ombi.Store.Entities
public int? EpisodeRequestLimit { get; set; }
public int? MusicRequestLimit { get; set; }
public RequestLimitType? MovieRequestLimitType { get; set; }
public RequestLimitType? EpisodeRequestLimitType { get; set; }
public RequestLimitType? MusicRequestLimitType { get; set; }
public string UserAccessToken { get; set; }
public List<NotificationUserId> NotificationUserIds { get; set; }
@ -69,4 +73,11 @@ namespace Ombi.Store.Entities
}
}
public enum RequestLimitType
{
Day = 0,
Week = 1,
Month = 2,
}
}

@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations.OmbiMySql
{
public partial class UserRequestLimits : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitType",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitType",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitType",
table: "AspNetUsers",
type: "int",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitType",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitType",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitType",
table: "AspNetUsers");
}
}
}

@ -0,0 +1,43 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations.OmbiMySql
{
public partial class UserRequestLimits_Pt2 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitAmount",
table: "AspNetUsers");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitAmount",
table: "AspNetUsers",
type: "int",
nullable: true);
}
}
}

@ -266,6 +266,9 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<int?>("EpisodeRequestLimit")
.HasColumnType("int");
b.Property<int?>("EpisodeRequestLimitType")
.HasColumnType("int");
b.Property<string>("Language")
.HasColumnType("longtext");
@ -281,9 +284,15 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<int?>("MovieRequestLimit")
.HasColumnType("int");
b.Property<int?>("MovieRequestLimitType")
.HasColumnType("int");
b.Property<int?>("MusicRequestLimit")
.HasColumnType("int");
b.Property<int?>("MusicRequestLimitType")
.HasColumnType("int");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("varchar(256)");

@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations.OmbiSqlite
{
public partial class UserRequestLimits : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitType",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitType",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitType",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitType",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitType",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitType",
table: "AspNetUsers");
}
}
}

@ -0,0 +1,43 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations.OmbiSqlite
{
public partial class UserRequestLimits_Pt2 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MovieRequestLimitAmount",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MusicRequestLimitAmount",
table: "AspNetUsers");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EpisodeRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MovieRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "MusicRequestLimitAmount",
table: "AspNetUsers",
type: "INTEGER",
nullable: true);
}
}
}

@ -265,6 +265,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<int?>("EpisodeRequestLimit")
.HasColumnType("INTEGER");
b.Property<int?>("EpisodeRequestLimitType")
.HasColumnType("INTEGER");
b.Property<string>("Language")
.HasColumnType("TEXT");
@ -280,9 +283,15 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<int?>("MovieRequestLimit")
.HasColumnType("INTEGER");
b.Property<int?>("MovieRequestLimitType")
.HasColumnType("INTEGER");
b.Property<int?>("MusicRequestLimit")
.HasColumnType("INTEGER");
b.Property<int?>("MusicRequestLimitType")
.HasColumnType("INTEGER");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");

@ -12,6 +12,7 @@
],
"discord.enabled": true,
"conventionalCommits.scopes": [
"discover"
"discover",
"request-limits"
]
}

@ -20,12 +20,23 @@ export interface IUser {
userQualityProfiles: IUserQualityProfiles;
streamingCountry: string;
movieRequestLimitType?: RequestLimitType;
episodeRequestLimitType?: RequestLimitType;
musicRequestLimitType?: RequestLimitType;
// FOR UI
episodeRequestQuota: IRemainingRequests | null;
movieRequestQuota: IRemainingRequests | null;
musicRequestQuota: IRemainingRequests | null;
}
export enum RequestLimitType
{
Day = 0,
Week = 1,
Month = 2,
}
export interface IUserDropdown {
username: string;
id: string;

@ -1,8 +1,10 @@
import { Component, Input, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { RequestType } from "../../interfaces";
import { IRemainingRequests } from "../../interfaces/IRemainingRequests";
import { RequestService } from "../../services";
import { RequestType } from "../../interfaces";
import { TranslateService } from "@ngx-translate/core";
@Component({
selector: "app-remaining-requests",
templateUrl: "remaining-requests.component.html",
@ -43,7 +45,7 @@ export class RemainingRequestsComponent implements OnInit {
private start() {
const callback = (remaining => {
const callback = ((remaining: IRemainingRequests) => {
this.remaining = remaining;
if (this.remaining && this.remaining.hasLimit) {
this.calculateTime();

@ -42,21 +42,51 @@
<div class="col-md-3 col-sm-12">
<label class="control-label"><h3>Request Limits</h3></label>
<div>
<mat-form-field>
<input id="movieRequestLimit" matInput placeholder="Movie Request Limit" [(ngModel)]="user.movieRequestLimit">
</mat-form-field>
<div class="row">
<div class="col-6">
<mat-form-field>
<input id="movieRequestLimit" matInput placeholder="Movie Request Limit" [(ngModel)]="user.movieRequestLimit">
</mat-form-field>
</div>
<div class="col-6">
<mat-label>Movie Request Limit Type</mat-label>
<mat-select [(value)]="user.movieRequestLimitType">
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
{{RequestLimitType[value]}}
</mat-option>
</mat-select>
</div>
</div>
<div>
<mat-form-field>
<input id="episodeRequestLimit" matInput placeholder="Episode Request Limit" [(ngModel)]="user.episodeRequestLimit">
</mat-form-field>
<div class="row">
<div class="col-6">
<mat-form-field>
<input id="episodeRequestLimit" matInput placeholder="Episode Request Limit" [(ngModel)]="user.episodeRequestLimit">
</mat-form-field>
</div>
<div class="col-6">
<mat-label>Episode Request Limit Type</mat-label>
<mat-select [(value)]="user.episodeRequestLimitType">
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
{{RequestLimitType[value]}}
</mat-option>
</mat-select>
</div>
</div>
<div>
<div class="row">
<div class="col-6">
<mat-form-field>
<input id="musicRequestLimit" matInput placeholder="Music Request Limit" [(ngModel)]="user.musicRequestLimit">
</mat-form-field>
</div>
<div class="col-6">
<mat-label>Music Request Limit Type</mat-label>
<mat-select [(value)]="user.musicRequestLimitType">
<mat-option *ngFor="let value of requestLimitTypes" [value]="value">
{{RequestLimitType[value]}}
</mat-option>
</mat-select>
</div>
</div>
<label class="control-label"><h3>Quality & Root Path Preferences</h3></label>
<mat-form-field *ngIf="sonarrQualities">
<mat-label>Sonarr Quality Profile</mat-label>

@ -1,6 +1,6 @@
import { ActivatedRoute, Router } from "@angular/router";
import { Component, OnInit } from "@angular/core";
import { ICheckbox, ICustomizationSettings, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces";
import { ICheckbox, ICustomizationSettings, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, RequestLimitType, UserType } from "../interfaces";
import { IdentityService, MessageService, RadarrService, SettingsService, SonarrService } from "../services";
import { Clipboard } from '@angular/cdk/clipboard';
@ -27,6 +27,8 @@ export class UserManagementUserComponent implements OnInit {
public edit: boolean;
public countries: string[];
public requestLimitTypes: RequestLimitType[];
public RequestLimitType = RequestLimitType;
private customization: ICustomizationSettings;
private accessToken: string;
@ -53,7 +55,7 @@ export class UserManagementUserComponent implements OnInit {
}
public ngOnInit() {
this.requestLimitTypes = [RequestLimitType.Day, RequestLimitType.Week, RequestLimitType.Month];
this.identityService.getSupportedStreamingCountries().subscribe(x => this.countries = x);
this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x);
if(this.edit) {

@ -61,13 +61,13 @@
<th mat-header-cell *matHeaderCellDef> Next Request Due </th>
<td mat-cell *matCellDef="let u">
<div *ngIf="u.movieRequestQuota != null && u.movieRequestQuota.remaining != u.movieRequestLimit">
{{'UserManagment.MovieDue' | translate: {date: (u.movieRequestQuota.nextRequest | amLocal | amDateFormat: 'l LT')} }}
{{'UserManagment.MovieDue' | translate: {date: (u.movieRequestQuota.nextRequest | amLocal | amDateFormat: 'l')} }}
</div>
<div *ngIf="u.episodeRequestQuota != null && u.episodeRequestQuota.remaining != u.episodeRequestLimit">
{{'UserManagment.TvDue' | translate: {date: (u.episodeRequestQuota.nextRequest | amLocal | amDateFormat: 'l LT')} }}
{{'UserManagment.TvDue' | translate: {date: (u.episodeRequestQuota.nextRequest | amLocal | amDateFormat: 'l')} }}
</div>
<div *ngIf="u.musicRequestQuota != null && u.musicRequestQuota.remaining != u.musicRequestLimit">
{{'UserManagment.MusicDue' | translate: {date: (u.musicRequestQuota.nextRequest | amLocal | amDateFormat: 'l LT')} }}
{{'UserManagment.MusicDue' | translate: {date: (u.musicRequestQuota.nextRequest | amLocal | amDateFormat: 'l')} }}
</div>
</td>
</ng-container>
@ -75,7 +75,7 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header> Last Logged In </th>
<td mat-cell *matCellDef="let u">
<span *ngIf="u.lastLoggedIn">
{{u.lastLoggedIn | amLocal | amDateFormat: 'l LT'}}
{{u.lastLoggedIn | amFromUtc | amLocal | amDateFormat: 'l LT'}}
</span>
<span *ngIf="!u.lastLoggedIn">
Not logged in yet!

@ -17,6 +17,7 @@ using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers;
using Ombi.Core.Models.UI;
using Ombi.Core.Services;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
@ -68,6 +69,7 @@ namespace Ombi.Controllers.V1
ITvRequestEngine tvRequestEngine,
IMusicRequestEngine musicEngine,
IUserDeletionEngine deletionEngine,
IRequestLimitService requestLimitService,
ICacheService cacheService)
{
UserManager = user;
@ -96,11 +98,13 @@ namespace Ombi.Controllers.V1
_userQualityProfiles = userProfiles;
MusicRequestEngine = musicEngine;
_deletionEngine = deletionEngine;
_requestLimitService = requestLimitService;
_cacheService = cacheService;
}
private OmbiUserManager UserManager { get; }
private readonly IUserDeletionEngine _deletionEngine;
private readonly IRequestLimitService _requestLimitService;
private readonly ICacheService _cacheService;
private RoleManager<IdentityRole> RoleManager { get; }
@ -395,6 +399,9 @@ namespace Ombi.Controllers.V1
EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0,
MovieRequestLimit = user.MovieRequestLimit ?? 0,
MusicRequestLimit = user.MusicRequestLimit ?? 0,
MovieRequestLimitType = user.MovieRequestLimitType ?? RequestLimitType.Week,
EpisodeRequestLimitType = user.EpisodeRequestLimitType ?? RequestLimitType.Week,
MusicRequestLimitType = user.MusicRequestLimitType ?? RequestLimitType.Week,
Language = user.Language,
StreamingCountry = user.StreamingCountry
};
@ -422,17 +429,17 @@ namespace Ombi.Controllers.V1
if (vm.EpisodeRequestLimit > 0)
{
vm.EpisodeRequestQuota = await TvRequestEngine.GetRemainingRequests(user);
vm.EpisodeRequestQuota = await _requestLimitService.GetRemainingTvRequests(user);
}
if (vm.MovieRequestLimit > 0)
{
vm.MovieRequestQuota = await MovieRequestEngine.GetRemainingRequests(user);
vm.MovieRequestQuota = await _requestLimitService.GetRemainingMovieRequests(user);
}
if (vm.MusicRequestLimit > 0)
{
vm.MusicRequestQuota = await MusicRequestEngine.GetRemainingRequests(user);
vm.MusicRequestQuota = await _requestLimitService.GetRemainingMusicRequests(user);
}
// Get the quality profiles
@ -637,6 +644,9 @@ namespace Ombi.Controllers.V1
user.MovieRequestLimit = ui.MovieRequestLimit;
user.EpisodeRequestLimit = ui.EpisodeRequestLimit;
user.MusicRequestLimit = ui.MusicRequestLimit;
user.EpisodeRequestLimitType = ui.EpisodeRequestLimitType;
user.MusicRequestLimitType = ui.MusicRequestLimitType;
user.MovieRequestLimitType = ui.MovieRequestLimitType;
if (ui.Password.HasValue())
{
user.PasswordHash = UserManager.PasswordHasher.HashPassword(user, ui.Password);

@ -10,6 +10,7 @@ using Ombi.Core.Engine;
using Ombi.Core.Models;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.UI;
using Ombi.Core.Services;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using ILogger = Microsoft.Extensions.Logging.ILogger;
@ -22,16 +23,18 @@ namespace Ombi.Controllers.V1
[ApiController]
public class MusicRequestController : ControllerBase
{
public MusicRequestController(IMusicRequestEngine engine, IVoteEngine voteEngine, ILogger<MusicRequestController> log)
public MusicRequestController(IMusicRequestEngine engine, IVoteEngine voteEngine, ILogger<MusicRequestController> log, IRequestLimitService requestLimitService)
{
_engine = engine;
_voteEngine = voteEngine;
_log = log;
_requestLimitService = requestLimitService;
}
private readonly IMusicRequestEngine _engine;
private readonly IVoteEngine _voteEngine;
private readonly ILogger _log;
private readonly IRequestLimitService _requestLimitService;
/// <summary>
/// Gets album requests.
@ -169,7 +172,7 @@ namespace Ombi.Controllers.V1
[HttpGet("remaining")]
public async Task<RequestQuotaCountModel> GetRemainingMusicRequests()
{
return await _engine.GetRemainingRequests();
return await _requestLimitService.GetRemainingMusicRequests();
}
private string GetApiAlias()
{

@ -11,6 +11,7 @@ using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.UI;
using Ombi.Core.Services;
using Ombi.Models;
using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
@ -23,13 +24,16 @@ namespace Ombi.Controllers.V1
[ApiController]
public class RequestController : ControllerBase
{
private readonly IRequestLimitService _requestLimitService;
public RequestController(IMovieRequestEngine engine, ITvRequestEngine tvRequestEngine, IVoteEngine vote,
ILogger<RequestController> log)
ILogger<RequestController> log, IRequestLimitService requestLimitService)
{
MovieRequestEngine = engine;
TvRequestEngine = tvRequestEngine;
VoteEngine = vote;
Log = log;
_requestLimitService = requestLimitService;
}
private IMovieRequestEngine MovieRequestEngine { get; }
@ -523,7 +527,7 @@ namespace Ombi.Controllers.V1
[HttpGet("movie/remaining")]
public async Task<RequestQuotaCountModel> GetRemainingMovieRequests()
{
return await MovieRequestEngine.GetRemainingRequests();
return await _requestLimitService.GetRemainingMovieRequests();
}
/// <summary>
@ -532,7 +536,7 @@ namespace Ombi.Controllers.V1
[HttpGet("tv/remaining")]
public async Task<RequestQuotaCountModel> GetRemainingTvRequests()
{
return await TvRequestEngine.GetRemainingRequests();
return await _requestLimitService.GetRemainingTvRequests();
}
private string GetApiAlias()

Loading…
Cancel
Save