diff --git a/server/AyaNova/Controllers/AttachmentController.cs b/server/AyaNova/Controllers/AttachmentController.cs index 5da0efc0..964e1ddd 100644 --- a/server/AyaNova/Controllers/AttachmentController.cs +++ b/server/AyaNova/Controllers/AttachmentController.cs @@ -471,6 +471,7 @@ namespace AyaNova.Api.Controllers //TODO: notify OPSNOTIFY //TODO: notify this should trigger some kind of notification to the ops people //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"; log.LogError(errText); diff --git a/server/AyaNova/biz/AttachmentBiz.cs b/server/AyaNova/biz/AttachmentBiz.cs new file mode 100644 index 00000000..09217c3c --- /dev/null +++ b/server/AyaNova/biz/AttachmentBiz.cs @@ -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 +{ + + + /// + /// Handle attachment file related cleanup and checking + /// + 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()}"); + } + } + + + /// + /// Handle the job + /// + /// + 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(); + // var timeZoneOffset = jobData["timeZoneOffset"].Value(); + // 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 + diff --git a/server/AyaNova/biz/JobType.cs b/server/AyaNova/biz/JobType.cs index 1a02f375..bc97402c 100644 --- a/server/AyaNova/biz/JobType.cs +++ b/server/AyaNova/biz/JobType.cs @@ -12,7 +12,8 @@ namespace AyaNova.Biz CoreJobSweeper = 2, SeedTestData = 4, BulkCoreBizObjectOperation = 5, - Backup = 6 + Backup = 6, + AttachmentMaintenance=7 } diff --git a/server/AyaNova/biz/JobsBiz.cs b/server/AyaNova/biz/JobsBiz.cs index 6c60c35e..46e2d0bf 100644 --- a/server/AyaNova/biz/JobsBiz.cs +++ b/server/AyaNova/biz/JobsBiz.cs @@ -284,6 +284,9 @@ namespace AyaNova.Biz case JobType.SeedTestData: o = (IJobObject)BizObjectFactory.GetBizObject(AyaType.TrialSeeder, ct); break; + case JobType.AttachmentMaintenance: + o = (IJobObject)BizObjectFactory.GetBizObject(AyaType.FileAttachment, ct); + break; case JobType.BulkCoreBizObjectOperation: //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 diff --git a/server/AyaNova/models/FileAttachment.cs b/server/AyaNova/models/FileAttachment.cs index 4b7908c8..7ca4f76f 100644 --- a/server/AyaNova/models/FileAttachment.cs +++ b/server/AyaNova/models/FileAttachment.cs @@ -7,9 +7,11 @@ namespace AyaNova.Models public class FileAttachment { - public FileAttachment(){ + public FileAttachment() + { //all start out as synchronized - InSync=true; + Exists = true; + } public long Id { get; set; } public uint Concurrency { get; set; } @@ -30,6 +32,8 @@ namespace AyaNova.Models public DateTime LastModified { 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 + } } \ No newline at end of file diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index aec0604b..2e5db660 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -22,7 +22,7 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! 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; //!!!!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 );"); //This is what is needed during Searching - //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 - // await ExecQueryAsync("CREATE INDEX asearchkey_wordid_otype_idx ON asearchkey (wordid, 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 + // await ExecQueryAsync("CREATE INDEX asearchkey_wordid_otype_idx ON asearchkey (wordid, objecttype);"); await ExecQueryAsync("CREATE INDEX asearchkey_wordid_idx ON asearchkey (wordid);"); //Search indexing stored procedure @@ -391,7 +391,7 @@ $BODY$; await ExecQueryAsync("CREATE TABLE afileattachment (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " + "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) //LOOKAT: isn't this useless without the ID as well or is that not fetched? diff --git a/server/AyaNova/util/FileUtil.cs b/server/AyaNova/util/FileUtil.cs index c33834fc..ea52351a 100644 --- a/server/AyaNova/util/FileUtil.cs +++ b/server/AyaNova/util/FileUtil.cs @@ -424,13 +424,13 @@ namespace AyaNova.Util /// /// /// - internal static async Task DeleteFileAttachmentAsync(FileAttachment fileAttachmentToBeDeleted, AyContext ct) + internal static async Task DeleteFileAttachmentAsync(FileAttachment fileAttachmentToBeDeleted, AyContext ct) { //check ref count of file var count = await ct.FileAttachment.LongCountAsync(z => z.StoredFileName == fileAttachmentToBeDeleted.StoredFileName); - //Store in DB + //Remove from the DB ct.FileAttachment.Remove(fileAttachmentToBeDeleted); await ct.SaveChangesAsync(); @@ -448,7 +448,7 @@ namespace AyaNova.Util } //Return AyFileInfo object - return fileAttachmentToBeDeleted; + return; }