#182 options api is now complete

This commit is contained in:
Hamid Reza Mohammadi 2021-07-21 10:04:19 +04:30
parent 7015087393
commit 31a4bbd910
15 changed files with 3025 additions and 17 deletions

View File

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Controllers;
using RSecurityBackend.Services;
namespace RMuseum.Controllers
{
/// <summary>
/// options
/// </summary>
[Produces("application/json")]
[Route("api/options")]
public class RGenericOptionsController : RGenericOptionsControllerBase
{
/// <summary>
/// constructor
/// </summary>
/// <param name="optionsService"></param>
public RGenericOptionsController(IRGenericOptionsService optionsService) : base(optionsService)
{
}
}
}

View File

@ -22,7 +22,7 @@ namespace RMuseum.DbContext
{
public RMuseumDbContext(DbContextOptions<RMuseumDbContext> options) : base(options)
{
//Database.Migrate();
Database.Migrate();
}
/// <summary>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace RMuseum.Migrations
{
public partial class Options : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Options",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(450)", nullable: true),
Value = table.Column<string>(type: "nvarchar(max)", nullable: true),
RAppUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Options", x => x.Id);
table.ForeignKey(
name: "FK_Options_AspNetUsers_RAppUserId",
column: x => x.RAppUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Options_RAppUserId_Name",
table: "Options",
columns: new[] { "RAppUserId", "Name" },
unique: true,
filter: "[RAppUserId] IS NOT NULL AND [Name] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Options");
}
}
}

View File

@ -1848,6 +1848,31 @@ namespace RMuseum.Migrations
b.ToTable("VerifyQueueItems");
});
modelBuilder.Entity("RSecurityBackend.Models.Generic.Db.RGenericOption", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<Guid?>("RAppUserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RAppUserId", "Name")
.IsUnique()
.HasFilter("[RAppUserId] IS NOT NULL AND [Name] IS NOT NULL");
b.ToTable("Options");
});
modelBuilder.Entity("RSecurityBackend.Models.Generic.Db.RLongRunningJobStatus", b =>
{
b.Property<Guid>("Id")
@ -2576,6 +2601,15 @@ namespace RMuseum.Migrations
b.Navigation("RAppUser");
});
modelBuilder.Entity("RSecurityBackend.Models.Generic.Db.RGenericOption", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "RAppUser")
.WithMany()
.HasForeignKey("RAppUserId");
b.Navigation("RAppUser");
});
modelBuilder.Entity("RSecurityBackend.Models.Notification.RUserNotification", b =>
{
b.HasOne("RSecurityBackend.Models.Auth.Db.RAppUser", "User")

View File

@ -1468,6 +1468,17 @@
IAppUserService instance
</summary>
</member>
<member name="T:RMuseum.Controllers.RGenericOptionsController">
<summary>
options
</summary>
</member>
<member name="M:RMuseum.Controllers.RGenericOptionsController.#ctor(RSecurityBackend.Services.IRGenericOptionsService)">
<summary>
constructor
</summary>
<param name="optionsService"></param>
</member>
<member name="T:RMuseum.Controllers.RImageController">
<summary>
Generic Image Provider

View File

@ -276,6 +276,9 @@ namespace RMuseum
//long running job service
services.AddTransient<ILongRunningJobProgressService, LongRunningJobProgressServiceEF>();
//generic options service
services.AddTransient<IRGenericOptionsService, RGenericOptionsServiceEF>();
//site banner service
services.AddTransient<ISiteBannersService, SiteBannersService>();

View File

@ -0,0 +1,108 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Services;
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace RSecurityBackend.Controllers
{
/// <summary>
/// options controller
/// </summary>
[Produces("application/json")]
[Route("api/options")]
public abstract class RGenericOptionsControllerBase : Controller
{
/// <summary>
/// get user level option
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet("{name}")]
[Authorize]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> GetValue(string name)
{
Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value);
var res = await _optionsService.GetValueAsync(name, loggedOnUserId);
if (!string.IsNullOrEmpty(res.ExceptionString))
return BadRequest(res.ExceptionString);
return Ok(res.Result);
}
/// <summary>
/// get user level option
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
[HttpPut("{name}")]
[Authorize]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> SetValue(string name, [FromBody] string value)
{
Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value);
var res = await _optionsService.SetAsync(name, value, loggedOnUserId);
if (!string.IsNullOrEmpty(res.ExceptionString))
return BadRequest(res.ExceptionString);
return Ok(res.Result);
}
/// <summary>
/// get global option value
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet("global/{name}")]
[Authorize(Policy = SecurableItem.GlobalOptionsEntityShortName + ":" + SecurableItem.ViewOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> GetGlobalOptionValue(string name)
{
var res = await _optionsService.GetValueAsync(name, null);
if (!string.IsNullOrEmpty(res.ExceptionString))
return BadRequest(res.ExceptionString);
return Ok(res.Result);
}
/// <summary>
/// set global option value
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
[HttpPut("global/{name}")]
[Authorize(Policy = SecurableItem.GlobalOptionsEntityShortName + ":" + SecurableItem.ModifyOperationShortName)]
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))]
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
public async Task<IActionResult> SetGlobalOptionValue(string name, [FromBody] string value)
{
var res = await _optionsService.SetAsync(name, value, null);
if (!string.IsNullOrEmpty(res.ExceptionString))
return BadRequest(res.ExceptionString);
return Ok(res.Result);
}
/// <summary>
/// constructor
/// </summary>
/// <param name="optionsService">
/// </param>
public RGenericOptionsControllerBase(IRGenericOptionsService optionsService)
{
_optionsService = optionsService;
}
/// <summary>
/// Options Service
/// </summary>
protected readonly IRGenericOptionsService _optionsService;
}
}

