This commit is contained in:
2020-05-13 21:18:18 +00:00
parent 53d1456e6f
commit a45abb7467
4 changed files with 284 additions and 160 deletions

View File

@@ -11,7 +11,6 @@ using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
[ApiController]
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
@@ -23,7 +22,6 @@ namespace AyaNova.Api.Controllers
private readonly ILogger<CustomerController> log;
private readonly ApiServerState serverState;
/// <summary>
/// ctor
/// </summary>
@@ -84,7 +82,6 @@ namespace AyaNova.Api.Controllers
return CreatedAtAction(nameof(CustomerController.GetCustomer), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
}
/// <summary>
/// Get Customer
/// </summary>
@@ -105,8 +102,6 @@ namespace AyaNova.Api.Controllers
return Ok(ApiOkResponse.Response(o, !Authorized.HasModifyRole(HttpContext.Items, biz.BizType)));
}
/// <summary>
/// Put (update) Customer
/// </summary>

View File

@@ -46,8 +46,38 @@ namespace AyaNova.Api.Controllers
serverState = apiServerState;
}
// /// <summary>
// /// Create widget
// /// </summary>
// /// <param name="newObject"></param>
// /// <param name="apiVersion">From route path</param>
// /// <returns></returns>
// [HttpPost]
// public async Task<IActionResult> PostWidget([FromBody] Widget newObject, ApiVersion apiVersion)
// {
// if (!serverState.IsOpen)
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, 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(newObject);
// 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>
/// Create widget
/// Create Widget
/// </summary>
/// <param name="newObject"></param>
/// <param name="apiVersion">From route path</param>
@@ -57,27 +87,79 @@ namespace AyaNova.Api.Controllers
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, 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(newObject);
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
// /// </summary>
// /// <param name="id">Create a duplicate of this items id</param>
// /// <param name="apiVersion">From route path</param>
// /// <returns></returns>
// [HttpPost("duplicate/{id}")]
// public async Task<IActionResult> DuplicateWidget([FromRoute] long id, ApiVersion apiVersion)
// {
// if (!serverState.IsOpen)
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, 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.GetAsync(id, false);
// 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>
/// Duplicate Widget
/// (Wiki and Attachments are not duplicated)
/// </summary>
/// <param name="id">Source object id</param>
/// <param name="apiVersion">From route path</param>
/// <returns>Widget</returns>
[HttpPost("duplicate/{id}")]
public async Task<IActionResult> DuplicateWidget([FromRoute] long id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
WidgetBiz biz = WidgetBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
Widget o = await biz.DuplicateAsync(id);
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>
/// Get full widget object
/// </summary>
@@ -154,40 +236,7 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Duplicate widget
/// </summary>
/// <param name="id">Create a duplicate of this items id</param>
/// <param name="apiVersion">From route path</param>
/// <returns></returns>
[HttpPost("duplicate/{id}")]
public async Task<IActionResult> DuplicateWidget([FromRoute] long id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, 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.GetAsync(id, false);
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));
}

View File

