Files
raven/server/AyaNova/Controllers/WidgetController.cs
2020-01-17 00:44:54 +00:00

452 lines
18 KiB
C#

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
//DOCUMENTATING THE API
//https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/recommended-tags-for-documentation-comments
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore#include-descriptions-from-xml-comments
/// <summary>
/// Sample controller class used during development for testing purposes
/// </summary>
[ApiController]
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class WidgetController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<WidgetController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public WidgetController(AyContext dbcontext, ILogger<WidgetController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get full widget object
///
/// Required roles:
/// BizAdminFull, InventoryFull, BizAdminLimited, InventoryLimited
/// </summary>
/// <param name="id"></param>
/// <returns>A single widget</returns>
[HttpGet("{id}")]
public async Task<IActionResult> GetWidget([FromRoute] long id)
{
if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
//NOTE: This is the first check and often the only check but in some cases with some objects this will also need to check biz object rules
if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var o = await biz.GetAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// NOTE: HERE would be the second check of biz rules before returning the object
// in cases where there is also a business rule to affect retrieval on top of basic rights
return Ok(ApiOkResponse.Response(o, !Authorized.HasModifyRole(HttpContext.Items, biz.BizType)));
}
/// <summary>
/// Get list for selection / viewing
///
/// Required roles: Any (some roles might have restrictions on exact fields that are returned)
///
/// </summary>
/// <param name="pagingOptions">Paging, filtering and sorting options</param>
/// <returns>Collection with paging data</returns>
[HttpGet("List", Name = nameof(List))]
public ActionResult List([FromQuery] ListOptions pagingOptions)
{
if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
ApiPagedResponse pr = biz.GetList(Url, nameof(List), pagingOptions).Result;
return Ok(new ApiOkWithPagingResponse(pr));
// string ret= biz.GetList(Url, nameof(List), pagingOptions).Result;
// return Ok(ret);
}
/// <summary>
/// TEST list with relationships
///
/// Required roles: Any (some roles might have restrictions on exact fields that are returned)
///
/// </summary>
/// <param name="pagingOptions">Paging, filtering and sorting options</param>
/// <returns>Collection with paging data</returns>
[HttpGet("TestGetWidgetUserEmailList", Name = nameof(TestGetWidgetUserEmailList))]
public ActionResult TestGetWidgetUserEmailList([FromQuery] ListOptions pagingOptions)
{
if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
ApiPagedResponse pr = biz.TestGetWidgetUserEmailList(Url, nameof(List), pagingOptions).Result;
return Ok(new ApiOkWithPagingResponse(pr));
// string ret= biz.GetList(Url, nameof(List), pagingOptions).Result;
// return Ok(ret);
}
// /// <summary>
// /// Get paged list of widgets
// ///
// /// Required roles:
// /// BizAdminFull, InventoryFull, BizAdminLimited, InventoryLimited
// /// </summary>
// /// <returns>Paged collection of widgets with paging data</returns>
// [HttpGet("ListWidgets", Name = nameof(ListWidgets))]//We MUST have a "Name" defined or we can't get the link for the pagination, non paged urls don't need a name
// public async Task<IActionResult> ListWidgets([FromQuery] ListOptions pagingOptions)
// {
// if (serverState.IsClosed)
// return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
// //Instantiate the business object handler
// WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
// if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
// return StatusCode(403, new ApiNotAuthorizedResponse());
// if (!ModelState.IsValid)
// return BadRequest(new ApiErrorResponse(ModelState));
// ApiPagedResponse<Widget> pr = await biz.GetManyAsync(Url, nameof(ListWidgets), pagingOptions);
// return Ok(new ApiOkWithPagingResponse<Widget>(pr));
// }
// /// <summary>
// /// Get pick list
// ///
// /// Required roles: Any
// ///
// /// </summary>
// /// <param name="pagingOptions">Paging, filtering and sorting options</param>
// /// <returns>Paged id/name collection with paging data</returns>
// [HttpGet("PickList", Name = nameof(WidgetPickList))]
// public ActionResult WidgetPickList([FromQuery] ListOptions pagingOptions)
// {
// if (serverState.IsClosed)
// return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
// if (!ModelState.IsValid)
// return BadRequest(new ApiErrorResponse(ModelState));
// //Instantiate the business object handler
// WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
// ApiPagedResponse<NameIdItem> pr = biz.GetPickList(Url, nameof(WidgetPickList), pagingOptions);
// return Ok(new ApiOkWithPagingResponse<NameIdItem>(pr));
// }
/// <summary>
/// Put (update) widget
///
/// Required roles:
/// BizAdminFull, InventoryFull
///
/// </summary>
/// <param name="id"></param>
/// <param name="inObj"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<IActionResult> PutWidget([FromRoute] long id, [FromBody] Widget inObj)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
try
{
if (!biz.Put(o, inObj))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true));
}
/// <summary>
/// Patch (update) widget
///
/// Required roles:
/// BizAdminFull, InventoryFull
///
/// </summary>
/// <param name="id"></param>
/// <param name="concurrencyToken"></param>
/// <param name="objectPatch"></param>
/// <returns></returns>
[HttpPatch("{id}/{concurrencyToken}")]
public async Task<IActionResult> PatchWidget([FromRoute] long id, [FromRoute] uint concurrencyToken, [FromBody]JsonPatchDocument<Widget> objectPatch)
{
//https://dotnetcoretutorials.com/2017/11/29/json-patch-asp-net-core/
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
try
{
//patch and validate
if (!biz.Patch(o, objectPatch, concurrencyToken))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true));
}
/// <summary>
/// Post widget
///
/// Required roles:
/// BizAdminFull, InventoryFull
/// </summary>
/// <param name="inObj"></param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostWidget([FromBody] Widget inObj, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
//If a user has change roles
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Create and validate
Widget o = await biz.CreateAsync(inObj);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction(nameof(WidgetController.GetWidget), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Duplicate widget
///
/// Required roles:
/// BizAdminFull, InventoryFull
/// </summary>
/// <param name="id">Create a duplicate of this items id</param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost("duplicate/{id}")]
public async Task<IActionResult> DuplicateWidget([FromRoute] long id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
//If a user has change roles
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
var oSrc = await biz.GetNoLogAsync(id);
if (oSrc == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
//Create and validate
Widget o = await biz.DuplicateAsync(oSrc);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return CreatedAtAction(nameof(WidgetController.GetWidget), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Delete widget
///
/// Required roles:
/// BizAdminFull, InventoryFull
///
///
/// </summary>
/// <param name="id"></param>
/// <returns>Ok</returns>
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteWidget([FromRoute] long id)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
var o = await biz.GetNoLogAsync(id);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!biz.Delete(o))
return BadRequest(new ApiErrorResponse(biz.Errors));
return NoContent();
}
/// <summary>
/// Get route that triggers exception for testing
/// </summary>
/// <returns>Nothing, triggers exception</returns>
[HttpGet("exception")]
public ActionResult GetException()
{
//log.LogInformation("Widget::getexception-> Test exception and log from controller test");
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
throw new System.NotSupportedException("Test exception from widget controller");
}
/// <summary>
/// Get route that triggers an alternate type of exception for testing
/// </summary>
/// <returns>Nothing, triggers exception</returns>
[HttpGet("altexception")]
public ActionResult GetAltException()
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
throw new System.ArgumentException("Test exception (ALT) from widget controller");
}
/// <summary>
/// Get route that submits a simulated long running operation job for testing
/// </summary>
/// <returns>Nothing</returns>
[HttpGet("TestWidgetJob")]
public ActionResult TestWidgetJob()
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
if (!Authorized.HasModifyRole(HttpContext.Items, AyaType.JobOperations))
return StatusCode(403, new ApiNotAuthorizedResponse());
//Create the job here
OpsJob j = new OpsJob();
j.Name = "TestWidgetJob";
j.JobType = JobType.TestWidgetJob;
JobsBiz.AddJob(j, ct);
return Accepted(new { JobId = j.GId });//202 accepted
}
//------------
}//eoc
}//eons