View File

@ -61,7 +61,7 @@ namespace RSecurityBackend.Controllers
}
/// <summary>
/// Artifact Service
/// Jobs Service
/// </summary>
protected readonly ILongRunningJobProgressService _jobService;
}

View File

@ -64,8 +64,9 @@ namespace RSecurityBackend.DbContext
.IsUnique();
builder.Entity<RGenericOption>()
.HasIndex(o => o.Name)
.HasIndex(o => new { o.RAppUserId, o.Name })
.IsUnique();
}
/// <summary>

View File

@ -19,6 +19,11 @@
/// </summary>
public const string AuditLogEntityShortName = "audit";
/// <summary>
/// global options
/// </summary>
public const string GlobalOptionsEntityShortName = "option";
/// <summary>
/// view
@ -105,6 +110,16 @@
{
new SecurableItemOperation(ViewOperationShortName, "مشاهده", false),
}
},
new SecurableItem()
{
ShortName = GlobalOptionsEntityShortName,
Description = "تنظیمات عمومی",
Operations = new SecurableItemOperation[]
{
new SecurableItemOperation(ViewOperationShortName, "مشاهده", false),
new SecurableItemOperation(ModifyOperationShortName, "اصلاح", false),
}
}
};
}

View File

@ -1,4 +1,7 @@
namespace RSecurityBackend.Models.Generic.Db
using RSecurityBackend.Models.Auth.Db;
using System;
namespace RSecurityBackend.Models.Generic.Db
{
/// <summary>
/// generic option
@ -19,5 +22,15 @@
///value
/// </summary>
public string Value { get; set; }
/// <summary>
/// user id
/// </summary>
public Guid? RAppUserId { get; set; }
/// <summary>
/// user
/// </summary>
public virtual RAppUser RAppUser { get; set; }
}
}

View File

