Skip to content
Snippets Groups Projects
Commit 0a8a242d authored by Petar Hristov's avatar Petar Hristov :speech_balloon:
Browse files

Merge branch 'Issue/1951-quotaImplementation' into 'dev'

Breaking: Quota implementation

See merge request !209
parents 7c0a0126 10a8a43f
Branches
Tags
2 merge requests!213Release: Sprint/2022 14 :robot:,!209Breaking: Quota implementation
......@@ -158,8 +158,7 @@ namespace Coscine.Api.Project.Tests
RDSResourceTypeModel rdsResourceTypeModel = new RDSResourceTypeModel();
RdsResourceType = new RdsresourceType()
{
BucketName = "c",
Size = 25,
BucketName = "c"
};
rdsResourceTypeModel.Insert(RdsResourceType);
......
......@@ -63,7 +63,7 @@ namespace Coscine.Api.Project.Tests
//[Test]
public void TestControllerGet()
{
var actionResult = Controller.Get(Projects[0].Id.ToString()).Result;
var actionResult = Controller.Get(Projects[0].Id).Result;
Assert.IsTrue(actionResult.GetType() == typeof(OkObjectResult));
OkObjectResult okObjectResult = (OkObjectResult)actionResult;
......@@ -82,7 +82,7 @@ namespace Coscine.Api.Project.Tests
//[Test]
public void TestControllerUpdate()
{
var actionResult = Controller.Get(Projects[0].Id.ToString()).Result;
var actionResult = Controller.Get(Projects[0].Id).Result;
ProjectObject projectObject = (ProjectObject)((OkObjectResult)actionResult).Value;
projectObject.Description = "ChangedDescription";
......@@ -97,7 +97,7 @@ namespace Coscine.Api.Project.Tests
// Cleanup
stream.Close();
var actionResultPrevious = Controller.Get(Projects[1].Id.ToString()).Result;
var actionResultPrevious = Controller.Get(Projects[1].Id).Result;
ProjectObject projectObjectPrevious = (ProjectObject)((OkObjectResult)actionResultPrevious).Value;
stream = ObjectFactory<ProjectObject>.SerializeToStream(projectObjectPrevious);
......@@ -173,7 +173,7 @@ namespace Coscine.Api.Project.Tests
ProjectModel projectModel = new ProjectModel();
var project = projectModel.GetById(createdProjectObject.Id);
actionResult = Controller.Delete(createdProjectObject.Id.ToString());
actionResult = Controller.Delete(createdProjectObject.Id);
Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));
result = (JsonResult)actionResult;
......@@ -217,10 +217,10 @@ namespace Coscine.Api.Project.Tests
FakeControllerContext(Users[0], stream);
ProjectModel projectModel = new ProjectModel();
var projectModel = new ProjectModel();
var project = projectModel.GetById(createdProjectObject.Id);
actionResult = Controller.Delete(createdProjectObject.Id.ToString());
actionResult = Controller.Delete(createdProjectObject.Id);
Assert.IsTrue(actionResult.GetType() == typeof(JsonResult));
result = (JsonResult)actionResult;
......
This diff is collapsed.
using Coscine.Api.Project.ParameterObjects;
using Coscine.ApiCommons;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Database.Util;
using Coscine.Logging;
using Coscine.Metadata;
using Coscine.ResourceTypes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Coscine.Api.Project.Controllers
{
/// <summary>
/// /// This controller represents the actions which can be taken with a project object.
/// </summary>
[Authorize]
public class ProjectQuotaController : Controller
{
private readonly Authenticator _authenticator;
private readonly ProjectModel _projectModel;
private readonly ProjectRoleModel _projectRoleModel;
private readonly ProjectQuotaModel _projectQuotaModel;
private readonly ResourceTypeModel _resourceTypeModel;
private readonly ResourceModel _resourceModel;
private readonly CoscineLogger _coscineLogger;
private readonly RdfStoreConnector _rdfStoreConnector;
/// <summary>
/// ProjectQuotaController constructor
/// </summary>
/// <param name="logger">Logger</param>
public ProjectQuotaController(ILogger<ProjectQuotaController> logger)
{
_authenticator = new Authenticator(this, Program.Configuration);
_projectModel = new ProjectModel();
_projectRoleModel = new ProjectRoleModel();
_resourceTypeModel = new ResourceTypeModel();
_resourceModel = new ResourceModel();
_projectQuotaModel = new ProjectQuotaModel();
_coscineLogger = new CoscineLogger(logger);
_rdfStoreConnector = new RdfStoreConnector(Program.Configuration.GetString("coscine/local/virtuoso/additional/url"));
}
/// <summary>
/// Retrieves all project quotas in GiB grouped by resource type for the selected project.
/// </summary>
/// <param name="id">Id of the project</param>
/// <returns>List of project quotas per resource type</returns>
[HttpGet("[controller]/{id}/-/all")]
public ActionResult<IEnumerable<ProjectQuotaReturnObject>> Quotas(Guid id)
{
var user = _authenticator.GetUser();
var project = _projectModel.GetById(id);
if (project == null)
{
return base.NotFound($"Could not find project with id: {id}");
}
if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
{
return Unauthorized("The user is not authorized to perform a get on the selected project!");
}
var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled == true);
return Json(resourceTypes.Select(x => Helpers.CreateProjectQuotaReturnObject(x, id)));
}
/// <summary>
/// Retrieves the project quota in GiB of a resource type for the selected project together with all individual resources of this resource type.
/// </summary>
/// <param name="id">Id of the project</param>
/// <param name="resourceTypeId">Id of the resource type</param>
/// <returns>The project quota for the resource type together with all individual resources of this resource type.</returns>
[HttpGet("[controller]/{id}/{resourceTypeId}")]
public ActionResult<ProjectQuotaExtendedReturnObject> Quota(Guid id, Guid resourceTypeId)
{
var user = _authenticator.GetUser();
var project = _projectModel.GetById(id);
if (project == null)
{
return NotFound($"Could not find project with id: {id}");
}
if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
{
return Unauthorized("The user is not authorized to perform a get on the selected project!");
}
var resourceType = _resourceTypeModel.GetById(resourceTypeId);
if (resourceType?.Enabled.HasValue != true || !resourceType.Enabled.Value)
{
return base.NotFound($"Could not find resourceType with id: {resourceTypeId}");
}
return Json(Helpers.CreateProjectQuotaExtendedReturnObject(resourceType, id));
}
/// <summary>
/// Updates the reserved project quota of a resource type for a selected poject. Quota value in GiB.
/// </summary>
/// <param name="id">Id of the project</param>
/// <param name="resourceTypeId">Id of the resource type</param>
/// <param name="updateProjectQuotaObject">Object containing the update values.</param>
/// <returns>NoContent (204).</returns>
[HttpPost("[controller]/{id}/{resourceTypeId}")]
public IActionResult UpdateQuota(Guid id, Guid resourceTypeId, [FromBody] UpdateProjectQuotaObject updateProjectQuotaObject)
{
var user = _authenticator.GetUser();
var project = _projectModel.GetById(id);
if (project == null)
{
return NotFound($"Could not find project with id: {id}");
}
if (!_projectModel.HasAccess(user, project, UserRoles.Owner))
{
return Unauthorized("The user is not authorized to perform a get on the selected project!");
}
var resourceType = _resourceTypeModel.GetById(resourceTypeId);
if (resourceType?.Enabled.HasValue != true || !resourceType.Enabled.Value)
{
return NotFound($"Could not find resourceType with id: {resourceTypeId}");
}
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resourceType.Type, resourceType.SpecificType);
if (resourceTypeDefinition == null)
{
return BadRequest($"No provider for: \"{resourceType.DisplayName}\".");
}
if (!resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAdjustable)
{
return BadRequest($"Cannot adjust quota for {resourceType.DisplayName}.");
}
if (updateProjectQuotaObject.AllocatedGiB < 0)
{
return BadRequest($"Allocated {updateProjectQuotaObject.AllocatedGiB}. Cannot be less than 0 GB.");
}
var projectQuotaForCurrent = _projectQuotaModel.GetWhere(x => x.ProjectId == id && x.ResourceTypeId == resourceTypeId);
var totalReserved = Helpers.CalculateTotalReservedQuota(resourceType, id);
if (Helpers.ConvertCapacityUnits(totalReserved, QuotaUnit.GibiBYTE) > updateProjectQuotaObject.AllocatedGiB)
{
return BadRequest($"Cannot set quota ({updateProjectQuotaObject.AllocatedGiB} GB) below the total ({Helpers.ConvertCapacityUnits(totalReserved, QuotaUnit.GibiBYTE)} GB) that are currently reserved.");
}
if (updateProjectQuotaObject.AllocatedGiB > projectQuotaForCurrent.MaxQuota)
{
return BadRequest($"Cannot set quota to {updateProjectQuotaObject.AllocatedGiB} GB. It would exceed the maximum of {projectQuotaForCurrent.MaxQuota} GB for {resourceType.SpecificType} resources.");
}
var defaultQuotas = _rdfStoreConnector.GetQuotaDefault(user.Id.ToString());
var defaultQuota = defaultQuotas.FirstOrDefault(q => q.ResourceType == resourceType.DisplayName);
if (projectQuotaForCurrent == null)
{
var projectQuota = new ProjectQuota
{
MaxQuota = (defaultQuota?.DefaultMaxQuota) ?? 0,
Quota = (defaultQuota?.DefaultQuota) ?? 0,
ProjectId = project.Id,
ResourceTypeId = resourceType.Id
};
_projectQuotaModel.Insert(projectQuota);
}
else
{
projectQuotaForCurrent.Quota = updateProjectQuotaObject.AllocatedGiB;
_projectQuotaModel.Update(projectQuotaForCurrent);
}
if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
{
LogAnalyticsOwnerProjectQuotaChange(project, user);
}
return NoContent();
}
private void LogAnalyticsOwnerProjectQuotaChange(Database.DataModel.Project project, User user)
{
var resourceTypes = _resourceTypeModel.GetAllWhere(x => x.Enabled == true);
var objects = resourceTypes.Select(x => Helpers.CreateProjectQuotaReturnObject(x, project.Id));
_coscineLogger.AnalyticsLog(
new AnalyticsLogObject
{
Type = "Action",
Operation = "Owner Project Quota Change",
RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(),
ProjectId = project.Id.ToString(),
QuotaSize = objects.Select(x => $"{x.Name}: {x.TotalReserved}/{x.Allocated}").ToList()
});
}
}
}
\ No newline at end of file
......@@ -46,23 +46,22 @@ namespace Coscine.Api.Project.Controllers
}
/// <summary>
/// Lists all users to the given project
/// Lists all users in a project.
/// </summary>
/// <param name="projectId">Id of the project</param>
/// <returns>ProjectRoleObject or Json Object or Statuscode 401</returns>
[Route("[controller]/{projectId}")]
public ActionResult<IEnumerable<ProjectRoleObject>> Index(string projectId)
public ActionResult<IEnumerable<ProjectRoleObject>> Index(Guid projectId)
{
var userModel = new UserModel();
var roleModel = new RoleModel();
var projectModel = new ProjectModel();
Guid.TryParse(projectId, out Guid projectIdGuid);
var user = _authenticator.GetUser();
if (projectModel.HasAccess(user, projectModel.GetById(projectIdGuid), UserRoles.Owner, UserRoles.Member))
if (projectModel.HasAccess(user, projectModel.GetById(projectId), UserRoles.Owner, UserRoles.Member))
{
var users = _projectRoleModel.GetAllWhere((projectRole) =>
(projectRole.ProjectId == projectIdGuid)
(projectRole.ProjectId == projectId)
).Select((projectRole) =>
{
var userInst = projectRole.User;
......@@ -79,7 +78,7 @@ namespace Coscine.Api.Project.Controllers
});
if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
{
LogAnalytics("View Users", user, users, projectId);
LogAnalytics("View Users", user, users, projectId.ToString());
}
return Json(users);
......@@ -91,21 +90,20 @@ namespace Coscine.Api.Project.Controllers
}
/// <summary>
/// Gets all roles for current user and given object
/// Gets all roles for the current user and a project.
/// </summary>
/// <param name="projectId">Id of the project</param>
/// <returns>Json Object or ProjectRoleObject</returns>
[HttpGet("[controller]/project/{projectId}")]
public ActionResult<IEnumerable<ProjectRoleObject>> Get(string projectId)
public ActionResult<IEnumerable<ProjectRoleObject>> Get(Guid projectId)
{
var roleModel = new RoleModel();
Guid.TryParse(projectId, out Guid projectIdGuid);
var user = _authenticator.GetUser();
var userObject = new UserObject(user.Id, user.DisplayName, user.Givenname, user.Surname, user.EmailAddress);
return Json(_projectRoleModel.GetAllWhere((projectRole) =>
(projectRole.UserId == user.Id &&
projectRole.ProjectId == projectIdGuid)
projectRole.ProjectId == projectId)
).Select((projectRole) =>
{
if (projectRole.Role == null)
......@@ -117,13 +115,12 @@ namespace Coscine.Api.Project.Controllers
}
/// <summary>
/// sets a project role for the given project
/// Sets a role for the current user in a project
/// </summary>
/// <returns>Json Object or Statuscode 401</returns>
/// <returns>Json Object or Status Code 401</returns>
[HttpPost("[controller]")]
public ActionResult<ProjectRoleObject> Set()
public ActionResult<ProjectRoleObject> Set([FromBody] ProjectRoleObject projectRoleObject)
{
var projectRoleObject = ObjectFactory<ProjectRoleObject>.DeserializeFromStream(Request.Body);
var projectModel = new ProjectModel();
var project = projectModel.GetById(projectRoleObject.ProjectId);
var roleModel = new RoleModel();
......@@ -153,7 +150,7 @@ namespace Coscine.Api.Project.Controllers
}
/// <summary>
/// deletes project role for the given project
/// Deletes a user from a project
/// </summary>
/// <param name="projectId">Id of the project</param>
/// <param name="userId">Id of the user</param>
......@@ -193,7 +190,7 @@ namespace Coscine.Api.Project.Controllers
}
/// <summary>
/// Deletes user from a project
/// Deletes the current user from a project
/// </summary>
/// <param name="projectId">Id of the project</param>
/// <returns>Json Object or Statuscode 401</returns>
......@@ -246,7 +243,7 @@ namespace Coscine.Api.Project.Controllers
if (users != null)
{
List<string> shownUsers = new List<string>();
var shownUsers = new List<string>();
foreach (var entry in users)
{
shownUsers.Add(entry.User.Id.ToString());
......
......@@ -8,13 +8,8 @@ namespace Coscine.Api.Project.ParameterObjects
public class UpdateProjectQuotaObject
{
/// <summary>
/// Id of the resourceType
/// New quota that will be the current allocated value.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// New Quota value.
/// </summary>
public int Allocated { get; set; }
public int AllocatedGiB { get; set; }
}
}
\ No newline at end of file
......@@ -22,6 +22,6 @@
<PackageReference Include="Coscine.Database" Version="2.*-*" />
<PackageReference Include="Coscine.Logging" Version="2.*-*" />
<PackageReference Include="Coscine.Metadata" Version="2.*-*" />
<PackageReference Include="Coscine.ResourceTypes" Version="*-*" />
<PackageReference Include="Coscine.ResourceTypes" Version="1.*-*" />
</ItemGroup>
</Project>
using System;
namespace Coscine.Api.Project.ReturnObjects
{
/// <summary>
/// Return object for an invitation.
/// </summary>
public class InvitationReturnObject
{
/// <summary>
/// The invitation id.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// When the invite will expire.
/// </summary>
public DateTime Expiration { get; set; }
/// <summary>
/// Email of the invitee.
/// </summary>
public string UserMail { get; set; }
/// <summary>
/// Id of the issuer.
/// </summary>
public Guid Issuer { get; set; }
/// <summary>
/// Id of the project.
/// </summary>
public Guid ProjectId { get; set; }
/// <summary>
/// Id of the target Role.
/// </summary>
public Guid RoleId { get; set; }
}
}
\ No newline at end of file
using System;
namespace Coscine.Api.Project.ReturnObjects
{
/// <summary>
/// Return object containing the maximum project quota.
/// </summary>
public class MaxProjectQuota
{
/// <summary>
/// The resource type id.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Available amount in gb.
/// </summary>
public int Available { get; set; }
}
}
\ No newline at end of file
using System;
namespace Coscine.Api.Project.ReturnObjects
{
/// <summary>
/// Contains information about the quota of a project by resource type.
/// </summary>
public class ProjectQuotaReturnObject
{
/// <summary>
/// Id of the resoure type.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Display name of the resource type.
/// </summary>
public string Name { get; set; }
/// <summary>
/// How much space is used by the resources (in gb).
/// </summary>
public int Used { get; set; }
/// <summary>
/// How much space is availabe to be taken by resources (in gb).
/// </summary>
public int Allocated { get; set; }
/// <summary>
/// Maximum amount of quota (in gb).
/// </summary>
public int Maximum { get; set; }
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment