diff --git a/server/AyaNova/Controllers/WorkOrderController.cs b/server/AyaNova/Controllers/WorkOrderController.cs index c4786a90..5f6dad02 100644 --- a/server/AyaNova/Controllers/WorkOrderController.cs +++ b/server/AyaNova/Controllers/WorkOrderController.cs @@ -41,80 +41,52 @@ namespace AyaNova.Api.Controllers #region WorkOrder top level routes - /// - /// Create Workorder + /// Create WorkOrder "header" /// - /// + /// WorkOrder top level only, no descendents are evaluated /// Automatically filled from route path, no need to specify in body - /// + /// WorkOrder "header" object [HttpPost] public async Task PostWorkOrder([FromBody] WorkOrder newObject, ApiVersion apiVersion) { 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 WorkOrder 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)); - } - - //TODO: CreateFromTemplate(templateid) - //TODO: Createfromquote(quoteid) - //todo: createfrompm(pmid) - - - - /// /// Duplicate WorkOrder + /// (Wiki and Attachments are not duplicated) /// - /// Create a duplicate of this items id + /// Source object id /// Automatically filled from route path, no need to specify in body - /// + /// Full WorkOrder object including all descendents [HttpPost("duplicate/{id}")] public async Task DuplicateWorkOrder([FromRoute] long id, ApiVersion apiVersion) { 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)); - - // var oSrc = await biz.GetAsync(id, false); - // if (oSrc == null) - // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); - - //Create and validate WorkOrder o = await biz.DuplicateAsync(id); 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)); - } @@ -122,48 +94,31 @@ namespace AyaNova.Api.Controllers /// Get full WorkOrder object /// /// - /// A single WorkOrder + /// Entire WorkOrder object including all descendents [HttpGet("{id}")] public async Task GetWorkOrder([FromRoute] long id) { if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); - - //Instantiate the business object handler WorkOrderBiz biz = WorkOrderBiz.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 - //NO, NOT HERE, in biz object surely? - + if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); return Ok(ApiOkResponse.Response(o, !Authorized.HasModifyRole(HttpContext.Items, biz.BizType))); } - //TODO: GET BY RELATIVE - //get by descendent type and id - - - /// /// Put (update) WorkOrder - /// This route does not update the entire WorkOrder object graph, only the top level - /// in other words nothing in any descendent child collections such as WorkOrderItems and below. - /// Use the separate routes to update descendents of the WorkOrder + /// This route does not evaluate nor update nor return the entire WorkOrder object graph + /// only the "header" or top level + /// Descendent objects must be updated via their individual routes seperately /// /// WorkOrder id - /// Workorder top level only, no descendents are evaluated + /// WorkOrder top level only, no descendents are evaluated /// New concurrency token [HttpPut("{id}")] public async Task PutWorkOrder([FromRoute] long id, [FromBody] WorkOrder updatedObject) @@ -187,82 +142,55 @@ namespace AyaNova.Api.Controllers } +//TODO: will need to traverse, don't need it now for PROPOSAL testing so not coding it yet + // /// + // /// Delete WorkOrder + // /// + // /// + // /// Ok + // [HttpDelete("{id}")] + // public async Task DeleteWorkOrder([FromRoute] long id) + // { + // if (!serverState.IsOpen) + // return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); + // if (!ModelState.IsValid) + // return BadRequest(new ApiErrorResponse(ModelState)); + // //Instantiate the business object handler + // WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext); + // var o = await biz.GetAsync(id, false); + // if (o == null) + // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); + // if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) + // return StatusCode(403, new ApiNotAuthorizedResponse()); + // if (!await biz.DeleteAsync(o)) + // return BadRequest(new ApiErrorResponse(biz.Errors)); - /// - /// Delete WorkOrder - /// - /// - /// Ok - [HttpDelete("{id}")] - public async Task DeleteWorkOrder([FromRoute] long id) - { - if (!serverState.IsOpen) - return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); - - if (!ModelState.IsValid) - return BadRequest(new ApiErrorResponse(ModelState)); - - //Instantiate the business object handler - WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext); - - var o = await biz.GetAsync(id, false); - if (o == null) - return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); - - if (!Authorized.HasDeleteRole(HttpContext.Items, biz.BizType)) - return StatusCode(403, new ApiNotAuthorizedResponse()); - - if (!await biz.DeleteAsync(o)) - return BadRequest(new ApiErrorResponse(biz.Errors)); - - return NoContent(); - } - - #endregion WorkorderTopLevel routes - - #region WorkOrder Item - //TODO: Plot exact routes needed here, not all are needed, particularly the collection ones - //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/{woitemid} <- CRUD single woitemid - //https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design#define-operations-in-terms-of-http-methods - /* - Resource POST GET PUT DELETE - /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/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 - //GET PUT and DELETE on a collecdtion always mean the entire collection, POST is the outlier here - + // return NoContent(); + // } + #endregion WorkOrderTopLevel routes + #region WorkOrderItem /// /// Create WorkOrderItem /// - /// + /// WorkOrderItem level only no descendents /// Automatically filled from route path, no need to specify in body - /// + /// WorkOrderItem object (no descendents) [HttpPost("items")] public async Task 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)) + if (!Authorized.HasCreateRole(HttpContext.Items, AyaType.WorkOrderItem)) return StatusCode(403, new ApiNotAuthorizedResponse()); if (!ModelState.IsValid) @@ -370,8 +298,8 @@ namespace AyaNova.Api.Controllers { //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/{WorkOrderId}/WorkorderItems + //WorkOrder/{woid}/WorkOrderItems <- all workorderitems, post to add new, put to update all as a collection + //WorkOrder/{WorkOrderId}/WorkOrderItems if (!serverState.IsOpen) return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason)); @@ -393,7 +321,7 @@ namespace AyaNova.Api.Controllers // 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)) + // if (!o.WorkOrderItems.Exists(m => m.Id == workOrderItemId)) // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); @@ -558,13 +486,13 @@ namespace AyaNova.Api.Controllers // if (o == null) // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); - // //Get WorkorderItem - // var woitem = o.WorkorderItems.FirstOrDefault(m => m.Id == workOrderItemId); + // //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); + // var woitemlabor = woitem.WorkOrderItemLabors.FirstOrDefault(m => m.Id == workOrderItemLaborId); // if (woitem == null) // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); @@ -729,7 +657,7 @@ namespace AyaNova.Api.Controllers // 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)) + // if (!o.WorkOrderItems.Exists(m => m.Id == workOrderItemId)) // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index 4c8a8092..f293c8a5 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -94,6 +94,9 @@ namespace AyaNova.Biz // internal async Task GetAsync(long id, bool logTheGetEvent = true) { + //Note: there could be rules checking here in future, i.e. can only get own workorder or something + //if so, then need to implement AddError and in route handle Null return with Error check just like PUT route does now + //https://docs.microsoft.com/en-us/ef/core/querying/related-data //docs say this will not query twice but will recognize the duplicate woitem bit which is required for multiple grandchild collections var ret =