This commit is contained in:
@@ -471,6 +471,7 @@ namespace AyaNova.Api.Controllers
|
|||||||
//TODO: notify OPSNOTIFY
|
//TODO: notify OPSNOTIFY
|
||||||
//TODO: notify this should trigger some kind of notification to the ops people
|
//TODO: notify this should trigger some kind of notification to the ops people
|
||||||
//and a red light on the dashboard
|
//and a red light on the dashboard
|
||||||
|
//TODO: this should reset the validity
|
||||||
|
|
||||||
var errText = $"Physical file {dbObj.StoredFileName} not found despite attachment record, this file is missing";
|
var errText = $"Physical file {dbObj.StoredFileName} not found despite attachment record, this file is missing";
|
||||||
log.LogError(errText);
|
log.LogError(errText);
|
||||||
|
|||||||
109
server/AyaNova/biz/AttachmentBiz.cs
Normal file
109
server/AyaNova/biz/AttachmentBiz.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using AyaNova.Util;
|
||||||
|
using AyaNova.Api.ControllerHelpers;
|
||||||
|
using AyaNova.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AyaNova.Models;
|
||||||
|
using AyaNova.Biz;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Bogus;
|
||||||
|
using AyaNova.Api.ControllerHelpers;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AyaNova.Biz
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle attachment file related cleanup and checking
|
||||||
|
/// </summary>
|
||||||
|
internal class AttachmentBiz : BizObject, IJobObject
|
||||||
|
{
|
||||||
|
internal AttachmentBiz(AyContext dbcontext, long currentUserId, AuthorizationRoles userRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
CurrentUserRoles = userRoles;
|
||||||
|
BizType = AyaType.FileAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
public async Task HandleJobAsync(OpsJob job)
|
||||||
|
{
|
||||||
|
//Hand off the particular job to the corresponding processing code
|
||||||
|
//NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so
|
||||||
|
//basically any error condition during job processing should throw up an exception if it can't be handled
|
||||||
|
|
||||||
|
//There might be future other job types so doing it like this for all biz job handlers for now
|
||||||
|
switch (job.JobType)
|
||||||
|
{
|
||||||
|
case JobType.AttachmentMaintenance:
|
||||||
|
await ProcessAttachmentMaintenanceAsync(job);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new System.ArgumentOutOfRangeException($"AttachmentBiz.HandleJobAsync -> Invalid job type{job.JobType.ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle the job
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="job"></param>
|
||||||
|
private async Task ProcessAttachmentMaintenanceAsync(OpsJob job)
|
||||||
|
{
|
||||||
|
ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("AttachmentMaintenanceJob");
|
||||||
|
ApiServerState apiServerState = (ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(ApiServerState));
|
||||||
|
|
||||||
|
//get the current server state so can set back to it later
|
||||||
|
ApiServerState.ServerState wasServerState = apiServerState.GetState();
|
||||||
|
string wasReason = apiServerState.Reason;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Running);
|
||||||
|
await JobsBiz.LogJobAsync(job.GId, $"Starting...");
|
||||||
|
apiServerState.SetOpsOnly("Attachment file maintenance");
|
||||||
|
|
||||||
|
//JObject jobData = JObject.Parse(job.JobInfo);
|
||||||
|
// var seedLevel = (Seeder.Level.SeedLevel)jobData["seedLevel"].Value<int>();
|
||||||
|
// var timeZoneOffset = jobData["timeZoneOffset"].Value<decimal>();
|
||||||
|
// var seed = new Util.Seeder();
|
||||||
|
// await seed.SeedDatabaseAsync(seedLevel, job.GId, timeZoneOffset);
|
||||||
|
|
||||||
|
|
||||||
|
await JobsBiz.LogJobAsync(job.GId, "Finished.");
|
||||||
|
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Completed);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.LogError(ex, "AttachmentMaintenanceJob error during ops");
|
||||||
|
|
||||||
|
await JobsBiz.LogJobAsync(job.GId, $"AttachmentMaintenanceJob error during ops\r\n{ex.Message}");
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
log.LogInformation($"AttachmentMaintenanceJob: setting server state back to {wasServerState.ToString()}");
|
||||||
|
apiServerState.SetState(wasServerState, wasReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
@@ -12,7 +12,8 @@ namespace AyaNova.Biz
|
|||||||
CoreJobSweeper = 2,
|
CoreJobSweeper = 2,
|
||||||
SeedTestData = 4,
|
SeedTestData = 4,
|
||||||
BulkCoreBizObjectOperation = 5,
|
BulkCoreBizObjectOperation = 5,
|
||||||
Backup = 6
|
Backup = 6,
|
||||||
|
AttachmentMaintenance=7
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,6 +284,9 @@ namespace AyaNova.Biz
|
|||||||
case JobType.SeedTestData:
|
case JobType.SeedTestData:
|
||||||
o = (IJobObject)BizObjectFactory.GetBizObject(AyaType.TrialSeeder, ct);
|
o = (IJobObject)BizObjectFactory.GetBizObject(AyaType.TrialSeeder, ct);
|
||||||
break;
|
break;
|
||||||
|
case JobType.AttachmentMaintenance:
|
||||||
|
o = (IJobObject)BizObjectFactory.GetBizObject(AyaType.FileAttachment, ct);
|
||||||
|
break;
|
||||||
case JobType.BulkCoreBizObjectOperation:
|
case JobType.BulkCoreBizObjectOperation:
|
||||||
//bulk op, hand off to biz object to deal with
|
//bulk op, hand off to biz object to deal with
|
||||||
//note, convention is that there is an idList in job.jobinfo json if preselected else it's all objects of type
|
//note, convention is that there is an idList in job.jobinfo json if preselected else it's all objects of type
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ namespace AyaNova.Models
|
|||||||
|
|
||||||
public class FileAttachment
|
public class FileAttachment
|
||||||
{
|
{
|
||||||
public FileAttachment(){
|
public FileAttachment()
|
||||||
|
{
|
||||||
//all start out as synchronized
|
//all start out as synchronized
|
||||||
InSync=true;
|
Exists = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public uint Concurrency { get; set; }
|
public uint Concurrency { get; set; }
|
||||||
@@ -30,6 +32,8 @@ namespace AyaNova.Models
|
|||||||
public DateTime LastModified { get; set; }
|
public DateTime LastModified { get; set; }
|
||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
|
|
||||||
public bool InSync {get;set;}//was on disk last sync check
|
[Required]
|
||||||
|
public bool Exists { get; set; }//was on disk last sync check
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace AyaNova.Util
|
|||||||
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!!
|
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!!
|
||||||
private const int DESIRED_SCHEMA_LEVEL = 11;
|
private const int DESIRED_SCHEMA_LEVEL = 11;
|
||||||
|
|
||||||
internal const long EXPECTED_COLUMN_COUNT = 326;
|
internal const long EXPECTED_COLUMN_COUNT = 327;
|
||||||
internal const long EXPECTED_INDEX_COUNT = 134;
|
internal const long EXPECTED_INDEX_COUNT = 134;
|
||||||
|
|
||||||
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!!
|
//!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!!
|
||||||
@@ -268,9 +268,9 @@ namespace AyaNova.Util
|
|||||||
//await ExecQueryAsync("CREATE INDEX asearchkey_typeid_idx ON asearchkey (objectid, objecttype );");
|
//await ExecQueryAsync("CREATE INDEX asearchkey_typeid_idx ON asearchkey (objectid, objecttype );");
|
||||||
|
|
||||||
//This is what is needed during Searching
|
//This is what is needed during Searching
|
||||||
//search does a lot of hits on searchkey looking for the wordid and optionally objecttype
|
//search does a lot of hits on searchkey looking for the wordid and optionally objecttype
|
||||||
//In testing this did not pan out, in fact it was much faster to search both with and without a objecttype specified to simply have an index on wordid
|
//In testing this did not pan out, in fact it was much faster to search both with and without a objecttype specified to simply have an index on wordid
|
||||||
// await ExecQueryAsync("CREATE INDEX asearchkey_wordid_otype_idx ON asearchkey (wordid, objecttype);");
|
// await ExecQueryAsync("CREATE INDEX asearchkey_wordid_otype_idx ON asearchkey (wordid, objecttype);");
|
||||||
await ExecQueryAsync("CREATE INDEX asearchkey_wordid_idx ON asearchkey (wordid);");
|
await ExecQueryAsync("CREATE INDEX asearchkey_wordid_idx ON asearchkey (wordid);");
|
||||||
|
|
||||||
//Search indexing stored procedure
|
//Search indexing stored procedure
|
||||||
@@ -391,7 +391,7 @@ $BODY$;
|
|||||||
|
|
||||||
await ExecQueryAsync("CREATE TABLE afileattachment (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " +
|
await ExecQueryAsync("CREATE TABLE afileattachment (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " +
|
||||||
"attachtoobjectid bigint not null, attachtoobjecttype integer not null, " +
|
"attachtoobjectid bigint not null, attachtoobjecttype integer not null, " +
|
||||||
"storedfilename text not null, displayfilename text not null, contenttype text, lastmodified timestamp not null, notes text, insync bool)");
|
"storedfilename text not null, displayfilename text not null, contenttype text, lastmodified timestamp not null, notes text, exists bool not null)");
|
||||||
|
|
||||||
//index required for ops that need to check if file already in db (delete, count refs etc)
|
//index required for ops that need to check if file already in db (delete, count refs etc)
|
||||||
//LOOKAT: isn't this useless without the ID as well or is that not fetched?
|
//LOOKAT: isn't this useless without the ID as well or is that not fetched?
|
||||||
|
|||||||
@@ -424,13 +424,13 @@ namespace AyaNova.Util
|
|||||||
/// <param name="fileAttachmentToBeDeleted"></param>
|
/// <param name="fileAttachmentToBeDeleted"></param>
|
||||||
/// <param name="ct"></param>
|
/// <param name="ct"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal static async Task<FileAttachment> DeleteFileAttachmentAsync(FileAttachment fileAttachmentToBeDeleted, AyContext ct)
|
internal static async Task DeleteFileAttachmentAsync(FileAttachment fileAttachmentToBeDeleted, AyContext ct)
|
||||||
{
|
{
|
||||||
|
|
||||||
//check ref count of file
|
//check ref count of file
|
||||||
var count = await ct.FileAttachment.LongCountAsync(z => z.StoredFileName == fileAttachmentToBeDeleted.StoredFileName);
|
var count = await ct.FileAttachment.LongCountAsync(z => z.StoredFileName == fileAttachmentToBeDeleted.StoredFileName);
|
||||||
|
|
||||||
//Store in DB
|
//Remove from the DB
|
||||||
ct.FileAttachment.Remove(fileAttachmentToBeDeleted);
|
ct.FileAttachment.Remove(fileAttachmentToBeDeleted);
|
||||||
await ct.SaveChangesAsync();
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@ namespace AyaNova.Util
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Return AyFileInfo object
|
//Return AyFileInfo object
|
||||||
return fileAttachmentToBeDeleted;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user