This commit is contained in:
2020-05-07 18:44:56 +00:00
parent f2c6002ac3
commit ea3e5fee2f
4 changed files with 98 additions and 51 deletions

View File

@@ -107,7 +107,7 @@ public class BuildingController : Controller {
If the client updates part of the wo graph, only that exact record really needs dependency checking. If the client updates part of the wo graph, only that exact record really needs dependency checking.
There *is* however business rules that might take hold but that's all at the server and not related to concurrency directly There *is* however business rules that might take hold but that's all at the server and not related to concurrency directly
For example, on any change to the wo graph the server has to see if the wo is still editable and hasn't been locked or user's rights changed For example, on any change to the wo graph the server has to see if the wo is still editable and hasn't been locked or user's rights changed
But that's not strictly concurrency related in teh sense that another user change the *same* record being updated But that's not strictly concurrency related in the sense that another user change the *same* record being updated
So, for v8 as long as it can handle a portional update to part of the graph and uses the concurrency of that exact record to check then it sidesteps a lot of multi-user scenarios So, for v8 as long as it can handle a portional update to part of the graph and uses the concurrency of that exact record to check then it sidesteps a lot of multi-user scenarios
This was only an issue in v7 due to it using only the wo header itself as the source of concurrency checking which would *always* involve the whole graph in any change anywhere This was only an issue in v7 due to it using only the wo header itself as the source of concurrency checking which would *always* involve the whole graph in any change anywhere

View File

@@ -237,16 +237,16 @@ namespace AyaNova.Api.Controllers
//but maybe handy? Like do I need delete on entire woitems collection? //but maybe handy? Like do I need delete on entire woitems collection?
//WorkOrder/{woid}/WorkorderItems <- all workorderitems, post to add new, put to update all as a collection //WorkOrder/{woid}/WorkorderItems <- all workorderitems, post to add new, put to update all as a collection
//WorkOrder/{woid}/WorkOrderItems/{woitemid} <- CRUD single woitemid //WorkOrder/{woid}/WorkOrderItems/{woitemid} <- CRUD single woitemid
//https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design#define-operations-in-terms-of-http-methods //https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design#define-operations-in-terms-of-http-methods
/* /*
Resource POST GET PUT DELETE Resource POST GET PUT DELETE
/customers Create a new customer Retrieve all customers Bulk update of customers Remove all customers /customers Create a new customer Retrieve all customers Bulk update of customers Remove all customers
/customers/1 Error Retrieve the details for customer 1 Update the details of customer 1 if it exists Remove customer 1 /customers/1 Error Retrieve the details for customer 1 Update the details of customer 1 if it exists Remove customer 1
/customers/1/orders Create a new order for customer 1 Retrieve all orders for customer 1 Bulk update of orders for customer 1 Remove all orders for customer 1 /customers/1/orders Create a new order for customer 1 Retrieve all orders for customer 1 Bulk update of orders for customer 1 Remove all orders for customer 1
*/ */
//So Post into a collection means create one item in that collection, never a whole collection being created at once //So Post into a collection means create one item in that collection, never a whole collection being created at once
//GET PUT and DELETE on a collecdtion always mean the entire collection, POST is the outlier here //GET PUT and DELETE on a collecdtion always mean the entire collection, POST is the outlier here
// /// <summary> // /// <summary>
@@ -363,19 +363,53 @@ Resource POST GET PUT DELETE
//TODO: I can see adding or updating a collection of workorderitems but not deleting an entire collection
/// <summary>
/// Create WorkOrderItem
/// </summary>
/// <param name="newObject"></param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost("/items")]
public async Task<IActionResult> PostWorkOrderItem([FromBody] WorkOrderItem newObject, ApiVersion apiVersion)
{
//NOTE: we don't need the workorder id in the route because the workorder item must contain the workorder id anyway
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
//Instantiate the business object handler
WorkOrderBiz biz = WorkOrderBiz.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
// WorkOrderItem o = await biz.CreateAsync(newObject);
// if (o == null)
// return BadRequest(new ApiErrorResponse(biz.Errors));
// else
// return CreatedAtAction(nameof(WorkOrderController.GetWorkOrder), new { id = o.Id, version = apiVersion.ToString() }, new ApiCreatedResponse(o));
return NoContent();
}
/// <summary> /// <summary>
/// Delete WorkOrderItem /// Delete WorkOrderItem
/// </summary> /// </summary>
/// <param name="workOrderId"></param>
/// <param name="workOrderItemId"></param> /// <param name="workOrderItemId"></param>
/// <returns>Ok-no content</returns> /// <returns>Ok-no content</returns>
[HttpDelete("{WorkOrderId}/items/{WorkOrderItemId}")] [HttpDelete("/items/{WorkOrderItemId}")]
public async Task<IActionResult> DeleteWorkOrderItem([FromRoute] long workOrderId, [FromRoute] long workOrderItemId) public async Task<IActionResult> DeleteWorkOrderItem([FromRoute] long workOrderItemId)
{ {
//NOTE: we don't need the workorder id in the route because the workorder item must contain the workorder id anyway
//WorkOrder/{woid}/WorkorderItems <- all workorderitems, post to add new, put to update all as a collection //WorkOrder/{woid}/WorkorderItems <- all workorderitems, post to add new, put to update all as a collection
//WorkOrder/{WorkOrderId}/WorkorderItems //WorkOrder/{WorkOrderId}/WorkorderItems
if (!serverState.IsOpen) if (!serverState.IsOpen)
@@ -390,12 +424,17 @@ Resource POST GET PUT DELETE
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse()); return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.GetAsync(workOrderId, false); //*******************************************************************************
if (o == null) //NOTE: I'm thinking there should be no db access in controller
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); //let the biz object return not found if necessary
//Make sure the item exists first before getting into it //*******************************************************************************
if (!o.WorkorderItems.Exists(m => m.Id == workOrderItemId))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); // var o = await biz.GetAsync(workOrderId, false);
// if (o == null)
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// //Make sure the item exists first before getting into it
// if (!o.WorkorderItems.Exists(m => m.Id == workOrderItemId))
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
@@ -416,13 +455,11 @@ Resource POST GET PUT DELETE
/// <summary> /// <summary>
/// Delete WorkOrderItemLabor /// Delete WorkOrderItemLabor
/// </summary> /// </summary>
/// <param name="workOrderId"></param>
/// <param name="workOrderItemId"></param>
/// <param name="workOrderItemLaborId"></param> /// <param name="workOrderItemLaborId"></param>
/// <returns>Ok-no content</returns> /// <returns>Ok-no content</returns>
[HttpDelete("{WorkOrderId}/items/{WorkOrderItemId}/labors/{WorkOrderItemLaborId}")] [HttpDelete("/items/labors/{WorkOrderItemLaborId}")]
public async Task<IActionResult> DeleteWorkOrderItemLabor([FromRoute] long workOrderId, [FromRoute] long workOrderItemId, [FromRoute] long workOrderItemLaborId) public async Task<IActionResult> DeleteWorkOrderItemLabor([FromRoute] long workOrderItemLaborId)
{ {
if (!serverState.IsOpen) if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -436,19 +473,25 @@ Resource POST GET PUT DELETE
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse()); return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.GetAsync(workOrderId, false); //*******************************************************************************
if (o == null) //NOTE: I'm thinking there should be no db access in controller
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); //let the biz object return not found if necessary
//*******************************************************************************
//Get WorkorderItem
var woitem = o.WorkorderItems.FirstOrDefault(m => m.Id == workOrderItemId);
if (woitem == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
//Get WorkOrderItemLabor // var o = await biz.GetAsync(workOrderId, false);
var woitemlabor = woitem.WorkorderItemLabors.FirstOrDefault(m => m.Id == workOrderItemLaborId); // if (o == null)
if (woitem == null) // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// //Get WorkorderItem
// var woitem = o.WorkorderItems.FirstOrDefault(m => m.Id == workOrderItemId);
// if (woitem == null)
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// //Get WorkOrderItemLabor
// var woitemlabor = woitem.WorkorderItemLabors.FirstOrDefault(m => m.Id == workOrderItemLaborId);
// if (woitem == null)
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
@@ -466,13 +509,11 @@ Resource POST GET PUT DELETE
/// <summary> /// <summary>
/// Delete WorkOrderItemPart /// Delete WorkOrderItemPart
/// </summary> /// </summary>
/// <param name="workOrderId"></param>
/// <param name="workOrderItemId"></param>
/// <param name="workOrderItemPartId"></param> /// <param name="workOrderItemPartId"></param>
/// <returns>Ok-no content</returns> /// <returns>Ok-no content</returns>
[HttpDelete("{WorkOrderId}/items/{WorkOrderItemId}/parts/{WorkOrderItemPartId}")] [HttpDelete("/items/parts/{WorkOrderItemPartId}")]
public async Task<IActionResult> DeleteWorkOrderItemPart([FromRoute] long workOrderId, [FromRoute] long workOrderItemId, [FromRoute] long workOrderItemPartId) public async Task<IActionResult> DeleteWorkOrderItemPart([FromRoute] long workOrderItemPartId)
{ {
if (!serverState.IsOpen) if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -486,12 +527,18 @@ Resource POST GET PUT DELETE
if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse()); return StatusCode(403, new ApiNotAuthorizedResponse());
var o = await biz.GetAsync(workOrderId, false);
if (o == null) //*******************************************************************************
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); //NOTE: I'm thinking there should be no db access in controller
//Make sure the item exists first before getting into it //let the biz object return not found if necessary
if (!o.WorkorderItems.Exists(m => m.Id == workOrderItemId)) //*******************************************************************************
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// var o = await biz.GetAsync(workOrderId, false);
// if (o == null)
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
// //Make sure the item exists first before getting into it
// if (!o.WorkorderItems.Exists(m => m.Id == workOrderItemId))
// return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));

View File

@@ -96,7 +96,7 @@ namespace AyaNova.Biz
//AyaNova.Biz.BizObjectNameFetcherDIRECT //AyaNova.Biz.BizObjectNameFetcherDIRECT
//and in the CLIENT in ayatype.js //and in the CLIENT in ayatype.js
//and need TRANSLATION KEYS because any type could show in the event log at teh client end //and need TRANSLATION KEYS because any type could show in the event log at the client end
} }

View File

@@ -366,7 +366,7 @@ namespace AyaNova.Biz
{ {
Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminFull, Change = AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminFull,
//Anyone can read it because they need to to open a form, but also in UI //Anyone can read it because they need to to open a form, but also in UI
//only the bizadminlimited actually gets a link to see teh customization page //only the bizadminlimited actually gets a link to see the customization page
ReadFullRecord = AuthorizationRoles.All ReadFullRecord = AuthorizationRoles.All
}); });
@@ -390,7 +390,7 @@ namespace AyaNova.Biz
//Only BizAdminFull can modify forms //Only BizAdminFull can modify forms
Change = AuthorizationRoles.BizAdminFull, Change = AuthorizationRoles.BizAdminFull,
//Anyone can read it because they need to to open a form, but also in UI //Anyone can read it because they need to to open a form, but also in UI
//only the bizadminlimited actually gets a link to see teh customization page //only the bizadminlimited actually gets a link to see the customization page
ReadFullRecord = AuthorizationRoles.All ReadFullRecord = AuthorizationRoles.All
}); });