@@ -6,10 +6,8 @@ using AyaNova.Models;
namespace AyaNova.Biz
{
internal class CustomerBiz : BizObject, ISearchAbleObject
{
internal CustomerBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
@@ -27,8 +25,6 @@ namespace AyaNova.Biz
return new CustomerBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
@@ -92,7 +88,6 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//GET
//
//
internal async Task<Customer> GetAsync(long id, bool logTheGetEvent = true)
{
var ret = await ct.Customer.SingleOrDefaultAsync(m => m.Id == id);
@@ -138,7 +133,6 @@ namespace AyaNova.Biz
return dbObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//

View File

@@ -1,6 +1,5 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using EnumsNET;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
@@ -8,10 +7,8 @@ using AyaNova.Models;
namespace AyaNova.Biz
{
internal class WidgetBiz : BizObject, IJobObject, ISearchAbleObject
{
internal WidgetBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
@@ -23,15 +20,12 @@ namespace AyaNova.Biz
internal static WidgetBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
if (httpContext != null)
return new WidgetBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else//when called internally for internal ops there will be no context so need to set default values for that
else
return new WidgetBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
@@ -39,28 +33,9 @@ namespace AyaNova.Biz
return await ct.Widget.AnyAsync(e => e.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
///
///
internal async Task<Widget> GetAsync(long fetchId, bool logTheGetEvent = true)
{
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
var ret = await ct.Widget.SingleOrDefaultAsync(m => m.Id == fetchId);
if (logTheGetEvent && ret != null)
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//Called from route and also seeder
//
internal async Task<Widget> CreateAsync(Widget newObject)
{
await ValidateAsync(newObject, null);
@@ -80,84 +55,215 @@ namespace AyaNova.Biz
}
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //DUPLICATE
// //
// internal async Task<Widget> DuplicateAsync(Widget dbObj)
// {
// Widget outObj = new Widget();
// CopyObject.Copy(dbObj, outObj, "Wiki");
// // outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
// //generate unique name
// string newUniqueName = string.Empty;
// bool NotUnique = true;
// long l = 1;
// do
// {
// newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObj.Name, l++, 255);
// NotUnique = await ct.Widget.AnyAsync(m => m.Name == newUniqueName);
// } while (NotUnique);
// outObj.Name = newUniqueName;
// outObj.Id = 0;
// outObj.Concurrency = 0;
// //Test get serial id visible id number from generator
// outObj.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
// await ct.Widget.AddAsync(outObj);
// await ct.SaveChangesAsync();
// //Handle child and associated items:
// await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
// await SearchIndexAsync(outObj, true);
// await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
// return outObj;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//DUPLICATE
//
internal async Task<Widget> DuplicateAsync(Widget dbObj)
internal async Task<Widget> DuplicateAsync(long id)
{
Widget outObj = new Widget();
CopyObject.Copy(dbObj, outObj, "Wiki");
// outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
//generate unique name
Widget dbObject = await GetAsync(id, false);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND, "id");
return null;
}
Widget newObject = new Widget();
CopyObject.Copy(dbObject, newObject, "Wiki");
string newUniqueName = string.Empty;
bool NotUnique = true;
long l = 1;
do
{
newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObj.Name, l++, 255);
newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObject.Name, l++, 255);
NotUnique = await ct.Widget.AnyAsync(m => m.Name == newUniqueName);
} while (NotUnique);
outObj.Name = newUniqueName;
outObj.Id = 0;
outObj.Concurrency = 0;
//Test get serial id visible id number from generator
outObj.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
await ct.Widget.AddAsync(outObj);
newObject.Name = newUniqueName;
newObject.Serial = ServerBootConfig.WIDGET_SERIAL.GetNext();
newObject.Id = 0;
newObject.Concurrency = 0;
await ct.Widget.AddAsync(newObject);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(outObj, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
return outObj;
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(newObject, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null);
return newObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GET
//
internal async Task<Widget> GetAsync(long id, bool logTheGetEvent = true)
{
var ret = await ct.Widget.SingleOrDefaultAsync(m => m.Id == id);
if (logTheGetEvent && ret != null)
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, AyaEvent.Retrieved), ct);
return ret;
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //UPDATE
// //
// //put
// internal async Task<bool> PutAsync(Widget dbObj, Widget inObj)
// {
// //make a snapshot of the original for validation but update the original to preserve workflow
// Widget SnapshotOfOriginalDBObj = new Widget();
// CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
// //Replace the db object with the PUT object
// CopyObject.Copy(inObj, dbObj, "Id,Serial");
// dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
// dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
// //Set "original" value of concurrency token to input token
// //this will allow EF to check it out
// ct.Entry(dbObj).OriginalValues["Concurrency"] = inObj.Concurrency;
// await ValidateAsync(dbObj, SnapshotOfOriginalDBObj);
// if (HasErrors)
// return false;
// //Log event and save context
// await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
// await SearchIndexAsync(dbObj, false);
// await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObj.Tags, SnapshotOfOriginalDBObj.Tags);
// return true;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
//put
internal async Task<bool> PutAsync(Widget dbObj, Widget inObj)
internal async Task<Widget> PutAsync(Widget putObject)
{
//make a snapshot of the original for validation but update the original to preserve workflow
Widget dbObject = await ct.Widget.SingleOrDefaultAsync(m => m.Id == putObject.Id);
if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND, "id");
return null;
}
Widget SnapshotOfOriginalDBObj = new Widget();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
//Replace the db object with the PUT object
CopyObject.Copy(inObj, dbObj, "Id,Serial");
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
ct.Entry(dbObj).OriginalValues["Concurrency"] = inObj.Concurrency;
await ValidateAsync(dbObj, SnapshotOfOriginalDBObj);
if (HasErrors)
return false;
//Log event and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObj, false);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObj.Tags, SnapshotOfOriginalDBObj.Tags);
return true;
CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj);
CopyObject.Copy(putObject, dbObject, "Id,Serial");
dbObject.Tags = TagUtil.NormalizeTags(dbObject.Tags);
dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields);
ct.Entry(dbObject).OriginalValues["Concurrency"] = putObject.Concurrency;
await ValidateAsync(dbObject, SnapshotOfOriginalDBObj);
if (HasErrors) return null;
try
{
await ct.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await ExistsAsync(putObject.Id))
AddError(ApiErrorCode.NOT_FOUND);
else
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObject, false);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags);
return dbObject;
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //DELETE
// //
// internal async Task<bool> DeleteAsync(Widget dbObj)
// {
// //Determine if the object can be deleted, do the deletion tentatively
// //Probably also in here deal with tags and associated search text etc
// //NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
// if (HasErrors)
// return false;
// ct.Widget.Remove(dbObj);
// await ct.SaveChangesAsync();
// //Log event
// await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
// await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
// await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
// return true;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(long id)
{
using (var transaction = await ct.Database.BeginTransactionAsync())
{
try
{
Widget dbObject = await ct.Widget.SingleOrDefaultAsync(m => m.Id == id);
ValidateCanDelete(dbObject);
if (HasErrors)
return false;
if (HasErrors)
return false;
ct.Widget.Remove(dbObject);
await ct.SaveChangesAsync();
//Log event
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType);
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
//all good do the commit
await transaction.CommitAsync();
}
catch
{
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
throw;
}
return true;
}
}
private async Task SearchIndexAsync(Widget obj, bool isNew)
{
@@ -181,26 +287,6 @@ namespace AyaNova.Biz
return SearchParams;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(Widget dbObj)
{
//Determine if the object can be deleted, do the deletion tentatively
//Probably also in here deal with tags and associated search text etc
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.Widget.Remove(dbObj);
await ct.SaveChangesAsync();
//Log event
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
return true;
}