This commit is contained in:
2020-12-08 23:50:54 +00:00
parent a211470b54
commit dd6d8f8b76
11 changed files with 139 additions and 83 deletions

View File

@@ -30,3 +30,4 @@ Here are all the API level error codes that can be returned by the API server:
| 2207 | Validation error - The start date must be earlier than the end date | | 2207 | Validation error - The start date must be earlier than the end date |
| 2208 | Validation error - Modifying the object (usually a delete) would break the link to other records in the database and operation was disallowed to preserve data integrity | | 2208 | Validation error - Modifying the object (usually a delete) would break the link to other records in the database and operation was disallowed to preserve data integrity |
| 2209 | Validation error - Indicates the attempted property change is invalid because the value is fixed and cannot be changed | | 2209 | Validation error - Indicates the attempted property change is invalid because the value is fixed and cannot be changed |
| 2210 | Child object error - Indicates the attempted operation resulted in errors in linked child object |

View File

@@ -27,7 +27,8 @@ namespace AyaNova.Biz
VALIDATION_NOT_UNIQUE = 2206, VALIDATION_NOT_UNIQUE = 2206,
VALIDATION_STARTDATE_AFTER_ENDDATE = 2207, VALIDATION_STARTDATE_AFTER_ENDDATE = 2207,
VALIDATION_REFERENTIAL_INTEGRITY = 2208, VALIDATION_REFERENTIAL_INTEGRITY = 2208,
VALIDATION_NOT_CHANGEABLE = 2209 VALIDATION_NOT_CHANGEABLE = 2209,
CHILD_OBJECT_ERROR = 2210
/* /*
| 2000 | API closed - Server is running but access to the API has been closed to all users | | 2000 | API closed - Server is running but access to the API has been closed to all users |

View File

@@ -54,6 +54,8 @@ namespace AyaNova.Biz
return "Modifying the object (usually a delete) would break the link to other records in the database and operation was disallowed to preserve data integrity"; return "Modifying the object (usually a delete) would break the link to other records in the database and operation was disallowed to preserve data integrity";
case ApiErrorCode.VALIDATION_NOT_CHANGEABLE: case ApiErrorCode.VALIDATION_NOT_CHANGEABLE:
return "the value is fixed and cannot be changed"; return "the value is fixed and cannot be changed";
case ApiErrorCode.CHILD_OBJECT_ERROR:
return "Errors in child object during operation";

View File

@@ -62,6 +62,12 @@ namespace AyaNova.Biz
_errors.Add(new ValidationError() { Code = errorCode, Message = errorMessage, Target = propertyName }); _errors.Add(new ValidationError() { Code = errorCode, Message = errorMessage, Target = propertyName });
} }
// //Add a bunch of errors, generally from a child object failed operastion
// public void AddErrors(List<ValidationError> errors)
// {
// _errors.AddRange(errors);
// }
public string GetErrorsAsString() public string GetErrorsAsString()
{ {
if (!HasErrors) return string.Empty; if (!HasErrors) return string.Empty;

View File

@@ -151,7 +151,8 @@ namespace AyaNova.Biz
try try
{ {
Customer dbObject = await ct.Customer.SingleOrDefaultAsync(z => z.Id == id); Customer dbObject = await ct.Customer.SingleOrDefaultAsync(z => z.Id == id);
if (dbObject == null){ if (dbObject == null)
{
AddError(ApiErrorCode.NOT_FOUND); AddError(ApiErrorCode.NOT_FOUND);
return false; return false;
} }
@@ -159,10 +160,38 @@ namespace AyaNova.Biz
if (HasErrors) if (HasErrors)
return false; return false;
//todo: delete contacts and customer notes
//see workorder* collections and delete for transactional inclusion etc //DELETE DIRECT CHILD OBJECTS
//(note: the convention is to allow deletion of children created *in* the same UI area so this will delete contacts, customer notes, but not workorders of this customer for example)
//Will need to possibly display refintegrity error from the *linked* object delete attemp, i.e. Contact for here //Will need to possibly display refintegrity error from the *linked* object delete attemp, i.e. Contact for here
//so should test that out so that it flows to the UI so user understands they can't delete the Customer due to a Contact having links to other objects //so should test that out so that it flows to the UI so user understands they can't delete the Customer due to a Contact having links to other objects
{
var ContactIds = await ct.User.AsNoTracking().Where(z => z.CustomerId == id).Select(z => z.Id).ToListAsync();
if (ContactIds.Count() > 0)
{
UserBiz b = new UserBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
foreach (long ItemId in ContactIds)
if (!await b.DeleteAsync(ItemId, transaction))
{
AddError(ApiErrorCode.VALIDATION_REQUIRED,b.GetErrorsAsString());
return false;
}
}
}
{
var CustomerNoteIds = await ct.CustomerNote.AsNoTracking().Where(z => z.CustomerId == id).Select(z => z.Id).ToListAsync();
if (CustomerNoteIds.Count() > 0)
{
CustomerNoteBiz b = new CustomerNoteBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
foreach (long ItemId in CustomerNoteIds)
if (!await b.DeleteAsync(ItemId, transaction))
{
AddErrors(b.Errors);
return false;
}
}
}
ct.Customer.Remove(dbObject); ct.Customer.Remove(dbObject);
await ct.SaveChangesAsync(); await ct.SaveChangesAsync();
@@ -177,6 +206,7 @@ namespace AyaNova.Biz
} }
catch catch
{ {
//NOTE: no need to rollback the transaction, it will auto-rollback if not committed and it is disposed when it goes out of scope either way
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here //Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
throw; throw;

View File

@@ -109,39 +109,45 @@ namespace AyaNova.Biz
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE //DELETE
// //
internal async Task<bool> DeleteAsync(long id) internal async Task<bool> DeleteAsync(long id, Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction parentTransaction = null)
{ {
using (var transaction = await ct.Database.BeginTransactionAsync()) //this may be part of a larger delete operation involving other objects (e.g. Customer delete and remove contacts)
//if so then there will be a parent transaction otherwise we make our own
Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = null;
if (parentTransaction == null)
transaction = await ct.Database.BeginTransactionAsync();
try
{ {
try CustomerNote dbObject = await ct.CustomerNote.SingleOrDefaultAsync(m => m.Id == id);
if (dbObject == null)
{ {
CustomerNote dbObject = await ct.CustomerNote.SingleOrDefaultAsync(m => m.Id == id); AddError(ApiErrorCode.NOT_FOUND);
if (dbObject == null){ return false;
AddError(ApiErrorCode.NOT_FOUND); }
return false; if (HasErrors)
} return false;
if (HasErrors) if (HasErrors)
return false; return false;
if (HasErrors) ct.CustomerNote.Remove(dbObject);
return false; await ct.SaveChangesAsync();
ct.CustomerNote.Remove(dbObject);
await ct.SaveChangesAsync();
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "CustomerNote", ct); await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "CustomerNote", ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct); await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct);
await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct);
//all good do the commit if it's ours
if (parentTransaction == null)
await transaction.CommitAsync(); await transaction.CommitAsync();
// await NotifyEventProcessor.HandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject);
}
catch
{
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
throw;
}
return true;
} }
catch
{
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
throw;
}
return true;
} }
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -442,65 +442,71 @@ namespace AyaNova.Biz
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE //DELETE
// //
internal async Task<bool> DeleteAsync(long id) internal async Task<bool> DeleteAsync(long id, Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction parentTransaction = null)
{ {
//this may be part of a larger delete operation involving other objects (e.g. Customer delete and remove contacts)
using (var transaction = await ct.Database.BeginTransactionAsync()) //if so then there will be a parent transaction otherwise we make our own
Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = null;
if (parentTransaction == null)
transaction = await ct.Database.BeginTransactionAsync();
try
{ {
try User dbObject = await ct.User.SingleOrDefaultAsync(z => z.Id == id);
if (dbObject == null)
{ {
User dbObject = await ct.User.SingleOrDefaultAsync(z => z.Id == id); AddError(ApiErrorCode.NOT_FOUND);
if (dbObject == null) return false;
{ }
AddError(ApiErrorCode.NOT_FOUND);
return false;
}
//Also used for Contacts (customer type user or ho type user) //Also used for Contacts (customer type user or ho type user)
//by users with no User right but with Customer rights so need to double check here //by users with no User right but with Customer rights so need to double check here
if ( if (
(dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.Customer)) || (dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.Customer)) ||
(!dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.User)) (!dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.User))
) )
{ {
AddError(ApiErrorCode.NOT_AUTHORIZED); AddError(ApiErrorCode.NOT_AUTHORIZED);
return false; return false;
} }
await ValidateCanDelete(dbObject); await ValidateCanDelete(dbObject);
if (HasErrors) if (HasErrors)
return false; return false;
//Delete sibling objects //Delete sibling objects
//USEROPTIONS //USEROPTIONS
await ct.Database.ExecuteSqlInterpolatedAsync($"delete from auseroptions where userid = {dbObject.Id}"); await ct.Database.ExecuteSqlInterpolatedAsync($"delete from auseroptions where userid = {dbObject.Id}");
//NOTIFY SUBSCRIPTIONS //NOTIFY SUBSCRIPTIONS
await ct.Database.ExecuteSqlInterpolatedAsync($"delete from anotifysubscription where userid = {dbObject.Id}"); await ct.Database.ExecuteSqlInterpolatedAsync($"delete from anotifysubscription where userid = {dbObject.Id}");
//personal datalistview //personal datalistview
await ct.Database.ExecuteSqlInterpolatedAsync($"delete from adatalistview where public = {false} and userid = {dbObject.Id}"); await ct.Database.ExecuteSqlInterpolatedAsync($"delete from adatalistview where public = {false} and userid = {dbObject.Id}");
//Dashboard view //Dashboard view
await ct.Database.ExecuteSqlInterpolatedAsync($"delete from adashboardview where userid = {dbObject.Id}"); await ct.Database.ExecuteSqlInterpolatedAsync($"delete from adashboardview where userid = {dbObject.Id}");
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct); //Remove the object
await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct); ct.User.Remove(dbObject);
await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); await ct.SaveChangesAsync();
await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct);
//Remove the object await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct);
ct.User.Remove(dbObject); await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct);
await ct.SaveChangesAsync(); await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct);
//all good do the commit if it's ours
if (parentTransaction == null)
await transaction.CommitAsync(); await transaction.CommitAsync();
await NotifyEventProcessor.HandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject); await NotifyEventProcessor.HandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject);
}
catch
{
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
throw;
}
return true;
} }
catch
{
//Just re-throw for now, let exception handler deal, but in future may want to deal with this more here
//no need to rollback, the transaction will rollback automatically if it's disposed without committing
throw;
}
//Note: Transaction does not need to be disposed it will automatically when it goes out of scope due to Using statement
return true;
} }

View File

@@ -1616,6 +1616,7 @@
"ErrorAPI2207": "Das Startdatum muss vor dem Enddatum liegen", "ErrorAPI2207": "Das Startdatum muss vor dem Enddatum liegen",
"ErrorAPI2208": "Dieses Objekt ist mit anderen verknüpft und kann auf diese Weise nicht geändert werden", "ErrorAPI2208": "Dieses Objekt ist mit anderen verknüpft und kann auf diese Weise nicht geändert werden",
"ErrorAPI2209": "Dieser Wert kann nicht geändert werden", "ErrorAPI2209": "Dieser Wert kann nicht geändert werden",
"ErrorAPI2210": "Untergeordneter Objektfehler",
"ErrorServerUnresponsive": "Der Server reagiert nicht (E17)", "ErrorServerUnresponsive": "Der Server reagiert nicht (E17)",
"ErrorUserNotAuthenticated": "Nicht authentifiziert (E16)", "ErrorUserNotAuthenticated": "Nicht authentifiziert (E16)",
"ErrorUserNotAuthorized": "Nicht autorisiert", "ErrorUserNotAuthorized": "Nicht autorisiert",

View File

@@ -1616,6 +1616,7 @@
"ErrorAPI2207": "Start date must be before end date", "ErrorAPI2207": "Start date must be before end date",
"ErrorAPI2208": "This object is linked to others and can not be changed this way", "ErrorAPI2208": "This object is linked to others and can not be changed this way",
"ErrorAPI2209": "This value cannot be changed", "ErrorAPI2209": "This value cannot be changed",
"ErrorAPI2210": "Child object error",
"ErrorServerUnresponsive": "Server not responding (E17)", "ErrorServerUnresponsive": "Server not responding (E17)",
"ErrorUserNotAuthenticated": "Not authenticated (E16)", "ErrorUserNotAuthenticated": "Not authenticated (E16)",
"ErrorUserNotAuthorized": "Not authorized", "ErrorUserNotAuthorized": "Not authorized",

View File

@@ -1616,6 +1616,7 @@
"ErrorAPI2207": "La fecha de inicio debe ser anterior a la fecha de finalización", "ErrorAPI2207": "La fecha de inicio debe ser anterior a la fecha de finalización",
"ErrorAPI2208": "Este objeto está vinculado a otros y no puede ser cambiado de esta manera", "ErrorAPI2208": "Este objeto está vinculado a otros y no puede ser cambiado de esta manera",
"ErrorAPI2209": "Este valor no puede ser cambiado", "ErrorAPI2209": "Este valor no puede ser cambiado",
"ErrorAPI2210": "Error de objeto secundario",
"ErrorServerUnresponsive": "El servidor no responde (E17)", "ErrorServerUnresponsive": "El servidor no responde (E17)",
"ErrorUserNotAuthenticated": "No autenticado (E16)", "ErrorUserNotAuthenticated": "No autenticado (E16)",
"ErrorUserNotAuthorized": "No autorizado", "ErrorUserNotAuthorized": "No autorizado",

View File

@@ -1616,6 +1616,7 @@
"ErrorAPI2207": "La date de début doit être antérieure à la date de fin", "ErrorAPI2207": "La date de début doit être antérieure à la date de fin",
"ErrorAPI2208": "Cet objet est lié à d'autres et ne peut pas être modifié de cette façon", "ErrorAPI2208": "Cet objet est lié à d'autres et ne peut pas être modifié de cette façon",
"ErrorAPI2209": "Cette valeur ne peut pas être changée", "ErrorAPI2209": "Cette valeur ne peut pas être changée",
"ErrorAPI2210": "Erreur d'objet enfant",
"ErrorServerUnresponsive": "Le serveur ne répond pas (E17)", "ErrorServerUnresponsive": "Le serveur ne répond pas (E17)",
"ErrorUserNotAuthenticated": "Non authentifié (E16)", "ErrorUserNotAuthenticated": "Non authentifié (E16)",
"ErrorUserNotAuthorized": "Non autorisé", "ErrorUserNotAuthorized": "Non autorisé",