@ -394,6 +394,53 @@
</summary>
<param name="notificationService"></param>
</member>
<member name="T:RSecurityBackend.Controllers.RGenericOptionsControllerBase">
<summary>
options controller
</summary>
</member>
<member name="M:RSecurityBackend.Controllers.RGenericOptionsControllerBase.GetValue(System.String)">
<summary>
get user level option
</summary>
<param name="name"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Controllers.RGenericOptionsControllerBase.SetValue(System.String,System.String)">
<summary>
get user level option
</summary>
<param name="name"></param>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Controllers.RGenericOptionsControllerBase.GetGlobalOptionValue(System.String)">
<summary>
get global option value
</summary>
<param name="name"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Controllers.RGenericOptionsControllerBase.SetGlobalOptionValue(System.String,System.String)">
<summary>
set global option value
</summary>
<param name="name"></param>
<param name="value"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Controllers.RGenericOptionsControllerBase.#ctor(RSecurityBackend.Services.IRGenericOptionsService)">
<summary>
constructor
</summary>
<param name="optionsService">
</param>
</member>
<member name="F:RSecurityBackend.Controllers.RGenericOptionsControllerBase._optionsService">
<summary>
Options Service
</summary>
</member>
<member name="T:RSecurityBackend.Controllers.RImageControllerBase">
<summary>
Generic Image Provider
@ -457,7 +504,7 @@
</member>
<member name="F:RSecurityBackend.Controllers.RLongRunningJobsControllerBase._jobService">
<summary>
Artifact Service
Jobs Service
</summary>
</member>
<member name="T:RSecurityBackend.Controllers.RoleControllerBase">
@ -1003,6 +1050,11 @@
audit
</summary>
</member>
<member name="F:RSecurityBackend.Models.Auth.Memory.SecurableItem.GlobalOptionsEntityShortName">
<summary>
global options
</summary>
</member>
<member name="F:RSecurityBackend.Models.Auth.Memory.SecurableItem.ViewOperationShortName">
<summary>
view
@ -1769,6 +1821,16 @@
value
</summary>
</member>
<member name="P:RSecurityBackend.Models.Generic.Db.RGenericOption.RAppUserId">
<summary>
user id
</summary>
</member>
<member name="P:RSecurityBackend.Models.Generic.Db.RGenericOption.RAppUser">
<summary>
user
</summary>
</member>
<member name="T:RSecurityBackend.Models.Generic.Db.RLongRunningJobStatus">
<summary>
Long Running Job Status
@ -3300,19 +3362,21 @@
generic options service
</summary>
</member>
<member name="M:RSecurityBackend.Services.Implementation.RGenericOptionsServiceEF.SetAsync(System.String,System.String)">
<member name="M:RSecurityBackend.Services.Implementation.RGenericOptionsServiceEF.SetAsync(System.String,System.String,System.Nullable{System.Guid})">
<summary>
modify an option or add a new one if an option with the requested name does not exist
</summary>
<param name="optionName"></param>
<param name="optionValue"></param>
<param name="userId"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Services.Implementation.RGenericOptionsServiceEF.GetValueAsync(System.String)">
<member name="M:RSecurityBackend.Services.Implementation.RGenericOptionsServiceEF.GetValueAsync(System.String,System.Nullable{System.Guid})">
<summary>
get option value
</summary>
<param name="optionName"></param>
<param name="userId"></param>
<returns></returns>
</member>
<member name="F:RSecurityBackend.Services.Implementation.RGenericOptionsServiceEF._context">
@ -3533,19 +3597,21 @@
generic options service
</summary>
</member>
<member name="M:RSecurityBackend.Services.IRGenericOptionsService.SetAsync(System.String,System.String)">
<member name="M:RSecurityBackend.Services.IRGenericOptionsService.SetAsync(System.String,System.String,System.Nullable{System.Guid})">
<summary>
modify an option or add a new one if an option with the requested name does not exist
</summary>
<param name="optionName"></param>
<param name="optionValue"></param>
<param name="userId"></param>
<returns></returns>
</member>
<member name="M:RSecurityBackend.Services.IRGenericOptionsService.GetValueAsync(System.String)">
<member name="M:RSecurityBackend.Services.IRGenericOptionsService.GetValueAsync(System.String,System.Nullable{System.Guid})">
<summary>
get option value
</summary>
<param name="optionName"></param>
<param name="userId"></param>
<returns></returns>
</member>
<member name="T:RSecurityBackend.Services.IRNotificationService">

View File

@ -1,5 +1,6 @@
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Models.Generic.Db;
using System;
using System.Threading.Tasks;
namespace RSecurityBackend.Services
@ -14,14 +15,16 @@ namespace RSecurityBackend.Services
/// </summary>
/// <param name="optionName"></param>
/// <param name="optionValue"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<RGenericOption>> SetAsync(string optionName, string optionValue);
Task<RServiceResult<RGenericOption>> SetAsync(string optionName, string optionValue, Guid? userId);
/// <summary>
/// get option value
/// </summary>
/// <param name="optionName"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<RServiceResult<string>> GetValueAsync(string optionName);
Task<RServiceResult<string>> GetValueAsync(string optionName, Guid? userId);
}
}

View File

@ -19,10 +19,11 @@ namespace RSecurityBackend.Services.Implementation
/// </summary>
/// <param name="optionName"></param>
/// <param name="optionValue"></param>
/// <param name="userId"></param>
/// <returns></returns>
public async Task<RServiceResult<RGenericOption>> SetAsync(string optionName, string optionValue)
public async Task<RServiceResult<RGenericOption>> SetAsync(string optionName, string optionValue, Guid? userId)
{
var existing = await _context.Options.Where(o => o.Name == optionName).SingleOrDefaultAsync();
var existing = await _context.Options.Where(o => o.Name == optionName && o.RAppUserId == userId).SingleOrDefaultAsync();
if (existing != null)
{
existing.Value = optionValue;
@ -35,7 +36,8 @@ namespace RSecurityBackend.Services.Implementation
var option = new RGenericOption()
{
Name = optionName,
Value = optionValue
Value = optionValue,
RAppUserId = userId
};
_context.Options.Add(option);
@ -48,12 +50,13 @@ namespace RSecurityBackend.Services.Implementation
/// get option value
/// </summary>
/// <param name="optionName"></param>
/// <param name="userId"></param>
/// <returns></returns>
public async Task<RServiceResult<string>> GetValueAsync(string optionName)
public async Task<RServiceResult<string>> GetValueAsync(string optionName, Guid? userId)
{
var option = await _context.Options.AsNoTracking().Where(o => o.Name == optionName).SingleOrDefaultAsync();
var option = await _context.Options.AsNoTracking().Where(o => o.Name == optionName && o.RAppUserId == userId).SingleOrDefaultAsync();
if (option == null)
return new RServiceResult<string>(null, $"option '{optionName}' not found!");
return new RServiceResult<string>(""); //empty value
return new RServiceResult<string>(option.Value);
}