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> <ItemGroup>
<PackageReference Include="AutoFixture" Version="4.11.0" /> <PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.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" Version="3.12.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.11.1" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.11.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.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<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search); Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
Task<bool> UserHasRequest(string userId); 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>> GetRequestsByStatus(int count, int position, string sort, string sortOrder, RequestStatus available);
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, string sort, string sortOrder); Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, string sort, string sortOrder);
} }

@ -35,7 +35,7 @@ namespace Ombi.Core.Engine.Interfaces
return null; return null;
} }
var username = Username.ToUpper(); 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() protected async Task<string> UserAlias()

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

@ -583,7 +583,7 @@ namespace Ombi.Core.Engine
new RequestEngineResult { Result = false, ErrorMessage = $"The whole collection {collections.name} Is already monitored or requested!" }; 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) 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 }; 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 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) public async Task<RequestEngineResult> MarkAvailable(int modelId)
{ {

@ -955,56 +955,7 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, RequestId = model.Id }; 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) public async Task<RequestEngineResult> UpdateAdvancedOptions(MediaAdvancedOptions options)
{ {

@ -24,6 +24,9 @@ namespace Ombi.Core.Models.UI
public RequestQuotaCountModel MusicRequestQuota { get; set; } public RequestQuotaCountModel MusicRequestQuota { get; set; }
public int MusicRequestLimit { get; set; } public int MusicRequestLimit { get; set; }
public UserQualityProfiles UserQualityProfiles { 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 public class ClaimCheckboxes

@ -31,6 +31,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication; using Ombi.Core.Authentication;
using Ombi.Core.Rule.Interfaces; using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Services;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository; using Ombi.Store.Repository;
@ -39,45 +40,37 @@ namespace Ombi.Core.Rule.Rules.Request
{ {
public class RequestLimitRule : BaseRequestRule, IRules<BaseRequest> public class RequestLimitRule : BaseRequestRule, IRules<BaseRequest>
{ {
public RequestLimitRule(IRepository<RequestLog> rl, OmbiUserManager um) public RequestLimitRule(IRequestLimitService requestLimitService)
{ {
_requestLog = rl; _requestLimitService = requestLimitService;
_userManager = um;
} }
private readonly IRepository<RequestLog> _requestLog; private readonly IRequestLimitService _requestLimitService;
private readonly OmbiUserManager _userManager;
public async Task<RuleResult> Execute(BaseRequest obj) 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 (obj.RequestType == RequestType.Movie)
{ {
if (movieLimit <= 0) var remainingLimitsModel = await _requestLimitService.GetRemainingMovieRequests();
if (!remainingLimitsModel.HasLimit)
{
return Success(); return Success();
}
var movieLogs = requestLog.Where(x => x.RequestType == RequestType.Movie); if (remainingLimitsModel.Remaining < 1)
// 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)
{ {
return Fail("You have exceeded your Movie request quota!"); return Fail("You have exceeded your Movie request quota!");
} }
} }
else if (obj.RequestType == RequestType.TvShow) if (obj.RequestType == RequestType.TvShow)
{
var remainingLimitsModel = await _requestLimitService.GetRemainingTvRequests();
if (!remainingLimitsModel.HasLimit)
{ {
if (episodeLimit <= 0)
return Success(); return Success();
}
var child = (ChildRequests) obj; var child = (ChildRequests)obj;
var requestCount = 0; var requestCount = 0;
// Get the count of requests to be made // Get the count of requests to be made
foreach (var s in child.SeasonRequests) foreach (var s in child.SeasonRequests)
@ -85,32 +78,20 @@ namespace Ombi.Core.Rule.Rules.Request
requestCount += s.Episodes.Count; requestCount += s.Episodes.Count;
} }
var tvLogs = requestLog.Where(x => x.RequestType == RequestType.TvShow); if ((remainingLimitsModel.Remaining - requestCount) < 0)
// 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)
{ {
return Fail("You have exceeded your Episode request quota!"); return Fail("You have exceeded your Episode request quota!");
} }
} else if (obj.RequestType == RequestType.Album) }
if (obj.RequestType == RequestType.Album)
{
var remainingLimitsModel = await _requestLimitService.GetRemainingMusicRequests();
if (!remainingLimitsModel.HasLimit)
{ {
if (musicLimit <= 0)
return Success(); return Success();
}
var albumLogs = requestLog.Where(x => x.RequestType == RequestType.Album); if (remainingLimitsModel.Remaining < 1)
// 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)
{ {
return Fail("You have exceeded your Album request quota!"); return Fail("You have exceeded your Album request quota!");
} }

@ -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 Ombi.Api.RottenTomatoes;
using System.Net.Http; using System.Net.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Core.Services;
namespace Ombi.DependencyInjection namespace Ombi.DependencyInjection
{ {
@ -80,10 +81,10 @@ namespace Ombi.DependencyInjection
services.RegisterEngines(); services.RegisterEngines();
services.RegisterEnginesV2(); services.RegisterEnginesV2();
services.RegisterApi(); services.RegisterApi();
services.RegisterHttp();
services.RegisterServices(); services.RegisterServices();
services.RegisterStore(); services.RegisterStore();
services.RegisterJobs(); services.RegisterJobs();
services.RegisterHttp();
} }
public static void RegisterEngines(this IServiceCollection services) public static void RegisterEngines(this IServiceCollection services)
@ -174,7 +175,8 @@ namespace Ombi.DependencyInjection
services.AddTransient<IRottenTomatoesApi, RottenTomatoesApi>(); services.AddTransient<IRottenTomatoesApi, RottenTomatoesApi>();
} }
public static void RegisterStore(this IServiceCollection services) { public static void RegisterStore(this IServiceCollection services)
{
//services.AddDbContext<OmbiContext>(); //services.AddDbContext<OmbiContext>();
//services.AddDbContext<SettingsContext>(); //services.AddDbContext<SettingsContext>();
//services.AddDbContext<ExternalContext>(); //services.AddDbContext<ExternalContext>();
@ -208,6 +210,7 @@ namespace Ombi.DependencyInjection
services.AddSingleton<ICacheService, CacheService>(); services.AddSingleton<ICacheService, CacheService>();
services.AddSingleton<IMediaCacheService, MediaCacheService>(); services.AddSingleton<IMediaCacheService, MediaCacheService>();
services.AddScoped<IImageService, ImageService>(); services.AddScoped<IImageService, ImageService>();
services.AddScoped<IRequestLimitService, RequestLimitService>();
services.AddTransient<IDiscordNotification, DiscordNotification>(); services.AddTransient<IDiscordNotification, DiscordNotification>();
services.AddTransient<IEmailNotification, EmailNotification>(); 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 // Not in the cache, so add this Key into our MediaServiceCache
await UpdateLocalCache(cacheKey); UpdateLocalCache(cacheKey);
return await _memoryCache.GetOrCreateAsync<T>(cacheKey, entry => 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); var mediaServiceCache = _memoryCache.Get<List<string>>(CacheKey);
if (mediaServiceCache == null) if (mediaServiceCache == null)

@ -31,6 +31,10 @@ namespace Ombi.Store.Entities
public int? EpisodeRequestLimit { get; set; } public int? EpisodeRequestLimit { get; set; }
public int? MusicRequestLimit { 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 string UserAccessToken { get; set; }
public List<NotificationUserId> NotificationUserIds { 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") b.Property<int?>("EpisodeRequestLimit")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int?>("EpisodeRequestLimitType")
.HasColumnType("int");
b.Property<string>("Language") b.Property<string>("Language")
.HasColumnType("longtext"); .HasColumnType("longtext");
@ -281,9 +284,15 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<int?>("MovieRequestLimit") b.Property<int?>("MovieRequestLimit")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int?>("MovieRequestLimitType")
.HasColumnType("int");
b.Property<int?>("MusicRequestLimit") b.Property<int?>("MusicRequestLimit")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int?>("MusicRequestLimitType")
.HasColumnType("int");
b.Property<string>("NormalizedEmail") b.Property<string>("NormalizedEmail")
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("varchar(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") b.Property<int?>("EpisodeRequestLimit")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int?>("EpisodeRequestLimitType")
.HasColumnType("INTEGER");
b.Property<string>("Language") b.Property<string>("Language")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -280,9 +283,15 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<int?>("MovieRequestLimit") b.Property<int?>("MovieRequestLimit")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int?>("MovieRequestLimitType")
.HasColumnType("INTEGER");
b.Property<int?>("MusicRequestLimit") b.Property<int?>("MusicRequestLimit")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int?>("MusicRequestLimitType")
.HasColumnType("INTEGER");
b.Property<string>("NormalizedEmail") b.Property<string>("NormalizedEmail")
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("TEXT"); .HasColumnType("TEXT");

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

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

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

@ -42,20 +42,50 @@
<div class="col-md-3 col-sm-12"> <div class="col-md-3 col-sm-12">
<label class="control-label"><h3>Request Limits</h3></label> <label class="control-label"><h3>Request Limits</h3></label>
<div> <div class="row">
<div class="col-6">
<mat-form-field> <mat-form-field>
<input id="movieRequestLimit" matInput placeholder="Movie Request Limit" [(ngModel)]="user.movieRequestLimit"> <input id="movieRequestLimit" matInput placeholder="Movie Request Limit" [(ngModel)]="user.movieRequestLimit">
</mat-form-field> </mat-form-field>
</div> </div>
<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 class="row">
<div class="col-6">
<mat-form-field> <mat-form-field>
<input id="episodeRequestLimit" matInput placeholder="Episode Request Limit" [(ngModel)]="user.episodeRequestLimit"> <input id="episodeRequestLimit" matInput placeholder="Episode Request Limit" [(ngModel)]="user.episodeRequestLimit">
</mat-form-field> </mat-form-field>
</div> </div>
<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 class="row">
<div class="col-6">
<mat-form-field> <mat-form-field>
<input id="musicRequestLimit" matInput placeholder="Music Request Limit" [(ngModel)]="user.musicRequestLimit"> <input id="musicRequestLimit" matInput placeholder="Music Request Limit" [(ngModel)]="user.musicRequestLimit">
</mat-form-field> </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> </div>
<label class="control-label"><h3>Quality & Root Path Preferences</h3></label> <label class="control-label"><h3>Quality & Root Path Preferences</h3></label>
<mat-form-field *ngIf="sonarrQualities"> <mat-form-field *ngIf="sonarrQualities">

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

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

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

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

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

Loading…
Cancel
Save